diff --git a/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowHost.cs b/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowHost.cs index abb7bc276d39..30560bb6d901 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowHost.cs +++ b/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowHost.cs @@ -6,6 +6,7 @@ using Windows.Devices.Input; using Windows.Foundation; +using Windows.Graphics; using Windows.Graphics.Display; using Windows.System; using Windows.UI.Core; @@ -57,6 +58,8 @@ public MacOSWindowHost(MacOSWindowNative nativeWindow, Window winUIWindow, XamlR // Display + internal event EventHandler? PositionChanged; + internal event EventHandler? SizeChanged; internal event EventHandler? RasterizationScaleChanged; @@ -171,7 +174,7 @@ public static unsafe void Register() NativeUno.uno_set_drawing_callbacks(&MetalDraw, &SoftDraw, &Resize); - NativeUno.uno_set_window_events_callbacks(&OnRawKeyDown, &OnRawKeyUp, &OnMouseEvent); + NativeUno.uno_set_window_events_callbacks(&OnRawKeyDown, &OnRawKeyUp, &OnMouseEvent, &OnMoveEvent, &Resize); ApiExtensibility.Register(typeof(IUnoKeyboardInputSource), o => (o as IUnoKeyboardInputSource)!); ApiExtensibility.Register(typeof(IUnoCorePointerInputSource), o => (o as IUnoCorePointerInputSource)!); @@ -240,6 +243,18 @@ private static void Resize(nint handle, double width, double height) } } + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static void OnMoveEvent(nint handle, double x, double y) + { + var window = GetWindowHost(handle); + if (window is not null) + { + window.PositionChanged?.Invoke(window, new PointInt32((int)x, (int)y)); + } + // the first event occurs before the managed side is ready to handle it + // this special case is handled inside MacOSWindowWrapper constructor + } + // IUnoKeyboardInputSource public event TypedEventHandler? KeyDown; diff --git a/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowWrapper.cs b/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowWrapper.cs index 59e3eb33f4a3..19021bde1756 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowWrapper.cs +++ b/src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowWrapper.cs @@ -4,6 +4,7 @@ using Uno.Disposables; using Uno.UI.Xaml.Controls; using Windows.Foundation; +using Windows.Graphics; using Windows.UI.Core.Preview; namespace Uno.UI.Runtime.Skia.MacOS; @@ -20,6 +21,10 @@ public MacOSWindowWrapper(MacOSWindowNative nativeWindow, Window window, XamlRoo nativeWindow.Host.RasterizationScaleChanged += Host_RasterizationScaleChanged; nativeWindow.Host.SizeChanged += (_, s) => OnHostSizeChanged(s); OnHostSizeChanged(initialSize); + nativeWindow.Host.PositionChanged += (_, s) => OnHostPositionChanged(s.X, s.Y); + // the initial event occurred before the managed side was ready to handle it + NativeUno.uno_window_get_position(nativeWindow.Handle, out var x, out var y); + OnHostPositionChanged(x, y); RasterizationScale = (float)_window.Host.RasterizationScale; } @@ -49,10 +54,44 @@ protected override void ShowCore() NativeUno.uno_window_activate(_window.Handle); } + public override void Close() + { + NativeUno.uno_window_close(_window.Handle); + } + + public override void Move(PointInt32 position) + { + // user input in physical pixels transformed into logical pixels + var x = position.X / RasterizationScale; + var y = position.Y / RasterizationScale; + NativeUno.uno_window_move(_window.Handle, x, y); + } + + public override void Resize(SizeInt32 size) + { + // user input in physical pixels transformed into logical pixels + var w = size.Width / RasterizationScale; + var h = size.Height / RasterizationScale; + NativeUno.uno_window_resize(_window.Handle, w, h); + } + + private void OnHostPositionChanged(double x, double y) + { + // in physical pixels + var sx = (int)(x * RasterizationScale); + var sy = (int)(y * RasterizationScale); + Position = new PointInt32(sx, sy); + } + private void OnHostSizeChanged(Size size) { + // in logical pixels Bounds = new Rect(default, size); VisibleBounds = Bounds; + // in physical pixels + int w = (int)(size.Width * RasterizationScale); + int h = (int)(size.Height * RasterizationScale); + Size = new SizeInt32(w, h); } private void OnWindowClosing(object? sender, CancelEventArgs e) diff --git a/src/Uno.UI.Runtime.Skia.MacOS/NativeUno.cs b/src/Uno.UI.Runtime.Skia.MacOS/NativeUno.cs index 27544b404875..7728808828b4 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/NativeUno.cs +++ b/src/Uno.UI.Runtime.Skia.MacOS/NativeUno.cs @@ -187,7 +187,9 @@ internal static unsafe partial void uno_set_window_screen_change_callbacks( internal static unsafe partial void uno_set_window_events_callbacks( delegate* unmanaged[Cdecl] keyDownCallback, delegate* unmanaged[Cdecl] keyUpCallback, - delegate* unmanaged[Cdecl] pointerCallback); + delegate* unmanaged[Cdecl] pointerCallback, + delegate* unmanaged[Cdecl] moveCallback, + delegate* unmanaged[Cdecl] resizeCallback); [LibraryImport("libUnoNativeMac.dylib")] internal static partial uint uno_get_system_theme(); @@ -207,6 +209,12 @@ internal static unsafe partial void uno_set_window_events_callbacks( [LibraryImport("libUnoNativeMac.dylib")] internal static partial void uno_window_invalidate(nint window); + [LibraryImport("libUnoNativeMac.dylib")] + internal static partial void uno_window_close(nint window); + + [LibraryImport("libUnoNativeMac.dylib")] + internal static partial void uno_window_get_position(nint window, out double x, out double y); + [LibraryImport("libUnoNativeMac.dylib", StringMarshalling = StringMarshalling.Utf8)] internal static partial string uno_window_get_title(nint window); @@ -259,6 +267,9 @@ internal static unsafe partial void uno_set_window_close_callbacks( [LibraryImport("libUnoNativeMac.dylib")] internal static partial nint uno_window_get_metal_context(nint window); + [LibraryImport("libUnoNativeMac.dylib")] + internal static partial void uno_window_move(nint window, double x, double y); + [LibraryImport("libUnoNativeMac.dylib")] [return: MarshalAs(UnmanagedType.I1)] internal static partial bool uno_window_resize(nint window, double width, double height); diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.h b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.h index 6db2db98c5a4..743cebd1daf4 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.h +++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.h @@ -9,9 +9,13 @@ NS_ASSUME_NONNULL_BEGIN -typedef void (*resize_fn_ptr)(void* /* window */, double /* width */, double /* height */); -resize_fn_ptr uno_get_resize_callback(void); -void uno_set_resize_callback(resize_fn_ptr p); +typedef void (*uno_drawable_resize_fn_ptr)(void* /* window */, double /* width */, double /* height */); +uno_drawable_resize_fn_ptr uno_get_resize_callback(void); +void uno_set_resize_callback(uno_drawable_resize_fn_ptr p); + +typedef void (*window_move_or_resize_fn_ptr)(NSWindow* /* window */, double /* x or width */, double /* y or height */); +window_move_or_resize_fn_ptr uno_get_window_move_callback(void); +window_move_or_resize_fn_ptr uno_get_window_resize_callback(void); @interface windowDidChangeScreenNoteClass : NSObject @@ -32,6 +36,7 @@ void uno_set_resize_callback(resize_fn_ptr p); - (void)sendEvent:(NSEvent *)event; - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; +- (void)windowDidMove:(NSNotification *)notification; - (bool)windowShouldClose:(NSWindow *)sender; - (void)windowWillClose:(NSNotification *)notification; @@ -42,8 +47,11 @@ NSWindow* uno_app_get_main_window(void); NSWindow* uno_window_create(double width, double height); void uno_window_activate(NSWindow *window); void uno_window_invalidate(NSWindow *window); +void uno_window_close(NSWindow *window); +void uno_window_move(NSWindow *window, double x, double y); bool uno_window_resize(NSWindow *window, double width, double height); +void uno_window_get_position(NSWindow *window, double *x, double *y); char* uno_window_get_title(NSWindow *window); void uno_window_set_title(NSWindow *window, const char* title); @@ -296,7 +304,7 @@ struct MouseEventData { typedef int32_t (*window_key_callback_fn_ptr)(UNOWindow* window, VirtualKey key, VirtualKeyModifiers mods, uint32 scanCode, UniChar unicode); typedef int32_t (*window_mouse_callback_fn_ptr)(UNOWindow* window, struct MouseEventData *data); -void uno_set_window_events_callbacks(window_key_callback_fn_ptr keyDown, window_key_callback_fn_ptr keyUp, window_mouse_callback_fn_ptr pointer); +void uno_set_window_events_callbacks(window_key_callback_fn_ptr keyDown, window_key_callback_fn_ptr keyUp, window_mouse_callback_fn_ptr pointer, window_move_or_resize_fn_ptr move, window_move_or_resize_fn_ptr resize); typedef bool (*window_should_close_fn_ptr)(UNOWindow* window); window_should_close_fn_ptr uno_get_window_should_close_callback(void); diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m index 77454c72a282..2e67cc692bc7 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m +++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m @@ -16,11 +16,11 @@ // libSkiaSharp extern void* gr_direct_context_make_metal(id device, id queue); -static resize_fn_ptr window_resize; +static uno_drawable_resize_fn_ptr window_resize; static metal_draw_fn_ptr metal_draw; static soft_draw_fn_ptr soft_draw; -inline resize_fn_ptr uno_get_resize_callback(void) +inline uno_drawable_resize_fn_ptr uno_get_resize_callback(void) { return window_resize; } @@ -35,7 +35,7 @@ inline soft_draw_fn_ptr uno_get_soft_draw_callback(void) return soft_draw; } -void uno_set_drawing_callbacks(metal_draw_fn_ptr metal, soft_draw_fn_ptr soft, resize_fn_ptr resize) +void uno_set_drawing_callbacks(metal_draw_fn_ptr metal, soft_draw_fn_ptr soft, uno_drawable_resize_fn_ptr resize) { metal_draw = metal; soft_draw = soft; @@ -150,6 +150,22 @@ void uno_window_invalidate(NSWindow *window) window.contentViewController.view.needsDisplay = true; } +void uno_window_close(NSWindow *window) +{ +#if DEBUG + NSLog(@"uno_window_close %@", window); +#endif + [window performClose:nil]; +} + +void uno_window_move(NSWindow *window, double x, double y) +{ +#if DEBUG + NSLog(@"uno_window_move %@ x: %g y: %g", window, x, y); +#endif + [window setFrameOrigin:NSMakePoint(x, y)]; +} + bool uno_window_resize(NSWindow *window, double width, double height) { #if DEBUG @@ -173,6 +189,16 @@ void uno_window_set_min_size(NSWindow *window, double width, double height) window.minSize = CGSizeMake(width, height); } +void uno_window_get_position(NSWindow *window, double *x, double *y) +{ + CGPoint origin = window.frame.origin; +#if DEBUG + NSLog (@"uno_window_get_position %@ %f %f", window, origin.x, origin.y); +#endif + *x = origin.x; + *y = origin.y; +} + char* uno_window_get_title(NSWindow *window) { return strdup(window.title.UTF8String); @@ -560,11 +586,27 @@ inline static window_mouse_callback_fn_ptr uno_get_window_mouse_event_callback(v return window_mouse_event; } -void uno_set_window_events_callbacks(window_key_callback_fn_ptr keyDown, window_key_callback_fn_ptr keyUp, window_mouse_callback_fn_ptr pointer) +static window_move_or_resize_fn_ptr window_move_event; + +inline static window_move_or_resize_fn_ptr uno_get_window_move_event_callback(void) +{ + return window_move_event; +} + +static window_move_or_resize_fn_ptr window_resize_event; + +inline static window_move_or_resize_fn_ptr uno_get_window_resize_event_callback(void) +{ + return window_resize_event; +} + +void uno_set_window_events_callbacks(window_key_callback_fn_ptr keyDown, window_key_callback_fn_ptr keyUp, window_mouse_callback_fn_ptr pointer, window_move_or_resize_fn_ptr move, window_move_or_resize_fn_ptr resize) { window_key_down = keyDown; window_key_up = keyUp; window_mouse_event = pointer; + window_move_event = move; + window_resize_event = resize; } static window_should_close_fn_ptr window_should_close; @@ -774,6 +816,22 @@ - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame { return window.collectionBehavior != (NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorFullScreenNone|NSWindowCollectionBehaviorFullScreenDisallowsTiling); } +- (void)windowDidMove:(NSNotification *)notification { + CGPoint position = self.frame.origin; +#if DEBUG + NSLog(@"UNOWindow %p windowDidMove %@ x: %g y: %g", self, notification, position.x, position.y); +#endif + uno_get_window_move_event_callback()(self, position.x, position.y); +} + +- (void)windowDidResize:(NSNotification *)notification { + CGSize size = self.frame.size; +#if DEBUG + NSLog(@"UNOWindow %p windowDidMove %@ x: %g y: %g", self, notification, size.width, size.height); +#endif + uno_get_window_resize_event_callback()(self, size.width, size.height); +} + - (bool)windowShouldClose:(NSWindow *)sender { // see `ISystemNavigationManagerPreviewExtension` diff --git a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Windowing/Given_AppWindow.cs b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Windowing/Given_AppWindow.cs index 6b1f0d4bdc91..29d93636f3d3 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Windowing/Given_AppWindow.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Windowing/Given_AppWindow.cs @@ -151,7 +151,8 @@ void OnChanged(AppWindow s, AppWindowChangedEventArgs e) private void AssertPositioningAndSizingSupport() { if (!OperatingSystem.IsLinux() && - !OperatingSystem.IsWindows() || + !OperatingSystem.IsWindows() && + !OperatingSystem.IsMacOS() || TestServices.WindowHelper.IsXamlIsland || IsGtk()) {