Browse Source

Merge branch 'master' into feature/tabindex

pull/5996/head
Dan Walmsley 5 years ago
committed by GitHub
parent
commit
fdcb744455
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      native/Avalonia.Native/src/OSX/window.h
  2. 97
      native/Avalonia.Native/src/OSX/window.mm
  3. 4
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 22
      src/Avalonia.Controls/ApiCompatBaseline.txt
  5. 4
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  6. 17
      src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
  7. 9
      src/Avalonia.Controls/ApplicationLifetimes/ShutdownRequestedEventArgs.cs
  8. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  9. 3
      src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs
  10. 37
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  11. 4
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  12. 4
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  13. 9
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  14. 6
      src/Avalonia.Controls/TopLevel.cs
  15. 145
      src/Avalonia.Controls/Window.cs
  16. 32
      src/Avalonia.Controls/WindowBase.cs
  17. 8
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  18. 8
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  19. 13
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  20. 4
      src/Avalonia.Input/TappedEventArgs.cs
  21. 5
      src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
  22. 6
      src/Avalonia.Native/PopupImpl.cs
  23. 16
      src/Avalonia.Native/WindowImplBase.cs
  24. 15
      src/Avalonia.Native/avn.idl
  25. 20
      src/Avalonia.Visuals/Media/DrawingGroup.cs
  26. 37
      src/Avalonia.X11/X11Window.cs
  27. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  28. 6
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  29. 4
      src/Windows/Avalonia.Win32/PopupImpl.cs
  30. 29
      src/Windows/Avalonia.Win32/Win32Platform.cs
  31. 36
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  32. 33
      src/Windows/Avalonia.Win32/WindowImpl.cs
  33. 4
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  34. 24
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  35. 2
      tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
  36. 2
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  37. 2
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  38. 120
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  39. 9
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs

22
native/Avalonia.Native/src/OSX/window.h

@ -10,6 +10,8 @@ class WindowBaseImpl;
-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
-(void) onClosed;
-(AvnPixelSize) getPixelSize;
-(AvnPlatformResizeReason) getResizeReason;
-(void) setResizeReason:(AvnPlatformResizeReason)reason;
@end
@interface AutoFitContentView : NSView
@ -34,6 +36,7 @@ class WindowBaseImpl;
-(double) getScaling;
-(double) getExtendedTitleBarHeight;
-(void) setIsExtended:(bool)value;
-(bool) isDialog;
@end
struct INSWindowHolder
@ -50,4 +53,23 @@ struct IWindowStateChanged
virtual AvnWindowState WindowState () = 0;
};
class ResizeScope
{
public:
ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason)
{
_view = view;
_restore = [view getResizeReason];
[view setResizeReason:reason];
}
~ResizeScope()
{
[_view setResizeReason:_restore];
}
private:
AvnView* _Nonnull _view;
AvnPlatformResizeReason _restore;
};
#endif /* window_h */

97
native/Avalonia.Native/src/OSX/window.mm

