changeset 434:30cc1deda4c1

add: input-processing for panels
author Sam <sam@basx.dev>
date Sat, 10 Feb 2024 21:19:31 +0700
parents 08b8facaf3b7
children 065854cdba52
files semicongine/engine.nim semicongine/panel.nim tests/test_panel.nim
diffstat 3 files changed, 120 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/engine.nim	Sat Feb 10 15:55:05 2024 +0700
+++ b/semicongine/engine.nim	Sat Feb 10 21:19:31 2024 +0700
@@ -218,3 +218,25 @@
 
 func limits*(engine: Engine): VkPhysicalDeviceLimits =
   engine.gpuDevice().physicalDevice.properties.limits
+
+proc processEventsFor*(engine: Engine, panel: var Panel) =
+  let hasMouseNow = panel.contains(engine.mousePositionNormalized())
+
+  # enter/leave events
+  if hasMouseNow:
+    if panel.hasMouse:
+      if panel.onMouseMove != nil: panel.onMouseMove(panel)
+    else:
+      if panel.onMouseEnter != nil: panel.onMouseEnter(panel)
+  else:
+    if panel.hasMouse:
+      if panel.onMouseLeave != nil: panel.onMouseLeave(panel)
+
+  # button events
+  if hasMouseNow:
+    if engine.input.mouseWasPressed.len > 0:
+      if panel.onMouseDown != nil: panel.onMouseDown(panel, engine.input.mouseWasPressed)
+    if engine.input.mouseWasReleased.len > 0:
+      if panel.onMouseUp != nil: panel.onMouseUp(panel, engine.input.mouseWasReleased)
+
+  panel.hasMouse = hasMouseNow
--- a/semicongine/panel.nim	Sat Feb 10 15:55:05 2024 +0700
+++ b/semicongine/panel.nim	Sat Feb 10 21:19:31 2024 +0700
@@ -5,6 +5,7 @@
 import ./mesh
 import ./material
 import ./vulkan/shader
+import ./events
 
 const
   # font shader
@@ -45,13 +46,19 @@
   Panel* = object
     position: Vec2f
     size: Vec2f
-
     texture: Texture
     horizontalAlignment: HorizontalAlignment = Center
     verticalAlignment: VerticalAlignment = Center
     aspect_ratio: float32
     dirty: bool
     mesh: Mesh
+    # input handling
+    onMouseDown*: proc(panel: var Panel, buttons: set[MouseButton])
+    onMouseUp*: proc(panel: var Panel, buttons: set[MouseButton])
+    onMouseEnter*: proc(panel: var Panel)
+    onMouseMove*: proc(panel: var Panel)
+    onMouseLeave*: proc(panel: var Panel)
+    hasMouse*: bool
 
 proc `$`*(panel: Panel): string =
   &"Panel {panel.position} (size {panel.size})"
@@ -89,9 +96,33 @@
 
   panel.dirty = false
 
-proc initPanel*(position = newVec2f(), size = newVec2f(), color = newVec4f(1, 1, 1, 1), texture = EMPTY_TEXTURE, horizontalAlignment = HorizontalAlignment.Center, verticalAlignment = VerticalAlignment.Center): Panel =
+proc initPanel*(
+  position = newVec2f(),
+  size = newVec2f(),
+  color = newVec4f(1, 1, 1, 1),
+  texture = EMPTY_TEXTURE,
+  horizontalAlignment = HorizontalAlignment.Center,
+  verticalAlignment = VerticalAlignment.Center,
+  onMouseDown: proc(panel: var Panel, buttons: set[MouseButton]) = nil,
+  onMouseUp: proc(panel: var Panel, buttons: set[MouseButton]) = nil,
+  onMouseEnter: proc(panel: var Panel) = nil,
+  onMouseMove: proc(panel: var Panel) = nil,
+  onMouseLeave: proc(panel: var Panel) = nil,
+): Panel =
 
