Skip to content

Commit

Permalink
Dragging outside the original component should still send drag events…
Browse files Browse the repository at this point in the history
… to original component.

Closes Hexworks#374.
  • Loading branch information
nanodeath committed May 1, 2021
1 parent 650f056 commit d455f1c
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class UIEventToComponentDispatcher(

private var lastMousePosition = Position.unknown()
private var lastHoveredComponent: InternalComponent = root
private var lastPressedComponent: InternalComponent? = null

private val shortcutsConfig = RuntimeConfig.config.shortcutsConfig

Expand Down Expand Up @@ -192,13 +193,23 @@ class UIEventToComponentDispatcher(
phase: UIEventPhase,
previousResult: UIEventResponse
): UIEventResponse {
val activeComponent = lastPressedComponent
?.takeIf { phase == TARGET && (event.type == MOUSE_DRAGGED || event.type == MOUSE_RELEASED) }
?: component
var result = previousResult
result = result.pickByPrecedence(component.process(event, phase))
result = result.pickByPrecedence(activeComponent.process(event, phase))
if (result.shouldStopPropagation()) {
// logger.debug("Propagation was stopped by component: $component.")
} else if (result.allowsDefaults()) {
// logger.debug("Trying defaults for component $component, with event $event and phase $phase")
result = result.pickByPrecedence(tryDefaultsFor(component, event, phase))
result = result.pickByPrecedence(tryDefaultsFor(activeComponent, event, phase))
// Update lastPressedComponent depending on event type
if (phase == TARGET) {
when (event.type) {
MOUSE_PRESSED -> lastPressedComponent = component
MOUSE_RELEASED -> lastPressedComponent = null
}
}
}
return result
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.hexworks.zircon.internal.uievent.impl

import org.assertj.core.api.Assertions.assertThat
import org.hexworks.cobalt.datatypes.Maybe
import org.hexworks.zircon.api.data.Position
import org.hexworks.zircon.api.uievent.*
import org.hexworks.zircon.api.uievent.ComponentEventType.*
import org.hexworks.zircon.internal.behavior.ComponentFocusOrderList
import org.hexworks.zircon.internal.component.InternalContainer
import org.hexworks.zircon.internal.component.impl.RootContainer
Expand Down Expand Up @@ -137,6 +140,42 @@ class UIEventToComponentDispatcherTest {
assertThat(result).isEqualTo(StopPropagation)
}

@Test
fun dispatchShouldAlwaysDispatchToPressedComponent() {
// Summary: when you click and drag, the start component gets the MOUSE_PRESSED event and the activation event,
// drag events are sent to the start component,
// and when you release the click, the start component gets the MOUSE_RELEASED event and the end component gets the
// deactivation event.

// When we press the mouse on child0
whenever(rootMock.fetchComponentByPosition(MOUSE_PRESSED.position)).thenReturn(Maybe.of(child0Mock))
// we send the mouse press events to child0
whenever(child0Mock.process(MOUSE_PRESSED, UIEventPhase.TARGET)).thenReturn(Pass)
whenever(child0Mock.mousePressed(MOUSE_PRESSED, UIEventPhase.TARGET)).thenReturn(Pass)
// which also causes it to become activated
whenever(child0Mock.process(ComponentEvent(ACTIVATED), UIEventPhase.TARGET)).thenReturn(Processed)
whenever(child0Mock.activated()).thenReturn(Processed)

// Even when we drag the mouse over child1
whenever(rootMock.fetchComponentByPosition(MOUSE_DRAGGED.position)).thenReturn(Maybe.of(child1Mock))
// the drag events are still sent to child0
whenever(child0Mock.process(MOUSE_DRAGGED, UIEventPhase.TARGET)).thenReturn(Pass)
whenever(child0Mock.mouseDragged(MOUSE_DRAGGED, UIEventPhase.TARGET)).thenReturn(Pass)

// When we finally release the mouse click (over child1)
whenever(rootMock.fetchComponentByPosition(MOUSE_RELEASED.position)).thenReturn(Maybe.of(child1Mock))
// child0 gets the release event
whenever(child0Mock.process(MOUSE_RELEASED, UIEventPhase.TARGET)).thenReturn(Pass)
whenever(child0Mock.mouseReleased(MOUSE_RELEASED, UIEventPhase.TARGET)).thenReturn(Pass)
// HOWEVER child1 is deactivated
whenever(child1Mock.process(ComponentEvent(DEACTIVATED), UIEventPhase.TARGET)).thenReturn(Processed)
whenever(child1Mock.deactivated()).thenReturn(Processed)

target.dispatch(MOUSE_PRESSED)
target.dispatch(MOUSE_DRAGGED)
target.dispatch(MOUSE_RELEASED)
}


companion object {
val KEY_A_PRESSED_EVENT = KeyboardEvent(
Expand All @@ -148,5 +187,15 @@ class UIEventToComponentDispatcherTest {
type = KeyboardEventType.KEY_PRESSED,
code = KeyCode.TAB,
key = "\t")

val MOUSE_PRESSED = MouseEvent(MouseEventType.MOUSE_PRESSED,
1,
Position.create(1, 1))
val MOUSE_DRAGGED = MouseEvent(MouseEventType.MOUSE_DRAGGED,
1,
Position.create(2, 2))
val MOUSE_RELEASED = MouseEvent(MouseEventType.MOUSE_RELEASED,
1,
Position.create(3, 3))
}
}

1 comment on commit d455f1c

@nanodeath
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is blocked on Hexworks#384 because it touches the same tests.

Please sign in to comment.