Major node selector improvements

This commit is contained in:
LeshaInc 2020-05-30 22:11:23 +03:00
parent e13b522030
commit 2f4b7dd596
No known key found for this signature in database
GPG Key ID: B4855290FC36DE72
11 changed files with 97 additions and 31 deletions

BIN
sprites/nodes/NewNode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -77,17 +77,18 @@ nodes/Computer 339 180 16 16
nodes/ComputerActivityOverlay 356 180 16 16
nodes/ComputerErrorOverlay 373 180 16 16
nodes/ComputerOnOverlay 390 180 16 16
nodes/Screen 407 180 16 16
nodes/ScreenOnOverlay 424 180 16 16
nodes/NewNode 407 180 16 16
nodes/Screen 424 180 16 16
nodes/ScreenOnOverlay 441 180 16 16
screen/BorderB 310 197 2 8
screen/BorderT 307 197 2 10
screen/CornerBL 459 180 8 8
screen/CornerBR 468 180 8 8
screen/CornerTL 441 180 8 10
screen/CornerTR 450 180 8 10
screen/CornerBL 476 180 8 8
screen/CornerBR 485 180 8 8
screen/CornerTL 458 180 8 10
screen/CornerTR 467 180 8 10
window/BorderDark 333 197 1 4
window/BorderLight 335 197 1 4
window/CloseButton 477 180 7 6
window/CloseButton 494 180 7 6
window/CornerBL 313 197 4 4
window/CornerBR 318 197 4 4
window/CornerTL 323 197 4 4

View File

