mirror of
https://github.com/Stone-Red-Code/RemSox.git
synced 2026-09-04 00:56:19 +02:00
Add mouse event handling and improve key event handling for controls and windows
This commit is contained in:
@@ -2,7 +2,7 @@ using Cosmos.Kernel.System.Keyboard;
|
|||||||
|
|
||||||
using RemSox.Processing;
|
using RemSox.Processing;
|
||||||
using RemSox.UI.CLI;
|
using RemSox.UI.CLI;
|
||||||
using RemSox.UI.GUI.UIEelements;
|
using RemSox.UI.GUI.UIEelements.Shapes;
|
||||||
using RemSox.UI.GUI.Windows;
|
using RemSox.UI.GUI.Windows;
|
||||||
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Cosmos.Kernel.System.Keyboard;
|
||||||
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
namespace RemSox.UI.GUI.UIEelements;
|
namespace RemSox.UI.GUI.UIEelements;
|
||||||
@@ -15,4 +17,12 @@ public abstract class Control(string type) : UIElement(type)
|
|||||||
get;
|
get;
|
||||||
set => SetProperty(nameof(Size), ref field, value);
|
set => SetProperty(nameof(Size), ref field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void HandleMouseEvent(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void HandleKeyEvent(KeyEvent keyEvent)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
namespace RemSox.UI.GUI.UIEelements;
|
namespace RemSox.UI.GUI.UIEelements.Shapes;
|
||||||
|
|
||||||
public class Text() : UIElement("Text")
|
public class Text() : UIElement("Text")
|
||||||
{
|
{
|
||||||
+106
-11
@@ -28,16 +28,14 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
/// <summary> Event raised when a keyboard event is handled by this window. </summary>
|
/// <summary> Event raised when a keyboard event is handled by this window. </summary>
|
||||||
public event Action<Sys.Keyboard.KeyEvent>? OnKeyEvent;
|
public event Action<Sys.Keyboard.KeyEvent>? OnKeyEvent;
|
||||||
|
|
||||||
/// <summary> Dispatches a key event to the window's registered event handlers. </summary>
|
/// <summary> Event raised when a mouse event is handled by this window. </summary>
|
||||||
public void HandleKeyEvent(Sys.Keyboard.KeyEvent keyEvent)
|
public event Action<MouseEvent>? OnMouseEvent;
|
||||||
{
|
|
||||||
OnKeyEvent?.Invoke(keyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets or sets whether this window is currently focused. </summary>
|
/// <summary> Gets or sets whether this window is currently focused. </summary>
|
||||||
public bool IsFocused
|
public bool IsFocused
|
||||||
{
|
{
|
||||||
get => WindowManager.IsWindowFocused(this); set => WindowManager.FocusWindow(value ? this : null);
|
get => WindowManager.IsWindowFocused(this);
|
||||||
|
set => WindowManager.FocusWindow(value ? this : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Gets or sets whether the window is visible. </summary>
|
/// <summary> Gets or sets whether the window is visible. </summary>
|
||||||
@@ -62,7 +60,10 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
public bool IsResizing => currentInteraction is InteractionMode.ResizeTop or InteractionMode.ResizeBottom or InteractionMode.ResizeLeft or InteractionMode.ResizeRight or InteractionMode.ResizeTopLeft or InteractionMode.ResizeTopRight or InteractionMode.ResizeBottomLeft or InteractionMode.ResizeBottomRight;
|
public bool IsResizing => currentInteraction is InteractionMode.ResizeTop or InteractionMode.ResizeBottom or InteractionMode.ResizeLeft or InteractionMode.ResizeRight or InteractionMode.ResizeTopLeft or InteractionMode.ResizeTopRight or InteractionMode.ResizeBottomLeft or InteractionMode.ResizeBottomRight;
|
||||||
|
|
||||||
private readonly Lock uiElementsLock = new();
|
private readonly Lock uiElementsLock = new();
|
||||||
|
private readonly Lock controlsLock = new();
|
||||||
private readonly Dictionary<int, UIElement> uiElements = [];
|
private readonly Dictionary<int, UIElement> uiElements = [];
|
||||||
|
private readonly Dictionary<int, Control> controls = [];
|
||||||
|
private Control? focusedControl = null;
|
||||||
|
|
||||||
private int nextUIElementId = 1;
|
private int nextUIElementId = 1;
|
||||||
|
|
||||||
@@ -101,19 +102,53 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (AutoFlush)
|
|
||||||
{
|
|
||||||
Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (uiElementsLock)
|
lock (uiElementsLock)
|
||||||
{
|
{
|
||||||
uiElements.Add(uiElementId, uiElement);
|
uiElements.Add(uiElementId, uiElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (controlsLock)
|
||||||
|
{
|
||||||
|
if (uiElement is Control control)
|
||||||
|
{
|
||||||
|
controls.Add(uiElementId, control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AutoFlush)
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
|
||||||
return uiElement;
|
return uiElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveUIElement(int elementId)
|
||||||
|
{
|
||||||
|
bool removed = false;
|
||||||
|
|
||||||
|
lock (uiElementsLock)
|
||||||
|
{
|
||||||
|
if (uiElements.Remove(elementId))
|
||||||
|
{
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (controlsLock)
|
||||||
|
{
|
||||||
|
if (controls.Remove(elementId))
|
||||||
|
{
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed && AutoFlush)
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalidates the window state, forcing a full redraw on the next flush.
|
/// Invalidates the window state, forcing a full redraw on the next flush.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,6 +244,42 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Dispatches a key event to the window's registered event handlers. </summary>
|
||||||
|
public void HandleKeyEvent(Sys.Keyboard.KeyEvent keyEvent)
|
||||||
|
{
|
||||||
|
OnKeyEvent?.Invoke(keyEvent);
|
||||||
|
|
||||||
|
focusedControl?.HandleKeyEvent(keyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Dispatches a mouse event to the window's registered event handlers. </summary>
|
||||||
|
public void HandleMouseEvent(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
OnMouseEvent?.Invoke(mouseEvent);
|
||||||
|
|
||||||
|
Point local = new(mouseEvent.X - Position.X, mouseEvent.Y - Position.Y);
|
||||||
|
|
||||||
|
Control? target = HitTestControl(local);
|
||||||
|
|
||||||
|
if (mouseEvent.Type == MouseEventType.ButtonDown && mouseEvent.Button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
focusedControl = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseEvent localEvent = mouseEvent with
|
||||||
|
{
|
||||||
|
X = local.X,
|
||||||
|
Y = local.Y
|
||||||
|
};
|
||||||
|
|
||||||
|
target.HandleMouseEvent(localEvent);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a pointer position starts an interaction (drag/resize) and handles focus.
|
/// Checks if a pointer position starts an interaction (drag/resize) and handles focus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -361,6 +432,30 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
currentInteraction = InteractionMode.None;
|
currentInteraction = InteractionMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Control? HitTestControl(Point windowRelativePosition)
|
||||||
|
{
|
||||||
|
lock (controlsLock)
|
||||||
|
{
|
||||||
|
// top-most last (simple Z-order assumption: insertion order)
|
||||||
|
foreach (Control control in controls.Values.Reverse())
|
||||||
|
{
|
||||||
|
Rectangle bounds = new(
|
||||||
|
control.Position.X,
|
||||||
|
control.Position.Y,
|
||||||
|
control.Size.Width,
|
||||||
|
control.Size.Height
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bounds.Contains(windowRelativePosition))
|
||||||
|
{
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsPointInTitleBar(Point pointerPosition)
|
private bool IsPointInTitleBar(Point pointerPosition)
|
||||||
{
|
{
|
||||||
return pointerPosition.X >= Position.X
|
return pointerPosition.X >= Position.X
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public static class WindowManager
|
|||||||
private static Window? activeInteractWindow = null;
|
private static Window? activeInteractWindow = null;
|
||||||
private static Point lastPointerPosition = Point.Empty;
|
private static Point lastPointerPosition = Point.Empty;
|
||||||
private static bool wasLeftButtonDown = false;
|
private static bool wasLeftButtonDown = false;
|
||||||
|
private static bool wasRightButtonDown = false;
|
||||||
|
private static bool wasMiddleButtonDown = false;
|
||||||
|
|
||||||
private static readonly MuliRenderSource renderSource = new([]);
|
private static readonly MuliRenderSource renderSource = new([]);
|
||||||
|
|
||||||
@@ -36,6 +38,8 @@ public static class WindowManager
|
|||||||
{
|
{
|
||||||
Point pointerPosition = new(MouseManager.X, MouseManager.Y);
|
Point pointerPosition = new(MouseManager.X, MouseManager.Y);
|
||||||
bool leftButtonDown = MouseManager.LeftButton;
|
bool leftButtonDown = MouseManager.LeftButton;
|
||||||
|
bool rightButtonDown = MouseManager.RightButton;
|
||||||
|
bool middleButtonDown = MouseManager.MiddleButton;
|
||||||
|
|
||||||
Canvas canvas = FullScreenCanvas.GetFullScreenCanvas();
|
Canvas canvas = FullScreenCanvas.GetFullScreenCanvas();
|
||||||
|
|
||||||
@@ -54,12 +58,19 @@ public static class WindowManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
wasLeftButtonDown = leftButtonDown;
|
wasLeftButtonDown = leftButtonDown;
|
||||||
|
wasRightButtonDown = rightButtonDown;
|
||||||
|
wasMiddleButtonDown = middleButtonDown;
|
||||||
|
|
||||||
while (KeyboardManager.TryReadKey(out KeyEvent? keyEvent) && keyEvent is not null)
|
while (KeyboardManager.TryReadKey(out KeyEvent? keyEvent) && keyEvent is not null)
|
||||||
{
|
{
|
||||||
focusedWindow?.HandleKeyEvent(keyEvent);
|
focusedWindow?.HandleKeyEvent(keyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (focusedWindow is not null && activeInteractWindow is null)
|
||||||
|
{
|
||||||
|
DispatchMouseEvents(focusedWindow, pointerPosition);
|
||||||
|
}
|
||||||
|
|
||||||
CanvasRenderSource.CompositeAndDisplay(canvas, pointerPosition);
|
CanvasRenderSource.CompositeAndDisplay(canvas, pointerPosition);
|
||||||
|
|
||||||
lastPointerPosition = pointerPosition;
|
lastPointerPosition = pointerPosition;
|
||||||
@@ -323,9 +334,23 @@ public static class WindowManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to begin interaction (drag/resize) with a window at the specified pointer position.
|
/// Forces a full redraw of all windows in the system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Window? TryBeginInteract(Point pointerPosition)
|
public static void InvalidateAll()
|
||||||
|
{
|
||||||
|
List<Window> allWindows;
|
||||||
|
lock (windowsLock)
|
||||||
|
{
|
||||||
|
allWindows = windows.Values.SelectMany(w => w).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Window window in allWindows)
|
||||||
|
{
|
||||||
|
window.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Window? TryBeginInteract(Point pointerPosition)
|
||||||
{
|
{
|
||||||
List<Window> allWindows;
|
List<Window> allWindows;
|
||||||
lock (windowsLock)
|
lock (windowsLock)
|
||||||
@@ -343,20 +368,45 @@ public static class WindowManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private static void DispatchMouseEvents(Window window, Point pos)
|
||||||
/// Forces a full redraw of all windows in the system.
|
|
||||||
/// </summary>
|
|
||||||
public static void InvalidateAll()
|
|
||||||
{
|
{
|
||||||
List<Window> allWindows;
|
int delta = MouseManager.ScrollDelta;
|
||||||
lock (windowsLock)
|
|
||||||
|
if (pos != lastPointerPosition)
|
||||||
{
|
{
|
||||||
allWindows = windows.Values.SelectMany(w => w).ToList();
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.Move, pos.X, pos.Y, MouseButton.None, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Window window in allWindows)
|
if (MouseManager.LeftButton && !wasLeftButtonDown)
|
||||||
{
|
{
|
||||||
window.Invalidate();
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonDown, pos.X, pos.Y, MouseButton.Left, 0));
|
||||||
|
}
|
||||||
|
else if (!MouseManager.LeftButton && wasLeftButtonDown)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonUp, pos.X, pos.Y, MouseButton.Left, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MouseManager.RightButton && !wasRightButtonDown)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonDown, pos.X, pos.Y, MouseButton.Right, 0));
|
||||||
|
}
|
||||||
|
else if (!MouseManager.RightButton && wasRightButtonDown)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonUp, pos.X, pos.Y, MouseButton.Right, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MouseManager.MiddleButton && !wasMiddleButtonDown)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonDown, pos.X, pos.Y, MouseButton.Middle, 0));
|
||||||
|
}
|
||||||
|
else if (!MouseManager.MiddleButton && wasMiddleButtonDown)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.ButtonUp, pos.X, pos.Y, MouseButton.Middle, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta != 0)
|
||||||
|
{
|
||||||
|
window.HandleMouseEvent(new MouseEvent(MouseEventType.Wheel, pos.X, pos.Y, MouseButton.None, delta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace RemSox.UI;
|
||||||
|
|
||||||
|
public record MouseEvent(MouseEventType Type, int X = 0, int Y = 0, MouseButton Button = MouseButton.None, int Delta = 0)
|
||||||
|
{
|
||||||
|
public static MouseEvent Move(int x, int y)
|
||||||
|
{
|
||||||
|
return new(MouseEventType.Move, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MouseEvent ButtonDown(int x, int y, MouseButton button)
|
||||||
|
{
|
||||||
|
return new(MouseEventType.ButtonDown, x, y, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MouseEvent ButtonUp(int x, int y, MouseButton button)
|
||||||
|
{
|
||||||
|
return new(MouseEventType.ButtonUp, x, y, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MouseEvent Wheel(int x, int y, int delta)
|
||||||
|
{
|
||||||
|
return new(MouseEventType.Wheel, x, y, Delta: delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MouseEventType
|
||||||
|
{
|
||||||
|
Move,
|
||||||
|
ButtonDown,
|
||||||
|
ButtonUp,
|
||||||
|
Wheel
|
||||||
|
}
|
||||||
|
public enum MouseButton
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Middle,
|
||||||
|
None
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user