Cacao: More docs
This commit is contained in:
parent
500ad94442
commit
2c19b8456b
|
@ -0,0 +1,14 @@
|
|||
package net.shadowfacts.phycon.mixin.client;
|
||||
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
@Mixin(TextFieldWidget.class)
|
||||
public interface TextFieldWidgetAccessor {
|
||||
@Accessor("maxLength")
|
||||
int cacao_getMaxLength();
|
||||
}
|
|
@ -24,14 +24,15 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
|
|||
private val _windows = LinkedList<Window>()
|
||||
/**
|
||||
* The list of windows that belong to this screen.
|
||||
*
|
||||
* The window at the end of this list is the active window is the only window that will receive input events.
|
||||
*
|
||||
* This list should never be modified directly, only by using the [addWindow]/[removeWindow] methods.
|
||||
*/
|
||||
override val windows: List<Window> = _windows
|
||||
|
||||
/**
|
||||
* Adds the given window to this screen's window list.
|
||||
* By default, the new window is added at the tail of the window list, making it the active window.
|
||||
* Only the active window will receive input events.
|
||||
* Adds the given window to this screen's window list at the given position.
|
||||
*
|
||||
* @param window The Window to add to this screen.
|
||||
* @param index The index to insert the window into the window list at.
|
||||
|
@ -47,6 +48,9 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
|
|||
return window
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given window to the end of this screen's window list, making it the active window.
|
||||
*/
|
||||
override fun <T : Window> addWindow(window: T): T {
|
||||
return addWindow(window, _windows.size)
|
||||
}
|
||||
|
|
|
@ -4,17 +4,42 @@ import net.shadowfacts.cacao.util.KeyModifiers
|
|||
import net.shadowfacts.cacao.window.Window
|
||||
|
||||
/**
|
||||
* A responder is an object which participates in the responder chain, a mechanism for propagating indirect events (i.e.
|
||||
* events for which there is no on-screen position to determine the targeted view, such as key presses) through the
|
||||
* view hierarchy.
|
||||
*
|
||||
* @author shadowfacts
|
||||
*/
|
||||
interface Responder {
|
||||
|
||||
/**
|
||||
* The window that this responder is part of.
|
||||
*/
|
||||
val window: Window?
|
||||
|
||||
/**
|
||||
* Whether this responder is the first responder of its window.
|
||||
*
|
||||
* `false` if [window] is null.
|
||||
*
|
||||
* @see Window.firstResponder
|
||||
*/
|
||||
val isFirstResponder: Boolean
|
||||
get() = window?.firstResponder === this
|
||||
|
||||
/**
|
||||
* The next responder in the chain after this. The next responder will receive an event if this responder did not
|
||||
* accept it.
|
||||
*
|
||||
* The next responder may be `null` if this responder is at the end of the chain.
|
||||
*/
|
||||
val nextResponder: Responder?
|
||||
|
||||
/**
|
||||
* Makes this responder become the window's first responder.
|
||||
* @throws RuntimeException if [window] is null
|
||||
* @see Window.firstResponder
|
||||
*/
|
||||
fun becomeFirstResponder() {
|
||||
if (window == null) {
|
||||
throw RuntimeException("Cannot become first responder while not in Window")
|
||||
|
@ -22,8 +47,17 @@ interface Responder {
|
|||
window!!.firstResponder = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after this responder has become the window's first responder.
|
||||
* @see Window.firstResponder
|
||||
*/
|
||||
fun didBecomeFirstResponder() {}
|
||||
|
||||
/**
|
||||
* Removes this object as the window's first responder.
|
||||
* @throws RuntimeException if [window] is null
|
||||
* @see Window.firstResponder
|
||||
*/
|
||||
fun resignFirstResponder() {
|
||||
if (window == null) {
|
||||
throw RuntimeException("Cannot resign first responder while not in Window")
|
||||
|
@ -31,14 +65,36 @@ interface Responder {
|
|||
window!!.firstResponder = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately before this object is removed as the window's first responder.
|
||||
* @see Window.firstResponder
|
||||
*/
|
||||
fun didResignFirstResponder() {}
|
||||
|
||||
/**
|
||||
* Called when a character has been typed.
|
||||
*
|
||||
* @param char The character that was typed.
|
||||
* @param modifiers The key modifiers that were held down when the character was typed.
|
||||
* @return Whether this responder accepted the event. If `true`, it will not be passed to the next responder.
|
||||
*/
|
||||
fun charTyped(char: Char, modifiers: KeyModifiers): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a keyboard key is pressed.
|
||||
*
|
||||
* If the pressed key is a typed character, [charTyped] will also be called. The order in which the methods are
|
||||
* invoked is undefined and should not be relied upon.
|
||||
*
|
||||
* @param keyCode The integer code of the key that was pressed.
|
||||
* @param modifiers The key modifiers that were held down when the character was typed.
|
||||
* @return Whether this responder accepted the event. If `true`, it will not be passed to the next responder.
|
||||
* @see org.lwjgl.glfw.GLFW for key code constants
|
||||
*/
|
||||
fun keyPressed(keyCode: Int, modifiers: KeyModifiers): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ package net.shadowfacts.cacao.util
|
|||
import org.lwjgl.glfw.GLFW
|
||||
|
||||
/**
|
||||
* A set of modifier keys that are being pressed at given instant.
|
||||
*
|
||||
* @author shadowfacts
|
||||
* @param value The integer representation of the pressed modifiers as received from GLFW.
|
||||
*/
|
||||
class KeyModifiers(val value: Int) {
|
||||
|
||||
|
@ -29,4 +32,4 @@ class KeyModifiers(val value: Int) {
|
|||
return (value and mod) == mod
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ open class View(): Responder {
|
|||
*/
|
||||
override var window: Window? = null
|
||||
|
||||
/**
|
||||
* The next responder after this one.
|
||||
* For views, the next responder is the view's superview.
|
||||
*/
|
||||
override val nextResponder: Responder?
|
||||
// todo: should the view controller be a Responder?
|
||||
get() = superview
|
||||
|
@ -39,7 +43,7 @@ open class View(): Responder {
|
|||
}
|
||||
}
|
||||
/**
|
||||
* The constraint solver used by the [net.shadowfacts.cacao.Window] this view belongs to.
|
||||
* The constraint solver used by the [Window] this view belongs to.
|
||||
* Not initialized until [wasAdded] called, using it before that will throw a runtime exception.
|
||||
*/
|
||||
var solver: Solver by solverDelegate
|
||||
|
@ -189,7 +193,9 @@ open class View(): Responder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes the given view from this view's children and removes all constraints associated with it.
|
||||
* Removes the given view from this view's children and removes all constraints that connect the subview or any of
|
||||
* its children (recursively) to a view outside of the subview's hierarchy. Constraints internal to the subview's
|
||||
* hierarchy (e.g., one between the subview and its child) will be left in place.
|
||||
*
|
||||
* @param view The view to removed as a child of this view.
|
||||
* @throws RuntimeException If the given [view] is not a subview of this view.
|
||||
|
@ -199,7 +205,6 @@ open class View(): Responder {
|
|||
throw RuntimeException("Cannot remove subview whose superview is not this view")
|
||||
}
|
||||
|
||||
|
||||
_subviews.remove(view)
|
||||
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
|
||||
|
||||
|
@ -350,7 +355,13 @@ open class View(): Responder {
|
|||
open fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {}
|
||||
|
||||
/**
|
||||
* Called when this view is clicked. May delegate to [subviews].
|
||||
* Called when this view is clicked.
|
||||
*
|
||||
* The base implementation of this method forwards the click event to the first subview (sorted by [zIndex]) that
|
||||
* contains the clicked point. Additionally, any subviews of this view that do not contain the clicked point receive
|
||||
* the [mouseClickedOutside] event. If multiple views contain the point, any after one that returns `true` from this
|
||||
* method will not receive the event or the click-outside event.
|
||||
*
|
||||
* If overridden, the super-class method does not have to be called. Intentionally not calling it may be used
|
||||
* to prevent [subviews] from receiving click events.
|
||||
*
|
||||
|
@ -373,6 +384,14 @@ open class View(): Responder {
|
|||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse was clicked outside this view.
|
||||
*
|
||||
* The base implementation of this method simply forwards the event to all of this view's subviews.
|
||||
*
|
||||
* @param point The clicked point _in the coordinate space of this view_.
|
||||
* @param mouseButton The mouse button used to click.
|
||||
*/
|
||||
open fun mouseClickedOutside(point: Point, mouseButton: MouseButton) {
|
||||
for (view in subviews) {
|
||||
val pointInView = convert(point, to = view)
|
||||
|
|
|
@ -10,8 +10,8 @@ import net.shadowfacts.cacao.view.View
|
|||
import net.shadowfacts.kiwidsl.dsl
|
||||
|
||||
/**
|
||||
* A abstract button class. Cannot be constructed directly, used for creating button implementations with their own logic.
|
||||
* Use [Button] for a generic no-frills button.
|
||||
* An abstract button class. Cannot be constructed directly, used for creating button implementations with their own
|
||||
* logic. Use [Button] for a generic no-frills button.
|
||||
*
|
||||
* @author shadowfacts
|
||||
* @param Impl The type of the concrete implementation of the button.
|
||||
|
|
|
@ -8,9 +8,14 @@ import net.shadowfacts.cacao.view.View
|
|||
* @author shadowfacts
|
||||
* @param content The [View] that provides the content of this button.
|
||||
* @param padding The padding between the [content] and the edges of the button.
|
||||
* @param handler The handler function to invoke when this button is pressed.
|
||||
*/
|
||||
class Button(content: View, padding: Double = 4.0): AbstractButton<Button>(content, padding) {
|
||||
constructor(content: View, padding: Double = 4.0, handler: (Button) -> Unit): this(content, padding) {
|
||||
class Button(
|
||||
content: View,
|
||||
padding: Double = 4.0,
|
||||
handler: ((Button) -> Unit)? = null
|
||||
): AbstractButton<Button>(content, padding) {
|
||||
init {
|
||||
this.handler = handler
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import net.shadowfacts.cacao.view.Label
|
|||
*
|
||||
* @author shadowfacts
|
||||
* @param initialValue The initial enum value for this button.
|
||||
* @param localizer A function that takes an enum value and converts into a string for the button's label.
|
||||
* @param localizer A function that takes an enum value and converts into a [Text] for the button's label.
|
||||
*/
|
||||
class EnumButton<E: Enum<E>>(
|
||||
initialValue: E,
|
||||
|
|
|
@ -10,26 +10,58 @@ import net.shadowfacts.cacao.util.KeyModifiers
|
|||
import net.shadowfacts.cacao.util.MouseButton
|
||||
import net.shadowfacts.cacao.util.RenderHelper
|
||||
import net.shadowfacts.cacao.view.View
|
||||
import net.shadowfacts.phycon.mixin.client.TextFieldWidgetAccessor
|
||||
|
||||
/**
|
||||
* An abstract text field class. Cannot be constructed directly, use for creating other text fields with more specific
|
||||
* behavior. Use [TextField] for a plain text field.
|
||||
*
|
||||
* @author shadowfacts
|
||||
* @param Impl The type of the concrete implementation of the text field. Used to allow the [handler] to receive the
|
||||
* the exact type of text field.
|
||||
* @param initialText The initial value of the text field.
|
||||
*/
|
||||
abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
|
||||
initialText: String
|
||||
): View() {
|
||||
|
||||
/**
|
||||
* A function that is invoked when the text in this text field changes.
|
||||
*/
|
||||
var handler: ((Impl) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* Whether the text field is disabled.
|
||||
* Disabled text fields cannot be interacted with.
|
||||
*/
|
||||
var disabled = false
|
||||
|
||||
/**
|
||||
* Whether this text field is focused (i.e. [isFirstResponder]) and receives key events.
|
||||
*/
|
||||
val focused: Boolean
|
||||
get() = isFirstResponder
|
||||
|
||||
/**
|
||||
* The current text of this text field.
|
||||
*/
|
||||
var text: String
|
||||
get() = minecraftWidget.text
|
||||
set(value) {
|
||||
minecraftWidget.text = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length of text that this text field can hold.
|
||||
*
|
||||
* Defaults to the Minecraft text field's maximum length (currently 32, subject to change).
|
||||
*/
|
||||
var maxLength: Int
|
||||
get() = (minecraftWidget as TextFieldWidgetAccessor).cacao_getMaxLength()
|
||||
set(value) {
|
||||
minecraftWidget.setMaxLength(value)
|
||||
}
|
||||
|
||||
private lateinit var originInWindow: Point
|
||||
private var minecraftWidget = ProxyWidget()
|
||||
|
||||
|
@ -38,6 +70,9 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
|
|||
minecraftWidget.setTextPredicate { this.validate(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function used by subclasses to determine whether a proposed value is acceptable for this field.
|
||||
*/
|
||||
abstract fun validate(proposedText: String): Boolean
|
||||
|
||||
override fun didLayout() {
|
||||
|
@ -118,7 +153,7 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
|
|||
}
|
||||
|
||||
// todo: label for the TextFieldWidget?
|
||||
class ProxyWidget: TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 0, 0, LiteralText("")) {
|
||||
private class ProxyWidget: TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 0, 0, LiteralText("")) {
|
||||
// AbstractButtonWidget.height is protected
|
||||
fun setHeight(height: Int) {
|
||||
this.height = height
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package net.shadowfacts.cacao.view.textfield
|
||||
|
||||
/**
|
||||
* A simple, no-frills text field. This text field accepts any value.
|
||||
*
|
||||
* @author shadowfacts
|
||||
* @param initialText The initial value of this text field.
|
||||
* @param handler A function that is invoked when the value of the text field changes.
|
||||
*/
|
||||
class TextField(initialText: String): AbstractTextField<TextField>(initialText) {
|
||||
constructor(initialText: String, handler: (TextField) -> Unit): this(initialText) {
|
||||
class TextField(
|
||||
initialText: String,
|
||||
handler: ((TextField) -> Unit)? = null
|
||||
): AbstractTextField<TextField>(initialText) {
|
||||
init {
|
||||
this.handler = handler
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import net.shadowfacts.cacao.geometry.Axis
|
|||
import net.shadowfacts.cacao.geometry.Point
|
||||
import net.shadowfacts.cacao.geometry.Rect
|
||||
import net.shadowfacts.cacao.geometry.Size
|
||||
import net.shadowfacts.cacao.util.Color
|
||||
import net.shadowfacts.cacao.util.MouseButton
|
||||
import net.shadowfacts.cacao.util.texture.NinePatchTexture
|
||||
import net.shadowfacts.cacao.util.texture.Texture
|
||||
|
@ -17,22 +16,69 @@ import net.shadowfacts.cacao.view.TextureView
|
|||
import net.shadowfacts.cacao.view.View
|
||||
import net.shadowfacts.cacao.view.button.AbstractButton
|
||||
import net.shadowfacts.kiwidsl.dsl
|
||||
import java.lang.RuntimeException
|
||||
|
||||
/**
|
||||
* A tab view controller is divided into two sections: a tab bar at the top, and a content view at the bottom.
|
||||
*
|
||||
* The tab bar contains a tab button for each of the tabs in the VC and the content view contains the view of the
|
||||
* active tab's view controller.
|
||||
*
|
||||
* The active tab's view controller is also added as a child of the tab view controller.
|
||||
*
|
||||
* @author shadowfacts
|
||||
* @param T The type of the tab objects this view controller uses.
|
||||
* @param tabs The list of tabs in this controller.
|
||||
* @param initialTab The tab that is initially selected when the controller is first created.
|
||||
* @param onTabChange A function invoked immediately after the active tab has changed (and the content view has been
|
||||
* updated).
|
||||
*/
|
||||
class TabViewController<T: TabViewController.Tab>(
|
||||
val tabs: List<T>,
|
||||
initalTab: T = tabs.first()
|
||||
initialTab: T = tabs.first(),
|
||||
val onTabChange: ((T) -> Unit)? = null
|
||||
): ViewController() {
|
||||
|
||||
/**
|
||||
* The Tab interface defines the requirements for tab objects that can be used with this view controller.
|
||||
*
|
||||
* This is an interface to allow for tab objects to carry additional data. A simple implementation is provided.
|
||||
* @see SimpleTab
|
||||
*/
|
||||
interface Tab {
|
||||
/**
|
||||
* The view displayed on the button for this tab.
|
||||
*/
|
||||
val tabView: View
|
||||
|
||||
/**
|
||||
* The tooltip displayed when the button for this tab is hovered. `null` if no tooltip should be shown.
|
||||
*/
|
||||
val tooltip: Text?
|
||||
|
||||
/**
|
||||
* The view controller used as content when this tab is active. When switching tabs, the returned content VC
|
||||
* may be reused or created from scratch each time.
|
||||
*/
|
||||
val controller: ViewController
|
||||
}
|
||||
|
||||
var currentTab: T = initalTab
|
||||
/**
|
||||
* A simple [Tab] implementation that provides the minimum necessary information.
|
||||
* @param tabView The view to display on the tab's button.
|
||||
* @param tooltip The tooltip to display when the tab's button is hovered (or `null`, if none).
|
||||
* @param controller The content view controller for this tab.
|
||||
*/
|
||||
class SimpleTab(
|
||||
override val tabView: View,
|
||||
override val tooltip: Text? = null,
|
||||
override val controller: ViewController,
|
||||
): Tab
|
||||
|
||||
/**
|
||||
* The currently selected tab.
|
||||
*/
|
||||
var currentTab: T = initialTab
|
||||
private set
|
||||
|
||||
private lateinit var tabButtons: List<TabButton<T>>
|
||||
|
@ -57,7 +103,7 @@ class TabViewController<T: TabViewController.Tab>(
|
|||
|
||||
tabButtons = tabs.mapIndexed { index, tab ->
|
||||
val btn = TabButton(tab)
|
||||
btn.handler = this::selectTab
|
||||
btn.handler = { selectTab(it.tab) }
|
||||
if (tab == currentTab) {
|
||||
btn.setSelected(true)
|
||||
}
|
||||
|
@ -91,16 +137,30 @@ class TabViewController<T: TabViewController.Tab>(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectTab(button: TabButton<T>) {
|
||||
/**
|
||||
* Sets the provided tab as the currently active tab for this controller. Updates the state of tab bar buttons and
|
||||
* swaps the content view controller.
|
||||
*
|
||||
* After the tab and the content are changed, [onTabChange] is invoked.
|
||||
*
|
||||
* @throws RuntimeException If the provided tab was not passed in as part of the [tabs] list.
|
||||
*/
|
||||
fun selectTab(tab: T) {
|
||||
if (!tabs.contains(tab)) {
|
||||
throw RuntimeException("Cannot activate tab not in TabViewController.tabs")
|
||||
}
|
||||
|
||||
val oldTab = currentTab
|
||||
currentTab = button.tab
|
||||
currentTab = tab
|
||||
|
||||
// todo: unselect old button
|
||||
tabButtons.forEach { it.setSelected(false) }
|
||||
tabButtons.forEach {
|
||||
it.setSelected(it.tab === tab)
|
||||
}
|
||||
oldTab.controller.removeFromParent()
|
||||
|
||||
button.setSelected(true)
|
||||
embedChild(currentTab.controller, tabVCContainer)
|
||||
|
||||
onTabChange?.invoke(currentTab)
|
||||
|
||||
// todo: setNeedsLayout
|
||||
window!!.layout()
|
||||
}
|
||||
|
|
|
@ -76,6 +76,9 @@ abstract class ViewController {
|
|||
view = View()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the view for this controller has already been loaded.
|
||||
*/
|
||||
val isViewLoaded: Boolean
|
||||
get() = ::view.isInitialized
|
||||
|
||||
|
|
|
@ -77,6 +77,18 @@ open class Window(
|
|||
*/
|
||||
val centerYAnchor = Variable("centerY")
|
||||
|
||||
/**
|
||||
* The first responder of the a window is the first object that receives indirect events (e.g., keypresses).
|
||||
*
|
||||
* When an indirect event is received by the window, it is given to the first responder. If the first responder does
|
||||
* not accept it (i.e. returns `false` from the appropriate method), the event will be passed to that responder's
|
||||
* [Responder.nextResponder], and so on.
|
||||
*
|
||||
* The following is the order of events when setting this property:
|
||||
* 1. The old first responder (if any) has [Responder.didResignFirstResponder] invoked.
|
||||
* 2. The value of the field is updated.
|
||||
* 3. The new value (if any) has [Responder.didBecomeFirstResponder] invoked.
|
||||
*/
|
||||
var firstResponder: Responder? = null
|
||||
set(value) {
|
||||
field?.didResignFirstResponder()
|
||||
|
@ -183,6 +195,13 @@ open class Window(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a tooltip containing the given lines at the mouse pointer location.
|
||||
*
|
||||
* Implementation note: the tooltip is not drawn immediately, it is done after the window is done drawing all of its
|
||||
* views. This is done to prevent other views from being drawn in front of the tooltip. Additionally, more than one
|
||||
* tooltip cannot be drawn in a frame as they would appear at the same position.
|
||||
*/
|
||||
fun drawTooltip(text: List<Text>) {
|
||||
if (currentDeferredTooltip != null) {
|
||||
throw RuntimeException("Deferred tooltip already registered for current frame")
|
||||
|
@ -199,6 +218,7 @@ open class Window(
|
|||
* @return Whether the mouse click was handled by a view.
|
||||
*/
|
||||
fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
||||
// todo: isn't this always true?
|
||||
if (point in viewController.view.frame) {
|
||||
val mouseInView = Point(point.x - viewController.view.frame.left, point.y - viewController.view.frame.top)
|
||||
return viewController.view.mouseClicked(mouseInView, mouseButton)
|
||||
|
|
|
@ -24,78 +24,78 @@ import net.shadowfacts.kiwidsl.dsl
|
|||
class TestCacaoScreen: CacaoScreen() {
|
||||
|
||||
init {
|
||||
val viewController = object: ViewController() {
|
||||
override fun loadView() {
|
||||
view = View()
|
||||
}
|
||||
|
||||
override fun viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
val stack = view.addSubview(StackView(Axis.VERTICAL, StackView.Distribution.CENTER, spacing = 4.0)).apply {
|
||||
backgroundColor = Color.WHITE
|
||||
}
|
||||
val birch = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16))).apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
}
|
||||
val ninePatch = stack.addArrangedSubview(NinePatchView(NinePatchTexture.PANEL_BG)).apply {
|
||||
intrinsicContentSize = Size(75.0, 100.0)
|
||||
}
|
||||
val red = stack.addArrangedSubview(View()).apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
backgroundColor = Color.RED
|
||||
}
|
||||
|
||||
val label = Label(LiteralText("Test"), wrappingMode = Label.WrappingMode.NO_WRAP).apply {
|
||||
// textColor = Color.BLACK
|
||||
}
|
||||
// stack.addArrangedSubview(label)
|
||||
val button = red.addSubview(Button(label))
|
||||
|
||||
val field = TextField("Test") {
|
||||
println("new value: ${it.text}")
|
||||
}
|
||||
stack.addArrangedSubview(field)
|
||||
|
||||
view.solver.dsl {
|
||||
stack.topAnchor equalTo 0
|
||||
stack.centerXAnchor equalTo window!!.centerXAnchor
|
||||
stack.widthAnchor equalTo 100
|
||||
|
||||
|
||||
button.centerXAnchor equalTo red.centerXAnchor
|
||||
button.centerYAnchor equalTo red.centerYAnchor
|
||||
// label.heightAnchor equalTo 9
|
||||
button.heightAnchor equalTo 20
|
||||
|
||||
field.widthAnchor equalTo stack.widthAnchor
|
||||
field.heightAnchor equalTo 20
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
addWindow(Window(viewController))
|
||||
|
||||
// val viewController = object: ViewController() {
|
||||
// override fun loadView() {
|
||||
// view = View()
|
||||
// }
|
||||
//
|
||||
// override fun viewDidLoad() {
|
||||
// super.viewDidLoad()
|
||||
//
|
||||
// val tabs = arrayOf(
|
||||
// Tab(Label("A"), AViewController()),
|
||||
// Tab(Label("B"), BViewController()),
|
||||
// )
|
||||
// val tabVC = TabViewController(tabs)
|
||||
// embedChild(tabVC, pinEdges = false)
|
||||
// val stack = view.addSubview(StackView(Axis.VERTICAL, StackView.Distribution.CENTER, spacing = 4.0)).apply {
|
||||
// backgroundColor = Color.WHITE
|
||||
// }
|
||||
// val birch = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16))).apply {
|
||||
// intrinsicContentSize = Size(50.0, 50.0)
|
||||
// }
|
||||
// val ninePatch = stack.addArrangedSubview(NinePatchView(NinePatchTexture.PANEL_BG)).apply {
|
||||
// intrinsicContentSize = Size(75.0, 100.0)
|
||||
// }
|
||||
// val red = stack.addArrangedSubview(View()).apply {
|
||||
// intrinsicContentSize = Size(50.0, 50.0)
|
||||
// backgroundColor = Color.RED
|
||||
// }
|
||||
//
|
||||
// val label = Label(LiteralText("Test"), wrappingMode = Label.WrappingMode.NO_WRAP).apply {
|
||||
//// textColor = Color.BLACK
|
||||
// }
|
||||
//// stack.addArrangedSubview(label)
|
||||
// val button = red.addSubview(Button(label))
|
||||
//
|
||||
// val field = TextField("Test") {
|
||||
// println("new value: ${it.text}")
|
||||
// }
|
||||
// stack.addArrangedSubview(field)
|
||||
//
|
||||
// view.solver.dsl {
|
||||
// tabVC.view.centerXAnchor equalTo view.centerXAnchor
|
||||
// tabVC.view.centerYAnchor equalTo view.centerYAnchor
|
||||
// tabVC.view.widthAnchor equalTo 200
|
||||
// tabVC.view.heightAnchor equalTo 150
|
||||
// stack.topAnchor equalTo 0
|
||||
// stack.centerXAnchor equalTo window!!.centerXAnchor
|
||||
// stack.widthAnchor equalTo 100
|
||||
//
|
||||
//
|
||||
// button.centerXAnchor equalTo red.centerXAnchor
|
||||
// button.centerYAnchor equalTo red.centerYAnchor
|
||||
//// label.heightAnchor equalTo 9
|
||||
// button.heightAnchor equalTo 20
|
||||
//
|
||||
// field.widthAnchor equalTo stack.widthAnchor
|
||||
// field.heightAnchor equalTo 20
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// addWindow(Window(viewController))
|
||||
|
||||
val viewController = object: ViewController() {
|
||||
override fun viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
val tabs = listOf(
|
||||
Tab(Label("A"), AViewController(), LiteralText("Tab A")),
|
||||
Tab(Label("B"), BViewController(), LiteralText("Tab B")),
|
||||
)
|
||||
val tabVC = TabViewController(tabs)
|
||||
embedChild(tabVC, pinEdges = false)
|
||||
|
||||
view.solver.dsl {
|
||||
tabVC.view.centerXAnchor equalTo view.centerXAnchor
|
||||
tabVC.view.centerYAnchor equalTo view.centerYAnchor
|
||||
tabVC.view.widthAnchor equalTo 200
|
||||
tabVC.view.heightAnchor equalTo 150
|
||||
}
|
||||
}
|
||||
}
|
||||
addWindow(Window(viewController))
|
||||
}
|
||||
|
||||
data class Tab(
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
package net.shadowfacts.phycon.screen.console
|
||||
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TranslatableText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.shadowfacts.cacao.CacaoScreen
|
||||
import net.shadowfacts.cacao.geometry.Rect
|
||||
import net.shadowfacts.cacao.geometry.Size
|
||||
import net.shadowfacts.cacao.util.Color
|
||||
import net.shadowfacts.cacao.util.texture.Texture
|
||||
import net.shadowfacts.cacao.view.Label
|
||||
import net.shadowfacts.cacao.view.TextureView
|
||||
import net.shadowfacts.cacao.view.View
|
||||
import net.shadowfacts.cacao.viewcontroller.TabViewController
|
||||
import net.shadowfacts.cacao.viewcontroller.ViewController
|
||||
import net.shadowfacts.cacao.window.Window
|
||||
import net.shadowfacts.kiwidsl.dsl
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.network.component.ActivationController
|
||||
|
@ -28,18 +24,18 @@ class DeviceConsoleScreen(
|
|||
val device: DeviceBlockEntity,
|
||||
): CacaoScreen(TranslatableText("item.phycon.console")) {
|
||||
|
||||
private val tabController: TabViewController<Tab>
|
||||
private val tabController: TabViewController<TabViewController.SimpleTab>
|
||||
|
||||
init {
|
||||
val tabs = mutableListOf(
|
||||
Tab(
|
||||
TabViewController.SimpleTab(
|
||||
Label("IP").apply { textColor = Color.TEXT },
|
||||
TranslatableText("gui.phycon.console.details"),
|
||||
DeviceDetailsViewController(device)
|
||||
)
|
||||
)
|
||||
if (device is ActivationController.ActivatableDevice) {
|
||||
tabs.add(Tab(
|
||||
tabs.add(TabViewController.SimpleTab(
|
||||
TextureView(Texture(Identifier("textures/item/ender_pearl.png"), 0, 0, 16, 16)).apply {
|
||||
intrinsicContentSize = Size(16.0, 16.0)
|
||||
},
|
||||
|
@ -48,7 +44,7 @@ class DeviceConsoleScreen(
|
|||
))
|
||||
}
|
||||
if (device is RedstoneControllerBlockEntity) {
|
||||
tabs.add(Tab(
|
||||
tabs.add(TabViewController.SimpleTab(
|
||||
TextureView(Texture(Identifier("textures/block/redstone_torch.png"), 0, 0, 16, 16)).apply {
|
||||
intrinsicContentSize = Size(16.0, 16.0)
|
||||
},
|
||||
|
@ -85,10 +81,4 @@ class DeviceConsoleScreen(
|
|||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
data class Tab(
|
||||
override val tabView: View,
|
||||
override val tooltip: Text?,
|
||||
override val controller: ViewController,
|
||||
): TabViewController.Tab
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
"package": "net.shadowfacts.phycon.mixin.client",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinHandledScreen"
|
||||
"MixinHandledScreen",
|
||||
"TextFieldWidgetAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue