From f321b2a06a3d311e89f9170ec4c2f0c8c2b18125 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 4 Mar 2021 19:44:31 -0500 Subject: [PATCH] Add interface priority syncing --- .../net/shadowfacts/cacao/view/Label.kt | 44 ++++++++++++----- .../kotlin/net/shadowfacts/cacao/view/View.kt | 2 +- .../cacao/view/button/ToggleButton.kt | 44 +++++++++++++---- .../cacao/viewcontroller/TabViewController.kt | 43 +++++++++++++++-- .../netinterface/InterfaceBlockEntity.kt | 3 ++ .../phycon/block/terminal/TerminalBlock.kt | 24 +++++++++- .../screen/console/ProviderViewController.kt | 48 ++++++++++++++++++- .../screen/console/ReceiverViewController.kt | 45 ++++++++++++++++- .../resources/assets/phycon/lang/en_us.json | 2 + 9 files changed, 224 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/Label.kt b/src/main/kotlin/net/shadowfacts/cacao/view/Label.kt index a9a5ca6..f76bc3f 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/Label.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/Label.kt @@ -10,6 +10,7 @@ import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.RenderHelper +import kotlin.math.min /** * A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used @@ -26,10 +27,10 @@ import net.shadowfacts.cacao.util.RenderHelper */ class Label( text: Text, - val shadow: Boolean = false, + var shadow: Boolean = false, val maxLines: Int = 0, val wrappingMode: WrappingMode = WrappingMode.WRAP, - val textAlignment: TextAlignment = TextAlignment.LEFT + var textAlignment: TextAlignment = TextAlignment.LEFT ): View() { companion object { @@ -59,7 +60,8 @@ class Label( var text: Text = text set(value) { field = value - updateIntrinsicContentSize() + // todo: uhhhh + updateIntrinsicContentSize(true) // todo: setNeedsLayout instead of force unwrapping window window!!.layout() } @@ -75,15 +77,23 @@ class Label( override fun wasAdded() { super.wasAdded() - updateIntrinsicContentSize() + updateIntrinsicContentSize(false) } - private fun updateIntrinsicContentSize() { - if (RenderHelper.disabled) return + private fun updateIntrinsicContentSize(canWrap: Boolean): Boolean { + if (RenderHelper.disabled) return false - val width = textRenderer.getWidth(text) - val height = textRenderer.fontHeight - intrinsicContentSize = Size(width.toDouble(), height.toDouble()) + val oldSize = intrinsicContentSize + if (wrappingMode == WrappingMode.WRAP && canWrap) { + val lines = textRenderer.wrapLines(text, bounds.width.toInt()) + val height = (if (maxLines == 0) lines.size else min(lines.size, maxLines)) * textRenderer.fontHeight + intrinsicContentSize = Size(bounds.width, height.toDouble()) + } else { + val width = textRenderer.getWidth(text) + val height = textRenderer.fontHeight + intrinsicContentSize = Size(width.toDouble(), height.toDouble()) + } + return oldSize != intrinsicContentSize } override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) { @@ -110,14 +120,22 @@ class Label( super.didLayout() computeLines() + if (updateIntrinsicContentSize(true)) { + // if the intrinsic content size changes, relayout + window!!.layout() + } } private fun computeLines() { - var lines = textRenderer.wrapLines(text, bounds.width.toInt()) - if (maxLines > 0 && maxLines < lines.size) { - lines = lines.dropLast(lines.size - maxLines) + if (wrappingMode == WrappingMode.WRAP) { + var lines = textRenderer.wrapLines(text, bounds.width.toInt()) + if (maxLines > 0 && maxLines < lines.size) { + lines = lines.dropLast(lines.size - maxLines) + } + this.lines = lines + } else { + this.lines = listOf(text.asOrderedText()) } - this.lines = lines } } diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/View.kt b/src/main/kotlin/net/shadowfacts/cacao/view/View.kt index b58ce42..68c690c 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/View.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/View.kt @@ -114,7 +114,7 @@ open class View(): Responder { * The intrinsic size of this view's content. May be null if the view doesn't have any content or there is no * intrinsic size. * - * Setting this creates/updates [no.birkett.kiwi.Strength.WEAK] constraints on this view's width/height using + * Setting this creates/updates [no.birkett.kiwi.Strength.MEDIUM] constraints on this view's width/height using * the size. */ var intrinsicContentSize: Size? = null diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/button/ToggleButton.kt b/src/main/kotlin/net/shadowfacts/cacao/view/button/ToggleButton.kt index 020b17b..9d766c2 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/button/ToggleButton.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/button/ToggleButton.kt @@ -1,25 +1,31 @@ package net.shadowfacts.cacao.view.button +import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.Identifier import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.texture.Texture import net.shadowfacts.cacao.view.TextureView +import net.shadowfacts.cacao.view.View /** * A button for toggling between on/off states. * * @author shadowfacts * @param initialState Whether the button starts as on or off. + * @param handler The handler function to invoke when this button is pressed. */ -class ToggleButton(initialState: Boolean): AbstractButton(TextureView(if (initialState) ON else OFF).apply { - intrinsicContentSize = Size(19.0, 19.0) -}, padding = 0.0) { +class ToggleButton( + initialState: Boolean, + handler: ((ToggleButton) -> Unit)? = null, +): AbstractButton(TextureView(if (initialState) ON else OFF), padding = 0.0) { companion object { - val ON = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 0) - val OFF = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 19) + val OFF = Texture(Identifier("textures/gui/checkbox.png"), 0, 0, 64, 64) + val OFF_HOVERED = Texture(Identifier("textures/gui/checkbox.png"), 20, 0, 64, 64) + val ON = Texture(Identifier("textures/gui/checkbox.png"), 0, 20, 64, 64) + val ON_HOVERED = Texture(Identifier("textures/gui/checkbox.png"), 20, 20, 64, 64) } private val textureView: TextureView @@ -30,10 +36,15 @@ class ToggleButton(initialState: Boolean): AbstractButton(TextureV * Updating this property updates the button's texture. */ var state: Boolean = initialState - set(value) { - field = value - textureView.texture = if (value) ON else OFF - } + + init { + this.handler = handler + intrinsicContentSize = Size(20.0, 20.0) + + background = null + disabledBackground = null + hoveredBackground = null + } override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean { if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) { @@ -43,4 +54,17 @@ class ToggleButton(initialState: Boolean): AbstractButton(TextureV return super.mouseClicked(point, mouseButton) } -} \ No newline at end of file + override fun draw(matrixStack: MatrixStack, mouse: Point, delta: Float) { + val hovered = mouse in bounds + textureView.texture = if (state) { + if (hovered) ON_HOVERED else ON + } else { + if (hovered) OFF_HOVERED else OFF + } + + super.draw(matrixStack, mouse, delta) + } + + override fun getCurrentBackground(mouse: Point) = null + +} diff --git a/src/main/kotlin/net/shadowfacts/cacao/viewcontroller/TabViewController.kt b/src/main/kotlin/net/shadowfacts/cacao/viewcontroller/TabViewController.kt index c6282f6..e1d4ca0 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/viewcontroller/TabViewController.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/viewcontroller/TabViewController.kt @@ -97,6 +97,7 @@ class TabViewController( private lateinit var outerStack: StackView private lateinit var tabStack: StackView + private lateinit var currentTabController: ViewController // todo: this shouldn't be public, use layout guides lateinit var tabVCContainer: View private set @@ -121,7 +122,11 @@ class TabViewController( tabVCContainer.zIndex = 1.0 view.addSubview(tabVCContainer) - embedChild(currentTab.controller, tabVCContainer) + currentTabController = currentTab.controller + currentTabController.willMoveTo(this) + embedChild(currentTabController, tabVCContainer) + currentTabController.didMoveTo(this) + // will/did appear events for the initial VC are provided by this class' implementations of those view.solver.dsl { outerStack.leftAnchor equalTo view.leftAnchor @@ -136,6 +141,26 @@ class TabViewController( } } + override fun viewWillAppear() { + super.viewWillAppear() + currentTabController.viewWillAppear() + } + + override fun viewDidAppear() { + super.viewDidAppear() + currentTabController.viewDidAppear() + } + + override fun viewWillDisappear() { + super.viewWillDisappear() + currentTabController.viewWillDisappear() + } + + override fun viewDidDisappear() { + super.viewDidDisappear() + currentTabController.viewDidDisappear() + } + private fun updateTabButtons() { while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first()) @@ -186,8 +211,20 @@ class TabViewController( tabButtons.forEach { it.setSelected(it.tab === tab) } - oldTab.controller.removeFromParent() - embedChild(currentTab.controller, tabVCContainer) + currentTabController.viewWillDisappear() + currentTabController.view.removeFromSuperview() + currentTabController.viewDidDisappear() + currentTabController.willMoveTo(null) + currentTabController.removeFromParent() + currentTabController.didMoveTo(null) + + currentTabController = currentTab.controller + + currentTabController.willMoveTo(this) + embedChild(currentTabController, tabVCContainer) + currentTabController.didMoveTo(this) + currentTabController.viewWillAppear() + currentTabController.viewDidAppear() onTabChange?.invoke(currentTab) diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt index 54daefd..12f7d8f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt @@ -34,6 +34,7 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), override var providerPriority = 0 override var receiverPriority = 0 + var syncPriorities = true // todo: should this be a weak ref? private var inventory: GroupedItemInv? = null @@ -121,11 +122,13 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), override fun writeDeviceConfiguration(tag: CompoundTag) { tag.putInt("ProviderPriority", providerPriority) tag.putInt("ReceiverPriority", receiverPriority) + tag.putBoolean("SyncPriorities", syncPriorities) } override fun loadDeviceConfiguration(tag: CompoundTag) { providerPriority = tag.getInt("ProviderPriority") receiverPriority = tag.getInt("ReceiverPriority") + syncPriorities = tag.getBoolean("SyncPriorities") } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlock.kt index 4f8f1a5..0a6ebaa 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlock.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlock.kt @@ -2,13 +2,20 @@ package net.shadowfacts.phycon.block.terminal import alexiil.mc.lib.attributes.AttributeList import alexiil.mc.lib.attributes.AttributeProvider +import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Material import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack +import net.minecraft.server.world.ServerWorld import net.minecraft.sound.BlockSoundGroup +import net.minecraft.state.StateManager +import net.minecraft.state.property.Properties import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.Identifier +import net.minecraft.util.ItemScatterer import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -18,7 +25,7 @@ import net.minecraft.world.WorldAccess import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.block.DeviceBlock -import java.util.* +import java.util.EnumSet /** * @author shadowfacts @@ -33,10 +40,23 @@ class TerminalBlock: DeviceBlock( companion object { val ID = Identifier(PhysicalConnectivity.MODID, "terminal") + val FACING = Properties.FACING } override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection { - return EnumSet.allOf(Direction::class.java) + val set = EnumSet.allOf(Direction::class.java) + set.remove(state[FACING]) + return set + } + + override fun appendProperties(builder: StateManager.Builder) { + super.appendProperties(builder) + builder.add(FACING) + } + + override fun getPlacementState(context: ItemPlacementContext): BlockState { + val facing = if (context.player?.isSneaking == true) context.side else context.playerFacing.opposite + return defaultState.with(FACING, facing) } override fun createBlockEntity(world: BlockView) = TerminalBlockEntity() diff --git a/src/main/kotlin/net/shadowfacts/phycon/screen/console/ProviderViewController.kt b/src/main/kotlin/net/shadowfacts/phycon/screen/console/ProviderViewController.kt index 4387e27..d133859 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/screen/console/ProviderViewController.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/screen/console/ProviderViewController.kt @@ -3,11 +3,16 @@ package net.shadowfacts.phycon.screen.console import net.minecraft.block.entity.BlockEntity import net.minecraft.client.MinecraftClient import net.minecraft.text.TranslatableText +import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.view.Label +import net.shadowfacts.cacao.view.StackView +import net.shadowfacts.cacao.view.View +import net.shadowfacts.cacao.view.button.ToggleButton import net.shadowfacts.cacao.view.textfield.NumberField import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.kiwidsl.dsl +import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.component.NetworkStackProvider import net.shadowfacts.phycon.networking.C2SConfigureDevice @@ -18,6 +23,9 @@ class ProviderViewController( private val device: T ): ViewController() where T: BlockEntity, T: NetworkStackProvider { + private lateinit var field: NumberField + private var syncButton: ToggleButton? = null + override fun viewDidLoad() { super.viewDidLoad() @@ -26,9 +34,14 @@ class ProviderViewController( } view.addSubview(label) - val field = NumberField(device.providerPriority) { + field = NumberField(device.providerPriority) { if (it.number != null) { device.providerPriority = it.number!! + + if (device is InterfaceBlockEntity && device.syncPriorities) { + device.receiverPriority = it.number!! + } + MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) } } @@ -39,6 +52,30 @@ class ProviderViewController( } view.addSubview(desc) + if (device is InterfaceBlockEntity) { + val syncLabel = Label(TranslatableText("gui.phycon.console.provider.sync")).apply { + textColor = Color.TEXT + textAlignment = Label.TextAlignment.RIGHT + } + view.addSubview(syncLabel) + + val syncButton = ToggleButton(device.syncPriorities) { + device.syncPriorities = it.state + device.receiverPriority = device.providerPriority + } + this.syncButton = syncButton + view.addSubview(syncButton) + + view.solver.dsl { + syncButton.topAnchor equalTo (desc.bottomAnchor + 4) + syncButton.leftAnchor equalTo field.leftAnchor + + syncLabel.centerYAnchor equalTo syncButton.centerYAnchor + syncLabel.leftAnchor equalTo view.leftAnchor + syncLabel.rightAnchor equalTo (syncButton.leftAnchor - 4) + } + } + view.solver.dsl { field.widthAnchor equalTo 100 field.heightAnchor equalTo 20 @@ -54,4 +91,13 @@ class ProviderViewController( } } + override fun viewWillAppear() { + super.viewWillAppear() + + field.number = device.providerPriority + if (device is InterfaceBlockEntity) { + syncButton!!.state = device.syncPriorities + } + } + } diff --git a/src/main/kotlin/net/shadowfacts/phycon/screen/console/ReceiverViewController.kt b/src/main/kotlin/net/shadowfacts/phycon/screen/console/ReceiverViewController.kt index 84488a2..9071d99 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/screen/console/ReceiverViewController.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/screen/console/ReceiverViewController.kt @@ -5,9 +5,11 @@ import net.minecraft.client.MinecraftClient import net.minecraft.text.TranslatableText import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.view.Label +import net.shadowfacts.cacao.view.button.ToggleButton import net.shadowfacts.cacao.view.textfield.NumberField import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.kiwidsl.dsl +import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.component.NetworkStackReceiver import net.shadowfacts.phycon.networking.C2SConfigureDevice @@ -18,6 +20,9 @@ class ReceiverViewController( private val device: T ): ViewController() where T: BlockEntity, T: NetworkStackReceiver { + private lateinit var field: NumberField + private var syncButton: ToggleButton? = null + override fun viewDidLoad() { super.viewDidLoad() @@ -26,9 +31,14 @@ class ReceiverViewController( } view.addSubview(label) - val field = NumberField(device.receiverPriority) { + field = NumberField(device.receiverPriority) { if (it.number != null) { device.receiverPriority = it.number!! + + if (device is InterfaceBlockEntity && device.syncPriorities) { + device.providerPriority = it.number!! + } + MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) } } @@ -39,6 +49,30 @@ class ReceiverViewController( } view.addSubview(desc) + if (device is InterfaceBlockEntity) { + val syncLabel = Label(TranslatableText("gui.phycon.console.receiver.sync")).apply { + textColor = Color.TEXT + textAlignment = Label.TextAlignment.RIGHT + } + view.addSubview(syncLabel) + + val syncButton = ToggleButton(device.syncPriorities) { + device.syncPriorities = it.state + device.providerPriority = device.receiverPriority + } + this.syncButton = syncButton + view.addSubview(syncButton) + + view.solver.dsl { + syncButton.topAnchor equalTo (desc.bottomAnchor + 4) + syncButton.leftAnchor equalTo field.leftAnchor + + syncLabel.centerYAnchor equalTo syncButton.centerYAnchor + syncLabel.leftAnchor equalTo view.leftAnchor + syncLabel.rightAnchor equalTo (syncButton.leftAnchor - 4) + } + } + view.solver.dsl { field.widthAnchor equalTo 100 field.heightAnchor equalTo 20 @@ -54,4 +88,13 @@ class ReceiverViewController( } } + override fun viewWillAppear() { + super.viewWillAppear() + + field.number = device.receiverPriority + if (device is InterfaceBlockEntity) { + syncButton!!.state = device.syncPriorities + } + } + } diff --git a/src/main/resources/assets/phycon/lang/en_us.json b/src/main/resources/assets/phycon/lang/en_us.json index 0b4f3bb..0a981fd 100644 --- a/src/main/resources/assets/phycon/lang/en_us.json +++ b/src/main/resources/assets/phycon/lang/en_us.json @@ -24,9 +24,11 @@ "gui.phycon.console.provider": "Item Provider", "gui.phycon.console.provider.priority": "Provider Priority", "gui.phycon.console.provider.priority_desc": "When a device requests items from the network, it send requests to providers (e.g., interfaces) with higher priorities first. Priorities can be negative.", + "gui.phycon.console.provider.sync": "Sync with Receiver Priority", "gui.phycon.console.receiver": "Item Receiver", "gui.phycon.console.receiver.priority": "Receiver Priority", "gui.phycon.console.receiver.priority_desc": "When a device puts items into the network, it starts with receiver (e.g., interfaces) with higher priorities. Priorities can be negative.", + "gui.phycon.console.receiver.sync": "Sync with Provider Priority", "gui.phycon.redstone_mode.high": "High", "gui.phycon.redstone_mode.low": "Low", "gui.phycon.redstone_mode.toggle": "Toggle",