@ -52,6 +52,7 @@ public:
[Window setBackingType:NSBackingStoreBuffered];
[Window setOpaque:false];
[Window setContentView: StandardContainer];
}
virtual HRESULT ObtainNSWindowHandle(void** ret) override
@ -115,7 +116,7 @@ public:
return Window;
}
virtual HRESULT Show(bool activate) override
virtual HRESULT Show(bool activate, bool isDialog) override
{
START_COM_CALL;
@ -124,7 +125,6 @@ public:
SetPosition(lastPositionSet);
UpdateStyle();
[Window setContentView: StandardContainer];
[Window setTitle:_lastTitle];
if(ShouldTakeFocusOnShow() && activate)
@ -277,7 +277,7 @@ public:
}
}
virtual HRESULT Resize(double x, double y) override
virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
{
if(_inResize)
{
@ -287,6 +287,7 @@ public:
_inResize = true;
START_COM_CALL;
auto resizeBlock = ResizeScope(View, reason);
@autoreleasepool
{
@ -317,7 +318,7 @@ public:
{
if(!_shown)
{
BaseEvents->Resized(AvnSize{x,y});
BaseEvents->Resized(AvnSize{x,y}, reason);
}
[Window setContentSize:NSSize{x, y}];
@ -577,6 +578,11 @@ public:
return S_OK;
}
virtual bool IsDialog()
{
return false;
}
protected:
virtual NSWindowStyleMask GetStyle()
{
@ -607,6 +613,7 @@ private:
NSRect _preZoomSize;
bool _transitioningWindowState;
bool _isClientAreaExtended;
bool _isDialog;
AvnExtendClientAreaChromeHints _extendClientHints;
FORWARD_IUNKNOWN()
@ -667,13 +674,14 @@ private:
}
}
virtual HRESULT Show (bool activate) override
virtual HRESULT Show (bool activate, bool isDialog) override
{
START_COM_CALL;
@autoreleasepool
{
WindowBaseImpl::Show(activate);
_isDialog = isDialog;
WindowBaseImpl::Show(activate, isDialog);
HideOrShowTrafficLights();
@ -1213,6 +1221,11 @@ private:
}
}
virtual bool IsDialog() override
{
return _isDialog;
}
protected:
virtual NSWindowStyleMask GetStyle() override
{
@ -1373,6 +1386,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _lastKeyHandled;
AvnPixelSize _lastPixelSize;
NSObject<IRenderTarget>* _renderTarget;
AvnPlatformResizeReason _resizeReason;
}
- (void)onClosed
@ -1484,7 +1498,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget];
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
}
}
@ -1983,6 +1998,16 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
- (AvnPlatformResizeReason)getResizeReason
{
return _resizeReason;
}
- (void)setResizeReason:(AvnPlatformResizeReason)reason
{
_resizeReason = reason;
}
@end
@ -2002,6 +2027,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_isExtended = value;
}
-(bool) isDialog
{
return _parent->IsDialog();
}
-(double) getScaling
{
return _lastScaling;
@ -2180,7 +2210,22 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
-(BOOL)canBecomeKeyWindow
{
return _canBecomeKeyAndMain;
if (_canBecomeKeyAndMain)
{
// If the window has a child window being shown as a dialog then don't allow it to become the key window.
for(NSWindow* uch in [self childWindows])
{
auto ch = objc_cast<AvnWindow>(uch);
if(ch == nil)
continue;
if (ch.isDialog)
return false;
}
return true;
}
return false;
}
-(BOOL)canBecomeMainWindow
@ -2188,22 +2233,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return _canBecomeKeyAndMain;
}
-(bool) activateAppropriateChild: (bool)activating
{
for(NSWindow* uch in [self childWindows])
{
auto ch = objc_cast<AvnWindow>(uch);
if(ch == nil)
continue;
[ch activateAppropriateChild:false];
return FALSE;
}
if(!activating)
[self makeKeyAndOrderFront:self];
return TRUE;
}
-(bool)shouldTryToHandleEvents
{
return _isEnabled;
@ -2214,26 +2243,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_isEnabled = enable;
}
-(void)makeKeyWindow
{
if([self activateAppropriateChild: true])
{
[super makeKeyWindow];
}
}
-(void)becomeKeyWindow
{
[self showWindowMenuWithAppMenu];
if([self activateAppropriateChild: true])
if(_parent != nullptr)
{
if(_parent != nullptr)
{
_parent->BaseEvents->Activated();
}
_parent->BaseEvents->Activated();
}
[super becomeKeyWindow];
}
@ -2243,7 +2261,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
if(parent != nil)
{
[parent removeChildWindow:self];
[parent activateAppropriateChild: false];
}
}
@ -2378,7 +2395,7 @@ protected:
return NSWindowStyleMaskBorderless;
}
virtual HRESULT Resize(double x, double y) override
virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
{
START_COM_CALL;

4
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -67,7 +67,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
@ -134,7 +134,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
protected virtual void OnResized(Size size)
{
Resized?.Invoke(size);
Resized?.Invoke(size, PlatformResizeReason.Unspecified);
}
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo

22
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -30,15 +30,29 @@ MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownV
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler<System.ComponentModel.CancelEventArgs> Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler<System.ComponentModel.CancelEventArgs>)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.remove_ShutdownRequested(System.EventHandler<System.ComponentModel.CancelEventArgs>)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs> Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs>)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.remove_ShutdownRequested(System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs>)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public System.Action<Avalonia.Size> Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.set(System.Action<Avalonia.Size>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Action<Avalonia.Size, Avalonia.Platform.PlatformResizeReason> Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Action<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Action<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.Resized.get()' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.Resized.set(System.Action<Avalonia.Size, Avalonia.Platform.PlatformResizeReason>)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.Resized.set(System.Action<Avalonia.Size>)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.Resized.set(System.Action<Avalonia.Size>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
Total Issues: 42
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowBaseImpl.Show(System.Boolean)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.IWindowBaseImpl.Show(System.Boolean)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowBaseImpl.Show(System.Boolean, System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
Total Issues: 56

4
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -48,7 +48,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup;
/// <inheritdoc/>
public event EventHandler<CancelEventArgs> ShutdownRequested;
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
/// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit;
@ -134,7 +134,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime = null;
}
private void OnShutdownRequested(object sender, CancelEventArgs e)
private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e)
{
ShutdownRequested?.Invoke(this, e);

17
src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs

@ -37,14 +37,21 @@ namespace Avalonia.Controls.ApplicationLifetimes
IReadOnlyList<Window> Windows { get; }
/// <summary>
/// Raised by the platform when a shutdown is requested.
/// Raised by the platform when an application shutdown is requested.
/// </summary>
/// <remarks>
/// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit. This event
/// provides a first-chance to cancel application shutdown; if shutdown is not canceled at this point the application
/// Application Shutdown can be requested for various reasons like OS shutdown.
///
/// On Windows this will be called when an OS Session (logout or shutdown) terminates. Cancelling the eventargs will
/// block OS shutdown.
///
/// On OSX this has the same behavior as on Windows and in addition:
/// This event is raised via the Quit menu or right-clicking on the application icon and selecting Quit.
///
/// This event provides a first-chance to cancel application shutdown; if shutdown is not canceled at this point the application
/// will try to close each non-owned open window, invoking the <see cref="Window.Closing"/> event on each and allowing
/// each window to cancel the shutdown.
/// each window to cancel the shutdown of the application. Windows cannot however prevent OS shutdown.
/// </remarks>
event EventHandler<CancelEventArgs> ShutdownRequested;
event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
}
}

9
src/Avalonia.Controls/ApplicationLifetimes/ShutdownRequestedEventArgs.cs

@ -0,0 +1,9 @@
using System.ComponentModel;
namespace Avalonia.Controls.ApplicationLifetimes
{
public class ShutdownRequestedEventArgs : CancelEventArgs
{
}
}

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
set
{
_clientSize = value;
Resized?.Invoke(value);
Resized?.Invoke(value, PlatformResizeReason.Unspecified);
}
}
@ -49,7 +49,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }

3
src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using Avalonia.Controls.ApplicationLifetimes;
namespace Avalonia.Platform
{
@ -11,6 +12,6 @@ namespace Avalonia.Platform
/// <remarks>
/// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit.
/// </remarks>
event EventHandler<CancelEventArgs> ShutdownRequested;
event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
}
}

37
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -3,11 +3,46 @@ using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Rendering;
using JetBrains.Annotations;
namespace Avalonia.Platform
{
/// <summary>
/// Describes the reason for a <see cref="ITopLevelImpl.Resized"/> message.
/// </summary>
public enum PlatformResizeReason
{
/// <summary>
/// The resize reason is unknown or unspecified.
/// </summary>
Unspecified,
/// <summary>
/// The resize was due to the user resizing the window, for example by dragging the
/// window frame.
/// </summary>
User,
/// <summary>
/// The resize was initiated by the application, for example by setting one of the sizing-
/// related properties on <see cref="Window"/> such as <see cref="Layoutable.Width"/> or
/// <see cref="Layoutable.Height"/>.
/// </summary>
Application,
/// <summary>
/// The resize was initiated by the layout system.
/// </summary>
Layout,
/// <summary>
/// The resize was due to a change in DPI.
/// </summary>
DpiChange,
}
/// <summary>
/// Defines a platform-specific top-level window implementation.
/// </summary>
@ -57,7 +92,7 @@ namespace Avalonia.Platform
/// <summary>
/// Gets or sets a method called when the toplevel is resized.
/// </summary>
Action<Size> Resized { get; set; }
Action<Size, PlatformResizeReason> Resized { get; set; }
/// <summary>
/// Gets or sets a method called when the toplevel's scaling changes.

4
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -7,7 +7,9 @@ namespace Avalonia.Platform
/// <summary>
/// Shows the window.
/// </summary>
void Show(bool activate);
/// <param name="activate">Whether to activate the shown window.</param>
/// <param name="isDialog">Whether the window is being shown as a dialog.</param>
void Show(bool activate, bool isDialog);
/// <summary>
/// Hides the window.

4
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -110,7 +110,9 @@ namespace Avalonia.Platform
/// <summary>
/// Sets the client size of the top level.
/// </summary>
void Resize(Size clientSize);
/// <param name="clientSize">The new client size.</param>
/// <param name="reason">The reason for the resize.</param>
void Resize(Size clientSize, PlatformResizeReason reason = PlatformResizeReason.Application);
/// <summary>
/// Sets the client size of the top level.

9
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -161,12 +161,9 @@ namespace Avalonia.Controls.Primitives
protected override sealed Size ArrangeSetBounds(Size size)
{
using (BeginAutoSizing())
{
_positionerParameters.Size = size;
UpdatePosition();
return ClientSize;
}
_positionerParameters.Size = size;
UpdatePosition();
return ClientSize;
}
}
}

