2023-04-18 00:45:59 +03:00

208 lines
7.3 KiB
Scala

package ocelot.desktop.windows
import ocelot.desktop.audio.{SoundSource, SoundSources}
import ocelot.desktop.color.Color
import ocelot.desktop.geometry.{Padding2D, Size2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.nodes.ComputerNode
import ocelot.desktop.ui.layout.{Layout, LinearLayout}
import ocelot.desktop.ui.widget._
import ocelot.desktop.ui.widget.tooltip.Tooltip
import ocelot.desktop.ui.widget.window.BasicWindow
import ocelot.desktop.util.animation.UnitAnimation
import ocelot.desktop.util.{DrawUtils, Orientation}
import ocelot.desktop.{ColorScheme, OcelotDesktop}
import totoro.ocelot.brain.entity.Case
import totoro.ocelot.brain.nbt.NBTTagCompound
class ComputerWindow(computerNode: ComputerNode) extends BasicWindow {
def computer: Case = computerNode.computer
def updateSlots(): Unit = {
eepromBox.children(0) = computerNode.eepromSlot
inner.children(1).children(2) = slotsWidget
}
private val eepromBox = new PaddingBox(computerNode.eepromSlot, Padding2D(right = 10))
private val bottomDrawerAnimation = UnitAnimation.easeInOutQuad(0.2f)
bottomDrawerAnimation.goDown()
private val bottomDrawer = new Widget {
override def shouldClip: Boolean = true
override def minimumSize: Size2D = Size2D(layout.minimumSize.width, 0)
children :+= new PaddingBox(new Widget {
children :+= new PaddingBox(new Widget {
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
class PerTickHistogram extends Histogram {
private var lastTick = OcelotDesktop.ticker.tick
def tickUpdate(): Unit = {}
def reset(): Unit = {
text = "N/A"
history = Seq.fill(21)(0.0f).toArray
}
override def update(): Unit = {
super.update()
val curTick = OcelotDesktop.ticker.tick
if (curTick > lastTick) {
if (!computer.machine.isRunning) reset()
lastTick = curTick
tickUpdate()
}
}
}
children :+= new PerTickHistogram {
override def tickUpdate(): Unit = {
val (free, total) = computer.machine.latestMemoryUsage
val used = total - free
val ratio = if (total == 0) 0 else used.toFloat / total.toFloat
text = f"${used.toFloat / 1024f / 1024f}%.1fM"
history = history.slice(1, history.length) ++ Array(ratio)
}
override protected val tooltip: Option[Tooltip] = Some(
new LabelTooltip("Memory usage (in Mb)")
)
}
children :+= new PaddingBox(new PerTickHistogram {
override def tickUpdate(): Unit = {
val (start, _end) = computer.machine.latestExecutionInfo
val end = if (start < _end) _end else System.nanoTime()
val cpuUsage = if (start == 0) 0 else ((end - start).toFloat / 1000000000f * OcelotDesktop.tpsCounter.fps).min(1f)
text = f"${cpuUsage * 100}%.0f%%"
history = history.slice(1, history.length) ++ Array(cpuUsage)
}
override protected val tooltip: Option[Tooltip] = Some(
new LabelTooltip("CPU usage (in %)")
)
}, Padding2D(top = 8))
children :+= new PaddingBox(new PerTickHistogram {
override def tickUpdate(): Unit = {
val (budget, maxBudget) = computer.machine.latestCallBudget
text = f"${maxBudget - budget}%.1f"
history = history.slice(1, history.length) ++ Array((1.0 - budget / maxBudget).toFloat)
}
override protected val tooltip: Option[Tooltip] = Some(
new LabelTooltip("CPU usage (call budget)")
)
}, Padding2D(top = 8))
}, Padding2D.equal(7))
override def draw(g: Graphics): Unit = {
DrawUtils.panel(g, position.x, position.y, width, height)
super.draw(g)
}
}, Padding2D(10, 12, 0, 12))
override def draw(g: Graphics): Unit = {
if (height < 1) return
super.draw(g)
g.rect(position.x + 6, position.y + 2, width - 12, 2, ColorScheme("BottomDrawerBorder"))
}
}
private val drawerButton = new IconButton(
"buttons/BottomDrawerOpen", "buttons/BottomDrawerClose",
isSwitch = true, tooltip = Some("Toggle computer usage histogram")
) {
override def onPressed(): Unit = bottomDrawerAnimation.goUp()
override def onReleased(): Unit = bottomDrawerAnimation.goDown()
}
private val inner = new Widget {
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
children :+= new PaddingBox(new Label {
override def text: String = computerNode.labelOrAddress.take(36)
override def isSmall: Boolean = true
override def color: Color = ColorScheme("ComputerAddress")
}, Padding2D(bottom = 8))
children :+= new Widget {
children :+= new PaddingBox(drawerButton, Padding2D(top = 120))
children :+= new PaddingBox(new Widget {
children :+= new PaddingBox(computerNode.eepromSlot, Padding2D(right = 10))
children :+= new IconButton(
"buttons/PowerOff",
"buttons/PowerOn",
isSwitch = true,
sizeMultiplier = 2
) {
override def isOn: Boolean = computer.machine.isRunning
override def onPressed(): Unit = computerNode.turnOn()
override def onReleased(): Unit = computerNode.turnOff()
protected override def clickSoundSource: SoundSource = SoundSources.MinecraftClick
}
}, Padding2D(top = 44, left = 22))
children :+= slotsWidget
}
}
children :+= new PaddingBox(new Widget {
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
children :+= new PaddingBox(inner, Padding2D(10, 12, 0, 12))
children :+= bottomDrawer
}, Padding2D(bottom = 10))
private def slotsWidget: Widget = {
val rows = Array(
(19, computerNode.cardSlots),
(8, Array(computerNode.cpuSlot) ++ computerNode.memorySlots),
(8, computerNode.diskSlots ++ computerNode.floppySlot.toArray)
)
new Widget {
for ((padding, row) <- rows) {
children :+= new PaddingBox(new Widget {
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
for (slot <- row) children :+= slot
}, Padding2D(left = padding, top = 8))
}
override def minimumSize: Size2D = Size2D(158, 140)
override def draw(g: Graphics): Unit = {
g.sprite("ComputerMotherboard", bounds.mapX(_ - 4).mapW(_ - 4))
drawChildren(g)
}
}
}
override def update(): Unit = {
super.update()
bottomDrawerAnimation.update()
height = minimumSize.height + (bottomDrawerAnimation.value * bottomDrawer.maximumSize.height).round
}
override def draw(g: Graphics): Unit = {
beginDraw(g)
DrawUtils.windowWithShadow(g, position.x, position.y, size.width, size.height, 1f, 0.5f)
drawChildren(g)
endDraw(g)
}
override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
bottomDrawerAnimation.save(nbt, "drawerAnimation")
}
override def load(nbt: NBTTagCompound): Unit = {
bottomDrawerAnimation.load(nbt, "drawerAnimation")
if (bottomDrawerAnimation.isGoingUp) drawerButton.playPressAnimation()
super.load(nbt)
}
}