diff --git a/src/Avalonia.Gpu/IGpuContext.cs b/src/Avalonia.Gpu/IGpuContext.cs index 2656b97c44..ea8f00d095 100644 --- a/src/Avalonia.Gpu/IGpuContext.cs +++ b/src/Avalonia.Gpu/IGpuContext.cs @@ -8,6 +8,7 @@ namespace Avalonia.Gpu IntPtr GetProcAddress(string symbol); (double, double) GetFramebufferSize(); void ResizeContext(double width, double height); + void MakeCurrent(); (double, double) GetDpi(); } } diff --git a/src/Avalonia.Windowing/Bindings/EventNotifier.cs b/src/Avalonia.Windowing/Bindings/EventNotifier.cs index a36beca8c6..ab17cdb0ae 100644 --- a/src/Avalonia.Windowing/Bindings/EventNotifier.cs +++ b/src/Avalonia.Windowing/Bindings/EventNotifier.cs @@ -11,6 +11,7 @@ namespace Avalonia.Windowing.Bindings public delegate void AwakenedEventCallback(); public delegate byte ShouldExitEventLoopCallback(WindowId windowId); public delegate void CloseRequestedCallback(WindowId windowId); + public delegate void FocusedCallback(WindowId windowId, byte focused); [StructLayout(LayoutKind.Sequential)] public struct EventNotifier @@ -22,5 +23,6 @@ namespace Avalonia.Windowing.Bindings public CharacterEventCallback OnCharacterEvent; public ShouldExitEventLoopCallback OnShouldExitEventLoop; public CloseRequestedCallback OnCloseRequested; + public FocusedCallback OnFocused; } } diff --git a/src/Avalonia.Windowing/Bindings/EventsLoop.cs b/src/Avalonia.Windowing/Bindings/EventsLoop.cs index dcf1ed00c0..956f3f5ffe 100644 --- a/src/Avalonia.Windowing/Bindings/EventsLoop.cs +++ b/src/Avalonia.Windowing/Bindings/EventsLoop.cs @@ -33,6 +33,7 @@ namespace Avalonia.Windowing.Bindings public event ResizeEventCallback OnResized; public event ShouldExitEventLoopCallback OnShouldExitEventLoop; public event CloseRequestedCallback OnCloseRequested; + public event FocusedCallback OnFocused; public EventsLoop() { @@ -46,7 +47,8 @@ namespace Avalonia.Windowing.Bindings OnResized = (windowId, resizeEvent) => OnResized?.Invoke(windowId, resizeEvent), OnAwakened = () => OnAwakened?.Invoke(), OnShouldExitEventLoop = (windowId) => (byte)OnShouldExitEventLoop?.Invoke(windowId), - OnCloseRequested = (windowId) => OnCloseRequested?.Invoke(windowId) + OnCloseRequested = (windowId) => OnCloseRequested?.Invoke(windowId), + OnFocused = (windowId, focused) => OnFocused?.Invoke(windowId, focused) }; } diff --git a/src/Avalonia.Windowing/Bindings/GlWindow.cs b/src/Avalonia.Windowing/Bindings/GlWindow.cs index a4775a9eea..add9a3d2b9 100644 --- a/src/Avalonia.Windowing/Bindings/GlWindow.cs +++ b/src/Avalonia.Windowing/Bindings/GlWindow.cs @@ -43,6 +43,9 @@ namespace Avalonia.Windowing.Bindings [DllImport("winit_wrapper")] private static extern void winit_gl_window_present(IntPtr handle); + [DllImport("winit_wrapper")] + private static extern void winit_gl_window_make_current(IntPtr handle); + [DllImport("winit_wrapper")] private static extern void winit_gl_window_show(IntPtr handle); @@ -165,5 +168,10 @@ namespace Avalonia.Windowing.Bindings var scaleFactor = GetScaleFactor(); return (scaleFactor, scaleFactor); } + + public void MakeCurrent() + { + winit_gl_window_make_current(_handle); + } } } diff --git a/src/Avalonia.Windowing/WindowImpl.cs b/src/Avalonia.Windowing/WindowImpl.cs index e0e3173117..36660ecf03 100644 --- a/src/Avalonia.Windowing/WindowImpl.cs +++ b/src/Avalonia.Windowing/WindowImpl.cs @@ -18,14 +18,16 @@ namespace Avalonia.Windowing { public class WindowImpl : IWindowImpl { - IWindowWrapper _windowWrapper; - private LogicalPosition _lastPosition; + private readonly IWindowWrapper _windowWrapper; + private readonly ManagedWindowResizeDragHelper _managedDrag; - const int FramesPerSecond = 60; + private LogicalPosition _lastPosition; + private const int FramesPerSecond = 60; public WindowImpl(IWindowWrapper wrapper) { _windowWrapper = wrapper; + _managedDrag = new ManagedWindowResizeDragHelper(this, _ => { }, ResizeForManagedDrag); // TODO: This is only necessary when using ImmediateRenderer Observable.Repeat(Observable.Timer(TimeSpan.FromMilliseconds(1000 / FramesPerSecond))) @@ -43,6 +45,12 @@ namespace Avalonia.Windowing }); } + private void ResizeForManagedDrag(Rect obj) + { + //this._windowWrapper.SetPosition(obj.X, obj.Y); + // this._windowWrapper.SetSize(obj.Width, obj.Height); + } + public WindowState WindowState { get; set; } public Action WindowStateChanged { get; set; } public Func Closing { get; set; } @@ -198,7 +206,7 @@ namespace Avalonia.Windowing if (evt.Character >= 32) { - Input + OnInput ( new RawTextInputEventArgs ( @@ -242,7 +250,7 @@ namespace Avalonia.Windowing if (keyCode.HasValue) { - Input + OnInput ( new RawKeyEventArgs ( @@ -256,6 +264,14 @@ namespace Avalonia.Windowing } } + public void OnInput(RawInputEventArgs args) + { + if (_managedDrag.PreprocessInputEvent(ref args)) + return; + + Input?.Invoke(args); + } + public void OnMouseEvent(MouseEvent evt) { Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); @@ -287,7 +303,7 @@ namespace Avalonia.Windowing modifiers |= InputModifiers.Windows; } - Input + OnInput ( eventType != RawMouseEventType.Wheel ? new RawMouseEventArgs @@ -315,9 +331,9 @@ namespace Avalonia.Windowing public void OnResizeEvent(ResizeEvent evt) { Resized?.Invoke(ClientSize); - if (_windowWrapper is IGpuContext gpuCtx) { - gpuCtx.ResizeContext(ClientSize.Width, ClientSize.Height); - } + + // TODO: There's a bug in winit where OSX resizing blocks the event loop, to work around this, paint while resizing. + Paint?.Invoke(new Rect(ClientSize)); } public void OnClosed() @@ -327,7 +343,15 @@ namespace Avalonia.Windowing public bool OnCloseRequested() { - return (bool)Closing?.Invoke(); + return Closing?.Invoke() ?? false; + } + + public void OnFocused(bool focused) + { + if (focused) + Activated?.Invoke(); + else + Deactivated?.Invoke(); } } } \ No newline at end of file diff --git a/src/Avalonia.Windowing/WindowResizeDragHelper.cs b/src/Avalonia.Windowing/WindowResizeDragHelper.cs new file mode 100644 index 0000000000..be3d179385 --- /dev/null +++ b/src/Avalonia.Windowing/WindowResizeDragHelper.cs @@ -0,0 +1,85 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; + +namespace Avalonia +{ + internal class ManagedWindowResizeDragHelper + { + private readonly IWindowBaseImpl _window; + private readonly Action _captureMouse; + private readonly Action _resize; + private WindowEdge? _edge; + private Point _prevPoint; + + public ManagedWindowResizeDragHelper(IWindowBaseImpl window, Action captureMouse, Action resize = null) + { + _window = window; + _captureMouse = captureMouse; + _resize = resize; + } + + public void BeginResizeDrag(WindowEdge edge, Point currentMousePosition) + { + _captureMouse(true); + _prevPoint = currentMousePosition; + _edge = edge; + } + + public bool PreprocessInputEvent(ref RawInputEventArgs e) + { + if (_edge == null) + return false; + if (e is RawMouseEventArgs args) + { + if (args.Type == RawMouseEventType.LeftButtonUp) + { + _edge = null; + _captureMouse(false); + } + if (args.Type == RawMouseEventType.Move) + { + MoveWindow(args.Position); + return true; + } + + + _edge = null; + } + + return false; + } + + private void MoveWindow(Point position) + { + var diff = position - _prevPoint; + var edge = _edge.Value; + var rc = new Rect(_window.Position, _window.ClientSize); + if (edge == WindowEdge.East || edge == WindowEdge.NorthEast || edge == WindowEdge.SouthEast) + { + rc = rc.WithWidth(rc.Width + diff.X); + _prevPoint = _prevPoint.WithX(position.X); + } + if (edge == WindowEdge.West || edge == WindowEdge.NorthWest || edge == WindowEdge.SouthWest) + rc = rc.WithX(rc.X + diff.X).WithWidth(rc.Width - diff.X); + if (edge == WindowEdge.South || edge == WindowEdge.SouthWest || edge == WindowEdge.SouthEast) + { + rc = rc.WithHeight(rc.Height + diff.Y); + _prevPoint = _prevPoint.WithY(position.Y); + } + if (edge == WindowEdge.North || edge == WindowEdge.NorthWest || edge == WindowEdge.NorthEast) + rc = rc.WithY(rc.Y + diff.Y).WithHeight(rc.Height - diff.Y); + if (_resize != null) + _resize(rc); + else + { + if (_window.Position != rc.Position) + _window.Position = rc.Position; + if (_window.ClientSize != rc.Size) + _window.Resize(rc.Size); + } + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Windowing/WIndowingPlatform.cs b/src/Avalonia.Windowing/WindowingPlatform.cs similarity index 94% rename from src/Avalonia.Windowing/WIndowingPlatform.cs rename to src/Avalonia.Windowing/WindowingPlatform.cs index 711c59b0fb..66d7f2a34f 100644 --- a/src/Avalonia.Windowing/WIndowingPlatform.cs +++ b/src/Avalonia.Windowing/WindowingPlatform.cs @@ -45,7 +45,7 @@ namespace Avalonia.Windowing _eventsLoop.OnResized += _eventsLoop_Resized; _eventsLoop.OnShouldExitEventLoop += _eventsLoop_OnShouldExitEventLoop; _eventsLoop.OnCloseRequested += _eventsLoop_OnCloseRequested; - + _eventsLoop.OnFocused += _eventsLoop_OnFocused; _windows = new Dictionary(); } @@ -67,9 +67,19 @@ namespace Avalonia.Windowing _windows[windowId].OnClosed(); _windows.Remove(windowId); } + return _windows.Any() ? (byte)0 : (byte)1; } + void _eventsLoop_OnFocused(WindowId windowId, byte focused) + { + if (_windows.ContainsKey(windowId)) + { + _windows[windowId].OnFocused(focused == 0 ? true : false); + } + } + + void _eventsLoop_Resized(WindowId windowId, ResizeEvent resizeEvent) { Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); @@ -169,6 +179,7 @@ namespace Avalonia.Windowing public void RunLoop(CancellationToken cancellationToken) { + // TODO: Support canceling the EventLoop here. _eventsLoop.Run(); } diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs index f4395d6210..17470d91b6 100644 --- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs @@ -10,7 +10,7 @@ namespace Avalonia.Skia { public class GlRenderTarget : IRenderTarget { - private readonly GRContext _grContext; + private GRContext _grContext; private readonly IGpuContext _context; public GlRenderTarget(IGpuContext context) @@ -71,6 +71,7 @@ namespace Avalonia.Skia return new DrawingContextImpl(createInfo, Disposable.Create(() => { + _context.MakeCurrent(); _grContext.Flush(); _context.Present(); }));