6
src/Avalonia.Controls/TopLevel.cs

@ -377,11 +377,15 @@ namespace Avalonia.Controls
LayoutManager?.Dispose();
}
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected virtual void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary>
/// <param name="clientSize">The new client size.</param>
protected virtual void HandleResized(Size clientSize)
/// <param name="reason">The reason for the resize.</param>
protected virtual void HandleResized(Size clientSize, PlatformResizeReason reason)
{
ClientSize = clientSize;
FrameSize = PlatformImpl.FrameSize;

145
src/Avalonia.Controls/Window.cs

@ -244,7 +244,7 @@ namespace Avalonia.Controls
impl.WindowStateChanged = HandleWindowStateChanged;
_maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size);
impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, PlatformResizeReason.Application));
PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar);
}
@ -258,6 +258,18 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets a value indicating how the window will size itself to fit its content.
/// </summary>
/// <remarks>
/// If <see cref="SizeToContent"/> has a value other than <see cref="SizeToContent.Manual"/>,
/// <see cref="SizeToContent"/> is automatically set to <see cref="SizeToContent.Manual"/>
/// if a user resizes the window by using the resize grip or dragging the border.
///
/// NOTE: Because of a limitation of X11, <see cref="SizeToContent"/> will be reset on X11 to
/// <see cref="SizeToContent.Manual"/> on any resize - including the resize that happens when
/// the window is first shown. This is because X11 resize notifications are asynchronous and
/// there is no way to know whether a resize came from the user or the layout system. To avoid
/// this, consider setting <see cref="CanResize"/> to false, which will disable user resizing
/// of the window.
/// </remarks>
public SizeToContent SizeToContent
{
get { return GetValue(SizeToContentProperty); }
@ -583,28 +595,23 @@ namespace Avalonia.Controls
return;
}
using (BeginAutoSizing())
{
Renderer?.Stop();
Renderer?.Stop();
if (Owner is Window owner)
{
owner.RemoveChild(this);
}
if (Owner is Window owner)
{
owner.RemoveChild(this);
}
if (_children.Count > 0)
if (_children.Count > 0)
{
foreach (var child in _children.ToArray())
{
foreach (var child in _children.ToArray())
{
child.child.Hide();
}
child.child.Hide();
}
Owner = null;
PlatformImpl?.Hide();
}
Owner = null;
PlatformImpl?.Hide();
IsVisible = false;
}
@ -675,29 +682,23 @@ namespace Avalonia.Controls
if (initialSize != ClientSize)
{
using (BeginAutoSizing())
{
PlatformImpl?.Resize(initialSize);
}
PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout);
}
LayoutManager.ExecuteInitialLayoutPass();
using (BeginAutoSizing())
if (parent != null)
{
if (parent != null)
{
PlatformImpl?.SetParent(parent.PlatformImpl);
}
Owner = parent;
parent?.AddChild(this, false);
SetWindowStartupLocation(Owner?.PlatformImpl);
PlatformImpl?.Show(ShowActivated);
Renderer?.Start();
PlatformImpl?.SetParent(parent.PlatformImpl);
}
Owner = parent;
parent?.AddChild(this, false);
SetWindowStartupLocation(Owner?.PlatformImpl);
PlatformImpl?.Show(ShowActivated, false);
Renderer?.Start();
OnOpened(EventArgs.Empty);
}
@ -760,41 +761,34 @@ namespace Avalonia.Controls
if (initialSize != ClientSize)
{
using (BeginAutoSizing())
{
PlatformImpl?.Resize(initialSize);
}
PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout);
}
LayoutManager.ExecuteInitialLayoutPass();
var result = new TaskCompletionSource<TResult>();
using (BeginAutoSizing())
{
PlatformImpl?.SetParent(owner.PlatformImpl);
Owner = owner;
owner.AddChild(this, true);
SetWindowStartupLocation(owner.PlatformImpl);
PlatformImpl?.Show(ShowActivated);
PlatformImpl?.SetParent(owner.PlatformImpl);
Owner = owner;
owner.AddChild(this, true);
Renderer?.Start();
SetWindowStartupLocation(owner.PlatformImpl);
Observable.FromEventPattern<EventHandler, EventArgs>(
x => Closed += x,
x => Closed -= x)
.Take(1)
.Subscribe(_ =>
{
owner.Activate();
result.SetResult((TResult)(_dialogResult ?? default(TResult)));
});
PlatformImpl?.Show(ShowActivated, true);
OnOpened(EventArgs.Empty);
}
Renderer?.Start();
Observable.FromEventPattern<EventHandler, EventArgs>(
x => Closed += x,
x => Closed -= x)
.Take(1)
.Subscribe(_ =>
{
owner.Activate();
result.SetResult((TResult)(_dialogResult ?? default(TResult)));
});
OnOpened(EventArgs.Empty);
return result.Task;
}
@ -937,11 +931,8 @@ namespace Avalonia.Controls
protected sealed override Size ArrangeSetBounds(Size size)
{
using (BeginAutoSizing())
{
PlatformImpl?.Resize(size);
return ClientSize;
}
PlatformImpl?.Resize(size, PlatformResizeReason.Layout);
return ClientSize;
}
protected sealed override void HandleClosed()
@ -958,18 +949,36 @@ namespace Avalonia.Controls
Owner = null;
}
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected sealed override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <inheritdoc/>
protected sealed override void HandleResized(Size clientSize)
protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason)
{
if (!AutoSizing)
if (ClientSize == clientSize)
return;
var sizeToContent = SizeToContent;
// If auto-sizing is enabled, and the resize came from a user resize (or the reason was
// unspecified) then turn off auto-resizing for any window dimension that is not equal
// to the requested size.
if (sizeToContent != SizeToContent.Manual &&
CanResize &&
reason == PlatformResizeReason.Unspecified ||
reason == PlatformResizeReason.User)
{
SizeToContent = SizeToContent.Manual;
if (clientSize.Width != ClientSize.Width)
sizeToContent &= ~SizeToContent.Width;
if (clientSize.Height != ClientSize.Height)
sizeToContent &= ~SizeToContent.Height;
SizeToContent = sizeToContent;
}
Width = clientSize.Width;
Height = clientSize.Height;
base.HandleResized(clientSize);
base.HandleResized(clientSize, reason);
}
/// <summary>

