Gondwana Game Engine Version 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
WidgetInputRouter.cs
1using System.Drawing;
2using System.Numerics;
9
10namespace Gondwana.Widgets;
11
15public sealed class WidgetInputRouter : IDisposable
16{
20 public const int MousePointerId = -1;
21
22 private static readonly MouseButton[] _mouseButtons =
23 [
24 MouseButton.Left,
25 MouseButton.Right,
27 ];
28
29 private readonly object _syncRoot = new();
30 private readonly List<WidgetBase> _widgets = [];
31 private readonly Dictionary<int, PointerCapture> _touchCaptures = [];
32
33 private readonly RenderSurfaceHostBase _renderSurfaceHost;
34 private readonly KeyboardEventPoller? _keyboardEventPoller;
35 private readonly MouseEventPoller? _mouseEventPoller;
36 private readonly TouchEventPoller? _touchEventPoller;
37
38 private PointerCapture? _mouseCapture;
39 private WidgetHit? _hoveredHit;
40 private WidgetBase? _focusedWidget;
41
42 private bool _isStarted;
43 private bool _disposed;
44
52 public WidgetInputRouter(RenderSurfaceHostBase renderSurfaceHost,
53 KeyboardEventPoller? keyboardEventPoller,
54 MouseEventPoller? mouseEventPoller,
55 TouchEventPoller? touchEventPoller)
56 {
57 _renderSurfaceHost = renderSurfaceHost ?? throw new ArgumentNullException(nameof(renderSurfaceHost));
58
59 _keyboardEventPoller = keyboardEventPoller;
60 _mouseEventPoller = mouseEventPoller;
61 _touchEventPoller = touchEventPoller;
62 }
63
70 public WidgetBase? FocusedWidget => _focusedWidget;
71
76 public void Register(WidgetBase widget)
77 {
78 ObjectDisposedException.ThrowIf(_disposed, this);
79 ArgumentNullException.ThrowIfNull(widget);
80
81 if (!ReferenceEquals(widget.RenderSurfaceHost, _renderSurfaceHost))
82 {
83 throw new ArgumentException("The widget belongs to a different RenderSurfaceHost.", nameof(widget));
84 }
85
86 lock (_syncRoot)
87 {
88 if (_widgets.Contains(widget))
89 return;
90
91 _widgets.Add(widget);
92 widget.Disposing += OnWidgetDisposing;
93 }
94 }
95
100 public void Unregister(WidgetBase widget)
101 {
102 ArgumentNullException.ThrowIfNull(widget);
103 Unregister(widget, dispatchPointerUp: true);
104 }
105
110 public void BringToFront(WidgetBase widget)
111 {
112 ObjectDisposedException.ThrowIf(_disposed, this);
113 ArgumentNullException.ThrowIfNull(widget);
114
115 lock (_syncRoot)
116 {
117 if (!_widgets.Remove(widget))
118 throw new InvalidOperationException("The widget is not registered.");
119
120 _widgets.Add(widget);
121 }
122 }
123
128 public void Focus(WidgetBase? widget)
129 {
130 if (ReferenceEquals(_focusedWidget, widget))
131 return;
132
133 if (widget is not null && !CanReceiveKeyboardInput(widget))
134 return;
135
136 WidgetBase? previous = _focusedWidget;
137 _focusedWidget = widget;
138
139 previous?.DispatchFocusLost();
140 widget?.DispatchFocusGained();
141 }
142
146 public void ClearFocus()
147 {
148 Focus(null);
149 }
150
154 public void Start()
155 {
156 ObjectDisposedException.ThrowIf(_disposed, this);
157
158 if (_isStarted)
159 return;
160
161 if (_keyboardEventPoller is not null)
162 _keyboardEventPoller.KeyDown += OnKeyDown;
163
164 if (_mouseEventPoller is not null)
165 _mouseEventPoller.MouseEvent += OnMouseEvent;
166
167 if (_touchEventPoller is not null)
168 {
169 _touchEventPoller.TouchBegan += OnTouchBegan;
170 _touchEventPoller.TouchMoved += OnTouchMoved;
171 _touchEventPoller.TouchEnded += OnTouchEnded;
172 }
173
174 WidgetInputRouterRegistry.Attach(
175 _renderSurfaceHost,
176 this);
177
178 _isStarted = true;
179 }
180
184 public void Stop()
185 {
186 if (!_isStarted)
187 return;
188
189 WidgetInputRouterRegistry.Detach(
190 _renderSurfaceHost,
191 this);
192
193 if (_keyboardEventPoller is not null)
194 _keyboardEventPoller.KeyDown -= OnKeyDown;
195
196 if (_mouseEventPoller is not null)
197 _mouseEventPoller.MouseEvent -= OnMouseEvent;
198
199 if (_touchEventPoller is not null)
200 {
201 _touchEventPoller.TouchBegan -= OnTouchBegan;
202 _touchEventPoller.TouchMoved -= OnTouchMoved;
203 _touchEventPoller.TouchEnded -= OnTouchEnded;
204 }
205
206 ClearFocus();
207
208 _mouseCapture = null;
209 _touchCaptures.Clear();
210 _hoveredHit = null;
211 _isStarted = false;
212 }
213
217 public void Dispose()
218 {
219 if (_disposed)
220 return;
221
222 Stop();
223
224 WidgetBase[] widgets;
225
226 lock (_syncRoot)
227 {
228 widgets = [.. _widgets];
229 _widgets.Clear();
230 }
231
232 foreach (WidgetBase widget in widgets)
233 widget.Disposing -= OnWidgetDisposing;
234
235 _disposed = true;
236 }
237
238 internal void NotifyWidgetHidden(WidgetBase widget)
239 {
240 ArgumentNullException.ThrowIfNull(widget);
241 ReleaseInputState(widget, dispatchPointerUp: true);
242 }
243
244 internal void NotifyWidgetInputDisabled(WidgetBase widget)
245 {
246 ArgumentNullException.ThrowIfNull(widget);
247 ReleaseInputState(widget, dispatchPointerUp: true);
248 }
249
250 internal void NotifyWidgetPointerInputDisabled(WidgetBase widget)
251 {
252 ArgumentNullException.ThrowIfNull(widget);
253 ReleasePointerState(widget, dispatchPointerUp: true);
254 }
255
256 internal void NotifyWidgetKeyboardFocusDisabled(WidgetBase widget)
257 {
258 ArgumentNullException.ThrowIfNull(widget);
259 ReleaseKeyboardFocus(widget);
260 }
261
262 private void OnKeyDown(KeyDownEventArgs args)
263 {
264 if (!_isStarted || _disposed)
265 return;
266
267 if (!int.TryParse(args.KeyConfig.Key, out int key))
268 return;
269
270 RouteKeyboardInput(key, args.KeyAction, args.Modifiers);
271 }
272
273 internal void RouteKeyboardInput(int key, KeyAction action, KeyboardModifierState modifiers)
274 {
275 WidgetBase? target = _focusedWidget;
276 if (target is not null && !CanReceiveKeyboardInput(target))
277 {
278 ClearFocus();
279 target = null;
280 }
281
282 if (target is not null)
283 {
284 var focusedArgs = new WidgetKeyboardEventArgs(target, key, action, modifiers);
285 target.DispatchKeyboardInput(focusedArgs);
286 if (focusedArgs.Handled)
287 return;
288 }
289
290 WidgetBase[] snapshot;
291 lock (_syncRoot)
292 snapshot = [.. _widgets];
293
294 // Most recently activated/registered eligible bar wins, just like pointer routing.
295 for (int index = snapshot.Length - 1; index >= 0; index--)
296 {
297 WidgetBase widget = snapshot[index];
298 if (widget is not IWidgetKeyboardFallback fallback || !IsRegistered(widget) ||
299 !HasValidTarget(widget) || !widget.Visible ||
300 !widget.IsInputEnabled || !widget.IsKeyboardInputEnabled)
301 continue;
302
303 var fallbackArgs = new WidgetKeyboardEventArgs(widget, key, action, modifiers);
304 fallback.HandleUnhandledKeyboardInput(fallbackArgs);
305 if (fallbackArgs.Handled)
306 return;
307 }
308 }
309
310 private void OnMouseEvent(MouseEventArgs args)
311 {
312 if (!_isStarted || _disposed)
313 return;
314
315 Point currentPosition = args.CurrentPosition;
316 WidgetHit? initialHit = HitTest(currentPosition);
317
318 UpdateMouseHover(initialHit, currentPosition, args.Tick);
319
320 foreach (MouseButton mouseButton in _mouseButtons)
321 {
322 if (args.IsButtonJustPressed(mouseButton))
323 {
324 ProcessMouseDown(initialHit, currentPosition, mouseButton, args.Tick);
325 }
326 }
327
328 if (args.PreviousPosition != currentPosition)
329 {
330 ProcessMouseMove(args.PreviousPosition, currentPosition, args.Tick);
331 }
332
333 if (args.ScrollDelta != 0 && initialHit is not null && IsPointerRoutable(initialHit.Widget, initialHit.View))
334 {
335 initialHit.Widget.DispatchMouseWheel(new WidgetMouseWheelEventArgs(
336 initialHit.Widget,
337 initialHit.View,
338 new PointF(currentPosition.X, currentPosition.Y),
339 args.ScrollDelta,
340 args.Tick));
341 }
342
343 foreach (MouseButton mouseButton in _mouseButtons)
344 {
345 if (args.IsButtonJustReleased(mouseButton))
346 {
347 ProcessMouseUp(currentPosition, mouseButton, args.Tick);
348 }
349 }
350
351 UpdateMouseHover(HitTest(currentPosition), currentPosition, args.Tick);
352 }
353
354 private void ProcessMouseDown(WidgetHit? hit,
355 Point position,
356 MouseButton mouseButton,
357 long tick)
358 {
359 if (_mouseCapture is not null || hit is null)
360 return;
361
362 FocusFromPointer(hit.Widget);
363
364 WidgetPointerButtonEnum widgetButton = MapMouseButton(mouseButton);
365
366 hit.Widget.DispatchPointerDown(CreatePointerArgs(hit.Widget,
367 hit.View,
368 position,
369 widgetButton,
370 tick,
372
373 _mouseCapture = new PointerCapture(hit.Widget,
374 hit.View,
375 widgetButton,
376 position,
377 hit.InstanceOffset);
378 }
379
380 private void ProcessMouseMove(Point previousPosition,
381 Point currentPosition,
382 long tick)
383 {
384 PointerCapture? capture = GetValidMouseCapture(tick);
385 WidgetHit? hit = capture is null ? HitTest(currentPosition) : null;
386 WidgetBase? recipient = capture?.Widget ?? hit?.Widget;
387 View? view = capture?.View ?? hit?.View;
388
389 if (recipient is null || view is null)
390 return;
391
392 var delta = new Vector2(currentPosition.X - previousPosition.X,
393 currentPosition.Y - previousPosition.Y);
394
395 WidgetPointerButtonEnum button = capture?.Button ?? WidgetPointerButtonEnum.None;
396
397 recipient.DispatchPointerMove(CreatePointerArgs(recipient,
398 view,
399 currentPosition,
400 button,
401 tick,
403 deltaPx: delta));
404
405 if (capture is not null)
406 capture.LastPosition = currentPosition;
407 }
408
409 private void ProcessMouseUp(Point position,
410 MouseButton mouseButton,
411 long tick)
412 {
413 WidgetPointerButtonEnum releasedButton = MapMouseButton(mouseButton);
414 PointerCapture? capture = GetValidMouseCapture(tick);
415
416 if (capture is null || capture.Button != releasedButton)
417 return;
418
419 WidgetBase recipient = capture.Widget;
420
421 recipient.DispatchPointerUp(CreatePointerArgs(recipient,
422 capture.View,
423 position,
424 releasedButton,
425 tick,
427
428 WidgetHit? releaseHit = HitTest(position);
429
430 if (IsSameHit(releaseHit, recipient, capture.View) && recipient.GetWrappedHitOffset(capture.View, position) == capture.InstanceOffset)
431 {
432 recipient.DispatchPointerClick(CreatePointerArgs(recipient,
433 capture.View,
434 position,
435 releasedButton,
436 tick,
438 clickCount: 1));
439 }
440
441 _mouseCapture = null;
442 }
443
444 private void UpdateMouseHover(WidgetHit? hit,
445 Point position,
446 long tick)
447 {
448 if (IsSameHit(_hoveredHit, hit))
449 return;
450
451 WidgetHit? previous = _hoveredHit;
452 _hoveredHit = hit;
453
454 if (previous is not null)
455 {
456 previous.Widget.DispatchPointerLeave(CreatePointerArgs(previous.Widget,
457 previous.View,
458 position,
460 tick,
462 }
463
464 if (hit is not null)
465 {
466 hit.Widget.DispatchPointerEnter(CreatePointerArgs(hit.Widget,
467 hit.View,
468 position,
470 tick,
472 }
473 }
474
475 private PointerCapture? GetValidMouseCapture(long tick)
476 {
477 if (_mouseCapture is null)
478 return null;
479
480 if (IsPointerRoutable(_mouseCapture.Widget, _mouseCapture.View))
481 return _mouseCapture;
482
483 _mouseCapture.Widget.DispatchPointerUp(CreatePointerArgs(_mouseCapture.Widget,
484 _mouseCapture.View,
485 _mouseCapture.LastPosition,
486 _mouseCapture.Button,
487 tick,
489
490 _mouseCapture = null;
491 return null;
492 }
493
494 private void OnTouchBegan(object? sender, TouchEventArgs args)
495 {
496 if (!_isStarted || _disposed)
497 return;
498
499 TouchPoint touch = args.Touch;
500
501 if (_touchCaptures.ContainsKey(touch.Id))
502 return;
503
504 WidgetHit? hit = HitTest(touch.Position);
505
506 if (hit is null)
507 return;
508
509 FocusFromPointer(hit.Widget);
510
511 hit.Widget.DispatchPointerDown(CreatePointerArgs(hit.Widget,
512 hit.View,
513 touch.Position,
515 args.Tick,
516 touch.Id));
517
518 _touchCaptures[touch.Id] = new PointerCapture(hit.Widget,
519 hit.View,
521 touch.Position,
522 hit.InstanceOffset);
523 }
524
525 private void OnTouchMoved(object? sender, TouchEventArgs args)
526 {
527 if (!_isStarted || _disposed)
528 return;
529
530 TouchPoint touch = args.Touch;
531
532 if (!_touchCaptures.TryGetValue(touch.Id, out PointerCapture? capture))
533 return;
534
535 if (!IsPointerRoutable(capture.Widget, capture.View))
536 {
537 capture.Widget.DispatchPointerUp(CreatePointerArgs(capture.Widget,
538 capture.View,
539 capture.LastPosition,
541 args.Tick,
542 touch.Id));
543
544 _touchCaptures.Remove(touch.Id);
545 return;
546 }
547
548 var delta = new Vector2(touch.Position.X - capture.LastPosition.X,
549 touch.Position.Y - capture.LastPosition.Y);
550
551 capture.Widget.DispatchPointerMove(CreatePointerArgs(capture.Widget,
552 capture.View,
553 touch.Position,
555 args.Tick,
556 touch.Id,
557 deltaPx: delta));
558
559 capture.LastPosition = touch.Position;
560 }
561
562 private void OnTouchEnded(object? sender, TouchEventArgs args)
563 {
564 if (!_isStarted || _disposed)
565 return;
566
567 TouchPoint touch = args.Touch;
568
569 if (!_touchCaptures.Remove(touch.Id, out PointerCapture? capture))
570 return;
571
572 WidgetBase recipient = capture.Widget;
573
574 recipient.DispatchPointerUp(CreatePointerArgs(recipient,
575 capture.View,
576 touch.Position,
578 args.Tick,
579 touch.Id, instanceOffset: capture.InstanceOffset));
580
581 WidgetHit? releaseHit = HitTest(touch.Position);
582
583 if (touch.Phase != TouchPhase.Cancelled && IsSameHit(releaseHit, recipient, capture.View) && recipient.GetWrappedHitOffset(capture.View, touch.Position) == capture.InstanceOffset)
584 {
585 recipient.DispatchPointerClick(CreatePointerArgs(recipient,
586 capture.View,
587 touch.Position,
589 args.Tick,
590 touch.Id,
591 clickCount: 1, instanceOffset: capture.InstanceOffset));
592 }
593 }
594
595 private WidgetHit? HitTest(Point screenPositionPx)
596 {
597 View? view = GetTopmostViewAt(screenPositionPx);
598
599 if (view is null)
600 return null;
601
602 WidgetBase[] snapshot;
603
604 lock (_syncRoot)
605 snapshot = [.. _widgets];
606
607 for (int index = snapshot.Length - 1; index >= 0; index--)
608 {
609 WidgetBase widget = snapshot[index];
610
611 if (widget.HitTest(view, screenPositionPx))
612 return new WidgetHit(widget, view, widget.GetWrappedHitOffset(view, screenPositionPx) ?? PointF.Empty);
613 }
614
615 return null;
616 }
617
618 private View? GetTopmostViewAt(Point screenPositionPx)
619 {
620 if (_renderSurfaceHost.RenderSurfaceAdapter is not null &&
621 (screenPositionPx.X < 0 || screenPositionPx.Y < 0 ||
622 screenPositionPx.X >= _renderSurfaceHost.Backbuffer.Width ||
623 screenPositionPx.Y >= _renderSurfaceHost.Backbuffer.Height))
624 return null;
625
626 var views = _renderSurfaceHost.ViewManager.Views;
627
628 for (int index = views.Count - 1; index >= 0; index--)
629 {
630 View view = views[index];
631
632 if (view.Viewport.TargetRectPx.Contains(screenPositionPx))
633 return view;
634 }
635
636 return null;
637 }
638
639 private bool IsRegistered(WidgetBase widget)
640 {
641 lock (_syncRoot)
642 return _widgets.Contains(widget);
643 }
644
645 private bool IsManagedView(View view)
646 {
647 return _renderSurfaceHost.ViewManager.Views.Contains(view);
648 }
649
650 private bool HasValidTarget(WidgetBase widget,
651 View? routedView = null)
652 {
653 if (widget.Mode == DirectDrawingMode.SceneLayer)
654 return widget.SceneLayer is not null;
655
656 if (widget.View is null || !IsManagedView(widget.View))
657 return false;
658
659 return routedView is null || ReferenceEquals(widget.View, routedView);
660 }
661
662 private bool IsPointerRoutable(WidgetBase widget,
663 View view)
664 {
665 return IsRegistered(widget) &&
666 IsManagedView(view) &&
667 ReferenceEquals(widget.RenderSurfaceHost, _renderSurfaceHost) &&
668 HasValidTarget(widget, view) &&
669 widget.IsInputEnabled &&
670 widget.IsPointerInputEnabled &&
671 widget.Visible;
672 }
673
674 private bool CanReceiveKeyboardInput(WidgetBase widget)
675 {
676 return IsRegistered(widget) &&
677 ReferenceEquals(widget.RenderSurfaceHost, _renderSurfaceHost) &&
678 HasValidTarget(widget) &&
679 widget.Visible &&
680 widget.IsInputEnabled &&
681 widget.IsKeyboardInputEnabled &&
682 widget.CanReceiveFocus;
683 }
684
685 private void FocusFromPointer(WidgetBase widget)
686 {
687 if (widget.IsInputEnabled &&
688 widget.IsKeyboardInputEnabled &&
689 widget.CanReceiveFocus)
690 {
691 Focus(widget);
692 }
693 }
694
695 private void ReleaseInputState(
696 WidgetBase widget,
697 bool dispatchPointerUp)
698 {
699 ReleasePointerState(
700 widget,
701 dispatchPointerUp);
702
703 ReleaseKeyboardFocus(widget);
704 }
705
706 private void ReleasePointerState(WidgetBase widget,
707 bool dispatchPointerUp)
708 {
709 if (ReferenceEquals(_hoveredHit?.Widget, widget))
710 _hoveredHit = null;
711
712 if (ReferenceEquals(_mouseCapture?.Widget, widget))
713 {
714 PointerCapture capture = _mouseCapture;
715
716 if (dispatchPointerUp)
717 {
718 widget.DispatchPointerUp(CreatePointerArgs(widget,
719 capture.View,
720 capture.LastPosition,
721 capture.Button,
722 tick: 0,
723 pointerId: MousePointerId));
724 }
725
726 _mouseCapture = null;
727 }
728
729 int[] touchIds = _touchCaptures.Where(x => ReferenceEquals(x.Value.Widget, widget))
730 .Select(x => x.Key)
731 .ToArray();
732
733 foreach (int touchId in touchIds)
734 {
735 PointerCapture capture = _touchCaptures[touchId];
736
737 if (dispatchPointerUp)
738 {
739 widget.DispatchPointerUp(CreatePointerArgs(widget,
740 capture.View,
741 capture.LastPosition,
743 tick: 0,
744 pointerId: touchId));
745 }
746
747 _touchCaptures.Remove(touchId);
748 }
749 }
750
751 private void ReleaseKeyboardFocus(WidgetBase widget)
752 {
753 if (ReferenceEquals(_focusedWidget, widget))
754 Focus(null);
755 }
756
757 private static bool IsSameHit(WidgetHit? left,
758 WidgetHit? right)
759 {
760 if (left is null || right is null)
761 return left is null && right is null;
762
763 return ReferenceEquals(left.Widget, right.Widget)
764 && ReferenceEquals(left.View, right.View);
765 }
766
767 private static bool IsSameHit(WidgetHit? hit,
768 WidgetBase widget,
769 View view)
770 {
771 return hit is not null
772 && ReferenceEquals(hit.Widget, widget)
773 && ReferenceEquals(hit.View, view);
774 }
775
776 private WidgetPointerEventArgs CreatePointerArgs(WidgetBase widget,
777 View view,
778 Point position,
780 long tick,
781 int pointerId,
782 int clickCount = 0,
783 Vector2 deltaPx = default,
784 PointF? instanceOffset = null)
785 {
786 return new WidgetPointerEventArgs(widget,
787 view,
788 new PointF(position.X, position.Y),
789 button,
790 clickCount,
791 deltaPx,
792 tick,
793 pointerId)
794 {
795 WrappedOffsetWorldPx = instanceOffset ?? (pointerId == MousePointerId && _mouseCapture is { } mouse && ReferenceEquals(mouse.Widget, widget)
796 ? mouse.InstanceOffset
797 : _touchCaptures.TryGetValue(pointerId, out var touch) && ReferenceEquals(touch.Widget, widget)
798 ? touch.InstanceOffset
799 : widget.GetWrappedHitOffset(view, position) ?? PointF.Empty)
800 };
801 }
802
803 private static WidgetPointerButtonEnum MapMouseButton(MouseButton button)
804 {
805 return button switch
806 {
807 MouseButton.Left => WidgetPointerButtonEnum.Left,
808 MouseButton.Right => WidgetPointerButtonEnum.Right,
809 MouseButton.Middle => WidgetPointerButtonEnum.Middle,
810 _ => WidgetPointerButtonEnum.None
811 };
812 }
813
814 private void OnWidgetDisposing(object? sender, IDirectDrawable drawing)
815 {
816 if (drawing is WidgetBase widget)
817 {
818 Unregister(widget, dispatchPointerUp: false);
819 }
820 }
821
822 private void Unregister(WidgetBase widget,
823 bool dispatchPointerUp)
824 {
825 bool removed;
826
827 lock (_syncRoot)
828 removed = _widgets.Remove(widget);
829
830 if (!removed)
831 return;
832
833 widget.Disposing -= OnWidgetDisposing;
834
835 ReleaseInputState(widget, dispatchPointerUp);
836 }
837
838 private sealed class WidgetHit
839 {
846 public WidgetHit(WidgetBase widget,
847 View view,
848 PointF instanceOffset)
849 {
850 Widget = widget;
851 View = view;
852 InstanceOffset = instanceOffset;
853 }
854
858 public PointF InstanceOffset { get; }
859
863 public WidgetBase Widget { get; }
864
868 public View View { get; }
869 }
870
871 private sealed class PointerCapture
872 {
881 public PointerCapture(WidgetBase widget,
882 View view,
884 Point lastPosition,
885 PointF instanceOffset)
886 {
887 Widget = widget;
888 View = view;
889 Button = button;
890 LastPosition = lastPosition;
891 InstanceOffset = instanceOffset;
892 }
893
897 public WidgetBase Widget { get; }
898
902 public View View { get; }
903
907 public WidgetPointerButtonEnum Button { get; }
908
915 public Point LastPosition { get; set; }
919 public PointF InstanceOffset { get; }
920 }
921}
RenderSurfaceHostBase RenderSurfaceHost
Gets the render surface host shared by the composite and its children.
virtual DirectComposite Clear()
Removes every child without disposing them.
KeyboardModifierState Modifiers
Gets the state of keyboard modifier keys (Shift, Ctrl, Alt) at the time this event was generated....
KeyEventConfiguration KeyConfig
Gets the configuration details for the keyboard key that generated this event, including the key iden...
KeyAction KeyAction
Gets the type of action that occurred for this key, indicating whether the key was initially pressed,...
string Key
Gets the identifier of the keyboard key associated with this configuration. This can be a key name su...
Cross-platform singleton keyboard handler with throttling and manual input state feeding.
Point PreviousPosition
Gets the previous position of the mouse cursor before the current polling interval....
Point CurrentPosition
Gets the current position of the mouse cursor at the time this event was generated....
long Tick
Gets the engine tick at the time this mouse event was generated.
bool IsButtonJustReleased(MouseButton button)
Determines whether the specified mouse button was released during the current polling interval.
int ScrollDelta
Gets the accumulated scroll wheel delta since the last poll, measured in implementation-defined units...
bool IsButtonJustPressed(MouseButton button)
Determines whether the specified mouse button was pressed during the current polling interval.
Provides centralized polling and event management for mouse input, including button presses,...
TouchPoint Touch
Gets the touch contact point associated with this event.
long Tick
Gets the engine tick at which this touch event was emitted.
Provides centralized polling and event management for touch input. This singleton class monitors touc...
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
Viewport Viewport
Gets the viewport that defines the screen-space rectangle, zoom level, and other rendering parameters...
Definition View.cs:44
Rectangle TargetRectPx
Screen-space rectangle (in SCREEN / RenderSurface pixels) where this view is drawn....
Definition Viewport.cs:35
Provides the base implementation for widgets that can be drawn and receive pointer,...
Definition WidgetBase.cs:12
void Focus(WidgetBase? widget)
Assigns keyboard focus to the specified widget.
void Unregister(WidgetBase widget)
Unregisters a widget so that it no longer receives routed input.
void ClearFocus()
Clears the current keyboard focus.
void Start()
Starts routing input events from the configured pollers to registered widgets.
void BringToFront(WidgetBase widget)
Moves a registered widget to the front of the input routing order.
WidgetBase? FocusedWidget
Gets the widget that currently has keyboard focus.
void Stop()
Stops routing input events and releases current pointer and focus state.
void Register(WidgetBase widget)
Registers a widget so that it can receive routed input.
WidgetInputRouter(RenderSurfaceHostBase renderSurfaceHost, KeyboardEventPoller? keyboardEventPoller, MouseEventPoller? mouseEventPoller, TouchEventPoller? touchEventPoller)
Initializes a new instance of the WidgetInputRouter class.
void Dispose()
Releases the resources used by the router and unregisters all widgets.
const int MousePointerId
Identifies mouse pointer events in routed widget input.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.
KeyboardModifierState
Represents the state of keyboard modifier keys (Shift, Ctrl, Alt) that can be pressed simultaneously ...
KeyAction
Represents the type of action performed on a keyboard key, indicating whether the key was pressed,...
Definition KeyAction.cs:10
MouseButton
Represents mouse button identifiers that can be used individually or combined to represent multiple s...
@ Middle
The middle mouse button, usually implemented as a clickable scroll wheel. This is often used for spec...
readonly record struct TouchPoint(int Id, Point Position, TouchPhase Phase)
Represents a single touch contact point, capturing its unique identifier, screen position,...
TouchPhase
Describes the current phase of a single touch contact point in its lifecycle.
Definition TouchPhase.cs:7
WidgetPointerButtonEnum
Represents a pointer button used by a widget interaction.