From 7506b8c118220350e5b1bff5efab18b07ecdc07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 30 Oct 2024 21:20:57 +0100 Subject: [PATCH] Allow changing the cursor, moved Window to GlitchyEngine.UI --- ...archyWindow.bf => UltralightMainWindow.bf} | 6 +- .../src/Ultralight/UltralightWindow.bf | 124 ++++++++++++++- GlitchyEditor/src/UltralightLayer.bf | 6 +- GlitchyEngine/src/Application.bf | 1 + .../src/Platform/Windows/WindowsInput.bf | 1 + .../src/Platform/Windows/WindowsWindow.bf | 142 +++++++++++++++++- GlitchyEngine/src/Renderer/GraphicsContext.bf | 1 + GlitchyEngine/src/Renderer/SwapChain.bf | 2 + GlitchyEngine/src/UI/CursorImage.bf | 48 ++++++ GlitchyEngine/src/{ => UI}/Window.bf | 14 +- GlitchyEngine/src/{ => UI}/WindowRectangle.bf | 0 11 files changed, 332 insertions(+), 13 deletions(-) rename GlitchyEditor/src/Ultralight/{UltralightEntityHierarchyWindow.bf => UltralightMainWindow.bf} (91%) create mode 100644 GlitchyEngine/src/UI/CursorImage.bf rename GlitchyEngine/src/{ => UI}/Window.bf (91%) rename GlitchyEngine/src/{ => UI}/WindowRectangle.bf (100%) diff --git a/GlitchyEditor/src/Ultralight/UltralightEntityHierarchyWindow.bf b/GlitchyEditor/src/Ultralight/UltralightMainWindow.bf similarity index 91% rename from GlitchyEditor/src/Ultralight/UltralightEntityHierarchyWindow.bf rename to GlitchyEditor/src/Ultralight/UltralightMainWindow.bf index 4bc29db..d2766f4 100644 --- a/GlitchyEditor/src/Ultralight/UltralightEntityHierarchyWindow.bf +++ b/GlitchyEditor/src/Ultralight/UltralightMainWindow.bf @@ -2,9 +2,9 @@ using Ultralight.CAPI; namespace GlitchyEditor.Ultralight; -class UltralightEntityHierarchyWindow : UltralightWindow +class UltralightMainWindow : UltralightWindow { - public this() : base("Entity Hierarchy", "file:///react-ui-prototype/dist/index.html") + public this() : base("Glitchy Engine", "file:///dist/index.html") // "file:///react-ui-prototype/dist/index.html" //public this() : base("Entity Hierarchy", "file:///another-react-test/index.html") { @@ -57,7 +57,7 @@ class UltralightEntityHierarchyWindow : UltralightWindow /// /// Create a JavaScript String containing the name of our callback. /// - JSStringRef name = JSStringCreateWithUTF8CString("GetMessage"); + JSStringRef name = JSStringCreateWithUTF8CString("OnCreateEntityClick"); /// /// Create a garbage-collected JavaScript function that is bound to our native C callback diff --git a/GlitchyEditor/src/Ultralight/UltralightWindow.bf b/GlitchyEditor/src/Ultralight/UltralightWindow.bf index c61a680..5d4d426 100644 --- a/GlitchyEditor/src/Ultralight/UltralightWindow.bf +++ b/GlitchyEditor/src/Ultralight/UltralightWindow.bf @@ -6,14 +6,16 @@ using GlitchyEngine.Content; using GlitchyEngine.Renderer; using Ultralight; using Ultralight.CAPI; +using DirectX.Windows; +using GlitchyEngine.UI; namespace GlitchyEditor.Ultralight; abstract class UltralightWindow { - protected Window _window ~ delete _; + protected internal Window _window ~ delete _; - protected ULView _view; + protected internal ULView _view; private int2 _cursorPosition; @@ -23,6 +25,7 @@ abstract class UltralightWindow { WindowDescription windowDesc = .Default; windowDesc.Title = title; + windowDesc.WindowStyle = .Borderless; _window = new Window(windowDesc); _window.EventCallback = new => EventHandler; @@ -43,11 +46,12 @@ abstract class UltralightWindow CreateTexture(); } - private void InitView() + protected virtual void InitView() { ULViewConfig viewConfig = ulCreateViewConfig(); ulViewConfigSetIsAccelerated(viewConfig, false); - + ulViewConfigSetIsTransparent(viewConfig, true); + _view = ulCreateView(UltralightLayer.renderer, _window.SwapChain.Width, _window.SwapChain.Height, viewConfig, null); ulDestroyViewConfig(viewConfig); @@ -61,6 +65,8 @@ abstract class UltralightWindow ulViewSetFailLoadingCallback(_view, => OnFailedLoading, userData); ulViewSetAddConsoleMessageCallback(_view, => OnAddConsoleMessage, userData); + + ulViewSetChangeCursorCallback(_view, => OnChangeCursor, userData); } #endregion Init @@ -280,6 +286,116 @@ abstract class UltralightWindow UltralightWindow window = (UltralightWindow)Internal.UnsafeCastToObject(user_data); window.OnAddConsoleMessage(caller, source, level, message, line_number, column_number, source_id); } + + + private static void OnChangeCursor(void* userData, C_View* caller, ULCursor ulCursor) + { + UltralightWindow window = (UltralightWindow)Internal.UnsafeCastToObject(userData); + + CursorImage cursor; + + switch (ulCursor) + { + case .kCursor_Pointer: + cursor = .Pointer; + + case .kCursor_Cross: + cursor = .Crosshair; + + case .kCursor_Hand: + cursor = .Hand; + + case .kCursor_IBeam: + cursor = .IBeam; + + case .kCursor_Wait: + cursor = .Wait; + + case .kCursor_Help: + cursor = .Help; + + case .kCursor_EastResize, .kCursor_WestResize, .kCursor_EastWestResize: + cursor = .ResizeEastWest; + + case .kCursor_NorthResize, .kCursor_SouthResize, .kCursor_NorthSouthResize: + cursor = .ResizeNorthSouth; + + case .kCursor_NorthEastResize, .kCursor_SouthWestResize, .kCursor_NorthEastSouthWestResize: + cursor = .ResizeNorthEastSouthWest; + + case .kCursor_NorthWestResize, .kCursor_SouthEastResize, .kCursor_NorthWestSouthEastResize: + cursor = .ResizeNorthWestSouthEast; + + case .kCursor_ColumnResize: + // TODO use cursor file + cursor = .ResizeColumn; + + case .kCursor_RowResize: + // TODO use cursor file + cursor = .ResizeRow; + + case .kCursor_MiddlePanning: + cursor = .PanMiddle; + case .kCursor_EastPanning: + cursor = .PanEast; + case .kCursor_WestPanning: + cursor = .PanWest; + case .kCursor_NorthPanning: + cursor = .PanNorth; + case .kCursor_SouthPanning: + cursor = .PanSouth; + case .kCursor_NorthEastPanning: + cursor = .PanNorthEast; + case .kCursor_NorthWestPanning: + cursor = .PanNorthWest; + case .kCursor_SouthEastPanning: + cursor = .PanSouthEast; + case .kCursor_SouthWestPanning: + cursor = .PanSouthWest; + + case .kCursor_Move: + cursor = .Move; + + case .kCursor_VerticalText: + cursor = .VerticalText; + + case .kCursor_Cell: + cursor = .Cell; + + case .kCursor_ContextMenu: + cursor = .ContextMenu; + + case .kCursor_Alias: + cursor = .Alias; + + case .kCursor_Progress: + cursor = .Progress; + + case .kCursor_NoDrop, .kCursor_NotAllowed: + cursor = .NotAllowed; + + case .kCursor_Copy: + cursor = .Copy; + + case .kCursor_None: + cursor = .None; + + case .kCursor_ZoomIn: + cursor = .ZoomIn; + case .kCursor_ZoomOut: + cursor = .ZoomOut; + + case .kCursor_Grab: + cursor = .Grab; + case .kCursor_Grabbing: + cursor = .Grabbing; + + case .kCursor_Custom: + cursor = .Custom; + } + + window._window.SetCursor(cursor); + } protected virtual void OnAddConsoleMessage(C_View* caller, ULMessageSource source, ULMessageLevel level, C_String* message, uint32 line_number, uint32 column_number, C_String* source_id) { diff --git a/GlitchyEditor/src/UltralightLayer.bf b/GlitchyEditor/src/UltralightLayer.bf index 7dcb1b5..21f3c6c 100644 --- a/GlitchyEditor/src/UltralightLayer.bf +++ b/GlitchyEditor/src/UltralightLayer.bf @@ -17,7 +17,7 @@ class UltralightLayer : Layer private List _windows = new .() ~ DeleteContainerAndItems!(_); - public UltralightEntityHierarchyWindow EntityHierarchyWindow; + public UltralightMainWindow EntityHierarchyWindow; private static Self _instance; @@ -29,7 +29,7 @@ class UltralightLayer : Layer InitUltralight(); - EntityHierarchyWindow = new UltralightEntityHierarchyWindow(); + EntityHierarchyWindow = new UltralightMainWindow(); _windows.Add(EntityHierarchyWindow); _copyEffect = Content.LoadAsset("Resources/Shaders/Copy.hlsl"); @@ -56,6 +56,8 @@ class UltralightLayer : Layer { ULConfig config = ulCreateConfig(); + //ulConfigSetUserStylesheet(config); + ULString logPath = ulCreateString(@"D:\Development\Projects\Beef\GlitchyEngine\GlitchyEditor\ul.log"); ulEnableDefaultLogger(logPath); ulDestroyString(logPath); diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index 5f35c8c..be51054 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -1,5 +1,6 @@ using System; using GlitchyEngine.Events; +using GlitchyEngine.UI; using GlitchyEngine.ImGui; using GlitchyEngine.Renderer; using GlitchyEngine.Debug; diff --git a/GlitchyEngine/src/Platform/Windows/WindowsInput.bf b/GlitchyEngine/src/Platform/Windows/WindowsInput.bf index 8a76c04..947f64c 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsInput.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsInput.bf @@ -8,6 +8,7 @@ using System; using System.Interop; using GlitchyEngine.Events; using GlitchyEngine.Math; +using GlitchyEngine.UI; using static System.Windows; diff --git a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf index 1490ebb..368189b 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf @@ -15,7 +15,7 @@ using static System.Windows; using internal GlitchyEngine; -namespace GlitchyEngine +namespace GlitchyEngine.UI { /// The windows specific Window implementation public extension Window @@ -38,6 +38,8 @@ namespace GlitchyEngine private bool _isActive; + private HCURSOR _cursor; + public override int32 MinWidth { get => _minMaxInfo.MinimumTrackingSize.x; @@ -228,6 +230,9 @@ namespace GlitchyEngine } } + [LinkName(.C)] + static extern Windows.IntBool SetLayeredWindowAttributes(HWND hwnd, uint32 crKey, uint8 bAlpha, DWORD dwFlags); + private void Init(WindowDescription desc) { Debug.Profiler.ProfileFunction!(); @@ -242,7 +247,18 @@ namespace GlitchyEngine { Debug.Profiler.ProfileScope!("CreateWindow"); - _windowHandle = CreateWindowExW(.None, WindowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, + DirectX.Windows.WindowStyles style = .WS_VISIBLE; + + switch (desc.WindowStyle) + { + case .Normal: + style |= .WS_OVERLAPPEDWINDOW | .WS_VISIBLE; + case .Borderless: + style |= .WS_POPUP | .WS_THICKFRAME | .WS_CAPTION | .WS_SYSMENU | .WS_MAXIMIZEBOX | .WS_MINIMIZEBOX; + } + + _windowHandle = CreateWindowExW(.None, WindowClass.ClassName, desc.Title.ToScopedNativeWChar!(), + style, CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null); } @@ -522,6 +538,14 @@ namespace GlitchyEngine delete window._title; window._title = null; } + case WM_SETCURSOR: + if (GetLowOrder(lParam) == /*HTCLIENT*/ 1) + { + Winuser.SetCursor(window._cursor); + + return 1; + } + break; case WM_SYSCOMMAND: { /* Remove beeping sound when ALT + some key is pressed. */ @@ -609,6 +633,120 @@ namespace GlitchyEngine _title = new String(titleSpan); } + + public override Result SetCursor(CursorImage cursorImage) + { + LPCTSTR cursor = null; + + // Firefox cursors: https://hg.mozilla.org/mozilla-central/file/tip/widget/windows/res + + switch (cursorImage) + { + case .Pointer: + cursor = Winuser.IDC_ARROW; + + case .Crosshair: + cursor = Winuser.IDC_CROSS; + + case .Hand: + cursor = Winuser.IDC_HAND; + + case .IBeam: + cursor = Winuser.IDC_IBEAM; + + case .Wait: + cursor = Winuser.IDC_WAIT; + + case .Help: + cursor = Winuser.IDC_HELP; + + case .ResizeEastWest: + cursor = Winuser.IDC_SIZEWE; + + case .ResizeNorthSouth: + cursor = Winuser.IDC_SIZENS; + + case .ResizeNorthEastSouthWest: + cursor = Winuser.IDC_SIZENESW; + + case .ResizeNorthWestSouthEast: + cursor = Winuser.IDC_SIZENWSE; + + case .ResizeColumn: + // TODO use cursor file + cursor = Winuser.IDC_SIZEWE; + + case .ResizeRow: + // TODO use cursor file + cursor = Winuser.IDC_SIZENS; + + case .PanMiddle: + cursor = Winuser.MAKEINTRESOURCEW(32654); + case .PanEast: + cursor = Winuser.MAKEINTRESOURCEW(32658); + case .PanWest: + cursor = Winuser.MAKEINTRESOURCEW(32657); + case .PanNorth: + cursor = Winuser.MAKEINTRESOURCEW(32655); + case .PanSouth: + cursor = Winuser.MAKEINTRESOURCEW(32656); + case .PanNorthEast: + cursor = Winuser.MAKEINTRESOURCEW(32660); + case .PanNorthWest: + cursor = Winuser.MAKEINTRESOURCEW(32659); + case .PanSouthEast: + cursor = Winuser.MAKEINTRESOURCEW(32662); + case .PanSouthWest: + cursor = Winuser.MAKEINTRESOURCEW(32661); + + case .Move: + cursor = Winuser.IDC_SIZEALL; + + case .VerticalText: + // TODO + + case .Cell: + // TODO + + case .ContextMenu: + // TODO + + case .Alias: + // TODO + + case .Progress: + cursor = Winuser.IDC_APPSTARTING; + + case .NotAllowed: + cursor = Winuser.IDC_NO; + + case .Copy: + // TODO + + case .None: + cursor = null; + + case .ZoomIn: + // TODO + + case .ZoomOut: + + case .Grab: + // TODO + case .Grabbing: + // TODO + + case .Custom: + // TODO: Use cursor file specified by user + } + + var hCursor = Winuser.LoadCursorW((.)0, cursor); + + _cursor = hCursor; + Winuser.SetCursor(hCursor); + + return .Ok; + } } } diff --git a/GlitchyEngine/src/Renderer/GraphicsContext.bf b/GlitchyEngine/src/Renderer/GraphicsContext.bf index 8650662..9336b74 100644 --- a/GlitchyEngine/src/Renderer/GraphicsContext.bf +++ b/GlitchyEngine/src/Renderer/GraphicsContext.bf @@ -1,6 +1,7 @@ using System; using GlitchyEngine.Core; using GlitchyEngine.Math; +using GlitchyEngine.UI; namespace GlitchyEngine.Renderer { diff --git a/GlitchyEngine/src/Renderer/SwapChain.bf b/GlitchyEngine/src/Renderer/SwapChain.bf index 6eed444..22502fa 100644 --- a/GlitchyEngine/src/Renderer/SwapChain.bf +++ b/GlitchyEngine/src/Renderer/SwapChain.bf @@ -1,3 +1,5 @@ +using GlitchyEngine.UI; + namespace GlitchyEngine.Renderer { public class SwapChain diff --git a/GlitchyEngine/src/UI/CursorImage.bf b/GlitchyEngine/src/UI/CursorImage.bf new file mode 100644 index 0000000..b1cb29d --- /dev/null +++ b/GlitchyEngine/src/UI/CursorImage.bf @@ -0,0 +1,48 @@ +namespace GlitchyEngine.UI; + +enum CursorImage +{ + None, + + Pointer, + Crosshair, + Hand, + IBeam, + Wait, + Help, + + ResizeEastWest, + ResizeNorthSouth, + ResizeNorthEastSouthWest, + ResizeNorthWestSouthEast, + ResizeColumn, + ResizeRow, + + PanMiddle, + PanNorth, + PanNorthEast, + PanNorthWest, + PanSouth, + PanSouthEast, + PanSouthWest, + PanEast, + PanWest, + + Move, + + VerticalText, + Cell, + ContextMenu, + Alias, + Progress, + NotAllowed, + Copy, + + ZoomIn, + ZoomOut, + + Grab, + Grabbing, + + Custom +} diff --git a/GlitchyEngine/src/Window.bf b/GlitchyEngine/src/UI/Window.bf similarity index 91% rename from GlitchyEngine/src/Window.bf rename to GlitchyEngine/src/UI/Window.bf index fc0fad7..996c455 100644 --- a/GlitchyEngine/src/Window.bf +++ b/GlitchyEngine/src/UI/Window.bf @@ -3,23 +3,31 @@ using GlitchyEngine.Events; using GlitchyEngine.Math; using GlitchyEngine.Renderer; -namespace GlitchyEngine +namespace GlitchyEngine.UI { + enum WindowStyle + { + Normal, + Borderless + } + public struct WindowDescription { public uint32 Width; public uint32 Height; public StringView Title; public StringView Icon; + public WindowStyle WindowStyle; public this() => this = default; - public this(uint32 width, uint32 height, StringView title, StringView icon = default) + public this(uint32 width, uint32 height, StringView title, StringView icon = default, WindowStyle windowStyle = .Normal) { Width = width; Height = height; Title = title; Icon = icon; + WindowStyle = windowStyle; } public static readonly WindowDescription Default => .(1280, 720, "Glitchy Engine") @@ -119,6 +127,8 @@ namespace GlitchyEngine */ public extern Result SetIcon(StringView filePath); + public extern Result SetCursor(CursorImage cursorImage); + private void DefaultEventHandler(Event e) { Log.EngineLogger.Error($"No event handler registered for window."); diff --git a/GlitchyEngine/src/WindowRectangle.bf b/GlitchyEngine/src/UI/WindowRectangle.bf similarity index 100% rename from GlitchyEngine/src/WindowRectangle.bf rename to GlitchyEngine/src/UI/WindowRectangle.bf