32
src/Avalonia.Controls/WindowBase.cs

@ -39,7 +39,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<WindowBase, bool>(nameof(Topmost));
private int _autoSizing;
private bool _hasExecutedInitialLayoutPass;
private bool _isActive;
private bool _ignoreVisibilityChange;
@ -95,10 +94,8 @@ namespace Avalonia.Controls
public Screens Screens { get; private set; }
/// <summary>
/// Whether an auto-size operation is in progress.
/// </summary>
protected bool AutoSizing => _autoSizing > 0;
[Obsolete("No longer used. Always returns false.")]
protected bool AutoSizing => false;
/// <summary>
/// Gets or sets the owner of the window.
@ -162,7 +159,7 @@ namespace Avalonia.Controls
LayoutManager.ExecuteInitialLayoutPass();
_hasExecutedInitialLayoutPass = true;
}
PlatformImpl?.Show(true);
PlatformImpl?.Show(true, false);
Renderer?.Start();
OnOpened(EventArgs.Empty);
}
@ -172,20 +169,9 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Begins an auto-resize operation.
/// </summary>
/// <returns>A disposable used to finish the operation.</returns>
/// <remarks>
/// When an auto-resize operation is in progress any resize events received will not be
/// cause the new size to be written to the <see cref="Layoutable.Width"/> and
/// <see cref="Layoutable.Height"/> properties.
/// </remarks>
protected IDisposable BeginAutoSizing()
{
++_autoSizing;
return Disposable.Create(() => --_autoSizing);
}
[Obsolete("No longer used. Has no effect.")]
protected IDisposable BeginAutoSizing() => Disposable.Empty;
/// <summary>
/// Ensures that the window is initialized.
@ -215,11 +201,15 @@ namespace Avalonia.Controls
}
}
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary>
/// <param name="clientSize">The new client size.</param>
protected override void HandleResized(Size clientSize)
/// <param name="reason">The reason for the resize.</param>
protected override void HandleResized(Size clientSize, PlatformResizeReason reason)
{
ClientSize = clientSize;
FrameSize = PlatformImpl.FrameSize;

8
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -20,7 +20,7 @@ namespace Avalonia.DesignerSupport.Remote
ClientSize = new Size(1, 1);
}
public void Show(bool activate)
public void Show(bool activate, bool isDialog)
{
}
@ -58,7 +58,7 @@ namespace Avalonia.DesignerSupport.Remote
base.OnMessage(transport, obj);
}
public void Resize(Size clientSize)
public void Resize(Size clientSize, PlatformResizeReason reason)
{
_transport.Send(new RequestViewportResizeMessage
{
@ -99,10 +99,6 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void ShowDialog(IWindowImpl parent)
{
}
public void SetSystemDecorations(SystemDecorations enabled)
{
}

8
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -27,7 +27,7 @@ namespace Avalonia.DesignerSupport.Remote
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Func<bool> Closing { get; set; }
public Action Closed { get; set; }
@ -54,7 +54,7 @@ namespace Avalonia.DesignerSupport.Remote
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent,
(_, size, __) =>
{
Resize(size);
Resize(size, PlatformResizeReason.Unspecified);
}));
}
@ -78,7 +78,7 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void Show(bool activate)
public void Show(bool activate, bool isDialog)
{
}
@ -98,7 +98,7 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void Resize(Size clientSize)
public void Resize(Size clientSize, PlatformResizeReason reason)
{
}

13
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -47,7 +47,7 @@ namespace Avalonia.Headless
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public IRenderer CreateRenderer(IRenderRoot root)
@ -76,7 +76,7 @@ namespace Avalonia.Headless
public Action Closed { get; set; }
public IMouseDevice MouseDevice { get; }
public void Show(bool activate)
public void Show(bool activate, bool isDialog)
{
if (activate)
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
@ -108,7 +108,7 @@ namespace Avalonia.Headless
public Action Activated { get; set; }
public IPlatformHandle Handle { get; } = new PlatformHandle(IntPtr.Zero, "STUB");
public Size MaxClientSize { get; } = new Size(1920, 1280);
public void Resize(Size clientSize)
public void Resize(Size clientSize, PlatformResizeReason reason)
{
// Emulate X11 behavior here
if (IsPopup)
@ -126,7 +126,7 @@ namespace Avalonia.Headless
if (ClientSize != clientSize)
{
ClientSize = clientSize;
Resized?.Invoke(clientSize);
Resized?.Invoke(clientSize, PlatformResizeReason.Unspecified);
}
}
@ -148,11 +148,6 @@ namespace Avalonia.Headless
}
public void ShowDialog(IWindowImpl parent)
{
Show(true);
}
public void SetSystemDecorations(bool enabled)
{

4
src/Avalonia.Input/TappedEventArgs.cs

@ -13,6 +13,10 @@ namespace Avalonia.Input
this.lastPointerEventArgs = lastPointerEventArgs;
}
public IPointer Pointer => lastPointerEventArgs.Pointer;
public KeyModifiers KeyModifiers => lastPointerEventArgs.KeyModifiers;
public ulong Timestamp => lastPointerEventArgs.Timestamp;
public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo);
}
}

