Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to get FrameSize of a window. #6076

Merged
merged 9 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions native/Avalonia.Native/src/OSX/window.mm
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ virtual HRESULT GetClientSize(AvnSize* ret) override
}
}

virtual HRESULT GetFrameSize(AvnSize* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
return E_POINTER;

auto frame = [Window frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;

return S_OK;
}
}

virtual HRESULT GetScaling (double* ret) override
{
@autoreleasepool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex)

public virtual Size ClientSize => Size.ToSize(RenderScaling);

public Size? FrameSize => null;

public IMouseDevice MouseDevice { get; } = new MouseDevice();

public Action Closed { get; set; }
Expand Down
4 changes: 3 additions & 1 deletion src/Avalonia.Controls/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints
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: 13
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.
Total Issues: 15
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public Size ClientSize
}
}

public Size? FrameSize => null;

public double RenderScaling
{
get { return _scaling; }
Expand Down
5 changes: 5 additions & 0 deletions src/Avalonia.Controls/Platform/ITopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public interface ITopLevelImpl : IDisposable
/// </summary>
Size ClientSize { get; }

/// <summary>
/// Gets the total size of the toplevel, excluding shadows.
/// </summary>
Size? FrameSize { get; }

/// <summary>
/// Gets the scaling factor for the toplevel. This is used for rendering.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions src/Avalonia.Controls/TopLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public abstract class TopLevel : ContentControl,
public static readonly DirectProperty<TopLevel, Size> ClientSizeProperty =
AvaloniaProperty.RegisterDirect<TopLevel, Size>(nameof(ClientSize), o => o.ClientSize);

/// <summary>
/// Defines the <see cref="FrameSize"/> property.
/// </summary>
public static readonly DirectProperty<TopLevel, Size?> FrameSizeProperty =
AvaloniaProperty.RegisterDirect<TopLevel, Size?>(nameof(FrameSize), o => o.FrameSize);

/// <summary>
/// Defines the <see cref="IInputRoot.PointerOverElement"/> property.
/// </summary>
Expand Down Expand Up @@ -74,6 +80,7 @@ public abstract class TopLevel : ContentControl,
private readonly IPlatformRenderInterface _renderInterface;
private readonly IGlobalStyles _globalStyles;
private Size _clientSize;
private Size? _frameSize;
private WindowTransparencyLevel _actualTransparencyLevel;
private ILayoutManager _layoutManager;
private Border _transparencyFallbackBorder;
Expand Down Expand Up @@ -161,6 +168,7 @@ public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver dependencyResolv
styler?.ApplyStyles(this);

ClientSize = impl.ClientSize;
FrameSize = impl.FrameSize;

this.GetObservable(PointerOverElementProperty)
.Select(
Expand Down Expand Up @@ -197,6 +205,15 @@ public Size ClientSize
protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
}

/// <summary>
/// Gets or sets the total size of the window.
/// </summary>
public Size? FrameSize
{
get { return _frameSize; }
protected set { SetAndRaise(FrameSizeProperty, ref _frameSize, value); }
}

/// <summary>
/// Gets or sets the <see cref="WindowTransparencyLevel"/> that the TopLevel should use when possible.
/// </summary>
Expand Down Expand Up @@ -366,6 +383,7 @@ protected virtual void HandleClosed()
protected virtual void HandleResized(Size clientSize)
{
ClientSize = clientSize;
FrameSize = PlatformImpl.FrameSize;
Width = clientSize.Width;
Height = clientSize.Height;
LayoutManager.ExecuteLayoutPass();
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Controls/WindowBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ protected override void HandleClosed()
protected override void HandleResized(Size clientSize)
{
ClientSize = clientSize;
FrameSize = PlatformImpl.FrameSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.DesignerSupport/Remote/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class WindowStub : IWindowImpl, IPopupImpl
public IPlatformHandle Handle { get; }
public Size MaxAutoSizeHint { get; }
public Size ClientSize { get; }
public Size? FrameSize => null;
public double RenderScaling { get; } = 1.0;
public double DesktopScaling => 1.0;
public IEnumerable<object> Surfaces { get; }
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Headless/HeadlessWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void Dispose()
}

public Size ClientSize { get; set; }
public Size? FrameSize => null;
public double RenderScaling { get; } = 1;
public double DesktopScaling => RenderScaling;
public IEnumerable<object> Surfaces { get; }
Expand Down
14 changes: 14 additions & 0 deletions src/Avalonia.Native/WindowImplBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ public Size ClientSize
}
}

