mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Merge branch 'refactor/event-capturing' into develop
This commit is contained in:
commit
498df9ed09
@ -1,7 +1,7 @@
|
||||
package ocelot.desktop.inventory
|
||||
|
||||
import ocelot.desktop.inventory.Inventory.SlotObserver
|
||||
import ocelot.desktop.ui.event.{BrainEvent, Event, EventAware}
|
||||
import ocelot.desktop.ui.event.{BrainEvent, Dispatchable, EventAware}
|
||||
import ocelot.desktop.util.Disposable
|
||||
import totoro.ocelot.brain.event.NodeEvent
|
||||
|
||||
@ -78,7 +78,7 @@ trait Inventory extends EventAware with Disposable {
|
||||
super.shouldReceiveEventsFor(address) ||
|
||||
inventoryIterator.flatMap(_.get).exists(_.shouldReceiveEventsFor(address))
|
||||
|
||||
override def handleEvent(event: Event): Unit = {
|
||||
override def handleEvent(event: Dispatchable): Unit = {
|
||||
super.handleEvent(event)
|
||||
|
||||
for (slot <- inventoryIterator; item <- slot.get) {
|
||||
|
||||
@ -6,8 +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.{Event, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.window.Window
|
||||
import ocelot.desktop.ui.event.{Capturing, CapturingEvent, Dispatchable, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.{RootWidget, Widget}
|
||||
import ocelot.desktop.util._
|
||||
import ocelot.desktop.{OcelotDesktop, Settings}
|
||||
@ -24,7 +23,6 @@ import java.nio.channels.Channels
|
||||
import java.nio.file.Paths
|
||||
import javax.imageio.ImageIO
|
||||
import javax.swing.JFileChooser
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.duration.DurationInt
|
||||
@ -427,16 +425,18 @@ object UiHandler extends Logging {
|
||||
Audio.destroy()
|
||||
}
|
||||
|
||||
private def dispatchEvent(iter: => IterableOnce[Widget] = hierarchy.reverseIterator)(event: Event): Unit = {
|
||||
for (widget <- iter) {
|
||||
if (event.consumed) {
|
||||
return
|
||||
}
|
||||
|
||||
private def dispatchEvent(iter: => IterableOnce[Widget] = hierarchy.reverseIterator)(event: Dispatchable): Unit = {
|
||||
for (widget <- iter if !event.consumed) {
|
||||
widget.handleEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
private def dispatchCapturing(target: Widget)(event: CapturingEvent): Unit = {
|
||||
val ancestors = target.ancestors.toSeq
|
||||
dispatchEvent(ancestors.reverseIterator ++ Some(target))(Capturing(event))
|
||||
dispatchEvent(Some(target))(event)
|
||||
}
|
||||
|
||||
private def dispatchBrainEvents(): Unit = {
|
||||
import totoro.ocelot.brain
|
||||
|
||||
@ -476,7 +476,10 @@ object UiHandler extends Logging {
|
||||
}
|
||||
|
||||
// TODO: dispatch to the focused widget instead of broadcasting to the entire hierarchy.
|
||||
KeyEvents.events.foreach(dispatchEvent())
|
||||
for (event <- KeyEvents.events) {
|
||||
dispatchEvent(hierarchy)(Capturing(event))
|
||||
dispatchEvent()(event)
|
||||
}
|
||||
|
||||
MouseEvents.events
|
||||
.foreach(dispatchEvent(hierarchy.reverseIterator.filter(w => w.enabled && w.receiveAllMouseEvents)))
|
||||
@ -487,17 +490,17 @@ object UiHandler extends Logging {
|
||||
val mouseTarget = hierarchy.reverseIterator
|
||||
.find(w => w.enabled && w.receiveMouseEvents && w.clippedBounds.contains(mousePos))
|
||||
|
||||
ScrollEvents.events.foreach(dispatchEvent(scrollTarget))
|
||||
for (scrollTarget <- scrollTarget) {
|
||||
ScrollEvents.events.foreach(dispatchCapturing(scrollTarget))
|
||||
}
|
||||
|
||||
for (event <- MouseEvents.events) {
|
||||
if (event.state == MouseEvent.State.Pressed) {
|
||||
dispatchEvent(mouseTarget)(event)
|
||||
|
||||
// TODO: this should be done in the event capturing phase in [[Window]] itself.
|
||||
for (mouseTarget <- mouseTarget if !mouseTarget.isInstanceOf[Window]) {
|
||||
focusParentWindow(mouseTarget.parent)
|
||||
for (mouseTarget <- mouseTarget) {
|
||||
dispatchCapturing(mouseTarget)(event)
|
||||
}
|
||||
} else {
|
||||
dispatchEvent(hierarchy)(Capturing(event))
|
||||
dispatchEvent(hierarchy.reverseIterator)(event)
|
||||
}
|
||||
}
|
||||
@ -515,15 +518,6 @@ object UiHandler extends Logging {
|
||||
root.update()
|
||||
}
|
||||
|
||||
@tailrec
|
||||
private def focusParentWindow(parent: Option[Widget]): Unit = {
|
||||
parent match {
|
||||
case Some(window: Window) => window.focus()
|
||||
case Some(widget) => focusParentWindow(widget.parent)
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
private def updateWindowSizeAndPosition(): Unit = {
|
||||
val width = Display.getWidth
|
||||
val height = Display.getHeight
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
trait CapturingEvent extends Event
|
||||
28
src/main/scala/ocelot/desktop/ui/event/Dispatchable.scala
Normal file
28
src/main/scala/ocelot/desktop/ui/event/Dispatchable.scala
Normal file
@ -0,0 +1,28 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
sealed trait Dispatchable {
|
||||
def consume(): Unit
|
||||
|
||||
def consumed: Boolean
|
||||
}
|
||||
|
||||
trait Event extends Dispatchable {
|
||||
private var _consumed: Boolean = false
|
||||
|
||||
def consume(): Unit = {
|
||||
_consumed = true
|
||||
}
|
||||
|
||||
def consumed: Boolean = _consumed
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for an event that is dispatched during the capture phase.
|
||||
*/
|
||||
case class Capturing[A <: CapturingEvent](event: A) extends Dispatchable {
|
||||
override def consume(): Unit = {
|
||||
event.consume()
|
||||
}
|
||||
|
||||
override def consumed: Boolean = event.consumed
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
trait Event extends AnyRef {
|
||||
private var _consumed: Boolean = false
|
||||
|
||||
def consume(): Unit = {
|
||||
_consumed = true
|
||||
}
|
||||
|
||||
def consumed: Boolean = _consumed
|
||||
}
|
||||
@ -7,7 +7,7 @@ trait EventAware {
|
||||
|
||||
def shouldReceiveEventsFor(address: String): Boolean = false
|
||||
|
||||
def handleEvent(event: Event): Unit = {
|
||||
def handleEvent(event: Dispatchable): Unit = {
|
||||
eventHandlers(event)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,4 +6,4 @@ object KeyEvent {
|
||||
}
|
||||
}
|
||||
|
||||
case class KeyEvent(state: KeyEvent.State.Value, code: Int, char: Char) extends Event
|
||||
case class KeyEvent(state: KeyEvent.State.Value, code: Int, char: Char) extends CapturingEvent
|
||||
|
||||
@ -12,4 +12,4 @@ object MouseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
case class MouseEvent(state: MouseEvent.State.Value, button: MouseEvent.Button.Value) extends Event
|
||||
case class MouseEvent(state: MouseEvent.State.Value, button: MouseEvent.Button.Value) extends CapturingEvent
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
case class ScrollEvent(offset: Int) extends Event
|
||||
case class ScrollEvent(offset: Int) extends CapturingEvent
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ui.event.Event
|
||||
import ocelot.desktop.ui.event.{Dispatchable, Event}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class EventHandlers extends PartialFunction[Event, Unit] {
|
||||
class EventHandlers extends PartialFunction[Dispatchable, Unit] {
|
||||
private val handlers = ArrayBuffer.empty[EventHandler]
|
||||
|
||||
def +=(handler: EventHandler): Unit = handlers += handler
|
||||
|
||||
def -=(handler: EventHandler): Unit = handlers -= handler
|
||||
|
||||
override def isDefinedAt(event: Event): Boolean = handlers.exists(_.isDefinedAt(event))
|
||||
override def isDefinedAt(event: Dispatchable): Boolean = handlers.exists(_.isDefinedAt(event))
|
||||
|
||||
override def apply(event: Event): Unit = {
|
||||
override def apply(event: Dispatchable): Unit = {
|
||||
for (handler <- handlers) {
|
||||
if (event.consumed) {
|
||||
return
|
||||
}
|
||||
|
||||
handler.applyOrElse(event, (_: Event) => ())
|
||||
handler.applyOrElse(event, (_: Dispatchable) => ())
|
||||
}
|
||||
}
|
||||
|
||||
type EventHandler = PartialFunction[Event, Unit]
|
||||
type EventHandler = PartialFunction[Dispatchable, Unit]
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.handlers.HoverHandler
|
||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||
import ocelot.desktop.ui.event.{MouseEvent, ScrollEvent}
|
||||
import ocelot.desktop.ui.event.{Capturing, MouseEvent, ScrollEvent}
|
||||
import ocelot.desktop.ui.layout.Layout
|
||||
import ocelot.desktop.ui.widget.ScrollView.{DecayFactor, DragState, MaxScrollVelocity, MinThumbSize, ScrollVelocity}
|
||||
import ocelot.desktop.util.Logging
|
||||
@ -49,10 +49,15 @@ class ScrollView(val inner: Widget) extends Widget with Logging with HoverHandle
|
||||
scrollToEnd = false
|
||||
}
|
||||
|
||||
case MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Left) =>
|
||||
case Capturing(event @ MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Left)) =>
|
||||
val pos = UiHandler.mousePosition
|
||||
|
||||
if (vThumbBounds.contains(pos) || hThumbBounds.contains(pos)) {
|
||||
event.consume()
|
||||
}
|
||||
|
||||
dragState = if (vThumbBounds.contains(pos)) {
|
||||
scrollToEnd = false
|
||||
DragState.Vertical(yOffset, pos)
|
||||
} else if (hThumbBounds.contains(pos)) {
|
||||
DragState.Horizontal(xOffset, pos)
|
||||
@ -60,10 +65,6 @@ class ScrollView(val inner: Widget) extends Widget with Logging with HoverHandle
|
||||
DragState.None
|
||||
}
|
||||
|
||||
if (dragState.isVertical) {
|
||||
scrollToEnd = false
|
||||
}
|
||||
|
||||
case MouseEvent(MouseEvent.State.Released, MouseEvent.Button.Left) =>
|
||||
dragState = DragState.None
|
||||
}
|
||||
|
||||
@ -59,6 +59,10 @@ class Widget extends EventAware with Updatable with Disposable {
|
||||
|
||||
final def root: Option[RootWidget] = _root
|
||||
|
||||
final def ancestors: Iterator[Widget] = {
|
||||
Iterator.unfold(this)(_.parent.map(w => (w, w)))
|
||||
}
|
||||
|
||||
def enabled: Boolean = true
|
||||
|
||||
def minimumSize: Size2D = layout.minimumSize
|
||||
|
||||
@ -4,7 +4,7 @@ import ocelot.desktop.Settings
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.handlers.MouseHandler
|
||||
import ocelot.desktop.ui.event.{DragEvent, KeyEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.event.{Capturing, DragEvent, KeyEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.Widget
|
||||
import ocelot.desktop.util.Persistable
|
||||
import org.lwjgl.input.Keyboard
|
||||
@ -30,7 +30,7 @@ trait Window extends Widget with Persistable with MouseHandler {
|
||||
override protected def receiveDragEvents: Boolean = true
|
||||
|
||||
eventHandlers += {
|
||||
case MouseEvent(MouseEvent.State.Pressed, _) =>
|
||||
case Capturing(MouseEvent(MouseEvent.State.Pressed, _)) =>
|
||||
focus()
|
||||
|
||||
case ev @ DragEvent(DragEvent.State.Start, MouseEvent.Button.Left, mousePos) =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user