5
src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Native.Interop;
using Avalonia.Platform;
@ -7,7 +8,7 @@ namespace Avalonia.Native
{
internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
{
public event EventHandler<CancelEventArgs> ShutdownRequested;
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
void IAvnApplicationEvents.FilesOpened(IAvnStringArray urls)
{
@ -17,7 +18,7 @@ namespace Avalonia.Native
public int TryShutdown()
{
if (ShutdownRequested is null) return 1;
var e = new CancelEventArgs();
var e = new ShutdownRequestedEventArgs();
ShutdownRequested(this, e);
return (!e.Cancel).AsComBool();
}

6
src/Avalonia.Native/PopupImpl.cs

@ -32,7 +32,7 @@ namespace Avalonia.Native
private void MoveResize(PixelPoint position, Size size, double scaling)
{
Position = position;
Resize(size);
Resize(size, PlatformResizeReason.Layout);
//TODO: We ignore the scaling override for now
}
@ -60,14 +60,14 @@ namespace Avalonia.Native
}
}
public override void Show(bool activate)
public override void Show(bool activate, bool isDialog)
{
var parent = _parent;
while (parent is PopupImpl p)
parent = p._parent;
if (parent is WindowImpl w)
w.Native.TakeFocusFromChildren();
base.Show(false);
base.Show(false, isDialog);
}
public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this);

16
src/Avalonia.Native/WindowImplBase.cs

@ -87,7 +87,7 @@ namespace Avalonia.Native
var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
.FirstOrDefault(m => m.Bounds.Contains(Position));
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d));
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), PlatformResizeReason.Layout);
}
public Size ClientSize
@ -146,7 +146,7 @@ namespace Avalonia.Native
public Action LostFocus { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup();
@ -186,13 +186,13 @@ namespace Avalonia.Native
_parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height));
}
void IAvnWindowBaseEvents.Resized(AvnSize* size)
void IAvnWindowBaseEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason)
{
if (_parent?._native != null)
{
var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s;
_parent.Resized?.Invoke(s);
_parent.Resized?.Invoke(s, (PlatformResizeReason)reason);
}
}
@ -320,9 +320,9 @@ namespace Avalonia.Native
}
}
public void Resize(Size clientSize)
public void Resize(Size clientSize, PlatformResizeReason reason)
{
_native.Resize(clientSize.Width, clientSize.Height);
_native.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
}
public IRenderer CreateRenderer(IRenderRoot root)
@ -365,9 +365,9 @@ namespace Avalonia.Native
}
public virtual void Show(bool activate)
public virtual void Show(bool activate, bool isDialog)
{
_native.Show(activate.AsComBool());
_native.Show(activate.AsComBool(), isDialog.AsComBool());
}

15
src/Avalonia.Native/avn.idl

@ -400,6 +400,15 @@ enum AvnExtendClientAreaChromeHints
AvnDefaultChrome = AvnPreferSystemChrome,
}
enum AvnPlatformResizeReason
{
ResizeUnspecified,
ResizeUser,
ResizeApplication,
ResizeLayout,
ResizeDpiChange,
}
[uuid(809c652e-7396-11d2-9771-00a0c9b4d50c)]
interface IAvaloniaNativeFactory : IUnknown
{
@ -430,7 +439,7 @@ interface IAvnString : IUnknown
[uuid(e5aca675-02b7-4129-aa79-d6e417210bda)]
interface IAvnWindowBase : IUnknown
{
HRESULT Show(bool activate);
HRESULT Show(bool activate, bool isDialog);
HRESULT Hide();
HRESULT Close();
HRESULT Activate();
@ -438,7 +447,7 @@ interface IAvnWindowBase : IUnknown
HRESULT GetFrameSize(AvnSize*ret);
HRESULT GetScaling(double*ret);
HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize);
HRESULT Resize(double width, double height);
HRESULT Resize(double width, double height, AvnPlatformResizeReason reason);
HRESULT Invalidate(AvnRect rect);
HRESULT BeginMoveDrag();
HRESULT BeginResizeDrag(AvnWindowEdge edge);
@ -492,7 +501,7 @@ interface IAvnWindowBaseEvents : IUnknown
void Closed();
void Activated();
void Deactivated();
void Resized([const] AvnSize& size);
void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
void PositionChanged(AvnPoint position);
void RawMouseEvent(AvnRawMouseEventType type,
uint timeStamp,

20
src/Avalonia.Visuals/Media/DrawingGroup.cs

@ -12,6 +12,12 @@ namespace Avalonia.Media
public static readonly StyledProperty<Transform> TransformProperty =
AvaloniaProperty.Register<DrawingGroup, Transform>(nameof(Transform));
public static readonly StyledProperty<Geometry> ClipGeometryProperty =
AvaloniaProperty.Register<DrawingGroup, Geometry>(nameof(ClipGeometry));
public static readonly StyledProperty<IBrush> OpacityMaskProperty =
AvaloniaProperty.Register<DrawingGroup, IBrush>(nameof(OpacityMask));
public double Opacity
{
get => GetValue(OpacityProperty);
@ -24,6 +30,18 @@ namespace Avalonia.Media
set => SetValue(TransformProperty, value);
}
public Geometry ClipGeometry
{
get => GetValue(ClipGeometryProperty);
set => SetValue(ClipGeometryProperty, value);
}
public IBrush OpacityMask
{
get => GetValue(OpacityMaskProperty);
set => SetValue(OpacityMaskProperty, value);
}
[Content]
public AvaloniaList<Drawing> Children { get; } = new AvaloniaList<Drawing>();
@ -31,6 +49,8 @@ namespace Avalonia.Media
{
using (context.PushPreTransform(Transform?.Value ?? Matrix.Identity))
using (context.PushOpacity(Opacity))
using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default(DrawingContext.PushedState))
using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, GetBounds()) : default(DrawingContext.PushedState))
{
foreach (var drawing in Children)
{

37
src/Avalonia.X11/X11Window.cs

@ -336,7 +336,7 @@ namespace Avalonia.X11
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
//TODO
public Action<double> ScalingChanged { get; set; }
public Action Deactivated { get; set; }
@ -510,7 +510,7 @@ namespace Avalonia.X11
UpdateImePosition();
if (changedSize && !updatedSizeViaScaling && !_popup)
Resized?.Invoke(ClientSize);
Resized?.Invoke(ClientSize, PlatformResizeReason.Unspecified);
Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
}, DispatcherPriority.Layout);
@ -565,7 +565,7 @@ namespace Avalonia.X11
UpdateImePosition();
SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize);
if(!skipResize)
Resize(oldScaledSize, true);
Resize(oldScaledSize, true, PlatformResizeReason.DpiChange);
return true;
}
@ -616,7 +616,7 @@ namespace Avalonia.X11
{
// Occurs once the window has been mapped, which is the earliest the extents
// can be retrieved, so invoke event to force update of TopLevel.FrameSize.
Resized.Invoke(ClientSize);
Resized.Invoke(ClientSize, PlatformResizeReason.Unspecified);
}
if (atom == _x11.Atoms._NET_WM_STATE)
@ -839,7 +839,7 @@ namespace Avalonia.X11
XSetTransientForHint(_x11.Display, _handle, parent.Handle.Handle);
}
public void Show(bool activate)
public void Show(bool activate, bool isDialog)
{
_wasMappedAtLeastOnce = true;
XMapWindow(_x11.Display, _handle);
@ -862,19 +862,19 @@ namespace Avalonia.X11
}
public void Resize(Size clientSize) => Resize(clientSize, false);
public void Resize(Size clientSize, PlatformResizeReason reason) => Resize(clientSize, false, reason);
public void Move(PixelPoint point) => Position = point;
private void MoveResize(PixelPoint position, Size size, double scaling)
{
Move(position);
_scalingOverride = scaling;
UpdateScaling(true);
Resize(size, true);
Resize(size, true, PlatformResizeReason.Layout);
}
PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
void Resize(Size clientSize, bool force)
void Resize(Size clientSize, bool force, PlatformResizeReason reason)
{
if (!force && clientSize == ClientSize)
return;
@ -891,7 +891,7 @@ namespace Avalonia.X11
if (force || !_wasMappedAtLeastOnce || (_popup && needImmediatePopupResize))
{
_realSize = pixelSize;
Resized?.Invoke(ClientSize);
Resized?.Invoke(ClientSize, reason);
}
}
@ -1024,15 +1024,22 @@ namespace Avalonia.X11
side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
BeginMoveResize(side, e);
}
public void SetTitle(string title)
{
var data = Encoding.UTF8.GetBytes(title);
fixed (void* pdata = data)
if (string.IsNullOrEmpty(title))
{
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME, _x11.Atoms.UTF8_STRING, 8,
PropertyMode.Replace, pdata, data.Length);
XStoreName(_x11.Display, _handle, title);
XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME);
}
else
{
var data = Encoding.UTF8.GetBytes(title);
fixed (void* pdata = data)
{
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME, _x11.Atoms.UTF8_STRING, 8,
PropertyMode.Replace, pdata, data.Length);
XStoreName(_x11.Display, _handle, title);
}
}
}