-  result = Panel(position: position, size: size, texture: texture, horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, aspect_ratio: 1)
+  result = Panel(
+    position: position,
+    size: size,
+    texture: texture,
+    horizontalAlignment: horizontalAlignment,
+    verticalAlignment: verticalAlignment,
+    aspect_ratio: 1,
+    onMouseDown: onMouseDown,
+    onMouseUp: onMouseUp,
+    onMouseEnter: onMouseEnter,
+    onMouseMove: onMouseMove,
+    onMouseLeave: onMouseLeave,
+  )
 
   result.mesh = newMesh(
     positions = newSeq[Vec3f](4),
@@ -151,3 +182,14 @@
   if value != panel.aspect_ratio:
     panel.aspect_ratio = value
     panel.dirty = true
+
+proc contains*(panel: Panel, p: Vec2f): bool =
+  let cursor = panel.mesh.transform * p.toVec3
+  let p1 = panel.mesh[POSITION_ATTRIB, 0, Vec3f]
+  let p2 = panel.mesh[POSITION_ATTRIB, 2, Vec3f]
+  let
+    left = min(p1.x, p2.x)
+    right = max(p1.x, p2.x)
+    top = min(p1.y, p2.y)
+    bottom = max(p1.y, p2.y)
+  return left <= cursor.x and cursor.x <= right and top <= cursor.y and cursor.y <= bottom
--- a/tests/test_panel.nim	Sat Feb 10 15:55:05 2024 +0700
+++ b/tests/test_panel.nim	Sat Feb 10 21:19:31 2024 +0700
@@ -2,18 +2,44 @@
 
 import semicongine
 
+var counter = 0
+var counterText: Text
+
+proc click(panel: var Panel, buttons: set[MouseButton]) =
+  if buttons.contains(Mouse1):
+    counter.inc
+  if buttons.contains(Mouse3):
+    counter.dec
+  counterText.text = $counter
+proc enter(panel: var Panel) =
+  panel.size = newVec2f(0.22, 0.22)
+proc leave(panel: var Panel) =
+  panel.size = newVec2f(0.2, 0.2)
 
 proc main() =
   # setup engine
   var engine = initEngine("Test panels")
   engine.initRenderer([])
 
+  const B = [0'u8, 0'u8, 0'u8, 255'u8]
+  const T = [0'u8, 0'u8, 0'u8, 0'u8]
   # build scene
+  #
   var
     font = loadFont("DejaVuSans.ttf", lineHeightPixels = 210'f32)
     scene = Scene(name: "main")
-    origin = initPanel(size = newVec2f(0.01, 0.01), color = newVec4f(0, 0, 0, 1))
-    panel = initPanel(size = newVec2f(0.2, 0.2), color = newVec4f(1, 0, 0, 1))
+    origin = initPanel(
+      size = newVec2f(0.02, 0.02),
+      color = newVec4f(1, 1, 1, 1),
+      texture = Texture(isGrayscale: false, colorImage: newImage[RGBAPixel](3, 3, [T, B, T, B, B, B, T, B, T]), sampler: NEAREST_SAMPLER),
+    )
+    panel = initPanel(
+      size = newVec2f(0.2, 0.2),
+      color = newVec4f(1, 0, 0, 0.5),
+      onMouseDown = click,
+      onMouseEnter = enter,
+      onMouseLeave = leave
+    )
     help_text = font.initText("""Controls
 
 Horizontal alignment:
@@ -25,6 +51,9 @@
   F5: Center
   F6: Bottom""", scale = 0.0002, position = newVec2f(-0.9, -0.9), horizontalAlignment = Left, verticalAlignment = Top)
 
+  counterText = font.initText($counter, maxLen = 99, scale = 0.0004)
+
+  scene.add counterText
   scene.add panel
   scene.add help_text
   scene.add origin
@@ -34,17 +63,33 @@
     if engine.windowWasResized():
       var winSize = engine.getWindow().size
       panel.aspect_ratio = winSize[0] / winSize[1]
+      counterText.aspect_ratio = winSize[0] / winSize[1]
       origin.aspect_ratio = winSize[0] / winSize[1]
       help_text.aspect_ratio = winSize[0] / winSize[1]
 
-    if engine.keyWasPressed(F1): panel.horizontalAlignment = Left
-    elif engine.keyWasPressed(F2): panel.horizontalAlignment = Center
-    elif engine.keyWasPressed(F3): panel.horizontalAlignment = Right
-    elif engine.keyWasPressed(F4): panel.verticalAlignment = Top
-    elif engine.keyWasPressed(F5): panel.verticalAlignment = Center
-    elif engine.keyWasPressed(F6): panel.verticalAlignment = Bottom
+    if engine.keyWasPressed(F1):
+      panel.horizontalAlignment = Left
+      counterText.horizontalAlignment = Left
+    elif engine.keyWasPressed(F2):
+      panel.horizontalAlignment = Center
+      counterText.horizontalAlignment = Center
+    elif engine.keyWasPressed(F3):
+      panel.horizontalAlignment = Right
+      counterText.horizontalAlignment = Right
+    elif engine.keyWasPressed(F4):
+      panel.verticalAlignment = Top
+      counterText.verticalAlignment = Top
+    elif engine.keyWasPressed(F5):
+      panel.verticalAlignment = Center
+      counterText.verticalAlignment = Center
+    elif engine.keyWasPressed(F6):
+      panel.verticalAlignment = Bottom
+      counterText.verticalAlignment = Bottom
+
+    engine.processEventsFor(panel)
 
     panel.refresh()
+    counterText.refresh()
     origin.refresh()
     help_text.refresh()