diff --git a/src/main/scala/ocelot/desktop/node/Node.scala b/src/main/scala/ocelot/desktop/node/Node.scala index 1734cce..c17ff0e 100644 --- a/src/main/scala/ocelot/desktop/node/Node.scala +++ b/src/main/scala/ocelot/desktop/node/Node.scala @@ -103,7 +103,7 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers override def update(): Unit = { super.update() - if (isHovered || isMoving) { + if (mouseOver || isMoving) { root.get.statusBar.addMouseEntry(IconSource.Icons.RMB, "Menu") root.get.statusBar.addMouseEntry(IconSource.Icons.DragLMB, "Move node") @@ -245,7 +245,7 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers private def stopMoving(): Unit = { isMoving = false - if (isHovered) + if (mouseOver) highlight.goto(HoveredHighlight) else highlight.goto(NoHighlight) diff --git a/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala b/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala index 0f5e9e3..3831f5f 100644 --- a/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala +++ b/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala @@ -50,7 +50,7 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi override def update(): Unit = { super.update() - if (isHovered) { + if (mouseOver) { root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, "Add node") } } diff --git a/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala b/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala index d39e02d..7d45510 100644 --- a/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala +++ b/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala @@ -22,7 +22,7 @@ trait ShiftClickNode extends Node { override def update(): Unit = { super.update() - if (isHovered || isMoving) + if (mouseOver || isMoving) root.get.statusBar.addKeyMouseEntry(IconSource.Icons.LMB, "SHIFT", hoveredShiftStatusBarText) } } diff --git a/src/main/scala/ocelot/desktop/node/WindowedNode.scala b/src/main/scala/ocelot/desktop/node/WindowedNode.scala index caa80a3..304893c 100644 --- a/src/main/scala/ocelot/desktop/node/WindowedNode.scala +++ b/src/main/scala/ocelot/desktop/node/WindowedNode.scala @@ -13,7 +13,7 @@ trait WindowedNode[T <: Window] extends Node with Windowed[T] { } override def update(): Unit = { - if (isHovered || isMoving) + if (mouseOver || isMoving) root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, if (windowCreated && window.isOpen) "Close" else "Open") super.update() diff --git a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala index e59c748..bd66c6f 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala @@ -26,7 +26,7 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity override def update(): Unit = { super.update() - if (isHovered || isMoving) { + if (mouseOver || isMoving) { root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, "Play sample") } } diff --git a/src/main/scala/ocelot/desktop/ui/UiHandler.scala b/src/main/scala/ocelot/desktop/ui/UiHandler.scala index 8f2ef55..17da776 100644 --- a/src/main/scala/ocelot/desktop/ui/UiHandler.scala +++ b/src/main/scala/ocelot/desktop/ui/UiHandler.scala @@ -6,7 +6,7 @@ import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D} import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.event.handlers.HoverHandler import ocelot.desktop.ui.event.sources.{BrainEvents, KeyEvents, MouseEvents, ScrollEvents} -import ocelot.desktop.ui.event.{Capturing, CapturingEvent, Dispatchable, MouseEvent} +import ocelot.desktop.ui.event.{Capturing, CapturingEvent, Dispatchable, HoverEvent, MouseEvent} import ocelot.desktop.ui.widget.{RootWidget, Widget} import ocelot.desktop.util._ import ocelot.desktop.{OcelotDesktop, Settings} @@ -28,7 +28,6 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.duration.DurationInt import scala.util.Try - object UiHandler extends Logging { var root: RootWidget = _ var graphics: Graphics = _ @@ -506,12 +505,16 @@ object UiHandler extends Logging { } hierarchy.reverseIterator.foreach { - case handler: HoverHandler if !mouseTarget.contains(handler) => handler.setHovered(false) + case h: HoverHandler if !mouseTarget.contains(h) && h._mouseOver.update(false) => + dispatchCapturing(h)(HoverEvent(HoverEvent.State.Leave)) + case _ => } mouseTarget.foreach { - case handler: HoverHandler => handler.setHovered(true) + case h: HoverHandler if h._mouseOver.update(true) => + dispatchCapturing(h)(HoverEvent(HoverEvent.State.Enter)) + case _ => } diff --git a/src/main/scala/ocelot/desktop/ui/event/HoverEvent.scala b/src/main/scala/ocelot/desktop/ui/event/HoverEvent.scala index c3abbc2..439bcf5 100644 --- a/src/main/scala/ocelot/desktop/ui/event/HoverEvent.scala +++ b/src/main/scala/ocelot/desktop/ui/event/HoverEvent.scala @@ -6,4 +6,4 @@ object HoverEvent { } } -case class HoverEvent(state: HoverEvent.State.Value) extends Event +case class HoverEvent(state: HoverEvent.State.Value) extends CapturingEvent diff --git a/src/main/scala/ocelot/desktop/ui/event/handlers/HoverHandler.scala b/src/main/scala/ocelot/desktop/ui/event/handlers/HoverHandler.scala index 61cf6ae..c49cd1a 100644 --- a/src/main/scala/ocelot/desktop/ui/event/handlers/HoverHandler.scala +++ b/src/main/scala/ocelot/desktop/ui/event/handlers/HoverHandler.scala @@ -1,21 +1,11 @@ package ocelot.desktop.ui.event.handlers -import ocelot.desktop.ui.event.HoverEvent import ocelot.desktop.ui.widget.Widget +import ocelot.desktop.util.Register trait HoverHandler extends Widget { - private var wasHovered = false - private var _isHovered: Boolean = false + // must only be updated in UiHandler. + val _mouseOver: Register.Writeable[Boolean] = Register.apply(false) - def isHovered: Boolean = _isHovered - - def setHovered(v: Boolean): Unit = { - _isHovered = v - - if (_isHovered && !wasHovered) - handleEvent(HoverEvent(HoverEvent.State.Enter)) - else if (!_isHovered && wasHovered) - handleEvent(HoverEvent(HoverEvent.State.Leave)) - wasHovered = _isHovered - } + def mouseOver: Boolean = _mouseOver.value } diff --git a/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala b/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala index 5f771ea..4f49bff 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala @@ -114,8 +114,8 @@ class IconButton( protected def pressedActiveColor: Color = pressedColor.toRGBANorm.mapRgb(_ * (1 - darkenActiveColorFactor)) protected def releasedActiveColor: Color = releasedColor.toRGBANorm.mapRgb(_ * (1 - darkenActiveColorFactor)) - private def targetPressedColor: Color = if (isHovered) pressedActiveColor else pressedColor - private def targetReleasedColor: Color = if (isHovered) releasedActiveColor else releasedColor + private def targetPressedColor: Color = if (mouseOver) pressedActiveColor else pressedColor + private def targetReleasedColor: Color = if (mouseOver) releasedActiveColor else releasedColor private def targetIconMix: Float = if (model.pressed) 1f else 0f diff --git a/src/main/scala/ocelot/desktop/ui/widget/Plot.scala b/src/main/scala/ocelot/desktop/ui/widget/Plot.scala index e59aeaf..e076156 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Plot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Plot.scala @@ -112,7 +112,7 @@ abstract class Plot extends Widget { } } - if (panel.isHovered) { + if (panel.mouseOver) { g.foreground = ColorScheme("PlotLine") val pos = UiHandler.mousePosition val x = xAxis.inversePos(pos.x - pBounds.x, pBounds.w) diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala b/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala index 8001b43..d05ac3f 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ScrollView.scala @@ -144,8 +144,8 @@ class ScrollView(val inner: Widget) extends Widget with Logging with HoverHandle val mousePos = UiHandler.mousePosition - val vAnimDir = if (isHovered && vThumbHoverArea.contains(mousePos) || dragState.isVertical) 1 else -1 - val hAnimDir = if (isHovered && hThumbHoverArea.contains(mousePos) || dragState.isHorizontal) 1 else -1 + val vAnimDir = if (mouseOver && vThumbHoverArea.contains(mousePos) || dragState.isVertical) 1 else -1 + val hAnimDir = if (mouseOver && hThumbHoverArea.contains(mousePos) || dragState.isHorizontal) 1 else -1 vAnim = (vAnim + UiHandler.dt / 0.2f * vAnimDir).clamped() hAnim = (hAnim + UiHandler.dt / 0.2f * hAnimDir).clamped() diff --git a/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala index 9c7c586..144b461 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala @@ -149,7 +149,7 @@ abstract class Viewport3DWidget extends Widget with MouseHandler with HoverHandl scene = createScene() - if (isHovered) { + if (mouseOver) { root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, "Rotate view") root.get.statusBar.addKeyMouseEntry(IconSource.Icons.LMB, "SHIFT", "Pan view") } diff --git a/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala b/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala index 3692160..d5737fe 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala @@ -584,7 +584,7 @@ class WorkspaceView extends Widget with Persistable with MouseHandler with Hover g.restore() gridAlpha.update() - if (KeyEvents.isControlDown && (isHovered || nodes.exists(_.isHovered))) { + if (KeyEvents.isControlDown && (mouseOver || nodes.exists(_.mouseOver))) { gridAlpha.goto(0.1f) drawGrid(g) } else { @@ -663,15 +663,15 @@ class WorkspaceView extends Widget with Persistable with MouseHandler with Hover nodes.foreach(_.update()) particleSystem.update(UiHandler.dt) - if (isHovered) { + if (mouseOver) { root.get.statusBar.addKeyEntry(Settings.get.keymap.name(Center), "Reset camera") } - if (isHovered || nodes.exists(_.isHovered)) { + if (mouseOver || nodes.exists(_.mouseOver)) { root.get.statusBar.addKeyEntry("CTRL", "Show grid") } - if (isHovered && newConnection.isEmpty) { + if (mouseOver && newConnection.isEmpty) { root.get.statusBar.addMouseEntry( IconSource.Icons.LMB, if (nodeSelector.isClosed) "Add node" else "Close selector", diff --git a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala index 709b89a..d790f16 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala @@ -23,7 +23,7 @@ class ContextMenuSubmenu( super.update() if ( !isClosing && !bounds.inflated(4).contains(UiHandler.mousePosition) - && !parentEntry.isHovered + && !parentEntry.mouseOver ) close() } } @@ -45,7 +45,7 @@ class ContextMenuSubmenu( } override def update(): Unit = { - if (!isHovered && submenu.isClosed) super.leave() + if (!mouseOver && submenu.isClosed) super.leave() super.update() } diff --git a/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala b/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala index 438cc94..3d28082 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala @@ -5,7 +5,7 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.{Graphics, IconSource} import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} -import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} +import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} import ocelot.desktop.ui.layout.{AlignItems, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} @@ -55,7 +55,7 @@ class StatusBar extends Widget { override def hThumbVisible: Boolean = false } - private lazy val hoverBoxWithPauseButton: HoverBox = new HoverBox(new IconButton( + children :+= new HoverBox(new IconButton( IconSource.Icons.Pause, IconSource.Icons.Play, mode = IconButton.Mode.Switch, @@ -64,23 +64,18 @@ class StatusBar extends Widget { tooltip = Some("Pause emulation"), model = new IconButton.ReadOnlyModel(() => OcelotDesktop.emulationPaused), ) { - eventHandlers += { - case HoverEvent(HoverEvent.State.Enter) => - hoverBoxWithPauseButton.startHoverEnterAnimation() - case HoverEvent(HoverEvent.State.Leave) => - hoverBoxWithPauseButton.startHoverLeaveAnimation() - } override def onPressed(): Unit = { OcelotDesktop.emulationPaused = true labelTooltip.foreach(_.setText("Resume emulation")) } + override def onReleased(): Unit = { OcelotDesktop.emulationPaused = false labelTooltip.foreach(_.setText("Pause emulation")) } }, Padding2D(left = 2, right = 2)) - private lazy val hoverBoxWithTpsLabel: HoverBox = new HoverBox(new Label with MouseHandler with HoverHandler { + children :+= new HoverBox(new Label with MouseHandler with HoverHandler { override protected def receiveClickEvents: Boolean = true eventHandlers += { @@ -113,18 +108,12 @@ class StatusBar extends Widget { } root.get.contextMenus.open(menu, pos) - - case HoverEvent(HoverEvent.State.Enter) => - hoverBoxWithTpsLabel.startHoverEnterAnimation() - - case HoverEvent(HoverEvent.State.Leave) => - hoverBoxWithTpsLabel.startHoverLeaveAnimation() } override def update(): Unit = { super.update() - if (isHovered) { + if (mouseOver) { root.get.statusBar.addMouseEntry(IconSource.Icons.RMB, "Change simulation speed") } } @@ -136,9 +125,6 @@ class StatusBar extends Widget { override def text: String = f"TPS: ${OcelotDesktop.tpsCounter.fps}%02.1f" }) - children :+= hoverBoxWithPauseButton - children :+= new PaddingBox(hoverBoxWithTpsLabel, Padding2D(left = 8)) - def addMouseEntry(icon: IconSource, text: String): Unit = { if (!keyMouseEntries.children.collect({ case e: MouseEntry => e.icon }).contains(icon)) { keyMouseEntries.children :+= new MouseEntry(icon, text) @@ -165,17 +151,15 @@ class StatusBar extends Widget { keyMouseEntries.children = ArraySeq.empty } - private class HoverBox(widget: Widget, padding: Padding2D = Padding2D(left = 8, right = 8)) extends PaddingBox(widget, padding) with HoverAnimation { + private class HoverBox(widget: Widget, padding: Padding2D = Padding2D(left = 8, right = 8)) + extends PaddingBox(widget, padding) + with HoverAnimation { + override def receiveMouseEvents: Boolean = true override protected val hoverAnimationColorActive: Color = ColorScheme("StatusBarActive") override protected val hoverAnimationColorDefault: Color = hoverAnimationColorActive.toRGBANorm.withAlpha(0) - // public re-exports - override def startHoverEnterAnimation(): Unit = super.startHoverEnterAnimation() - - override def startHoverLeaveAnimation(): Unit = super.startHoverLeaveAnimation() - override def draw(g: Graphics): Unit = { g.rect(bounds, hoverAnimation.color) super.draw(g) diff --git a/src/main/scala/ocelot/desktop/ui/widget/traits/HoverAnimation.scala b/src/main/scala/ocelot/desktop/ui/widget/traits/HoverAnimation.scala index 449c2af..7bcd827 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/traits/HoverAnimation.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/traits/HoverAnimation.scala @@ -3,17 +3,18 @@ package ocelot.desktop.ui.widget.traits import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color import ocelot.desktop.ui.Const._ -import ocelot.desktop.ui.event.{EventAware, HoverEvent} +import ocelot.desktop.ui.event.{Capturing, EventAware, HoverEvent} import ocelot.desktop.ui.event.handlers.HoverHandler import ocelot.desktop.ui.widget.{Updatable, Widget} import ocelot.desktop.util.animation.ColorAnimation -/** Helper trait that manages color animations for UI widgets that should be highlighted on mouse hover. */ - -//noinspection ScalaWeakerAccess - +/** + * Helper trait that manages color animations for UI widgets that should be highlighted on mouse hover. + */ trait HoverAnimation extends Widget with EventAware with HoverHandler with Updatable { + //noinspection ScalaWeakerAccess protected val hoverAnimationSpeedEnter: Float = AnimationSpeedHoverEnter + //noinspection ScalaWeakerAccess protected val hoverAnimationSpeedLeave: Float = AnimationSpeedHoverLeave protected val hoverAnimationColorDefault: Color = ColorScheme("ButtonBackground") protected val hoverAnimationColorActive: Color = ColorScheme("ButtonBackgroundActive") @@ -22,21 +23,13 @@ trait HoverAnimation extends Widget with EventAware with HoverHandler with Updat new ColorAnimation(hoverAnimationColorDefault, hoverAnimationSpeedEnter) eventHandlers += { - case HoverEvent(HoverEvent.State.Enter) => - startHoverEnterAnimation() + case Capturing(HoverEvent(HoverEvent.State.Enter)) => + hoverAnimation.speed = hoverAnimationSpeedEnter + hoverAnimation.goto(hoverAnimationColorActive) - case HoverEvent(HoverEvent.State.Leave) => - startHoverLeaveAnimation() - } - - protected def startHoverEnterAnimation(): Unit = { - hoverAnimation.speed = hoverAnimationSpeedEnter - hoverAnimation.goto(hoverAnimationColorActive) - } - - protected def startHoverLeaveAnimation(): Unit = { - hoverAnimation.speed = hoverAnimationSpeedLeave - hoverAnimation.goto(hoverAnimationColorDefault) + case Capturing(HoverEvent(HoverEvent.State.Leave)) => + hoverAnimation.speed = hoverAnimationSpeedLeave + hoverAnimation.goto(hoverAnimationColorDefault) } override def update(): Unit = { diff --git a/src/main/scala/ocelot/desktop/util/Register.scala b/src/main/scala/ocelot/desktop/util/Register.scala index c3840c2..6bb2757 100644 --- a/src/main/scala/ocelot/desktop/util/Register.scala +++ b/src/main/scala/ocelot/desktop/util/Register.scala @@ -35,6 +35,12 @@ object Register { changed } + + def update(nextValue: T): Boolean = { + this.nextValue = nextValue + + update() + } } class Sampling[T](next: () => T) extends Register[T] {