diff --git a/sprites/APU0.png b/sprites/APU0.png new file mode 100644 index 0000000..5949977 Binary files /dev/null and b/sprites/APU0.png differ diff --git a/sprites/APU1.png b/sprites/APU1.png new file mode 100644 index 0000000..d847566 Binary files /dev/null and b/sprites/APU1.png differ diff --git a/sprites/APU2.png b/sprites/APU2.png new file mode 100644 index 0000000..a4a9e42 Binary files /dev/null and b/sprites/APU2.png differ diff --git a/sprites/BorderH.png b/sprites/BorderH.png new file mode 100644 index 0000000..7b68a2a Binary files /dev/null and b/sprites/BorderH.png differ diff --git a/sprites/BorderV.png b/sprites/BorderV.png new file mode 100644 index 0000000..c74942b Binary files /dev/null and b/sprites/BorderV.png differ diff --git a/sprites/CPU0.png b/sprites/CPU0.png new file mode 100644 index 0000000..832c6eb Binary files /dev/null and b/sprites/CPU0.png differ diff --git a/sprites/CPU1.png b/sprites/CPU1.png new file mode 100644 index 0000000..9e947f4 Binary files /dev/null and b/sprites/CPU1.png differ diff --git a/sprites/CPU2.png b/sprites/CPU2.png new file mode 100644 index 0000000..77f5f5f Binary files /dev/null and b/sprites/CPU2.png differ diff --git a/sprites/CornerBL.png b/sprites/CornerBL.png new file mode 100644 index 0000000..86c5a79 Binary files /dev/null and b/sprites/CornerBL.png differ diff --git a/sprites/CornerBR.png b/sprites/CornerBR.png new file mode 100644 index 0000000..9016e6c Binary files /dev/null and b/sprites/CornerBR.png differ diff --git a/sprites/CornerTL.png b/sprites/CornerTL.png new file mode 100644 index 0000000..09e07c7 Binary files /dev/null and b/sprites/CornerTL.png differ diff --git a/sprites/CornerTR.png b/sprites/CornerTR.png new file mode 100644 index 0000000..fad4fda Binary files /dev/null and b/sprites/CornerTR.png differ diff --git a/sprites/EEPROM.png b/sprites/EEPROM.png new file mode 100644 index 0000000..7dbd821 Binary files /dev/null and b/sprites/EEPROM.png differ diff --git a/sprites/Empty.png b/sprites/Empty.png new file mode 100644 index 0000000..7d3a386 Binary files /dev/null and b/sprites/Empty.png differ diff --git a/sprites/GraphicsCard0.png b/sprites/GraphicsCard0.png new file mode 100644 index 0000000..f37f1a7 Binary files /dev/null and b/sprites/GraphicsCard0.png differ diff --git a/sprites/GraphicsCard1.png b/sprites/GraphicsCard1.png new file mode 100644 index 0000000..3c26805 Binary files /dev/null and b/sprites/GraphicsCard1.png differ diff --git a/sprites/GraphicsCard2.png b/sprites/GraphicsCard2.png new file mode 100644 index 0000000..35f9080 Binary files /dev/null and b/sprites/GraphicsCard2.png differ diff --git a/sprites/HardDiskDrive0.png b/sprites/HardDiskDrive0.png new file mode 100644 index 0000000..ef3ca62 Binary files /dev/null and b/sprites/HardDiskDrive0.png differ diff --git a/sprites/HardDiskDrive1.png b/sprites/HardDiskDrive1.png new file mode 100644 index 0000000..2d82d9d Binary files /dev/null and b/sprites/HardDiskDrive1.png differ diff --git a/sprites/HardDiskDrive2.png b/sprites/HardDiskDrive2.png new file mode 100644 index 0000000..fcaffb1 Binary files /dev/null and b/sprites/HardDiskDrive2.png differ diff --git a/sprites/InternetCard.png b/sprites/InternetCard.png new file mode 100644 index 0000000..6610431 Binary files /dev/null and b/sprites/InternetCard.png differ diff --git a/sprites/Memory0.png b/sprites/Memory0.png new file mode 100644 index 0000000..7f37212 Binary files /dev/null and b/sprites/Memory0.png differ diff --git a/sprites/Memory1.png b/sprites/Memory1.png new file mode 100644 index 0000000..53efb70 Binary files /dev/null and b/sprites/Memory1.png differ diff --git a/sprites/Memory2.png b/sprites/Memory2.png new file mode 100644 index 0000000..d1522c9 Binary files /dev/null and b/sprites/Memory2.png differ diff --git a/sprites/Memory3.png b/sprites/Memory3.png new file mode 100644 index 0000000..84bbc73 Binary files /dev/null and b/sprites/Memory3.png differ diff --git a/sprites/Memory4.png b/sprites/Memory4.png new file mode 100644 index 0000000..a732b17 Binary files /dev/null and b/sprites/Memory4.png differ diff --git a/sprites/Memory5.png b/sprites/Memory5.png new file mode 100644 index 0000000..2a851fa Binary files /dev/null and b/sprites/Memory5.png differ diff --git a/src/main/scala/ocelot/desktop/OcelotDesktop.scala b/src/main/scala/ocelot/desktop/OcelotDesktop.scala index d874d47..1052628 100644 --- a/src/main/scala/ocelot/desktop/OcelotDesktop.scala +++ b/src/main/scala/ocelot/desktop/OcelotDesktop.scala @@ -3,7 +3,7 @@ package ocelot.desktop import java.io.{PrintWriter, StringWriter} import ocelot.desktop.ui.UiHandler -import ocelot.desktop.ui.widget.ScreenWidget +import ocelot.desktop.ui.widget.{RootWidget, ScreenView} import ocelot.desktop.util.{Audio, ResourceManager} import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.scala.Logging @@ -17,7 +17,7 @@ import totoro.ocelot.brain.util.Tier object OcelotDesktop extends Logging { - var screenWidget: ScreenWidget = _ + var root: RootWidget = _ var ui: UiHandler = _ def mainInner(): Unit = { @@ -27,8 +27,8 @@ object OcelotDesktop extends Logging { Ocelot.initialize(LogManager.getLogger(Ocelot)) createWorkspace() - screenWidget = new ScreenWidget(screen) - ui = new UiHandler(screenWidget) + root = new RootWidget(screen) + ui = new UiHandler(root) setupEventHandlers() computer.turnOn() @@ -110,27 +110,27 @@ object OcelotDesktop extends Logging { }) EventBus.listenTo(classOf[TextBufferSetEvent], { case event: TextBufferSetEvent => - screenWidget.set(event.x, event.y, event.value, event.vertical) + root.screenView.set(event.x, event.y, event.value, event.vertical) }) EventBus.listenTo(classOf[TextBufferFillEvent], { case event: TextBufferFillEvent => - screenWidget.fill(event.x, event.y, event.width, event.height, event.value) + root.screenView.fill(event.x, event.y, event.width, event.height, event.value) }) EventBus.listenTo(classOf[TextBufferCopyEvent], { case event: TextBufferCopyEvent => - screenWidget.copy(event.x, event.y, event.width, event.height, event.horizontalTranslation, event.verticalTranslation) + root.screenView.copy(event.x, event.y, event.width, event.height, event.horizontalTranslation, event.verticalTranslation) }) EventBus.listenTo(classOf[TextBufferSetForegroundColorEvent], { case event: TextBufferSetForegroundColorEvent => - screenWidget.foreground = event.color + root.screenView.foreground = event.color }) EventBus.listenTo(classOf[TextBufferSetBackgroundColorEvent], { case event: TextBufferSetBackgroundColorEvent => - screenWidget.background = event.color + root.screenView.background = event.color }) EventBus.listenTo(classOf[TextBufferSetResolutionEvent], { case event: TextBufferSetResolutionEvent => - screenWidget.setResolution(event.width, event.height) + root.screenView.setResolution(event.width, event.height) }) } } \ No newline at end of file diff --git a/src/main/scala/ocelot/desktop/graphics/Graphics.scala b/src/main/scala/ocelot/desktop/graphics/Graphics.scala index 7651fad..61bce14 100644 --- a/src/main/scala/ocelot/desktop/graphics/Graphics.scala +++ b/src/main/scala/ocelot/desktop/graphics/Graphics.scala @@ -8,6 +8,8 @@ import ocelot.desktop.util.{FontLoader, Spritesheet} import org.apache.logging.log4j.scala.Logging import org.lwjgl.opengl.GL11 +import scala.collection.mutable.ArrayBuffer + class Graphics extends Logging { private var projection = Transform2D.viewport(800, 600) private var z = 0f @@ -28,6 +30,8 @@ class Graphics extends Logging { private var spriteRect = Spritesheet.sprites(_sprite) private val emptySpriteTrans = Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h) + private val transformStack = new ArrayBuffer[Transform2D]() + GL11.glEnable(GL11.GL_DEPTH_TEST) GL11.glDepthFunc(GL11.GL_LEQUAL) GL11.glEnable(GL11.GL_BLEND) @@ -78,6 +82,24 @@ class Graphics extends Logging { _fontSize = value } + def transform: Transform2D = transformStack.lastOption.getOrElse(Transform2D.identity) + + def pushTransform(): Unit = { + transformStack += transform + } + + def popTransform(): Unit = { + transformStack.remove(transformStack.length - 1) + } + + def transform(t: Transform2D): Unit = { + transformStack(transformStack.length - 1) = transform >> t + } + + def translate(x: Float, y: Float): Unit = { + transform(Transform2D.translate(x, y)) + } + def sprite: String = _sprite def sprite_=(value: String): Unit = { @@ -116,8 +138,10 @@ class Graphics extends Logging { // ^ dirty hack to avoid edge bleeding, somehow works ) - rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height), emptySpriteTrans)) - textRenderer.schedule(MeshInstance(_foreground, z + 1, Transform2D.translate(x, y) >> Transform2D.scale(width, height), uvTransform)) + val transform = this.transform >> Transform2D.translate(x, y) >> Transform2D.scale(width, height) + + rectRenderer.schedule(MeshInstance(_background, z, transform, emptySpriteTrans)) + textRenderer.schedule(MeshInstance(_foreground, z + 1, transform, uvTransform)) z += 2 } @@ -130,8 +154,9 @@ class Graphics extends Logging { } def rect(x: Float, y: Float, width: Float, height: Float): Unit = { + val transform = this.transform >> Transform2D.translate(x, y) >> Transform2D.scale(width, height) val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h) - rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height), uvTransform)) + rectRenderer.schedule(MeshInstance(_background, z, transform, uvTransform)) z += 1 } diff --git a/src/main/scala/ocelot/desktop/ui/UiHandler.scala b/src/main/scala/ocelot/desktop/ui/UiHandler.scala index 2b6b04d..e902a0e 100644 --- a/src/main/scala/ocelot/desktop/ui/UiHandler.scala +++ b/src/main/scala/ocelot/desktop/ui/UiHandler.scala @@ -4,7 +4,7 @@ import java.nio.{ByteBuffer, IntBuffer} import ocelot.desktop.geometry.{Size2D, Vector2D} import ocelot.desktop.graphics.Graphics -import ocelot.desktop.ui.widget.RootWidget +import ocelot.desktop.ui.widget.{RootWidget, Widget} import ocelot.desktop.util.{Audio, FPSCalculator, FontLoader, Spritesheet} import org.apache.logging.log4j.scala.Logging import org.lwjgl.glfw.{GLFW, GLFWErrorCallback} @@ -13,8 +13,6 @@ import org.lwjgl.opengl.{GL, GL11} import org.lwjgl.system.MemoryUtil.NULL class UiHandler(val root: RootWidget) extends Logging { - root.uiHandler = this - private var graphics: Graphics = _ private var window: Long = _ private var fullRedraw = true @@ -23,11 +21,11 @@ class UiHandler(val root: RootWidget) extends Logging { private var soundContext: Long = _ private val fpsCalculator = new FPSCalculator - private var windowTitle = root.windowTitle + private var windowTitle = "hello there" - private var windowSize: Size2D = Size2D(0, 0) + private var windowSize: Size2D = Size2D(800, 600) private var sizeLimits = SizeLimits(root.minimumSize, root.maximumSize) - root.size = Size2D.Zero + root.size = windowSize def fps: Float = fpsCalculator.fps @@ -126,6 +124,8 @@ class UiHandler(val root: RootWidget) extends Logging { } private def update(): Unit = { + updateWidget(root) + val newSizeLimits = SizeLimits(root.minimumSize, root.maximumSize) if (sizeLimits != newSizeLimits) { sizeLimits = newSizeLimits @@ -145,13 +145,27 @@ class UiHandler(val root: RootWidget) extends Logging { for (event <- KeyHandler.events.iterator ++ MouseHandler.events.iterator ++ ScrollHandler.events.iterator) root.handleEvent(event) + } - root.update() + private def updateWidget(widget: Widget): Unit = { + widget.uiHandler = this + widget.update() + + for (child <- widget.children) + updateWidget(child) + } + + private def drawWidget(widget: Widget, forceRedraw: Boolean = false): Unit = { + val redrawParent = forceRedraw || widget.shouldRedraw + if (redrawParent) widget.draw(graphics) + + for (child <- widget.children) + drawWidget(child, forceRedraw = redrawParent) } private def draw(): Unit = { graphics.clear() - root.draw(graphics) + drawWidget(root, forceRedraw = fullRedraw) graphics.commit() } diff --git a/src/main/scala/ocelot/desktop/ui/widget/RootWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/RootWidget.scala index 7388bd3..df3c062 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/RootWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/RootWidget.scala @@ -1,12 +1,11 @@ package ocelot.desktop.ui.widget -import ocelot.desktop.ui.UiHandler +import totoro.ocelot.brain.entity.Screen -abstract class RootWidget extends Widget { - // TODO: Remove - var uiHandler: UiHandler = _ +class RootWidget(screen: Screen) extends WrapperWidget { + val screenView = new ScreenView(screen) - def windowTitle: String + override protected val inner = new ScrollView(screenView) - def update(): Unit = {} + def windowTitle: String = f"Ocelot Desktop [FPS: ${uiHandler.fps}%2.3f]" } diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/ScreenView.scala similarity index 94% rename from src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala rename to src/main/scala/ocelot/desktop/ui/widget/ScreenView.scala index 125b66a..40b024c 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ScreenView.scala @@ -11,11 +11,11 @@ import totoro.ocelot.brain.user.User import scala.collection.mutable -class ScreenWidget(screen: Screen) extends RootWidget with Logging { +class ScreenView(screen: Screen) extends Widget with Logging { private val fontSize = 16f - var width = screen.getWidth - var height = screen.getHeight + private var width = screen.getWidth + private var height = screen.getHeight var background: Int = 0x000000 var foreground: Int = 0xFFFFFF @@ -76,13 +76,14 @@ class ScreenWidget(screen: Screen) extends RootWidget with Logging { } } - override def windowTitle: String = f"Ocelot Desktop [FPS: ${uiHandler.fps}%2.3f]" - override def draw(g: Graphics): Unit = { + g.pushTransform() + g.translate(position.x, position.y) + val w = math.round(fontSize * width / 2f) + 32 val h = math.round(fontSize * height) + 32 - g.clear(0, 0, w, h) + g.clear(position.x.toInt, position.y.toInt, w, h) g.background = IntColor(0x333333) g.sprite = "Empty" g.rect(0, 0, w, h) @@ -108,6 +109,8 @@ class ScreenWidget(screen: Screen) extends RootWidget with Logging { g.sprite("BorderH", 16, h - 16, w - 32, 16) g.sprite("BorderV", 0, 16, 16, h - 32) g.sprite("BorderV", w - 16, 16, 16, h - 32) + + g.popTransform() } def set(x: Int, y: Int, text: String, vertical: Boolean): Unit = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala b/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala new file mode 100644 index 0000000..5fb5993 --- /dev/null +++ b/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala @@ -0,0 +1,20 @@ +package ocelot.desktop.ui.widget +import ocelot.desktop.geometry.Size2D + +class ScrollView(contents: Widget) extends Widget { + override def minimumSize: Option[Size2D] = Some(Size2D(10, 10)) + + override def maximumSize: Option[Size2D] = None + + override def children: Seq[Widget] = { + Array(contents) + } + + class ScrollBar(orientation: Orientation.Value) extends Widget { + + } + + object Orientation extends Enumeration { + val Vertical, Horizontal = Value + } +} diff --git a/src/main/scala/ocelot/desktop/ui/widget/Widget.scala b/src/main/scala/ocelot/desktop/ui/widget/Widget.scala index 1172844..a72d24b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Widget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Widget.scala @@ -1,12 +1,17 @@ package ocelot.desktop.ui.widget -import ocelot.desktop.geometry.Size2D +import ocelot.desktop.geometry.{Size2D, Vector2D} import ocelot.desktop.graphics.Graphics -import ocelot.desktop.ui.EventHandlers +import ocelot.desktop.ui.{EventHandlers, UiHandler} import ocelot.desktop.ui.event.Event -class Widget { +abstract class Widget { + private var _position: Vector2D = Vector2D(0, 0) private var _size: Size2D = Size2D(0, 0) + private var _parent: Option[Widget] = None + + // TODO: Remove + var uiHandler: UiHandler = _ val eventHandlers = new EventHandlers @@ -19,11 +24,31 @@ class Widget { ) } + def position: Vector2D = _position + + def position_=(value: Vector2D): Unit = { + _position = value + } + def minimumSize: Option[Size2D] = None def maximumSize: Option[Size2D] = None - def draw(g: Graphics): Unit = {} + def children: Seq[Widget] = Array[Widget]() + + def parent: Option[Widget] = _parent + + def parent_=(value: Option[Widget]): Unit = { + _parent = value + } def handleEvent(event: Event): Unit = eventHandlers(event) + + def draw(g: Graphics): Unit = {} + + def shouldRedraw: Boolean = true + + def update(): Unit = {} + + def relayout(): Unit = {} } diff --git a/src/main/scala/ocelot/desktop/ui/widget/WrapperWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/WrapperWidget.scala new file mode 100644 index 0000000..15adc7a --- /dev/null +++ b/src/main/scala/ocelot/desktop/ui/widget/WrapperWidget.scala @@ -0,0 +1,13 @@ +package ocelot.desktop.ui.widget + +import ocelot.desktop.geometry.Size2D + +abstract class WrapperWidget extends Widget { + protected val inner: Widget + + override def minimumSize: Option[Size2D] = inner.minimumSize + + override def maximumSize: Option[Size2D] = inner.maximumSize + + override def children: Seq[Widget] = Array(inner) +}