Skip to content

Commit

Permalink
Add the ability to stop mouse event propagation
Browse files Browse the repository at this point in the history
  • Loading branch information
sliekens committed Jan 18, 2025
1 parent 3df7def commit d78b3b3
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 19 deletions.
14 changes: 11 additions & 3 deletions Blish HUD/Controls/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,24 @@ public void ClearChildren() {
}
}

public override Control TriggerMouseInput(MouseEventType mouseEventType, MouseState ms) {
public override Control TriggerMouseInput(MouseEventArgs args, MouseState ms) {
Control thisResult = null;
Control childResult = null;

if (CapturesInput() != CaptureType.None) {
thisResult = base.TriggerMouseInput(mouseEventType, ms);
thisResult = base.TriggerMouseInput(args, ms);
}

if (args.PropagationStopped) {
return thisResult;
}

List<Control> children = _children.ToList();
IOrderedEnumerable<Control> zSortedChildren = children.OrderByDescending(i => i.ZIndex).ThenByDescending(c => children.IndexOf(c));

foreach (var childControl in zSortedChildren) {
if (childControl.AbsoluteBounds.Contains(ms.Position) && childControl.Visible) {
childResult = childControl.TriggerMouseInput(mouseEventType, ms);
childResult = childControl.TriggerMouseInput(args, ms);

if (childResult != null) {
if (!childResult.Captures.HasFlag(CaptureType.Filter)) {
Expand All @@ -234,6 +238,10 @@ public override Control TriggerMouseInput(MouseEventType mouseEventType, MouseSt
childResult = null;
}
}

if (args.PropagationStopped) {
break;
}
}

return childResult ?? thisResult;
Expand Down
39 changes: 28 additions & 11 deletions Blish HUD/Controls/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,14 @@ protected virtual void OnLeftMouseButtonReleased(MouseEventArgs e) {
if (_enabled && _clickPrimed) {
// Distinguish click from double-click
if (GameService.Overlay.CurrentGameTime.TotalGameTime.TotalMilliseconds - _lastClickTime < SystemInformation.DoubleClickTime) {
OnClick(new MouseEventArgs(e.EventType, true));
var doubleClickArgs = new MouseEventArgs(e.EventType, true);
OnClick(doubleClickArgs);

// Ensure that when double-click propagation is stopped, it also
// stops propagating the single-click event
if (doubleClickArgs.PropagationStopped) {
e.StopPropagation();
}
} else {
_lastClickTime = GameService.Overlay.CurrentGameTime.TotalGameTime.TotalMilliseconds;
OnClick(e);
Expand Down Expand Up @@ -810,34 +817,44 @@ protected virtual CaptureType CapturesInput() {
return CaptureType.Mouse;
}

[Obsolete("This overload does not support stopping propagation, use the overload that takes MouseEventArgs.")]
protected void TriggerMouseEvent(MouseEventType mouseEventType) {
switch (mouseEventType) {
TriggerMouseEvent(new MouseEventArgs(mouseEventType));
}

protected void TriggerMouseEvent(MouseEventArgs args) {
switch (args.EventType) {
case MouseEventType.LeftMouseButtonPressed:
OnLeftMouseButtonPressed(new MouseEventArgs(MouseEventType.LeftMouseButtonPressed));
OnLeftMouseButtonPressed(args);
break;
case MouseEventType.LeftMouseButtonReleased:
OnLeftMouseButtonReleased(new MouseEventArgs(MouseEventType.LeftMouseButtonReleased));
OnLeftMouseButtonReleased(args);
break;
case MouseEventType.RightMouseButtonPressed:
OnRightMouseButtonPressed(new MouseEventArgs(MouseEventType.RightMouseButtonPressed));
OnRightMouseButtonPressed(args);
break;
case MouseEventType.RightMouseButtonReleased:
OnRightMouseButtonReleased(new MouseEventArgs(MouseEventType.RightMouseButtonReleased));
OnRightMouseButtonReleased(args);
break;
case MouseEventType.MouseMoved:
OnMouseMoved(new MouseEventArgs(MouseEventType.MouseMoved));
OnMouseMoved(args);
this.MouseOver = true;
break;
case MouseEventType.MouseWheelScrolled:
OnMouseWheelScrolled(new MouseEventArgs(MouseEventType.MouseWheelScrolled));
OnMouseWheelScrolled(args);
break;
}
}

[Obsolete("This overload does not support stopping propagation, use the overload that takes MouseEventArgs.")]
public virtual Control TriggerMouseInput(MouseEventType mouseEventType, MouseState ms) {
return TriggerMouseInput(new MouseEventArgs(mouseEventType), ms);
}

public virtual Control TriggerMouseInput(MouseEventArgs args, MouseState ms) {
var inputCapture = CapturesInput();

switch (mouseEventType) {
switch (args.EventType) {
case MouseEventType.MouseMoved:
case MouseEventType.LeftMouseButtonPressed:
case MouseEventType.LeftMouseButtonReleased:
Expand All @@ -846,13 +863,13 @@ public virtual Control TriggerMouseInput(MouseEventType mouseEventType, MouseSta
case MouseEventType.MouseEntered:
case MouseEventType.MouseLeft:
if (inputCapture.HasFlag(CaptureType.Mouse) || inputCapture.HasFlag(CaptureType.Filter)) {
TriggerMouseEvent(mouseEventType);
TriggerMouseEvent(args);
return this;
}
break;
case MouseEventType.MouseWheelScrolled:
if (inputCapture.HasFlag(CaptureType.MouseWheel) || inputCapture.HasFlag(CaptureType.Filter)) {
TriggerMouseEvent(mouseEventType);
TriggerMouseEvent(args);
return this;
}
break;
Expand Down
8 changes: 6 additions & 2 deletions Blish HUD/Controls/Panel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ public void NavigateToBuiltPanel(BuildUIDelegate buildCall, object obj) {

/// <inheritdoc />
protected override void OnClick(MouseEventArgs e) {
if (_canCollapse && _layoutHeaderBounds.Contains(this.RelativeMousePosition)) {
this.ToggleAccordionState();
if (!string.IsNullOrEmpty(_title) && _layoutHeaderBounds.Contains(this.RelativeMousePosition)) {
if (_canCollapse) {
this.ToggleAccordionState();
}

e.StopPropagation();
}

base.OnClick(e);
Expand Down
9 changes: 9 additions & 0 deletions Blish HUD/GameServices/Input/Mouse/MouseEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ internal int WheelDelta {
}
}

internal bool PropagationStopped { get; private set; }

/// <summary>
/// Prevents the current mouse event from being propagated to other event listeners.
/// </summary>
public void StopPropagation() {
this.PropagationStopped = true;
}

public MouseEventArgs(MouseEventType eventType) { this.EventType = eventType; }

public MouseEventArgs(MouseEventType eventType, bool isDoubleClick) : this(eventType) { this.IsDoubleClick = isDoubleClick; }
Expand Down
8 changes: 5 additions & 3 deletions Blish HUD/GameServices/Input/Mouse/MouseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,13 @@ public void Update() {

// Handle mouse moved
if (prevMouseState.Position != this.State.Position) {
var mouseMovedArgs = new MouseEventArgs(MouseEventType.MouseMoved);

if (this.CursorIsVisible) {
this.ActiveControl = GameService.Graphics.SpriteScreen.TriggerMouseInput(MouseEventType.MouseMoved, this.State);
this.ActiveControl = GameService.Graphics.SpriteScreen.TriggerMouseInput(mouseMovedArgs, this.State);
}

this.MouseMoved?.Invoke(this, new MouseEventArgs(MouseEventType.MouseMoved));
this.MouseMoved?.Invoke(this, mouseMovedArgs);
}

// Handle mouse events blocked by the mouse hook
Expand Down Expand Up @@ -218,7 +220,7 @@ private void SimulateNonCapturePressedEvent(MouseEventArgs mouseEvent) {

private void HandleMouseEvent(MouseEventArgs mouseEvent) {
if (HandleHookedMouseEvent(mouseEvent) && this.CursorIsVisible) {
GameService.Graphics.SpriteScreen.TriggerMouseInput(mouseEvent.EventType, this.State);
GameService.Graphics.SpriteScreen.TriggerMouseInput(mouseEvent, this.State);
}
}

Expand Down

0 comments on commit d78b3b3

Please sign in to comment.