@ -7,6 +7,8 @@ object Rect2D {
case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
def contains(p: Vector2D): Boolean = p.x >= x && p.y >= y && p.x <= x + w && p.y <= y + h
def contains(r: Rect2D): Boolean = r.x >= x && r.y >= y && r.x + r.w <= x + w && r.y + r.h <= y + h
def origin: Vector2D = Vector2D(x, y)
def min: Vector2D = origin

View File

@ -3,8 +3,8 @@ package ocelot.desktop.ui.widget
import ocelot.desktop.color.RGBAColorNorm
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.{KeyEvents, UiHandler}
import ocelot.desktop.ui.event.{MouseEvent, ScrollEvent}
import ocelot.desktop.ui.{KeyEvents, UiHandler}
import ocelot.desktop.util.Logging
class ScrollView(val inner: Widget) extends Widget with Logging {
@ -114,13 +114,13 @@ class ScrollView(val inner: Widget) extends Widget with Logging {
private def drawVThumb(g: Graphics): Unit = {
val b = vThumbBounds
g.rect(position.x + size.width - 11, position.y, 10, size.height, RGBAColorNorm(0.9f, 0.9f, 0.9f, vAnim * 0.15f))
g.rect(b.x + 3, b.y, b.w - 6, b.h, RGBAColorNorm(0.6f, 0.15f, 0.35f, vAnim * 0.5f + 0.4f))
g.rect(b.x + 3, b.y, b.w - 6, b.h, RGBAColorNorm(0.8f, 0.25f, 0.45f, vAnim * 0.5f + 0.4f))
}
private def drawHThumb(g: Graphics): Unit = {
val b = hThumbBounds
g.rect(position.x, position.y + size.height - 11, size.width - 12, 10, RGBAColorNorm(0.9f, 0.9f, 0.9f, hAnim * 0.15f))
g.rect(b.x, b.y + 3, b.w, b.h - 6, RGBAColorNorm(0.6f, 0.15f, 0.35f, hAnim * 0.5f + 0.4f))
g.rect(b.x, b.y + 3, b.w, b.h - 6, RGBAColorNorm(0.8f, 0.25f, 0.45f, hAnim * 0.5f + 0.4f))
}
private def maxXOffset: Float = inner.size.width - size.width

View File

@ -1,5 +1,6 @@
package ocelot.desktop.ui.window
import ocelot.desktop.color.RGBAColorNorm
import ocelot.desktop.geometry.{Padding2D, Rect2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.UiHandler
@ -7,7 +8,7 @@ import ocelot.desktop.ui.event.CloseWindowEvent
import ocelot.desktop.ui.layout.LinearLayout
import ocelot.desktop.ui.widget.{PaddingBox, ScrollView, Widget}
import ocelot.desktop.ui.workspace.{NodeRegistry, NodeTypeWidget}
import ocelot.desktop.util.animation.UnitAnimation
import ocelot.desktop.util.animation.{ColorAnimation, UnitAnimation}
import ocelot.desktop.util.{DrawUtils, Logging, Orientation}
class NodeSelector extends Window with Logging {
@ -52,10 +53,14 @@ class NodeSelector extends Window with Logging {
private var heightAnimation: UnitAnimation = _
private var animationTime: Float = 0f
private var FinalRingColor: RGBAColorNorm = RGBAColorNorm(0.3f, 0.5f, 0.4f)
val colorAnimation: ColorAnimation = new ColorAnimation(FinalRingColor.copy(a = 0f), speed = 3f)
def isShown: Boolean = _isShown
override def update(): Unit = {
if (isShown) {
colorAnimation.update()
heightAnimation.update()
if (isClosing) {
@ -81,7 +86,9 @@ class NodeSelector extends Window with Logging {
override def draw(g: Graphics): Unit = {
if (!isShown) return
DrawUtils.windowWithShadow(g, position.x, position.y, size.width, size.height, 0.5f, 0.3f)
g.rect(position.x, position.y, size.width, size.height, RGBAColorNorm(0, 0, 0, colorAnimation.getHSVA.a * 0.4f))
DrawUtils.ring(g, position.x, position.y, size.width, size.height, thickness = 1, color = colorAnimation.color)
super.draw(g)
}
@ -113,6 +120,8 @@ class NodeSelector extends Window with Logging {
override def show(): Unit = {
if (isClosing) return;
colorAnimation.goto(FinalRingColor.copy(a = 1f))
_isOpen = true
_isFocused = true
_isShown = true
@ -125,6 +134,8 @@ class NodeSelector extends Window with Logging {
override def hide(): Unit = {
if (isClosing) return;
colorAnimation.goto(FinalRingColor.copy(a = 0f))
heightAnimation = UnitAnimation.easeInQuad(0.25f)
heightAnimation.time = 1
heightAnimation.goDown()

View File

@ -9,7 +9,15 @@ object NodeRegistry {
types += t;
}
for (i <- 0 to 100) {
register(NodeType("Screen" + i, "nodes/Screen", () => new ScreenNode))
for (i <- 0 to 79) {
register(NodeType("Screen" + i, "nodes/Screen", i % 4, () => new ScreenNode))
}
for (i <- 0 to 3) {
register(NodeType("Screen" + i, "nodes/Screen", i % 4, () => new ScreenNode))
}
for (i <- 0 to 3) {
register(NodeType("Computer" + i, "nodes/Computer", i % 4, () => new ScreenNode))
}
}

View File

@ -1,5 +1,5 @@
package ocelot.desktop.ui.workspace
case class NodeType(name: String, icon: String, factory: () => Node) extends Ordered[NodeType] {
case class NodeType(name: String, icon: String, tier: Int, factory: () => Node) extends Ordered[NodeType] {
override def compare(that: NodeType): Int = this.name.compare(that.name)
}

View File

@ -3,6 +3,7 @@ package ocelot.desktop.ui.workspace
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.widget.Widget
import ocelot.desktop.util.TierColor
class NodeTypeWidget(nodeType: NodeType) extends Widget {
override def minimumSize: Size2D = Size2D(68, 68)
@ -12,6 +13,6 @@ class NodeTypeWidget(nodeType: NodeType) extends Widget {
size = maximumSize
override def draw(g: Graphics): Unit = {
g.sprite(nodeType.icon, position.x + 2, position.y + 2, size.width - 4, size.height - 4)
g.sprite(nodeType.icon, position.x + 2, position.y + 2, size.width - 4, size.height - 4, TierColor.get(nodeType.tier))
}
}

View File

@ -1,7 +1,7 @@
package ocelot.desktop.ui.workspace
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.color.RGBAColor
import ocelot.desktop.color.{Color, RGBAColor}
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.event._
@ -19,8 +19,9 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler {
private var nodeGrabPoint = Vector2D(0, 0)
private var cameraOffset = Vector2D(0, 0)
private val nodeSelector: NodeSelector = new NodeSelector
private val windowPool: WindowPool = new WindowPool
private var newNodePos = Vector2D(0, 0)
private val nodeSelector = new NodeSelector
private val windowPool = new WindowPool
children +:= windowPool
override def minimumSize: Size2D = Size2D(200, 200)
@ -67,12 +68,31 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler {
if (nodeSelector.isShown) {
nodeSelector.hide()
} else {
windowPool.handleEvent(OpenWindowEvent(nodeSelector))
openSelector(pos)
}
}
}
private def openSelector(p: Vector2D): Unit = {
val pos = p - Vector2D(32, 32)
windowPool.handleEvent(OpenWindowEvent(nodeSelector))
newNodePos = pos - cameraOffset
val size = Size2D(nodeSelector.maximumSize.width, 250)
val offsets = Array(
Vector2D(-size.width / 2 + 40, 100),
Vector2D(-size.width - 100, -size.height / 2 + 40),
Vector2D(100, -size.height / 2 + 40),
Vector2D(-size.width / 2 + 40, -size.height - 100),
)
for (offset <- offsets) {
nodeSelector.position = (p + offset).max(Vector2D(20, 20)).min(Vector2D(width - size.width - 20, height - size.height - 20))
if (!Rect2D(pos.x, pos.y, 64, 64).collides(Rect2D(nodeSelector.position, size))) return
}
}
private def checkCollision(node: Node, obstacle: Node): Unit = {
val a = node.bounds
val b = obstacle.bounds
@ -83,7 +103,9 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler {
node.position -= penetrationVector
}
private def drawConnection(g: Graphics, a: Rect2D, b: Rect2D, thickness: Float = 4): Unit = {
private def drawConnection(g: Graphics, a: Rect2D, b: Rect2D, thickness: Float = 4, col: Color = RGBAColor(150, 150, 150)): Unit = {
if (a.collides(b)) return
val dist = a.distanceTo(b) / 2f
val addition = if (dist <= 58.885f)
@ -96,16 +118,20 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler {
val aRect = a.inflate(addition.toFloat)
val bRect = b.inflate(addition.toFloat)
val pairs: Array[(Vector2D, Vector2D)] =
for (a <- aRect.centerPoints; b <- bRect.centerPoints)
yield (a, b)
val aCPInf = aRect.centerPoints
val bCPInf = bRect.centerPoints
val aCP = a.centerPoints
val bCP = b.centerPoints
val pair = pairs.minBy(pair => (pair._1 - pair._2).length.toInt)
val (start, end) = pair
val col = RGBAColor(150, 150, 150)
g.line(start, end, thickness, col)
g.line(start, a.center, thickness, col)
g.line(end, b.center, thickness, col)
val pairs =
for (a <- 0 to 3; b <- 0 to 3)
yield (aCP(a), bCP(b), aCPInf(a), bCPInf(b))
val pair = pairs.minBy(pair => (pair._3 - pair._4).length.toInt)
val (aStart, bStart, aEnd, bEnd) = pair
g.line(aEnd, bEnd, thickness, col)
g.line(aStart, aEnd, thickness, col)
g.line(bStart, bEnd, thickness, col)
}
override def draw(g: Graphics): Unit = {
@ -129,9 +155,19 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler {
g.save()
g.translate(cameraOffset.x + position.x, cameraOffset.y + position.y)
drawConnection(g, nodes(0).bounds, nodes(1).bounds)
nodes.foreach(_.draw(g))
val col = nodeSelector.colorAnimation.color
if (nodeSelector.isShown) {
g.sprite("nodes/NewNode", newNodePos.x, newNodePos.y, 64, 64, col)
}
g.restore()
if (nodeSelector.isShown)
drawConnection(g, Rect2D(newNodePos.x + cameraOffset.x, newNodePos.y + cameraOffset.y, 64, 64), nodeSelector.bounds, col = col)
drawChildren(g)
}

View File

@ -33,6 +33,13 @@ object DrawUtils {
DrawUtils.windowBorder(g, x, y, w, h, RGBAColorNorm(1, 1, 1, backgroundAlpha))
}
def ring(g: Graphics, x: Float, y: Float, w: Float, h: Float, thickness: Float = 4, color: Color = RGBAColor(255, 255, 255)): Unit = {
g.rect(x, y, thickness, h, color)
g.rect(x + w - thickness, y, thickness, h, color)
g.rect(x + thickness, y, w - thickness * 2f, thickness, color)
g.rect(x + thickness, y + h - thickness, w - thickness * 2f, thickness, color)
}
def windowBorder(g: Graphics, x: Float, y: Float, w: Float, h: Float,
color: Color = RGBAColor(255, 255, 255)): Unit = {
g.sprite("window/CornerTL", x, y, 8, 8, color)