public Size? FrameSize
{
get
{
if (_native != null)
{
var s = _native.FrameSize;
return new Size(s.Width, s.Height);
}

return default;
}
}

public IEnumerable<object> Surfaces => new[] {
(_gpu ? _glSurface : (object)null),
this
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Native/avn.idl
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ interface IAvnWindowBase : IUnknown
HRESULT Close();
HRESULT Activate();
HRESULT GetClientSize(AvnSize*ret);
HRESULT GetFrameSize(AvnSize*ret);
HRESULT GetScaling(double*ret);
HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize);
HRESULT Resize(double width, double height);
Expand Down
31 changes: 31 additions & 0 deletions src/Avalonia.X11/X11Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,30 @@ void UpdateSizeHints(PixelSize? preResize)

public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling);

public Size? FrameSize
{
get
{
XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero,
new IntPtr(4), false, (IntPtr)Atom.AnyPropertyType, out var _,
out var _, out var nitems, out var _, out var prop);

if (nitems.ToInt64() != 4)
{
// Window hasn't been mapped by the WM yet, so can't get the extents.
return null;
}

var data = (IntPtr*)prop.ToPointer();
var extents = new Thickness(data[0].ToInt32(), data[2].ToInt32(), data[1].ToInt32(), data[3].ToInt32());
XFree(prop);

return new Size(
(_realSize.Width + extents.Left + extents.Right) / RenderScaling,
(_realSize.Height + extents.Top + extents.Bottom) / RenderScaling);
}
}

public double RenderScaling
{
get
Expand Down Expand Up @@ -589,6 +613,13 @@ public WindowState WindowState

private void OnPropertyChange(IntPtr atom, bool hasValue)
{
if (atom == _x11.Atoms._NET_FRAME_EXTENTS)
{
// 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);
}

if (atom == _x11.Atoms._NET_WM_STATE)
{
WindowState state = WindowState.Normal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void SetCursor(ICursorImpl cursor)
}

public Size ClientSize => ScaledSize;
public Size? FrameSize => null;
public IMouseDevice MouseDevice => new MouseDevice();
public IPopupImpl CreatePopup() => null;

Expand Down
1 change: 1 addition & 0 deletions src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void Dispose()
}

Size ITopLevelImpl.ClientSize => _finalSize;
Size? ITopLevelImpl.FrameSize => null;
IMouseDevice ITopLevelImpl.MouseDevice => _mouse;

double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1;
Expand Down
23 changes: 23 additions & 0 deletions src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,26 @@ public enum WindowsMessage : uint
WM_DISPATCH_WORK_ITEM = WM_USER,
}

public enum DwmWindowAttribute : uint
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
};

public enum MapVirtualKeyMapTypes : uint
{
MAPVK_VK_TO_VSC = 0x00,
Expand Down Expand Up @@ -1388,6 +1408,9 @@ public static extern IntPtr CreateFileMapping(IntPtr hFile,
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);

[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(out bool enabled);

Expand Down
15 changes: 15 additions & 0 deletions src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ public Size ClientSize
}
}

public Size? FrameSize
{
get
{
if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
{
GetWindowRect(_hwnd, out var rcWindow);
return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
}

DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT)));
return new Size(rect.Width, rect.Height) / RenderScaling;
}
}

public IScreenImpl Screen { get; }

public IPlatformHandle Handle { get; private set; }
Expand Down
1 change: 1 addition & 0 deletions src/iOS/Avalonia.iOS/AvaloniaView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
}

public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height);
public Size? FrameSize => null;
public double RenderScaling => _view.ContentScaleFactor;
public IEnumerable<object> Surfaces { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
Expand Down