2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -69,7 +69,7 @@ namespace Avalonia.LinuxFramebuffer
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }

6
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -44,7 +44,7 @@ namespace Avalonia.Win32.Interop.Wpf
((FrameworkElement)PlatformImpl)?.InvalidateMeasure();
}
protected override void HandleResized(Size clientSize)
protected override void HandleResized(Size clientSize, PlatformResizeReason reason)
{
ClientSize = clientSize;
LayoutManager.ExecuteLayoutPass();
@ -114,7 +114,7 @@ namespace Avalonia.Win32.Interop.Wpf
if (_finalSize == _previousSize)
return finalSize;
_previousSize = _finalSize;
_ttl.Resized?.Invoke(finalSize.ToAvaloniaSize());
_ttl.Resized?.Invoke(finalSize.ToAvaloniaSize(), PlatformResizeReason.Unspecified);
return base.ArrangeOverride(finalSize);
}
@ -236,7 +236,7 @@ namespace Avalonia.Win32.Interop.Wpf
Action<RawInputEventArgs> ITopLevelImpl.Input { get; set; } //TODO
Action<Rect> ITopLevelImpl.Paint { get; set; }
Action<Size> ITopLevelImpl.Resized { get; set; }
Action<Size, PlatformResizeReason> ITopLevelImpl.Resized { get; set; }
Action<double> ITopLevelImpl.ScalingChanged { get; set; }
Action<WindowTransparencyLevel> ITopLevelImpl.TransparencyLevelChanged { get; set; }

4
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -17,7 +17,7 @@ namespace Avalonia.Win32
[ThreadStatic]
private static IntPtr s_parentHandle;
public override void Show(bool activate)
public override void Show(bool activate, bool isDialog)
{
// Popups are always shown non-activated.
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);
@ -135,7 +135,7 @@ namespace Avalonia.Win32
private void MoveResize(PixelPoint position, Size size, double scaling)
{
Move(position);
Resize(size);
Resize(size, PlatformResizeReason.Layout);
//TODO: We ignore the scaling override for now
}

29
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -6,18 +6,16 @@ using System.IO;
using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.Win32;
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@ -65,7 +63,7 @@ namespace Avalonia
namespace Avalonia.Win32
{
class Win32Platform : IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform, IPlatformIconLoader
class Win32Platform : IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform, IPlatformIconLoader, IPlatformLifetimeEventsImpl
{
private static readonly Win32Platform s_instance = new Win32Platform();
private static Thread _uiThread;
@ -122,7 +120,8 @@ namespace Avalonia.Win32
})
.Bind<IPlatformIconLoader>().ToConstant(s_instance)
.Bind<NonPumpingLockHelper.IHelperImpl>().ToConstant(new NonPumpingSyncContext.HelperImpl())
.Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider());
.Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider())
.Bind<IPlatformLifetimeEventsImpl>().ToConstant(s_instance);
Win32GlManager.Initialize();
@ -207,6 +206,8 @@ namespace Avalonia.Win32
public event Action<DispatcherPriority?> Signaled;
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
@ -214,6 +215,22 @@ namespace Avalonia.Win32
{
Signaled?.Invoke(null);
}
if(msg == (uint)WindowsMessage.WM_QUERYENDSESSION)
{
if (ShutdownRequested != null)
{
var e = new ShutdownRequestedEventArgs();
ShutdownRequested(this, e);
if(e.Cancel)
{
return IntPtr.Zero;
}
}
}
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
}
@ -253,7 +270,7 @@ namespace Avalonia.Win32
public IWindowImpl CreateEmbeddableWindow()
{
var embedded = new EmbeddedWindowImpl();
embedded.Show(true);
embedded.Show(true, false);
return embedded;
}

