mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2026-01-06 11:12:39 +01:00
Add context menus
This commit is contained in:
parent
17fae213ba
commit
47a1ef160e
@ -14,4 +14,6 @@ case class IntColor(color: Int) extends Color {
|
||||
override def toRGBANorm: RGBAColorNorm = toRGBA.toRGBANorm
|
||||
|
||||
override def toHSVA: HSVAColor = toRGBANorm.toHSVA
|
||||
|
||||
def withAlpha(a: Float): RGBAColorNorm = toRGBANorm.withAlpha(a)
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.event.handlers.{ClickHandler, DragHandler, HoverHandler}
|
||||
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.window.Window
|
||||
import ocelot.desktop.ui.widget.{Widget, WorkspaceView}
|
||||
import ocelot.desktop.util.DrawUtils
|
||||
@ -37,6 +38,11 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) =>
|
||||
window.foreach(window => workspaceView.windowPool.openWindow(window))
|
||||
|
||||
case ClickEvent(MouseEvent.Button.Right, _) =>
|
||||
val menu = new ContextMenu
|
||||
setupContextMenu(menu)
|
||||
workspaceView.rootWidget.contextMenus.open(menu)
|
||||
|
||||
case DragEvent(DragEvent.State.Start, MouseEvent.Button.Left, pos) =>
|
||||
startMoving(pos)
|
||||
|
||||
@ -65,6 +71,22 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
|
||||
highlight.goto(NoHighlight)
|
||||
}
|
||||
|
||||
def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("Cut"))
|
||||
menu.addEntry(new ContextMenuEntry("Copy"))
|
||||
menu.addEntry(new ContextMenuEntry("Paste"))
|
||||
menu.addSeparator()
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("Disconnect", () => {
|
||||
disconnectFromAll()
|
||||
}))
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("Delete", () => {
|
||||
disconnectFromAll()
|
||||
workspaceView.nodes -= (this)
|
||||
}))
|
||||
}
|
||||
|
||||
def environment: Environment
|
||||
|
||||
def icon: String = "icons/NA"
|
||||
@ -91,6 +113,12 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
|
||||
node.onConnectionRemoved(portB, this, portA)
|
||||
}
|
||||
|
||||
def disconnectFromAll(): Unit = {
|
||||
for ((a, node, b) <- connections) {
|
||||
disconnect(a, node, b)
|
||||
}
|
||||
}
|
||||
|
||||
def isConnected(portA: NodePort, node: Node, portB: NodePort): Boolean = {
|
||||
_connections.contains((portA, node, portB))
|
||||
}
|
||||
@ -139,6 +167,8 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
|
||||
|
||||
override def minimumSize: Size2D = Size2D(68, 68)
|
||||
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
|
||||
private def startMoving(pos: Vector2D): Unit = {
|
||||
highlight.goto(MovingHighlight)
|
||||
isMoving = true
|
||||
@ -195,5 +225,5 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
|
||||
}
|
||||
}
|
||||
|
||||
lazy val window: Option[Window] = None
|
||||
def window: Option[Window] = None
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import ocelot.desktop.util.TierColor
|
||||
|
||||
class NodeTypeWidget(val nodeType: NodeType) extends Widget with ClickHandler {
|
||||
override def minimumSize: Size2D = Size2D(68, 68)
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
|
||||
size = maximumSize
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.Node
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.util.TierColor
|
||||
import totoro.ocelot.brain.entity.traits.Computer
|
||||
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, InternetCard, Memory, NetworkCard}
|
||||
@ -28,7 +29,29 @@ class ComputerNode(val computer: Case) extends Node {
|
||||
override def environment: Computer = computer
|
||||
|
||||
override val icon: String = "nodes/Computer"
|
||||
override val iconColor: Color = TierColor.get(computer.tier)
|
||||
override def iconColor: Color = TierColor.get(computer.tier)
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
if (computer.machine.isRunning)
|
||||
menu.addEntry(new ContextMenuEntry("Turn off", () => computer.turnOff()))
|
||||
else
|
||||
menu.addEntry(new ContextMenuEntry("Turn on", () => computer.turnOn()))
|
||||
|
||||
menu.addEntry(new ContextMenuSubmenu("Set tier") {
|
||||
addEntry(new ContextMenuEntry("Tier 1", () => changeTier(Tier.One)))
|
||||
addEntry(new ContextMenuEntry("Tier 2", () => changeTier(Tier.Two)))
|
||||
addEntry(new ContextMenuEntry("Tier 3", () => changeTier(Tier.Three)))
|
||||
addEntry(new ContextMenuEntry("Tier 4 (Creative)", () => changeTier(Tier.Four)))
|
||||
})
|
||||
|
||||
menu.addSeparator()
|
||||
super.setupContextMenu(menu)
|
||||
}
|
||||
|
||||
private def changeTier(n: Int): Unit = {
|
||||
computer.tier = n
|
||||
currentWindow = new ComputerWindow(computer)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
@ -46,5 +69,12 @@ class ComputerNode(val computer: Case) extends Node {
|
||||
g.sprite("nodes/ComputerActivityOverlay", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
}
|
||||
|
||||
override lazy val window: Option[ComputerWindow] = Some(new ComputerWindow(computer))
|
||||
private var currentWindow: ComputerWindow = _
|
||||
|
||||
override def window: Option[ComputerWindow] = {
|
||||
if (currentWindow == null) {
|
||||
currentWindow = new ComputerWindow(computer)
|
||||
Some(currentWindow)
|
||||
} else Some(currentWindow)
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@ import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.Node
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.util.TierColor
|
||||
import totoro.ocelot.brain.entity.traits.Environment
|
||||
import totoro.ocelot.brain.entity.{Keyboard, Screen}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
class ScreenNode(val screen: Screen) extends Node {
|
||||
OcelotDesktop.workspace.add(screen)
|
||||
@ -22,6 +24,27 @@ class ScreenNode(val screen: Screen) extends Node {
|
||||
|
||||
override def iconColor: Color = TierColor.get(screen.tier)
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
if (screen.getPowerState)
|
||||
menu.addEntry(new ContextMenuEntry("Turn off", () => screen.setPowerState(false)))
|
||||
else
|
||||
menu.addEntry(new ContextMenuEntry("Turn on", () => screen.setPowerState(true)))
|
||||
|
||||
menu.addEntry(new ContextMenuSubmenu("Set tier") {
|
||||
addEntry(new ContextMenuEntry("Tier 1", () => changeTier(Tier.One)))
|
||||
addEntry(new ContextMenuEntry("Tier 2", () => changeTier(Tier.Two)))
|
||||
addEntry(new ContextMenuEntry("Tier 3", () => changeTier(Tier.Three)))
|
||||
})
|
||||
|
||||
menu.addSeparator()
|
||||
super.setupContextMenu(menu)
|
||||
}
|
||||
|
||||
private def changeTier(n: Int): Unit = {
|
||||
screen.tier = n
|
||||
currentWindow = new ScreenWindow(screen)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
|
||||
@ -29,5 +52,12 @@ class ScreenNode(val screen: Screen) extends Node {
|
||||
g.sprite("nodes/ScreenOnOverlay", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
}
|
||||
|
||||
override lazy val window: Option[ScreenWindow] = Some(new ScreenWindow(screen))
|
||||
private var currentWindow: ScreenWindow = _
|
||||
|
||||
override def window: Option[ScreenWindow] = {
|
||||
if (currentWindow == null) {
|
||||
currentWindow = new ScreenWindow(screen)
|
||||
Some(currentWindow)
|
||||
} else Some(currentWindow)
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,6 +203,9 @@ object UiHandler extends Logging {
|
||||
for (event <- KeyEvents.events)
|
||||
hierarchy.reverseIterator.foreach(_.handleEvent(event))
|
||||
|
||||
for (event <- MouseEvents.events)
|
||||
hierarchy.reverseIterator.filter(_.receiveAllMouseEvents).foreach(_.handleEvent(event))
|
||||
|
||||
val scrollTarget = hierarchy.reverseIterator
|
||||
.find(w => w.receiveScrollEvents && w.clippedBounds.contains(mousePos))
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@ class LinearLayout(widget: Widget,
|
||||
var usedSpace = 0f
|
||||
|
||||
for (child <- widget.children) {
|
||||
child.recalculateBounds()
|
||||
val minSize = child.minimumSize
|
||||
|
||||
if (stretch) {
|
||||
|
||||
@ -54,6 +54,7 @@ class IconButton(releasedIcon: String, pressedIcon: String,
|
||||
private def pressedIconSize: Size2D = Spritesheet.spriteSize(pressedIcon) * sizeMultiplier
|
||||
|
||||
override def minimumSize: Size2D = releasedIconSize.max(pressedIconSize)
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
if (alphaAnimation.value < 1f)
|
||||
|
||||
@ -14,7 +14,8 @@ class Label extends Widget {
|
||||
private var length = text.length * 8
|
||||
private def wideLength(g: Graphics): Int = text.map(g.font.charWidth(_)).sum
|
||||
|
||||
override def minimumSize: Size2D = Size2D(length, 8)
|
||||
override def minimumSize: Size2D = Size2D(length, if (isSmall) 8 else 16)
|
||||
override def maximumSize: Size2D = minimumSize.copy(width = Float.PositiveInfinity)
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
if (isSmall) g.setSmallFont()
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ui.layout.LinearLayout
|
||||
import ocelot.desktop.ui.layout.CopyLayout
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenus
|
||||
|
||||
class RootWidget extends Widget {
|
||||
override protected val layout = new LinearLayout(this)
|
||||
override protected val layout = new CopyLayout(this)
|
||||
|
||||
children :+= new WorkspaceView
|
||||
val workspaceView: WorkspaceView = new WorkspaceView
|
||||
val contextMenus: ContextMenus = new ContextMenus
|
||||
|
||||
children :+= workspaceView
|
||||
children :+= contextMenus
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ class SlotWidget extends Widget {
|
||||
def tierIcon: String = null
|
||||
|
||||
override def minimumSize: Size2D = Size2D(36, 36)
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
g.sprite("EmptySlot", bounds)
|
||||
|
||||
@ -21,7 +21,10 @@ class Widget {
|
||||
|
||||
final def relayout(): Unit = layout.relayout()
|
||||
|
||||
final def recalculateBounds(): Unit = layout.recalculateBounds()
|
||||
final def recalculateBounds(): Unit = {
|
||||
layout.recalculateBounds()
|
||||
size = _size
|
||||
}
|
||||
|
||||
final def relayoutParent(): Unit = {
|
||||
shouldRelayoutParent = true
|
||||
@ -62,8 +65,8 @@ class Widget {
|
||||
val clamped = value.clamped(minimumSize, maximumSize)
|
||||
|
||||
if (clamped != _size) {
|
||||
recalculateBounds()
|
||||
_size = clamped.copy()
|
||||
layout.recalculateBounds()
|
||||
_size = value.clamped(minimumSize, maximumSize)
|
||||
relayout()
|
||||
shouldRelayoutParent = true
|
||||
}
|
||||
@ -128,4 +131,6 @@ class Widget {
|
||||
def receiveScrollEvents: Boolean = false
|
||||
|
||||
def receiveMouseEvents: Boolean = false
|
||||
|
||||
def receiveAllMouseEvents: Boolean = false
|
||||
}
|
||||
|
||||
@ -39,6 +39,8 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
|
||||
|
||||
createDefaultWorkspace()
|
||||
|
||||
def rootWidget: RootWidget = parent.get.asInstanceOf[RootWidget]
|
||||
|
||||
def addNode(node: Node, pos: Vector2D = newNodePos): Unit = {
|
||||
node.position = pos + cameraOffset
|
||||
resolveCollision(node)
|
||||
@ -87,6 +89,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
|
||||
else
|
||||
p - Vector2D(32, 32)
|
||||
|
||||
nodeSelector.recalculateBounds()
|
||||
windowPool.openWindow(nodeSelector)
|
||||
newNodePos = pos - cameraOffset
|
||||
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
package ocelot.desktop.ui.widget.contextmenu
|
||||
|
||||
import ocelot.desktop.color.IntColor
|
||||
import ocelot.desktop.geometry.Padding2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.layout.LinearLayout
|
||||
import ocelot.desktop.ui.widget.{PaddingBox, Widget}
|
||||
import ocelot.desktop.util.animation.ValueAnimation
|
||||
import ocelot.desktop.util.animation.easing.EaseInOutQuad
|
||||
import ocelot.desktop.util.{DrawUtils, Orientation}
|
||||
|
||||
class ContextMenu extends Widget {
|
||||
private[contextmenu] var isClosing = false
|
||||
private[contextmenu] var isOpening = false
|
||||
|
||||
protected var _contextMenus: ContextMenus = _
|
||||
protected val alpha = new ValueAnimation(0f, 8f)
|
||||
protected var inner: Widget = new Widget {
|
||||
override protected val layout = new LinearLayout(this, orientation = Orientation.Vertical)
|
||||
}
|
||||
|
||||
children :+= new PaddingBox(inner, Padding2D(top = 4, bottom = 4))
|
||||
|
||||
private[contextmenu] def contextMenus: ContextMenus = _contextMenus
|
||||
|
||||
private[contextmenu] def contextMenus_=(value: ContextMenus): Unit = {
|
||||
entries.foreach(_.contextMenus = value)
|
||||
_contextMenus = value
|
||||
}
|
||||
|
||||
def addEntry(entry: ContextMenuEntry): Unit = {
|
||||
entry.contextMenus = contextMenus
|
||||
entry.contextMenu = this
|
||||
inner.children :+= entry
|
||||
}
|
||||
|
||||
def addSeparator(): Unit = {
|
||||
inner.children :+= new Separator
|
||||
}
|
||||
|
||||
private def entries: Array[ContextMenuEntry] = inner.children
|
||||
.filter(_.isInstanceOf[ContextMenuEntry])
|
||||
.map(_.asInstanceOf[ContextMenuEntry])
|
||||
|
||||
def isClosed: Boolean = {
|
||||
alpha.value == 0f
|
||||
}
|
||||
|
||||
def open(): Unit = {
|
||||
isClosing = false
|
||||
isOpening = true
|
||||
alpha.jump(0.001f)
|
||||
alpha.goto(1f)
|
||||
|
||||
var offset = 0.5f
|
||||
val step = 0.5f / entries.length.toFloat
|
||||
for (entry <- entries) {
|
||||
entry.isGhost = false
|
||||
entry.textAlpha.jump(offset)
|
||||
entry.textAlpha.goto(1f)
|
||||
entry.trans.easing = EaseInOutQuad
|
||||
entry.trans.jump(-6f + offset * 6f)
|
||||
entry.trans.goto(0f)
|
||||
offset -= step
|
||||
}
|
||||
}
|
||||
|
||||
def close(): Unit = {
|
||||
isClosing = true
|
||||
isOpening = false
|
||||
alpha.goto(0f)
|
||||
|
||||
for (entry <- entries) {
|
||||
entry.isGhost = true
|
||||
entry.textAlpha.goto(1f)
|
||||
entry.trans.easing = EaseInOutQuad
|
||||
entry.trans.goto(-8f)
|
||||
}
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
alpha.update()
|
||||
if (alpha.value < 1f) g.beginGroupAlpha() else isOpening = false
|
||||
|
||||
DrawUtils.shadow(g, bounds.x - 8, bounds.y - 8, bounds.w + 16, bounds.h + 20, 0.5f)
|
||||
g.rect(bounds, IntColor(0x222222).withAlpha(0.8f))
|
||||
DrawUtils.ring(g, bounds.x, bounds.y, bounds.w, bounds.h, 1, IntColor(0x444444))
|
||||
|
||||
drawChildren(g)
|
||||
|
||||
if (alpha.value < 1f) g.endGroupAlpha(alpha.value)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package ocelot.desktop.ui.widget.contextmenu
|
||||
|
||||
import ocelot.desktop.color.{Color, IntColor}
|
||||
import ocelot.desktop.geometry.{Padding2D, Size2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.event.handlers.{ClickHandler, HoverHandler}
|
||||
import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.{Label, PaddingBox, Widget}
|
||||
import ocelot.desktop.util.animation.ValueAnimation
|
||||
import ocelot.desktop.util.animation.easing.{EaseInQuad, EaseOutQuad}
|
||||
|
||||
class ContextMenuEntry(label: String, onClick: () => Unit = () => {}) extends Widget with ClickHandler with HoverHandler {
|
||||
private[contextmenu] val alpha = new ValueAnimation(0f, 10f)
|
||||
private[contextmenu] val textAlpha = new ValueAnimation(0f, 5f)
|
||||
private[contextmenu] val trans = new ValueAnimation(0f, 20f)
|
||||
private[contextmenu] var contextMenus: ContextMenus = _
|
||||
private[contextmenu] var contextMenu: ContextMenu = _
|
||||
private[contextmenu] var isGhost: Boolean = false
|
||||
|
||||
children :+= new PaddingBox(new Label {
|
||||
override def text: String = label
|
||||
override def color: Color = IntColor(0xB0B0B0)
|
||||
}, Padding2D(left = 12f, right = 16f, top = 5f, bottom = 5f))
|
||||
|
||||
override def receiveMouseEvents: Boolean = !isGhost
|
||||
|
||||
eventHandlers += {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) if !contextMenu.isOpening => clicked()
|
||||
case HoverEvent(HoverEvent.State.Enter) => enter()
|
||||
case HoverEvent(HoverEvent.State.Leave) if !isGhost => leave()
|
||||
}
|
||||
|
||||
override def minimumSize: Size2D = layout.minimumSize.max(Size2D(150, 1))
|
||||
|
||||
protected def clicked(): Unit = {
|
||||
onClick()
|
||||
contextMenus.closeAll()
|
||||
contextMenus.setGhost(this)
|
||||
|
||||
isGhost = true
|
||||
alpha.goto(0f)
|
||||
textAlpha.goto(0f)
|
||||
alpha.speed = 2.5f
|
||||
textAlpha.speed = 2.5f
|
||||
trans.speed = 0f
|
||||
}
|
||||
|
||||
protected def enter(): Unit = {
|
||||
alpha.speed = 10f
|
||||
alpha.goto(1f)
|
||||
trans.easing = EaseInQuad
|
||||
trans.goto(2f)
|
||||
}
|
||||
|
||||
protected def leave(): Unit = {
|
||||
alpha.speed = 1f
|
||||
alpha.goto(0f)
|
||||
trans.easing = EaseOutQuad
|
||||
trans.goto(0f)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
alpha.update()
|
||||
textAlpha.update()
|
||||
trans.update()
|
||||
|
||||
g.rect(bounds.mapW(_ - 8).mapX(_ + 4), IntColor(0x333333).withAlpha(alpha.value))
|
||||
|
||||
g.save()
|
||||
g.translate(trans.value, 0f)
|
||||
g.alphaMultiplier = textAlpha.value
|
||||
drawChildren(g)
|
||||
g.restore()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package ocelot.desktop.ui.widget.contextmenu
|
||||
|
||||
import ocelot.desktop.color.{Color, IntColor}
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.HoverEvent
|
||||
|
||||
class ContextMenuSubmenu(label: String) extends ContextMenuEntry(label) {
|
||||
private val parentEntry = this
|
||||
private val submenu = new ContextMenu {
|
||||
override def update(): Unit = {
|
||||
super.update()
|
||||
if (!isClosing && !bounds.inflate(4).contains(UiHandler.mousePosition)
|
||||
&& !parentEntry.isHovered) close()
|
||||
}
|
||||
}
|
||||
|
||||
def addEntry(entry: ContextMenuEntry): Unit = {
|
||||
submenu.addEntry(entry)
|
||||
}
|
||||
|
||||
def addSeparator(): Unit = {
|
||||
submenu.addSeparator()
|
||||
}
|
||||
|
||||
override def clicked(): Unit = {}
|
||||
|
||||
override def leave(): Unit = {
|
||||
if (submenu.isClosed) super.leave()
|
||||
}
|
||||
|
||||
override def update(): Unit = {
|
||||
if (!isHovered && submenu.isClosed) super.leave()
|
||||
super.update()
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
g.background = Color.Transparent
|
||||
g.foreground = IntColor(0xB0B0B0).withAlpha(textAlpha.value)
|
||||
g.text(position.x + width - 24, position.y + 5, ">")
|
||||
}
|
||||
|
||||
eventHandlers += {
|
||||
case HoverEvent(HoverEvent.State.Enter) if !isGhost =>
|
||||
contextMenus.open(submenu, position + Vector2D(width, 0), isSubmenu = true)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package ocelot.desktop.ui.widget.contextmenu
|
||||
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.{KeyEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.layout.Layout
|
||||
import ocelot.desktop.ui.widget.Widget
|
||||
import org.lwjgl.input.Keyboard
|
||||
|
||||
class ContextMenus extends Widget {
|
||||
override protected val layout: Layout = new Layout(this)
|
||||
|
||||
private var ghost: Option[ContextMenuEntry] = None
|
||||
|
||||
private def menus: Array[ContextMenu] = children.map(_.asInstanceOf[ContextMenu])
|
||||
|
||||
private[contextmenu] def setGhost(g: ContextMenuEntry): Unit = {
|
||||
ghost = Some(g)
|
||||
}
|
||||
|
||||
override def receiveAllMouseEvents: Boolean = true
|
||||
|
||||
eventHandlers += {
|
||||
case KeyEvent(KeyEvent.State.Press, Keyboard.KEY_ESCAPE, _) =>
|
||||
closeAll()
|
||||
|
||||
case MouseEvent(MouseEvent.State.Press, _) =>
|
||||
if (!menus.map(_.bounds).exists(_.contains(UiHandler.mousePosition))) closeAll()
|
||||
}
|
||||
|
||||
def open(menu: ContextMenu, pos: Vector2D = UiHandler.mousePosition + Vector2D(2, 2), isSubmenu: Boolean = false): Unit = {
|
||||
if (!isSubmenu) for (child <- menus) close(child)
|
||||
menu.position = pos
|
||||
menu.size = menu.minimumSize
|
||||
menu.contextMenus = this
|
||||
menu.open()
|
||||
children :+= menu
|
||||
}
|
||||
|
||||
def close(menu: ContextMenu): Unit = {
|
||||
menu.close()
|
||||
}
|
||||
|
||||
def closeAll(): Unit = {
|
||||
for (child <- menus) close(child)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
if (ghost.isDefined && ghost.get.alpha == 0f) ghost = None
|
||||
ghost.foreach(_.draw(g))
|
||||
}
|
||||
|
||||
override def update(): Unit = {
|
||||
super.update()
|
||||
children = children.filterNot(_.asInstanceOf[ContextMenu].isClosed)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package ocelot.desktop.ui.widget.contextmenu
|
||||
|
||||
import ocelot.desktop.color.IntColor
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.widget.Widget
|
||||
|
||||
class Separator extends Widget {
|
||||
override def minimumSize: Size2D = Size2D(50, 9)
|
||||
|
||||
override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 9)
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
g.rect(Rect2D(position + Vector2D(0, 4), size.copy(height = 1)), IntColor(0x444444))
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ object DrawUtils {
|
||||
|
||||
def windowWithShadow(g: Graphics, x: Float, y: Float, w: Float, h: Float,
|
||||
backgroundAlpha: Float, shadowAlpha: Float): Unit = {
|
||||
DrawUtils.shadow(g, x - 8, y - 8, w + 12, h + 20, shadowAlpha)
|
||||
DrawUtils.shadow(g, x - 8, y - 8, w + 16, h + 20, shadowAlpha)
|
||||
DrawUtils.windowBorder(g, x, y, w, h, RGBAColorNorm(1, 1, 1, backgroundAlpha))
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ object DrawUtils {
|
||||
g.sprite("ShadowBorder", 0, 0, h - 48, 24, col)
|
||||
g.restore()
|
||||
|
||||
g.rect(x + 24, y + 24, w - 48, h - 49, RGBAColorNorm(0, 0, 0, a))
|
||||
g.rect(x + 24, y + 24, w - 48, h - 48, RGBAColorNorm(0, 0, 0, a))
|
||||
}
|
||||
|
||||
private def rotSprite(g: Graphics, sprite: String, x: Float, y: Float, w: Float, h: Float, angle: Float,
|
||||
|
||||
@ -3,7 +3,7 @@ package ocelot.desktop.util.animation
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.util.animation.easing.{Easing, EasingFunction}
|
||||
|
||||
class ValueAnimation(init: Float = 0f, speed: Float = 10f) {
|
||||
class ValueAnimation(init: Float = 0f, var speed: Float = 10f) {
|
||||
private var _value = init
|
||||
private var start = init
|
||||
private var end = init
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user