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.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
}
}

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
* 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

View File

@ -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<ToggleButton>(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<ToggleButton>(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<ToggleButton>(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<ToggleButton>(TextureV
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 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<T: TabViewController.Tab>(
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<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() {
while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first())
@ -186,8 +211,20 @@ class TabViewController<T: TabViewController.Tab>(
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)

View File

@ -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")
}
}

View File

@ -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<TerminalBlockEntity>(
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
val FACING = Properties.FACING
}
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()

View File

@ -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<T>(
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<T>(
}
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<T>(
}
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<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.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<T>(
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<T>(
}
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<T>(
}
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<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.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",