Skip to content

Commit

Permalink
feature: Button collections (#10)
Browse files Browse the repository at this point in the history
* Deprecate SelectionDialog in favour of button collections

* ButtonStacks too

* inline all the things

* Add ScheduledForRemoval to SelectionDialog
  • Loading branch information
lucyydotp committed Sep 13, 2023
1 parent 67c4a02 commit 8aa1c5d
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.layouts.Layout
import net.minecraft.client.gui.layouts.LinearLayout
import net.minecraft.network.chat.Component
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
import kotlin.math.max

/**
Expand All @@ -38,6 +39,8 @@ import kotlin.math.max
*
* @param T a context object. This object is passed to entries when called.
*/
@Deprecated("Use button collections (GridLayout.iconButtonRow and ButtonStack) directly")
@ScheduledForRemoval(inVersion = "1.0.0")
public class SelectionDialog<out T>(
x: Int, y: Int,
private val entries: List<Entry<T>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public class GridLayoutBuilder(
private val layout = GridLayout(x, y).spacing(spacing)
public override fun build(): GridLayout = layout

private var lastRow = -1
@PublishedApi
internal var lastRow: Int = -1

/**
* Adds an element at a specific row and column.
Expand All @@ -43,6 +44,12 @@ public class GridLayoutBuilder(
return this
}

/**
* Adds an element at a specific row and column
* @see at
*/
public operator fun <T: LayoutElement> set(row: Int, col: Int, value: T): T = value.at(row, col)

/**
* Adds an element on a new row at the bottom of the grid.
*
Expand Down
33 changes: 33 additions & 0 deletions api/src/main/kotlin/com/noxcrew/sheeplib/layout/IconButtonRow.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.noxcrew.sheeplib.layout

import com.noxcrew.sheeplib.theme.Themed
import com.noxcrew.sheeplib.util.TextButtonCollectionBuilder
import com.noxcrew.sheeplib.widget.ThemedButton

/**
* Populates a [row] with small square buttons, or a new row at the bottom of the grid if not specified.
* Each button will occupy its own column, staring from [firstColumn].
*
* Buttons should contain a single character, possibly a Unicode symbol/emoji.
* They may have hover text, which will be shown when the button is hovered over, as per [ThemedButton].
*/
public inline fun GridLayoutBuilder.iconButtonRow(
theme: Themed,
row: Int = lastRow + 1,
firstColumn: Int = 0,
builder: TextButtonCollectionBuilder<() -> Unit>.() -> Unit
): Int {
// +1 works better for icons with an odd width
val width = theme.theme.dimensions.buttonHeight + 1
val buttons = TextButtonCollectionBuilder<() -> Unit>().apply(builder)
buttons.elements.forEachIndexed { index, (text, action) ->
ThemedButton(
text,
width = width,
centreText = true,
scrollText = false,
clickHandler = action
).at(row, firstColumn + index)
}
return buttons.elements.size
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.noxcrew.sheeplib.util

import com.noxcrew.sheeplib.layout.LayoutBuilderDsl
import net.minecraft.network.chat.Component

/**
* A DSL object that can be used to build collections of labelled buttons.
* @param T the type of callback for each button
*/
@LayoutBuilderDsl
public class TextButtonCollectionBuilder<T : Function<*>> {

@PublishedApi
internal val elements: MutableList<Pair<Component, T>> = mutableListOf()

/**
* Adds a button with string literal text.
* @param callback the function to execute when the button is clicked
*/
public infix fun String.literal(callback: T): Unit =
Component.literal(this) runs callback

/**
* Adds a button with translatable text with the given key.
* @param callback the function to execute when the button is clicked
*/
public infix fun String.runs(callback: T): Unit =
Component.translatable(this) runs callback

/**
* Adds a button with some given text.
* @param callback the function to execute when the button is clicked
*/
public infix fun Component.runs(callback: T) {
elements += this to callback
}
}
81 changes: 81 additions & 0 deletions api/src/main/kotlin/com/noxcrew/sheeplib/widget/ButtonStack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.noxcrew.sheeplib.widget

import com.noxcrew.sheeplib.CompoundWidget
import com.noxcrew.sheeplib.layout.AdjustableLinearLayout
import com.noxcrew.sheeplib.theme.StaticColorReference
import com.noxcrew.sheeplib.theme.Theme
import com.noxcrew.sheeplib.theme.Themed
import com.noxcrew.sheeplib.theme.ThemedColorReference
import com.noxcrew.sheeplib.util.TextButtonCollectionBuilder
import com.noxcrew.sheeplib.util.withOuterPadding
import net.minecraft.client.gui.layouts.Layout
import net.minecraft.client.gui.layouts.LinearLayout
import net.minecraft.network.chat.Component

/**
* A vertical stack of uniform buttons, ordered from top to bottom.
*
* @param width the width the widget, and of each button
* @param lineHeight the height of a single button
* @param theme the buttons' theme
* @param entries a list of text entries and callback handlers to create buttons from
*/
public class ButtonStack(
width: Int,
lineHeight: Int,
theme: Themed,
entries: List<Pair<Component, () -> Unit>>
) : CompoundWidget(0, 0, width, lineHeight * entries.size),
Themed by theme {

private companion object {
private val buttonStyle = Theme.ButtonStyle(
StaticColorReference(0),
ThemedColorReference.WIDGET_BACKGROUND_SECONDARY,
ThemedColorReference.WIDGET_BACKGROUND_SECONDARY,
)
}

override val layout: Layout = AdjustableLinearLayout(
0,
lineHeight * entries.size,
LinearLayout.Orientation.VERTICAL,
0,
) {
entries.forEach { (text, action) ->
+ThemedButton(
text,
withOuterPadding(theme.theme, 0),
width = width,
height = lineHeight,
style = buttonStyle,
clickHandler = action
)
}
}

init {
layout.arrangeElements()
layout.visitWidgets(this::addChild)
}
}

/**
* A vertical stack of uniform buttons, ordered from top to bottom.
*
* @param width the width the widget, and of each button
* @param lineHeight the height of a single button
* @param theme the buttons' theme
* @param builder a builder for buttons
*/
public inline fun ButtonStack(
theme: Themed,
width: Int,
lineHeight: Int,
builder: (TextButtonCollectionBuilder<() -> Unit>).() -> Unit
): ButtonStack = ButtonStack(
width,
lineHeight,
theme,
TextButtonCollectionBuilder<() -> Unit>().apply(builder).elements
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.noxcrew.sheeplib.testmod

import com.noxcrew.sheeplib.dialog.Dialog
import com.noxcrew.sheeplib.dialog.title.DialogTitleWidget
import com.noxcrew.sheeplib.dialog.title.TextTitleWidget
import com.noxcrew.sheeplib.layout.grid
import com.noxcrew.sheeplib.layout.iconButtonRow
import com.noxcrew.sheeplib.theme.Theme
import com.noxcrew.sheeplib.theme.Themed
import com.noxcrew.sheeplib.util.ComponentBuilder
import com.noxcrew.sheeplib.widget.ButtonStack
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.layouts.Layout
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TextColor

/**
* A dialog showcasing button collection builders.
*/
public class ButtonCollectionsDialog(x: Int, y: Int) : Dialog(x, y), Themed by Theme.Active {

private companion object {

private val letters = listOf("A", "B", "C", "D", "E", "F")

/** Adds a message to the chat window. */
private fun pushChatMessage(message: String) {
Minecraft.getInstance().gui.chat.addMessage(Component.literal(message))
}
}

override val title: DialogTitleWidget = TextTitleWidget(this, Component.literal("Button Collections"))

override fun layout(): Layout = grid {
val buttonCount = iconButtonRow(this@ButtonCollectionsDialog) {
letters.forEach {
it literal { pushChatMessage("Icon button $it pressed") }
}
}
ButtonStack(this@ButtonCollectionsDialog, 100, Minecraft.getInstance().font.lineHeight + 2) {
letters.forEach {
ComponentBuilder.literal("Button ") {
+ComponentBuilder.literal(it) {
color = TextColor.fromRgb(0x00ff00)
}
} runs { pushChatMessage("Text button $it pressed") }
}
}.atBottom(0, buttonCount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public object SheepLibTestMod : ClientModInitializer {
"popup" to ::PopupDialog,
"example" to ::MyFirstDialog,
"text" to ::textInputDialog,
"progress" to ::ProgressDialog
"progress" to ::ProgressDialog,
"buttons" to ::ButtonCollectionsDialog
)


Expand Down

0 comments on commit 8aa1c5d

Please sign in to comment.