Add interface priority syncing

This commit is contained in:
Shadowfacts 2021-03-04 19:44:31 -05:00
parent 385e36918f
commit f321b2a06a
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 224 additions and 31 deletions

View File

@ -10,6 +10,7 @@ import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.RenderHelper 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 * 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( class Label(
text: Text, text: Text,
val shadow: Boolean = false, var shadow: Boolean = false,
val maxLines: Int = 0, val maxLines: Int = 0,
val wrappingMode: WrappingMode = WrappingMode.WRAP, val wrappingMode: WrappingMode = WrappingMode.WRAP,
val textAlignment: TextAlignment = TextAlignment.LEFT var textAlignment: TextAlignment = TextAlignment.LEFT
): View() { ): View() {
companion object { companion object {
@ -59,7 +60,8 @@ class Label(
var text: Text = text var text: Text = text
set(value) { set(value) {
field = value field = value
updateIntrinsicContentSize() // todo: uhhhh
updateIntrinsicContentSize(true)
// todo: setNeedsLayout instead of force unwrapping window // todo: setNeedsLayout instead of force unwrapping window
window!!.layout() window!!.layout()
} }
@ -75,15 +77,23 @@ class Label(
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
updateIntrinsicContentSize() updateIntrinsicContentSize(false)
} }
private fun updateIntrinsicContentSize() { private fun updateIntrinsicContentSize(canWrap: Boolean): Boolean {
if (RenderHelper.disabled) return if (RenderHelper.disabled) return false
val width = textRenderer.getWidth(text) val oldSize = intrinsicContentSize
val height = textRenderer.fontHeight if (wrappingMode == WrappingMode.WRAP && canWrap) {
intrinsicContentSize = Size(width.toDouble(), height.toDouble()) 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) { override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {
@ -110,14 +120,22 @@ class Label(
super.didLayout() super.didLayout()
computeLines() computeLines()
if (updateIntrinsicContentSize(true)) {
// if the intrinsic content size changes, relayout
window!!.layout()
}
} }
private fun computeLines() { private fun computeLines() {
var lines = textRenderer.wrapLines(text, bounds.width.toInt()) if (wrappingMode == WrappingMode.WRAP) {
if (maxLines > 0 && maxLines < lines.size) { var lines = textRenderer.wrapLines(text, bounds.width.toInt())
lines = lines.dropLast(lines.size - maxLines) if (maxLines > 0 && maxLines < lines.size) {
lines = lines.dropLast(lines.size - maxLines)
}
this.lines = lines
} else {
this.lines = listOf(text.asOrderedText())
} }
this.lines = lines
} }
} }

View File

@ -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 * 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. * 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. * the size.
*/ */
var intrinsicContentSize: Size? = null var intrinsicContentSize: Size? = null

View File

@ -1,25 +1,31 @@
package net.shadowfacts.cacao.view.button package net.shadowfacts.cacao.view.button
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
/** /**
* A button for toggling between on/off states. * A button for toggling between on/off states.
* *
* @author shadowfacts * @author shadowfacts
* @param initialState Whether the button starts as on or off. * @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<ToggleButton>(TextureView(if (initialState) ON else OFF).apply { class ToggleButton(
intrinsicContentSize = Size(19.0, 19.0) initialState: Boolean,
}, padding = 0.0) { handler: ((ToggleButton) -> Unit)? = null,
): AbstractButton<ToggleButton>(TextureView(if (initialState) ON else OFF), padding = 0.0) {
companion object { companion object {
val ON = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 0) val OFF = Texture(Identifier("textures/gui/checkbox.png"), 0, 0, 64, 64)
val OFF = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 19) 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 private val textureView: TextureView
@ -30,10 +36,15 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
* Updating this property updates the button's texture. * Updating this property updates the button's texture.
*/ */
var state: Boolean = initialState var state: Boolean = initialState
set(value) {
field = value init {
textureView.texture = if (value) ON else OFF this.handler = handler
} intrinsicContentSize = Size(20.0, 20.0)
background = null
disabledBackground = null
hoveredBackground = null
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean { override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) { if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) {
@ -43,4 +54,17 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
return super.mouseClicked(point, mouseButton) return super.mouseClicked(point, mouseButton)
} }
} 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
}

View File

@ -97,6 +97,7 @@ class TabViewController<T: TabViewController.Tab>(
private lateinit var outerStack: StackView private lateinit var outerStack: StackView
private lateinit var tabStack: StackView private lateinit var tabStack: StackView
private lateinit var currentTabController: ViewController
// todo: this shouldn't be public, use layout guides // todo: this shouldn't be public, use layout guides
lateinit var tabVCContainer: View lateinit var tabVCContainer: View
private set private set
@ -121,7 +122,11 @@ class TabViewController<T: TabViewController.Tab>(
tabVCContainer.zIndex = 1.0 tabVCContainer.zIndex = 1.0
view.addSubview(tabVCContainer) 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 { view.solver.dsl {
outerStack.leftAnchor equalTo view.leftAnchor outerStack.leftAnchor equalTo view.leftAnchor
@ -136,6 +141,26 @@ class TabViewController<T: TabViewController.Tab>(
} }
} }
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() { private fun updateTabButtons() {
while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first()) while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first())
@ -186,8 +211,20 @@ class TabViewController<T: TabViewController.Tab>(
tabButtons.forEach { tabButtons.forEach {
it.setSelected(it.tab === tab) it.setSelected(it.tab === tab)
} }
oldTab.controller.removeFromParent() currentTabController.viewWillDisappear()
embedChild(currentTab.controller, tabVCContainer) 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) onTabChange?.invoke(currentTab)

View File

@ -34,6 +34,7 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override var providerPriority = 0 override var providerPriority = 0
override var receiverPriority = 0 override var receiverPriority = 0
var syncPriorities = true
// todo: should this be a weak ref? // todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null private var inventory: GroupedItemInv? = null
@ -121,11 +122,13 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override fun writeDeviceConfiguration(tag: CompoundTag) { override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("ProviderPriority", providerPriority) tag.putInt("ProviderPriority", providerPriority)
tag.putInt("ReceiverPriority", receiverPriority) tag.putInt("ReceiverPriority", receiverPriority)
tag.putBoolean("SyncPriorities", syncPriorities)
} }
override fun loadDeviceConfiguration(tag: CompoundTag) { override fun loadDeviceConfiguration(tag: CompoundTag) {
providerPriority = tag.getInt("ProviderPriority") providerPriority = tag.getInt("ProviderPriority")
receiverPriority = tag.getInt("ReceiverPriority") receiverPriority = tag.getInt("ReceiverPriority")
syncPriorities = tag.getBoolean("SyncPriorities")
} }
} }

View File

@ -2,13 +2,20 @@ package net.shadowfacts.phycon.block.terminal
import alexiil.mc.lib.attributes.AttributeList import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider import alexiil.mc.lib.attributes.AttributeProvider
import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Material import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity 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.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.ActionResult import net.minecraft.util.ActionResult
import net.minecraft.util.Hand import net.minecraft.util.Hand
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
@ -18,7 +25,7 @@ import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import java.util.* import java.util.EnumSet
/** /**
* @author shadowfacts * @author shadowfacts
@ -33,10 +40,23 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal") val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
val FACING = Properties.FACING
} }
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> { override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
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<Block, BlockState>) {
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() override fun createBlockEntity(world: BlockView) = TerminalBlockEntity()

View File

@ -3,11 +3,16 @@ package net.shadowfacts.phycon.screen.console
import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntity
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label 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.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackProvider import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.networking.C2SConfigureDevice import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +23,9 @@ class ProviderViewController<T>(
private val device: T private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackProvider { ): ViewController() where T: BlockEntity, T: NetworkStackProvider {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() { override fun viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -26,9 +34,14 @@ class ProviderViewController<T>(
} }
view.addSubview(label) view.addSubview(label)
val field = NumberField(device.providerPriority) { field = NumberField(device.providerPriority) {
if (it.number != null) { if (it.number != null) {
device.providerPriority = it.number!! device.providerPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.receiverPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
} }
@ -39,6 +52,30 @@ class ProviderViewController<T>(
} }
view.addSubview(desc) 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 { view.solver.dsl {
field.widthAnchor equalTo 100 field.widthAnchor equalTo 100
field.heightAnchor equalTo 20 field.heightAnchor equalTo 20
@ -54,4 +91,13 @@ class ProviderViewController<T>(
} }
} }
override fun viewWillAppear() {
super.viewWillAppear()
field.number = device.providerPriority
if (device is InterfaceBlockEntity) {
syncButton!!.state = device.syncPriorities
}
}
} }

View File

@ -5,9 +5,11 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.button.ToggleButton
import net.shadowfacts.cacao.view.textfield.NumberField import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackReceiver import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.networking.C2SConfigureDevice import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +20,9 @@ class ReceiverViewController<T>(
private val device: T private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackReceiver { ): ViewController() where T: BlockEntity, T: NetworkStackReceiver {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() { override fun viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -26,9 +31,14 @@ class ReceiverViewController<T>(
} }
view.addSubview(label) view.addSubview(label)
val field = NumberField(device.receiverPriority) { field = NumberField(device.receiverPriority) {
if (it.number != null) { if (it.number != null) {
device.receiverPriority = it.number!! device.receiverPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.providerPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
} }
@ -39,6 +49,30 @@ class ReceiverViewController<T>(
} }
view.addSubview(desc) 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 { view.solver.dsl {
field.widthAnchor equalTo 100 field.widthAnchor equalTo 100
field.heightAnchor equalTo 20 field.heightAnchor equalTo 20
@ -54,4 +88,13 @@ class ReceiverViewController<T>(
} }
} }
override fun viewWillAppear() {
super.viewWillAppear()
field.number = device.receiverPriority
if (device is InterfaceBlockEntity) {
syncButton!!.state = device.syncPriorities
}
}
} }

View File

@ -24,9 +24,11 @@
"gui.phycon.console.provider": "Item Provider", "gui.phycon.console.provider": "Item Provider",
"gui.phycon.console.provider.priority": "Provider Priority", "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.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": "Item Receiver",
"gui.phycon.console.receiver.priority": "Receiver Priority", "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.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.high": "High",
"gui.phycon.redstone_mode.low": "Low", "gui.phycon.redstone_mode.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle", "gui.phycon.redstone_mode.toggle": "Toggle",