36
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -2,9 +2,10 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Remote;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Win32.Input;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@ -18,7 +19,6 @@ namespace Avalonia.Win32
{
const double wheelDelta = 120.0;
uint timestamp = unchecked((uint)GetMessageTime());
RawInputEventArgs e = null;
var shouldTakeFocus = false;
@ -94,14 +94,19 @@ namespace Avalonia.Win32
var newDisplayRect = Marshal.PtrToStructure<RECT>(lParam);
_scaling = dpi / 96.0;
ScalingChanged?.Invoke(_scaling);
SetWindowPos(hWnd,
IntPtr.Zero,
newDisplayRect.left,
newDisplayRect.top,
newDisplayRect.right - newDisplayRect.left,
newDisplayRect.bottom - newDisplayRect.top,
SetWindowPosFlags.SWP_NOZORDER |
SetWindowPosFlags.SWP_NOACTIVATE);
using (SetResizeReason(PlatformResizeReason.DpiChange))
{
SetWindowPos(hWnd,
IntPtr.Zero,
newDisplayRect.left,
newDisplayRect.top,
newDisplayRect.right - newDisplayRect.left,
newDisplayRect.bottom - newDisplayRect.top,
SetWindowPosFlags.SWP_NOZORDER |
SetWindowPosFlags.SWP_NOACTIVATE);
}
return IntPtr.Zero;
}
@ -364,6 +369,11 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
case WindowsMessage.WM_ENTERSIZEMOVE:
_resizeReason = PlatformResizeReason.User;
break;
case WindowsMessage.WM_SIZE:
{
using(NonPumpingSyncContext.Use())
@ -379,7 +389,7 @@ namespace Avalonia.Win32
size == SizeCommand.Maximized))
{
var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16);
Resized(clientSize / RenderScaling);
Resized(clientSize / RenderScaling, _resizeReason);
}
var windowState = size == SizeCommand.Maximized ?
@ -403,6 +413,10 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
case WindowsMessage.WM_EXITSIZEMOVE:
_resizeReason = PlatformResizeReason.Unspecified;
break;
case WindowsMessage.WM_MOVE:
{
PositionChanged?.Invoke(new PixelPoint((short)(ToInt32(lParam) & 0xffff),

33
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -53,6 +53,7 @@ namespace Avalonia.Win32
private double _extendTitleBarHint = -1;
private bool _isUsingComposition;
private IBlurHost _blurHost;
private PlatformResizeReason _resizeReason;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
@ -160,7 +161,7 @@ namespace Avalonia.Win32
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
@ -482,7 +483,7 @@ namespace Avalonia.Win32
: new ImmediateRenderer(root);
}
public void Resize(Size value)
public void Resize(Size value, PlatformResizeReason reason)
{
int requestedClientWidth = (int)(value.Width * RenderScaling);
int requestedClientHeight = (int)(value.Height * RenderScaling);
@ -494,6 +495,7 @@ namespace Avalonia.Win32
{
GetWindowRect(_hwnd, out var windowRect);
using var scope = SetResizeReason(reason);
SetWindowPos(
_hwnd,
IntPtr.Zero,
@ -585,7 +587,7 @@ namespace Avalonia.Win32
_shown = false;
}
public virtual void Show(bool activate)
public virtual void Show(bool activate, bool isDialog)
{
SetParent(_parent);
ShowWindow(_showWindowState, activate);
@ -930,7 +932,7 @@ namespace Avalonia.Win32
_offScreenMargin = new Thickness();
_extendedMargins = new Thickness();
Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling));
Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout);
}
if(!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) &&
@ -1135,7 +1137,7 @@ namespace Avalonia.Win32
SetParent(null);
if (shown)
Show(activated);
Show(activated, false);
}
}
else
@ -1311,6 +1313,13 @@ namespace Avalonia.Win32
/// <inheritdoc/>
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0);
private ResizeReasonScope SetResizeReason(PlatformResizeReason reason)
{
var old = _resizeReason;
_resizeReason = reason;
return new ResizeReasonScope(this, old);
}
private struct SavedWindowInfo
{
public WindowStyles Style { get; set; }
@ -1325,5 +1334,19 @@ namespace Avalonia.Win32
public SystemDecorations Decorations;
public bool IsFullScreen;
}
private struct ResizeReasonScope : IDisposable
{
private readonly WindowImpl _owner;
private readonly PlatformResizeReason _restore;
public ResizeReasonScope(WindowImpl owner, PlatformResizeReason restore)
{
_owner = owner;
_restore = restore;
}
public void Dispose() => _owner._resizeReason = _restore;
}
}
}

4
src/iOS/Avalonia.iOS/AvaloniaView.cs

@ -96,7 +96,7 @@ namespace Avalonia.iOS
public IEnumerable<object> Surfaces { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
public Action Closed { get; set; }
@ -127,7 +127,7 @@ namespace Avalonia.iOS
public override void LayoutSubviews()
{
_topLevelImpl.Resized?.Invoke(_topLevelImpl.ClientSize);
_topLevelImpl.Resized?.Invoke(_topLevelImpl.ClientSize, PlatformResizeReason.Layout);
base.LayoutSubviews();
}

24
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -230,7 +230,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var window = PreparedWindow();
@ -268,7 +268,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(c.IsOpen);
popupImpl.Verify(x => x.Hide(), Times.Never);
popupImpl.Verify(x => x.Show(true), Times.Exactly(1));
popupImpl.Verify(x => x.Show(true, false), Times.Exactly(1));
}
}
@ -277,7 +277,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var window = PreparedWindow();
@ -306,7 +306,7 @@ namespace Avalonia.Controls.UnitTests
Assert.False(c.IsOpen);
popupImpl.Verify(x => x.Hide(), Times.Exactly(1));
popupImpl.Verify(x => x.Show(true), Times.Exactly(1));
popupImpl.Verify(x => x.Show(true, false), Times.Exactly(1));
}
}
@ -315,7 +315,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var sut = new ContextMenu();
@ -336,7 +336,7 @@ namespace Avalonia.Controls.UnitTests
_mouse.Up(target);
Assert.False(sut.IsOpen);
popupImpl.Verify(x => x.Show(true), Times.Once);
popupImpl.Verify(x => x.Show(true, false), Times.Once);
popupImpl.Verify(x => x.Hide(), Times.Once);
}
}
@ -346,7 +346,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var sut = new ContextMenu();
@ -368,7 +368,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(sut.IsOpen);
popupImpl.Verify(x => x.Hide(), Times.Once);
popupImpl.Verify(x => x.Show(true), Times.Exactly(2));
popupImpl.Verify(x => x.Show(true, false), Times.Exactly(2));
}
}
@ -413,7 +413,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
bool eventCalled = false;
var sut = new ContextMenu();
@ -429,7 +429,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(eventCalled);
Assert.False(sut.IsOpen);
popupImpl.Verify(x => x.Show(true), Times.Never);
popupImpl.Verify(x => x.Show(true, false), Times.Never);
}
}
@ -533,7 +533,7 @@ namespace Avalonia.Controls.UnitTests
{
using (Application())
{
popupImpl.Setup(x => x.Show(true)).Verifiable();
popupImpl.Setup(x => x.Show(true, false)).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
bool eventCalled = false;
@ -560,7 +560,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(eventCalled);
Assert.True(sut.IsOpen);
popupImpl.Verify(x => x.Show(true), Times.Once());
popupImpl.Verify(x => x.Show(true, false), Times.Once());
popupImpl.Verify(x => x.Hide(), Times.Never);
}
}

