From d9ca27ba876b3043b8bcf4aab521ece02c005efe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 6 May 2018 01:22:49 +0200 Subject: [PATCH 01/24] Stop renderer on Window minimize. Adds a new `WindowStateChanged` event to `IWindowImpl` and when a window is minimized, stops the associated renderer (only implemented on win32 currently). --- src/Avalonia.Controls/Platform/IWindowImpl.cs | 5 ++++ src/Avalonia.Controls/Window.cs | 13 ++++++++++ .../Remote/PreviewerWindowImpl.cs | 1 + src/Avalonia.DesignerSupport/Remote/Stubs.cs | 2 ++ src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 4 +++- src/OSX/Avalonia.MonoMac/WindowImpl.cs | 2 ++ .../Interop/UnmanagedMethods.cs | 24 +++++++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 21 ++++++++++++++++ 8 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 3f2c977718..c5d632c0f7 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -16,6 +16,11 @@ namespace Avalonia.Platform /// WindowState WindowState { get; set; } + /// + /// Gets or sets a method called when the changes. + /// + Action WindowStateChanged { get; set; } + /// /// Sets the title of the window. /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 16ee3a46b3..6814b8567e 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -136,6 +136,7 @@ namespace Avalonia.Controls : base(impl) { impl.Closing = HandleClosing; + impl.WindowStateChanged = HandleWindowStateChanged; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); Screens = new Screens(PlatformImpl?.Screen); } @@ -308,6 +309,18 @@ namespace Avalonia.Controls return args.Cancel; } + protected virtual void HandleWindowStateChanged(WindowState state) + { + if (state == WindowState.Minimized) + { + Renderer.Stop(); + } + else if (state == WindowState.Normal) + { + Renderer.Start(); + } + } + /// /// Hides the window but does not close it. /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 9750b46aa2..ef16d06b60 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -42,6 +42,7 @@ namespace Avalonia.DesignerSupport.Remote public Func Closing { get; set; } public IPlatformHandle Handle { get; } public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } public Size MaxClientSize { get; } = new Size(4096, 4096); public event Action LostFocus; diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index ee8569d748..72fce0d921 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -32,6 +32,8 @@ namespace Avalonia.DesignerSupport.Remote public Point Position { get; set; } public Action PositionChanged { get; set; } public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } + public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root); public void Dispose() { diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index 2d309e19d4..ac03816b02 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -43,7 +43,9 @@ namespace Avalonia.Gtk3 } } } - + + public Action WindowStateChanged { get; set; } + public IDisposable ShowDialog() { Native.GtkWindowSetModal(GtkWidget, true); diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index d01cbd6ae3..43d6918112 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -49,6 +49,8 @@ namespace Avalonia.MonoMac } } + public Action WindowStateChanged { get; set; } + bool IsZoomed => IsDecorated ? Window.IsZoomed : UndecoratedIsMaximized; public bool UndecoratedIsMaximized => Window.Frame == Window.Screen.VisibleFrame; diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 5c24aa1c69..fef0ef747d 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -104,6 +104,30 @@ namespace Avalonia.Win32.Interop ForceMinimize = 11 } + public enum SysCommands + { + SC_SIZE = 0xF000, + SC_MOVE = 0xF010, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_NEXTWINDOW = 0xF040, + SC_PREVWINDOW = 0xF050, + SC_CLOSE = 0xF060, + SC_VSCROLL = 0xF070, + SC_HSCROLL = 0xF080, + SC_MOUSEMENU = 0xF090, + SC_KEYMENU = 0xF100, + SC_ARRANGE = 0xF110, + SC_RESTORE = 0xF120, + SC_TASKLIST = 0xF130, + SC_SCREENSAVE = 0xF140, + SC_HOTKEY = 0xF150, + SC_DEFAULT = 0xF160, + SC_MONITORPOWER = 0xF170, + SC_CONTEXTHELP = 0xF180, + SC_SEPARATOR = 0xF00F, + } + public enum SystemMetric { SM_CXSCREEN = 0, // 0x00 diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index cf6cb40e58..399a09ea6c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -77,6 +77,8 @@ namespace Avalonia.Win32 public Action PositionChanged { get; set; } + public Action WindowStateChanged { get; set; } + public Thickness BorderThickness { get @@ -627,6 +629,25 @@ namespace Avalonia.Win32 return IntPtr.Zero; + case UnmanagedMethods.WindowsMessage.WM_SYSCOMMAND: + if (WindowStateChanged != null) + { + switch ((SysCommands)wParam) + { + case SysCommands.SC_MINIMIZE: + WindowStateChanged(WindowState.Minimized); + break; + case SysCommands.SC_MAXIMIZE: + WindowStateChanged(WindowState.Maximized); + break; + case SysCommands.SC_RESTORE: + WindowStateChanged(WindowState.Normal); + break; + } + } + + break; + case UnmanagedMethods.WindowsMessage.WM_MOVE: PositionChanged?.Invoke(new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16))); return IntPtr.Zero; From b3c0301ee0dce92152b6573aa8e88bade41be71e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 May 2018 14:47:54 -0500 Subject: [PATCH 02/24] Add missing annotations dependency. --- packages.cake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages.cake b/packages.cake index bcb135f6ad..7e7e722c82 100644 --- a/packages.cake +++ b/packages.cake @@ -120,6 +120,7 @@ public class Packages var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1; var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1; var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1; + var SystemComponentModelAnnotationsVersion = packageVersions["System.ComponentModel.Annotations"].FirstOrDefault().Item1; context.Information("Package: Serilog, version: {0}", SerilogVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion); @@ -238,6 +239,7 @@ public class Packages new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion }, new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }, new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version }, + new NuSpecDependency() { Id = "System.ComponentModel.Annotations", Version = SystemComponentModelAnnotationsVersion }, //.NET Core new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" }, new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" }, From 4eec8b42715233343e13bb403f204b56a78c304e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 May 2018 15:14:52 -0500 Subject: [PATCH 03/24] Fix #1531 by making IStyle an IAvaloniaObject. --- src/Avalonia.Controls/Design.cs | 18 +++++++----------- src/Avalonia.Styling/Styling/IStyle.cs | 2 +- src/Avalonia.Styling/Styling/Style.cs | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Controls/Design.cs b/src/Avalonia.Controls/Design.cs index ce52891749..3a67e554ac 100644 --- a/src/Avalonia.Controls/Design.cs +++ b/src/Avalonia.Controls/Design.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; +using Avalonia.Styling; namespace Avalonia.Controls { @@ -45,23 +46,18 @@ namespace Avalonia.Controls { return control.GetValue(DataContextProperty); } - - static readonly ConditionalWeakTable Substitutes = new ConditionalWeakTable(); - + public static readonly AttachedProperty PreviewWithProperty = AvaloniaProperty - .RegisterAttached("PreviewWith", typeof (Design)); + .RegisterAttached("PreviewWith", typeof (Design)); - public static void SetPreviewWith(object target, Control control) + public static void SetPreviewWith(IStyle target, Control control) { - Substitutes.Remove(target); - Substitutes.Add(target, control); + target.SetValue(PreviewWithProperty, control); } - public static Control GetPreviewWith(object target) + public static Control GetPreviewWith(IStyle target) { - Control rv; - Substitutes.TryGetValue(target, out rv); - return rv; + return target.GetValue(PreviewWithProperty); } public static void ApplyDesignModeProperties(Control target, Control source) diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Styling/Styling/IStyle.cs index 5f12763825..2ce06cc36c 100644 --- a/src/Avalonia.Styling/Styling/IStyle.cs +++ b/src/Avalonia.Styling/Styling/IStyle.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Defines the interface for styles. /// - public interface IStyle : IResourceNode + public interface IStyle : IAvaloniaObject, IResourceNode { /// /// Attaches the style to a control if the style's selector matches. diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 88ff695ba2..203f679683 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -15,7 +15,7 @@ namespace Avalonia.Styling /// /// Defines a style. /// - public class Style : IStyle, ISetStyleParent + public class Style : AvaloniaObject, IStyle, ISetStyleParent { private static Dictionary> _applied = new Dictionary>(); From 1bbc155303dbcdaffec3d8f36356e2467ba9fd99 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 May 2018 15:35:08 -0500 Subject: [PATCH 04/24] Use a concrete type for the host type to avoid #959. --- src/Avalonia.Controls/Design.cs | 6 +++--- src/Avalonia.DesignerSupport/DesignWindowLoader.cs | 3 +-- src/Avalonia.Styling/Styling/IStyle.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Design.cs b/src/Avalonia.Controls/Design.cs index 3a67e554ac..894240a09f 100644 --- a/src/Avalonia.Controls/Design.cs +++ b/src/Avalonia.Controls/Design.cs @@ -48,14 +48,14 @@ namespace Avalonia.Controls } public static readonly AttachedProperty PreviewWithProperty = AvaloniaProperty - .RegisterAttached("PreviewWith", typeof (Design)); + .RegisterAttached("PreviewWith", typeof (Design)); - public static void SetPreviewWith(IStyle target, Control control) + public static void SetPreviewWith(Style target, Control control) { target.SetValue(PreviewWithProperty, control); } - public static Control GetPreviewWith(IStyle target) + public static Control GetPreviewWith(Style target) { return target.GetValue(PreviewWithProperty); } diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index d1958ac9bf..6dca479d38 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -36,8 +36,7 @@ namespace Avalonia.DesignerSupport var styles = loaded as Styles; if (styles != null) { - var substitute = Design.GetPreviewWith(styles) ?? - styles.Select(Design.GetPreviewWith).FirstOrDefault(s => s != null); + var substitute = styles.OfType