2
tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs

@ -232,7 +232,7 @@ namespace Avalonia.Controls.UnitTests
++raised;
};
lifetimeEvents.Raise(x => x.ShutdownRequested += null, new CancelEventArgs());
lifetimeEvents.Raise(x => x.ShutdownRequested += null, new ShutdownRequestedEventArgs());
Assert.Equal(1, raised);
Assert.Equal(new[] { window }, lifetime.Windows);

2
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -139,7 +139,7 @@ namespace Avalonia.Controls.UnitTests
// The user has resized the window, so we can no longer auto-size.
var target = new TestTopLevel(impl.Object);
impl.Object.Resized(new Size(100, 200));
impl.Object.Resized(new Size(100, 200), PlatformResizeReason.Unspecified);
Assert.Equal(100, target.Width);
Assert.Equal(200, target.Height);

2
tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs

@ -137,7 +137,7 @@ namespace Avalonia.Controls.UnitTests
var target = new TestWindowBase(windowImpl.Object);
target.IsVisible = true;
windowImpl.Verify(x => x.Show(true));
windowImpl.Verify(x => x.Show(true, false));
}
}

120
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -663,10 +663,11 @@ namespace Avalonia.Controls.UnitTests
var clientSize = new Size(200, 200);
var maxClientSize = new Size(480, 480);
windowImpl.Setup(x => x.Resize(It.IsAny<Size>())).Callback<Size>(size =>
windowImpl.Setup(x => x.Resize(It.IsAny<Size>(), It.IsAny<PlatformResizeReason>()))
.Callback<Size, PlatformResizeReason>((size, reason) =>
{
clientSize = size.Constrain(maxClientSize);
windowImpl.Object.Resized?.Invoke(clientSize);
windowImpl.Object.Resized?.Invoke(clientSize, reason);
});
windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize);
@ -739,6 +740,36 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void SizeToContent_Should_Not_Be_Lost_On_Scaling_Change()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var child = new Canvas
{
Width = 209,
Height = 117,
};
var target = new Window()
{
SizeToContent = SizeToContent.WidthAndHeight,
Content = child
};
Show(target);
// Size before and after DPI change is a real-world example, with size after DPI
// change coming from Win32 WM_DPICHANGED.
target.PlatformImpl.ScalingChanged(1.5);
target.PlatformImpl.Resized(
new Size(210.66666666666666, 118.66666666666667),
PlatformResizeReason.DpiChange);
Assert.Equal(SizeToContent.WidthAndHeight, target.SizeToContent);
}
}
[Fact]
public void Width_Height_Should_Be_Updated_When_SizeToContent_Is_WidthAndHeight()
{
@ -791,8 +822,91 @@ namespace Avalonia.Controls.UnitTests
target.LayoutManager.ExecuteLayoutPass();
var windowImpl = Mock.Get(target.PlatformImpl);
windowImpl.Verify(x => x.Resize(new Size(410, 800)));
windowImpl.Verify(x => x.Resize(new Size(410, 800), PlatformResizeReason.Application));
Assert.Equal(410, target.Width);
}
}
[Fact]
public void User_Resize_Of_Window_Width_Should_Reset_SizeToContent()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new Window()
{
SizeToContent = SizeToContent.WidthAndHeight,
Content = new Canvas
{
Width = 400,
Height = 800,
},
};
Show(target);
Assert.Equal(400, target.Width);
Assert.Equal(800, target.Height);
target.PlatformImpl.Resized(new Size(410, 800), PlatformResizeReason.User);
Assert.Equal(410, target.Width);
Assert.Equal(800, target.Height);
Assert.Equal(SizeToContent.Height, target.SizeToContent);
}
}
[Fact]
public void User_Resize_Of_Window_Height_Should_Reset_SizeToContent()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new Window()
{
SizeToContent = SizeToContent.WidthAndHeight,
Content = new Canvas
{
Width = 400,
Height = 800,
},
};
Show(target);
Assert.Equal(400, target.Width);
Assert.Equal(800, target.Height);
target.PlatformImpl.Resized(new Size(400, 810), PlatformResizeReason.User);
Assert.Equal(400, target.Width);
Assert.Equal(810, target.Height);
Assert.Equal(SizeToContent.Width, target.SizeToContent);
}
}
[Fact]
public void Window_Resize_Should_Not_Reset_SizeToContent_If_CanResize_False()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new Window()
{
SizeToContent = SizeToContent.WidthAndHeight,
CanResize = false,
Content = new Canvas
{
Width = 400,
Height = 800,
},
};
Show(target);
Assert.Equal(400, target.Width);
Assert.Equal(800, target.Height);
target.PlatformImpl.Resized(new Size(410, 810), PlatformResizeReason.Unspecified);
Assert.Equal(400, target.Width);
Assert.Equal(800, target.Height);
Assert.Equal(SizeToContent.WidthAndHeight, target.SizeToContent);
}
}

9
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@ -52,13 +52,14 @@ namespace Avalonia.UnitTests
windowImpl.Object.PositionChanged?.Invoke(x);
});
windowImpl.Setup(x => x.Resize(It.IsAny<Size>())).Callback<Size>(x =>
windowImpl.Setup(x => x.Resize(It.IsAny<Size>(), It.IsAny<PlatformResizeReason>()))
.Callback<Size, PlatformResizeReason>((x, y) =>
{
clientSize = x.Constrain(s_screenSize);
windowImpl.Object.Resized?.Invoke(clientSize);
windowImpl.Object.Resized?.Invoke(clientSize, y);
});
windowImpl.Setup(x => x.Show(true)).Callback(() =>
windowImpl.Setup(x => x.Show(true, It.IsAny<bool>())).Callback(() =>
{
windowImpl.Object.Activated?.Invoke();
});
@ -75,7 +76,7 @@ namespace Avalonia.UnitTests
{
clientSize = size.Constrain(s_screenSize);
popupImpl.Object.PositionChanged?.Invoke(pos);
popupImpl.Object.Resized?.Invoke(clientSize);
popupImpl.Object.Resized?.Invoke(clientSize, PlatformResizeReason.Unspecified);
});
var positioner = new ManagedPopupPositioner(positionerHelper);

Loading…
Cancel
Save