Browse Source

Merge branch 'master' into x11-visibility-notify-expose

pull/3876/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
523f811fa1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      native/Avalonia.Native/inc/avalonia-native.h
  2. 4
      native/Avalonia.Native/src/OSX/window.h
  3. 309
      native/Avalonia.Native/src/OSX/window.mm
  4. 5
      samples/ControlCatalog/MainView.xaml
  5. 2
      samples/ControlCatalog/MainWindow.xaml
  6. 27
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  7. 2
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  8. 5
      src/Avalonia.Controls/WindowState.cs
  9. 2
      src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs
  10. 2
      src/Avalonia.Native/WindowImpl.cs
  11. 148
      src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
  12. 18
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  13. 20
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs
  14. 25
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  15. 14
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  16. 1
      src/Avalonia.X11/X11Atoms.cs
  17. 32
      src/Avalonia.X11/X11Window.cs
  18. 10
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  19. 9
      src/Skia/Avalonia.Skia/SkiaOptions.cs
  20. 2
      src/Skia/Avalonia.Skia/SkiaPlatform.cs
  21. 55
      src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
  22. 24
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  23. 11
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  24. 6
      src/Windows/Avalonia.Win32/SystemDialogImpl.cs
  25. 13
      src/Windows/Avalonia.Win32/Win32TypeExtensions.cs
  26. 192
      src/Windows/Avalonia.Win32/WindowImpl.cs

3
native/Avalonia.Native/inc/avalonia-native.h

@ -135,6 +135,7 @@ enum AvnWindowState
Normal,
Minimized,
Maximized,
FullScreen,
};
enum AvnStandardCursorType
@ -246,7 +247,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
{
virtual HRESULT ShowDialog (IAvnWindow* parent) = 0;
virtual HRESULT SetCanResize(bool value) = 0;
virtual HRESULT SetHasDecorations(SystemDecorations value) = 0;
virtual HRESULT SetDecorations(SystemDecorations value) = 0;
virtual HRESULT SetTitle (void* utf8Title) = 0;
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;

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

@ -35,6 +35,10 @@ struct INSWindowHolder
struct IWindowStateChanged
{
virtual void WindowStateChanged () = 0;
virtual void StartStateTransition () = 0;
virtual void EndStateTransition () = 0;
virtual SystemDecorations Decorations () = 0;
virtual AvnWindowState WindowState () = 0;
};
#endif /* window_h */

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

@ -391,7 +391,7 @@ protected:
void UpdateStyle()
{
[Window setStyleMask:GetStyle()];
[Window setStyleMask: GetStyle()];
}
public:
@ -404,10 +404,13 @@ public:
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{
private:
bool _canResize = true;
SystemDecorations _hasDecorations = SystemDecorationsFull;
CGRect _lastUndecoratedFrame;
bool _canResize;
bool _fullScreenActive;
SystemDecorations _decorations;
AvnWindowState _lastWindowState;
bool _inSetWindowState;
NSRect _preZoomSize;
bool _transitioningWindowState;
FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP()
@ -421,6 +424,11 @@ private:
ComPtr<IAvnWindowEvents> WindowEvents;
WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
{
_fullScreenActive = false;
_canResize = true;
_decorations = SystemDecorationsFull;
_transitioningWindowState = false;
_inSetWindowState = false;
_lastWindowState = Normal;
WindowEvents = events;
[Window setCanBecomeKeyAndMain];
@ -428,6 +436,20 @@ private:
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
}
void HideOrShowTrafficLights ()
{
for (id subview in Window.contentView.superview.subviews) {
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
NSView *titlebarView = [subview subviews][0];
for (id button in titlebarView.subviews) {
if ([button isKindOfClass:[NSButton class]]) {
[button setHidden: (_decorations != SystemDecorationsFull)];
}
}
}
}
}
virtual HRESULT Show () override
{
@autoreleasepool
@ -439,6 +461,8 @@ private:
WindowBaseImpl::Show();
HideOrShowTrafficLights();
return SetWindowState(_lastWindowState);
}
}
@ -459,41 +483,69 @@ private:
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
WindowBaseImpl::Show();
HideOrShowTrafficLights();
return S_OK;
}
}
void StartStateTransition () override
{
_transitioningWindowState = true;
}
void EndStateTransition () override
{
_transitioningWindowState = false;
}
SystemDecorations Decorations () override
{
return _decorations;
}
AvnWindowState WindowState () override
{
return _lastWindowState;
}
void WindowStateChanged () override
{
AvnWindowState state;
GetWindowState(&state);
WindowEvents->WindowStateChanged(state);
if(!_inSetWindowState && !_transitioningWindowState)
{
AvnWindowState state;
GetWindowState(&state);
if(_lastWindowState != state)
{
_lastWindowState = state;
WindowEvents->WindowStateChanged(state);
}
}
}
bool UndecoratedIsMaximized ()
{
return CGRectEqualToRect([Window frame], [Window screen].visibleFrame);
auto windowSize = [Window frame];
auto available = [Window screen].visibleFrame;
return CGRectEqualToRect(windowSize, available);
}
bool IsZoomed ()
{
return _hasDecorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized();
return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized();
}
void DoZoom()
{
switch (_hasDecorations)
switch (_decorations)
{
case SystemDecorationsNone:
if (!UndecoratedIsMaximized())
{
_lastUndecoratedFrame = [Window frame];
}
[Window zoom:Window];
case SystemDecorationsBorderOnly:
[Window setFrame:[Window screen].visibleFrame display:true];
break;
case SystemDecorationsBorderOnly:
case SystemDecorationsFull:
[Window performZoom:Window];
break;
@ -510,25 +562,52 @@ private:
}
}
virtual HRESULT SetHasDecorations(SystemDecorations value) override
virtual HRESULT SetDecorations(SystemDecorations value) override
{
@autoreleasepool
{
_hasDecorations = value;
auto currentWindowState = _lastWindowState;
_decorations = value;
if(_fullScreenActive)
{
return S_OK;
}
auto currentFrame = [Window frame];
UpdateStyle();
HideOrShowTrafficLights();
switch (_hasDecorations)
switch (_decorations)
{
case SystemDecorationsNone:
[Window setHasShadow:NO];
[Window setTitleVisibility:NSWindowTitleHidden];
[Window setTitlebarAppearsTransparent:YES];
if(currentWindowState == Maximized)
{
if(!UndecoratedIsMaximized())
{
DoZoom();
}
}
break;
case SystemDecorationsBorderOnly:
[Window setHasShadow:YES];
[Window setTitleVisibility:NSWindowTitleHidden];
[Window setTitlebarAppearsTransparent:YES];
if(currentWindowState == Maximized)
{
if(!UndecoratedIsMaximized())
{
DoZoom();
}
}
break;
case SystemDecorationsFull:
@ -536,6 +615,13 @@ private:
[Window setTitleVisibility:NSWindowTitleVisible];
[Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
if(currentWindowState == Maximized)
{
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
[View setFrameSize:newFrame];
}
break;
}
@ -592,13 +678,19 @@ private:
return E_POINTER;
}
if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen)
{
*ret = FullScreen;
return S_OK;
}
if([Window isMiniaturized])
{
*ret = Minimized;
return S_OK;
}
if([Window isZoomed])
if(IsZoomed())
{
*ret = Maximized;
return S_OK;
@ -610,16 +702,57 @@ private:
}
}
void EnterFullScreenMode ()
{
_fullScreenActive = true;
[Window setHasShadow:YES];
[Window setTitleVisibility:NSWindowTitleVisible];
[Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
[Window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable];
[Window toggleFullScreen:nullptr];
}
void ExitFullScreenMode ()
{
[Window toggleFullScreen:nullptr];
_fullScreenActive = false;
SetDecorations(_decorations);
}
virtual HRESULT SetWindowState (AvnWindowState state) override
{
@autoreleasepool
{
if(_lastWindowState == state)
{
return S_OK;
}
_inSetWindowState = true;
auto currentState = _lastWindowState;
_lastWindowState = state;
if(currentState == Normal)
{
_preZoomSize = [Window frame];
}
if(_shown)
{
switch (state) {
case Maximized:
if(currentState == FullScreen)
{
ExitFullScreenMode();
}
lastPositionSet.X = 0;
lastPositionSet.Y = 0;
@ -635,40 +768,66 @@ private:
break;
case Minimized:
[Window miniaturize:Window];
if(currentState == FullScreen)
{
ExitFullScreenMode();
}
else
{
[Window miniaturize:Window];
}
break;
case FullScreen:
if([Window isMiniaturized])
{
[Window deminiaturize:Window];
}
EnterFullScreenMode();
break;
default:
case Normal:
if([Window isMiniaturized])
{
[Window deminiaturize:Window];
}
if(currentState == FullScreen)
{
ExitFullScreenMode();
}
if(IsZoomed())
{
DoZoom();
if(_decorations == SystemDecorationsFull)
{
DoZoom();
}
else
{
[Window setFrame:_preZoomSize display:true];
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
[View setFrameSize:newFrame];
}
}
break;
}
}
_inSetWindowState = false;
return S_OK;
}
}
virtual void OnResized () override
{
if(_shown)
if(_shown && !_inSetWindowState && !_transitioningWindowState)
{
auto windowState = [Window isMiniaturized] ? Minimized
: (IsZoomed() ? Maximized : Normal);
if (windowState != _lastWindowState)
{
_lastWindowState = windowState;
WindowEvents->WindowStateChanged(windowState);
}
WindowStateChanged();
}
}
@ -677,22 +836,23 @@ protected:
{
unsigned long s = NSWindowStyleMaskBorderless;
switch (_hasDecorations)
switch (_decorations)
{
case SystemDecorationsNone:
s = s | NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskMiniaturizable;
break;
case SystemDecorationsBorderOnly:
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskMiniaturizable;
break;
case SystemDecorationsFull:
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskBorderless;
if(_canResize)
{
s = s | NSWindowStyleMaskResizable;
}
break;
}
@ -1171,6 +1331,20 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
}
- (void)performClose:(id)sender
{
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
{
if(![[self delegate] windowShouldClose:self]) return;
}
else if([self respondsToSelector:@selector(windowShouldClose:)])
{
if(![self windowShouldClose:self]) return;
}
[self close];
}
- (void)pollModalSession:(nonnull NSModalSession)session
{
auto response = [NSApp runModalSession:session];
@ -1399,7 +1573,66 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)windowDidResize:(NSNotification *)notification
{
_parent->OnResized();
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}
- (void)windowWillExitFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->StartStateTransition();
}
}
- (void)windowDidExitFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->EndStateTransition();
if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized)
{
NSRect screenRect = [[self screen] visibleFrame];
[self setFrame:screenRect display:YES];
}
if(parent->WindowState() == Minimized)
{
[self miniaturize:nullptr];
}
parent->WindowStateChanged();
}
}
- (void)windowWillEnterFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->StartStateTransition();
}
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->EndStateTransition();
parent->WindowStateChanged();
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame

5
samples/ControlCatalog/MainView.xaml

@ -59,8 +59,8 @@
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
<TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>
<TabControl.Tag>
<StackPanel Width="115" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<ComboBox x:Name="Decorations" SelectedIndex="0" Margin="0,0,0,8">
<StackPanel Width="115" Spacing="4" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="8">
<ComboBox x:Name="Decorations" SelectedIndex="0">
<ComboBoxItem>No Decorations</ComboBoxItem>
<ComboBoxItem>Border Only</ComboBoxItem>
<ComboBoxItem>Full Decorations</ComboBoxItem>
@ -69,6 +69,7 @@
<ComboBoxItem>Light</ComboBoxItem>
<ComboBoxItem>Dark</ComboBoxItem>
</ComboBox>
<ComboBox Items="{Binding WindowStates}" SelectedItem="{Binding WindowState}" />
</StackPanel>
</TabControl.Tag>
</TabControl>

2
samples/ControlCatalog/MainWindow.xaml

@ -7,7 +7,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow">
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}">
<NativeMenu.Menu>
<NativeMenu>

27
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -1,4 +1,5 @@
using System.Reactive;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Notifications;
using Avalonia.Dialogs;
@ -11,6 +12,8 @@ namespace ControlCatalog.ViewModels
private IManagedNotificationManager _notificationManager;
private bool _isMenuItemChecked = true;
private WindowState _windowState;
private WindowState[] _windowStates;
public MainWindowViewModel(IManagedNotificationManager notificationManager)
{
@ -45,10 +48,32 @@ namespace ControlCatalog.ViewModels
(App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown();
});
ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() =>
ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() =>
{
IsMenuItemChecked = !IsMenuItemChecked;
});
WindowState = WindowState.Normal;
WindowStates = new WindowState[]
{
WindowState.Minimized,
WindowState.Normal,
WindowState.Maximized,
WindowState.FullScreen,
};
}
public WindowState WindowState
{
get { return _windowState; }
set { this.RaiseAndSetIfChanged(ref _windowState, value); }
}
public WindowState[] WindowStates
{
get { return _windowStates; }
set { this.RaiseAndSetIfChanged(ref _windowStates, value); }
}
public IManagedNotificationManager NotificationManager

2
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -392,7 +392,7 @@ namespace Avalonia.Controls.Platform
{
var control = e.Source as ILogical;
if (!Menu.IsLogicalParentOf(control))
if (!Menu.IsLogicalAncestorOf(control))
{
Menu.Close();
}

5
src/Avalonia.Controls/WindowState.cs

@ -19,5 +19,10 @@ namespace Avalonia.Controls
/// The window is maximized.
/// </summary>
Maximized,
/// <summary>
/// The window is fullscreen.
/// </summary>
FullScreen,
}
}

2
src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs

@ -34,7 +34,7 @@ namespace Avalonia.Dialogs
return;
}
var isQuickLink = _quickLinksRoot.IsLogicalParentOf(e.Source as Control);
var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
if (e.ClickCount == 2 || isQuickLink)
{
if (model.ItemType == ManagedFileChooserItemType.File)

2
src/Avalonia.Native/WindowImpl.cs

@ -67,7 +67,7 @@ namespace Avalonia.Native
public void SetSystemDecorations(Controls.SystemDecorations enabled)
{
_native.HasDecorations = (Interop.SystemDecorations)enabled;
_native.Decorations = (Interop.SystemDecorations)enabled;
}
public void SetTitleBarColor (Avalonia.Media.Color color)

148
src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs

@ -1,11 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.LogicalTree
{
/// <summary>
/// Provides extension methods for working with the logical tree.
/// </summary>
public static class LogicalExtensions
{
/// <summary>
/// Enumerates the ancestors of an <see cref="ILogical"/> in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical's ancestors.</returns>
public static IEnumerable<ILogical> GetLogicalAncestors(this ILogical logical)
{
Contract.Requires<ArgumentNullException>(logical != null);
@ -19,6 +26,11 @@ namespace Avalonia.LogicalTree
}
}
/// <summary>
/// Enumerates an <see cref="ILogical"/> and its ancestors in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical and its ancestors.</returns>
public static IEnumerable<ILogical> GetSelfAndLogicalAncestors(this ILogical logical)
{
yield return logical;
@ -29,11 +41,50 @@ namespace Avalonia.LogicalTree
}
}
/// <summary>
/// Finds first ancestor of given type.
/// </summary>
/// <typeparam name="T">Ancestor type.</typeparam>
/// <param name="logical">The logical.</param>
/// <param name="includeSelf">If given logical should be included in search.</param>
/// <returns>First ancestor of given type.</returns>
public static T FindLogicalAncestorOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
{
if (logical is null)
{
return null;
}
ILogical parent = includeSelf ? logical : logical.LogicalParent;
while (parent != null)
{
if (parent is T result)
{
return result;
}
parent = parent.LogicalParent;
}
return null;
}
/// <summary>
/// Enumerates the children of an <see cref="ILogical"/> in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical children.</returns>
public static IEnumerable<ILogical> GetLogicalChildren(this ILogical logical)
{
return logical.LogicalChildren;
}
/// <summary>
/// Enumerates the descendants of an <see cref="ILogical"/> in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical's ancestors.</returns>
public static IEnumerable<ILogical> GetLogicalDescendants(this ILogical logical)
{
foreach (ILogical child in logical.LogicalChildren)
@ -47,6 +98,11 @@ namespace Avalonia.LogicalTree
}
}
/// <summary>
/// Enumerates an <see cref="ILogical"/> and its descendants in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical and its ancestors.</returns>
public static IEnumerable<ILogical> GetSelfAndLogicalDescendants(this ILogical logical)
{
yield return logical;
@ -57,16 +113,56 @@ namespace Avalonia.LogicalTree
}
}
/// <summary>
/// Finds first descendant of given type.
/// </summary>
/// <typeparam name="T">Descendant type.</typeparam>
/// <param name="logical">The logical.</param>
/// <param name="includeSelf">If given logical should be included in search.</param>
/// <returns>First descendant of given type.</returns>
public static T FindLogicalDescendantOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
{
if (logical is null)
{
return null;
}
if (includeSelf && logical is T result)
{
return result;
}
return FindDescendantOfTypeCore<T>(logical);
}
/// <summary>
/// Gets the logical parent of an <see cref="ILogical"/>.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The parent, or null if the logical is unparented.</returns>
public static ILogical GetLogicalParent(this ILogical logical)
{
return logical.LogicalParent;
}
/// <summary>
/// Gets the logical parent of an <see cref="ILogical"/>.
/// </summary>
/// <typeparam name="T">The type of the logical parent.</typeparam>
/// <param name="logical">The logical.</param>
/// <returns>
/// The parent, or null if the logical is unparented or its parent is not of type <typeparamref name="T"/>.
/// </returns>
public static T GetLogicalParent<T>(this ILogical logical) where T : class
{
return logical.LogicalParent as T;
}
/// <summary>
/// Enumerates the siblings of an <see cref="ILogical"/> in the logical tree.
/// </summary>
/// <param name="logical">The logical.</param>
/// <returns>The logical siblings.</returns>
public static IEnumerable<ILogical> GetLogicalSiblings(this ILogical logical)
{
ILogical parent = logical.LogicalParent;
@ -80,9 +176,55 @@ namespace Avalonia.LogicalTree
}
}
public static bool IsLogicalParentOf(this ILogical logical, ILogical target)
/// <summary>
/// Tests whether an <see cref="ILogical"/> is an ancestor of another logical.
/// </summary>
/// <param name="logical">The logical.</param>
/// <param name="target">The potential descendant.</param>
/// <returns>
/// True if <paramref name="logical"/> is an ancestor of <paramref name="target"/>;
/// otherwise false.
/// </returns>
public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target)
{
return target.GetLogicalAncestors().Any(x => x == logical);
ILogical current = target?.LogicalParent;
while (current != null)
{
if (current == logical)
{
return true;
}
current = current.LogicalParent;
}
return false;
}
private static T FindDescendantOfTypeCore<T>(ILogical logical) where T : class
{
var logicalChildren = logical.LogicalChildren;
var logicalChildrenCount = logicalChildren.Count;
for (var i = 0; i < logicalChildrenCount; i++)
{
ILogical child = logicalChildren[i];
if (child is T result)
{
return result;
}
var childResult = FindDescendantOfTypeCore<T>(child);
if (!(childResult is null))
{
return childResult;
}
}
return null;
}
}
}

18
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public class Scene : IDisposable
{
private Dictionary<IVisual, IVisualNode> _index;
private readonly Dictionary<IVisual, IVisualNode> _index;
/// <summary>
/// Initializes a new instance of the <see cref="Scene"/> class.
@ -83,7 +83,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <returns>The cloned scene.</returns>
public Scene CloneScene()
{
var index = new Dictionary<IVisual, IVisualNode>();
var index = new Dictionary<IVisual, IVisualNode>(_index.Count);
var root = Clone((VisualNode)Root, null, index);
var result = new Scene(root, index, Layers.Clone(), Generation + 1)
@ -162,9 +162,19 @@ namespace Avalonia.Rendering.SceneGraph
index.Add(result.Visual, result);
foreach (var child in source.Children)
var children = source.Children;
var childrenCount = children.Count;
if (childrenCount > 0)
{
result.AddChild(Clone((VisualNode)child, result, index));
result.TryPreallocateChildren(childrenCount);
for (var i = 0; i < childrenCount; i++)
{
var child = children[i];
result.AddChild(Clone((VisualNode)child, result, index));
}
}
return result;

20
src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs

@ -11,16 +11,28 @@ namespace Avalonia.Rendering.SceneGraph
public class SceneLayers : IEnumerable<SceneLayer>
{
private readonly IVisual _root;
private readonly List<SceneLayer> _inner = new List<SceneLayer>();
private readonly Dictionary<IVisual, SceneLayer> _index = new Dictionary<IVisual, SceneLayer>();
private readonly List<SceneLayer> _inner;
private readonly Dictionary<IVisual, SceneLayer> _index;
/// <summary>
/// Initializes a new instance of the <see cref="SceneLayers"/> class.
/// </summary>
/// <param name="root">The scene's root visual.</param>
public SceneLayers(IVisual root)
public SceneLayers(IVisual root) : this(root, 0)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SceneLayers"/> class.
/// </summary>
/// <param name="root">The scene's root visual.</param>
/// <param name="capacity">Initial layer capacity.</param>
public SceneLayers(IVisual root, int capacity)
{
_root = root;
_inner = new List<SceneLayer>(capacity);
_index = new Dictionary<IVisual, SceneLayer>(capacity);
}
/// <summary>
@ -84,7 +96,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <returns>The cloned layers.</returns>
public SceneLayers Clone()
{
var result = new SceneLayers(_root);
var result = new SceneLayers(_root, Count);
foreach (var src in _inner)
{

25
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Utilities;
@ -349,6 +349,11 @@ namespace Avalonia.Rendering.SceneGraph
context.Transform = transformRestore;
}
internal void TryPreallocateChildren(int count)
{
EnsureChildrenCreated(count);
}
private Rect CalculateBounds()
{
var result = new Rect();
@ -362,11 +367,11 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
private void EnsureChildrenCreated()
private void EnsureChildrenCreated(int capacity = 0)
{
if (_children == null)
{
_children = new List<IVisualNode>();
_children = new List<IVisualNode>(capacity);
}
}
@ -383,7 +388,15 @@ namespace Avalonia.Rendering.SceneGraph
}
else if (_drawOperationsCloned)
{
_drawOperations = new List<IRef<IDrawOperation>>(_drawOperations.Select(op => op.Clone()));
var oldDrawOperations = _drawOperations;
_drawOperations = new List<IRef<IDrawOperation>>(oldDrawOperations.Count);
foreach (var drawOperation in oldDrawOperations)
{
_drawOperations.Add(drawOperation.Clone());
}
_drawOperationsRefCounter.Dispose();
_drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations));
_drawOperationsCloned = false;
@ -399,9 +412,9 @@ namespace Avalonia.Rendering.SceneGraph
/// <returns>Disposable for given draw operations.</returns>
private static IDisposable CreateDisposeDrawOperations(List<IRef<IDrawOperation>> drawOperations)
{
return Disposable.Create(() =>
return Disposable.Create(drawOperations, operations =>
{
foreach (var operation in drawOperations)
foreach (var operation in operations)
{
operation.Dispose();
}

14
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -377,7 +377,19 @@ namespace Avalonia.VisualTree
/// </returns>
public static bool IsVisualAncestorOf(this IVisual visual, IVisual target)
{
return target.GetVisualAncestors().Any(x => x == visual);
IVisual current = target?.VisualParent;
while (current != null)
{
if (current == visual)
{
return true;
}
current = current.VisualParent;
}
return false;
}
public static IEnumerable<IVisual> SortByZIndex(this IEnumerable<IVisual> elements)

1
src/Avalonia.X11/X11Atoms.cs

@ -156,6 +156,7 @@ namespace Avalonia.X11
public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE;
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ;
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT;
public readonly IntPtr _NET_WM_STATE_FULLSCREEN;
public readonly IntPtr _XEMBED;
public readonly IntPtr _XEMBED_INFO;
public readonly IntPtr _MOTIF_WM_HINTS;

32
src/Avalonia.X11/X11Window.cs

@ -220,16 +220,11 @@ namespace Avalonia.X11
var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border |
MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH;
if (_popup || _systemDecorations == SystemDecorations.None)
{
if (_popup
|| _systemDecorations == SystemDecorations.None)
decorations = 0;
}
else if (_systemDecorations == SystemDecorations.BorderOnly)
{
decorations = MotifDecorations.Border;
}
if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly)
if (!_canResize)
{
functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize);
decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH);
@ -252,7 +247,7 @@ namespace Avalonia.X11
var min = _minMaxSize.minSize;
var max = _minMaxSize.maxSize;
if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly)
if (!_canResize)
max = min = _realSize;
if (preResize.HasValue)
@ -554,12 +549,21 @@ namespace Avalonia.X11
else if (value == WindowState.Maximized)
{
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
_x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
}
else if (value == WindowState.FullScreen)
{
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
_x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
}
else
{
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
_x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
}
@ -587,6 +591,12 @@ namespace Avalonia.X11
break;
}
if(pitems[c] == _x11.Atoms._NET_WM_STATE_FULLSCREEN)
{
state = WindowState.FullScreen;
break;
}
if (pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ ||
pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT)
{
@ -812,7 +822,7 @@ namespace Avalonia.X11
public void SetSystemDecorations(SystemDecorations enabled)
{
_systemDecorations = enabled;
_systemDecorations = enabled == SystemDecorations.Full ? SystemDecorations.Full : SystemDecorations.None;
UpdateMotifHints();
UpdateSizeHints(null);
}
@ -1054,7 +1064,7 @@ namespace Avalonia.X11
void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
{
if (atoms.Length < 1 || atoms.Length > 4)
if (atoms.Length != 1 && atoms.Length != 2)
throw new ArgumentException();
if (!_mapped)

10
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -18,7 +18,7 @@ namespace Avalonia.Skia
private GRContext GrContext { get; }
public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu)
public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes = 100663296)
{
if (customSkiaGpu != null)
{
@ -26,6 +26,10 @@ namespace Avalonia.Skia
GrContext = _customSkiaGpu.GrContext;
GrContext.GetResourceCacheLimits(out var maxResources, out _);
GrContext.SetResourceCacheLimits(maxResources, maxResourceBytes);
return;
}
@ -39,6 +43,10 @@ namespace Avalonia.Skia
: GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc)))
{
GrContext = GRContext.Create(GRBackend.OpenGL, iface);
GrContext.GetResourceCacheLimits(out var maxResources, out _);
GrContext.SetResourceCacheLimits(maxResources, maxResourceBytes);
}
display.ClearContext();
}

9
src/Skia/Avalonia.Skia/SkiaOptions.cs

@ -8,9 +8,18 @@ namespace Avalonia
/// </summary>
public class SkiaOptions
{
public SkiaOptions()
{
MaxGpuResourceSizeBytes = 100663296; // Value taken from skia.
}
/// <summary>
/// Custom gpu factory to use. Can be used to customize behavior of Skia renderer.
/// </summary>
public Func<ICustomSkiaGpu> CustomGpuFactory { get; set; }
/// <summary>
/// The maximum number of bytes for video memory to store textures and resources.
/// </summary>
public long MaxGpuResourceSizeBytes { get; set; }
}
}

2
src/Skia/Avalonia.Skia/SkiaPlatform.cs

@ -18,7 +18,7 @@ namespace Avalonia.Skia
public static void Initialize(SkiaOptions options)
{
var customGpu = options.CustomGpuFactory?.Invoke();
var renderInterface = new PlatformRenderInterface(customGpu);
var renderInterface = new PlatformRenderInterface(customGpu, options.MaxGpuResourceSizeBytes);
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(renderInterface)

55
src/Windows/Avalonia.Win32/Interop/TaskBarList.cs

@ -0,0 +1,55 @@
using System;
using System.Runtime.InteropServices;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.Interop
{
internal class TaskBarList
{
private static IntPtr s_taskBarList;
private static HrInit s_hrInitDelegate;
private static MarkFullscreenWindow s_markFullscreenWindowDelegate;
/// <summary>
/// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
/// </summary>
/// <param name="fullscreen">Fullscreen state.</param>
public static unsafe void MarkFullscreen(IntPtr hwnd, bool fullscreen)
{
if (s_taskBarList == IntPtr.Zero)
{
Guid clsid = ShellIds.TaskBarList;
Guid iid = ShellIds.ITaskBarList2;
int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out s_taskBarList);
if (s_taskBarList != IntPtr.Zero)
{
var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer();
if (s_hrInitDelegate is null)
{
s_hrInitDelegate = Marshal.GetDelegateForFunctionPointer<HrInit>((*ptr)->HrInit);
}
if (s_hrInitDelegate(s_taskBarList) != HRESULT.S_OK)
{
s_taskBarList = IntPtr.Zero;
}
}
}
if (s_taskBarList != IntPtr.Zero)
{
var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer();
if (s_markFullscreenWindowDelegate is null)
{
s_markFullscreenWindowDelegate = Marshal.GetDelegateForFunctionPointer<MarkFullscreenWindow>((*ptr)->MarkFullscreenWindow);
}
s_markFullscreenWindowDelegate(s_taskBarList, hwnd, fullscreen);
}
}
}
}

24
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -460,6 +460,7 @@ namespace Avalonia.Win32.Interop
WS_SIZEFRAME = 0x40000,
WS_SYSMENU = 0x80000,
WS_TABSTOP = 0x10000,
WS_THICKFRAME = 0x40000,
WS_VISIBLE = 0x10000000,
WS_VSCROLL = 0x200000,
WS_EX_DLGMODALFRAME = 0x00000001,
@ -1146,7 +1147,10 @@ namespace Avalonia.Win32.Interop
internal static extern int CoCreateInstance(ref Guid clsid,
IntPtr ignore1, int ignore2, ref Guid iid, [MarshalAs(UnmanagedType.IUnknown), Out] out object pUnkOuter);
[DllImport("ole32.dll", PreserveSig = true)]
internal static extern int CoCreateInstance(ref Guid clsid,
IntPtr ignore1, int ignore2, ref Guid iid, [Out] out IntPtr pUnkOuter);
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
@ -1642,6 +1646,8 @@ namespace Avalonia.Win32.Interop
public static readonly Guid SaveFileDialog = Guid.Parse("C0B4E2F3-BA21-4773-8DBA-335EC946EB8B");
public static readonly Guid IFileDialog = Guid.Parse("42F85136-DB7E-439C-85F1-E4075D135FC8");
public static readonly Guid IShellItem = Guid.Parse("43826D1E-E718-42EE-BC55-A1E261C37BFE");
public static readonly Guid TaskBarList = Guid.Parse("56FDF344-FD6D-11D0-958A-006097C9A090");
public static readonly Guid ITaskBarList2 = Guid.Parse("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf");
}
[ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
@ -1874,6 +1880,22 @@ namespace Avalonia.Win32.Interop
[MarshalAs(UnmanagedType.LPWStr)]
public string pszSpec;
}
public delegate void MarkFullscreenWindow(IntPtr This, IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fullscreen);
public delegate HRESULT HrInit(IntPtr This);
public struct ITaskBarList2VTable
{
public IntPtr IUnknown1;
public IntPtr IUnknown2;
public IntPtr IUnknown3;
public IntPtr HrInit;
public IntPtr AddTab;
public IntPtr DeleteTab;
public IntPtr ActivateTab;
public IntPtr SetActiveAlt;
public IntPtr MarkFullscreenWindow;
}
}
[Flags]

11
src/Windows/Avalonia.Win32/ScreenImpl.cs

@ -8,7 +8,7 @@ namespace Avalonia.Win32
{
public class ScreenImpl : IScreenImpl
{
public int ScreenCount
public int ScreenCount
{
get => GetSystemMetrics(SystemMetric.SM_CMONITORS);
}
@ -33,7 +33,7 @@ namespace Avalonia.Win32
var shcore = LoadLibrary("shcore.dll");
var method = GetProcAddress(shcore, nameof(GetDpiForMonitor));
if (method != IntPtr.Zero)
{
{
GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _);
dpi = (double)x;
}
@ -51,11 +51,8 @@ namespace Avalonia.Win32
RECT bounds = monitorInfo.rcMonitor;
RECT workingArea = monitorInfo.rcWork;
PixelRect avaloniaBounds = new PixelRect(bounds.left, bounds.top, bounds.right - bounds.left,
bounds.bottom - bounds.top);
PixelRect avaloniaWorkArea =
new PixelRect(workingArea.left, workingArea.top, workingArea.right - workingArea.left,
workingArea.bottom - workingArea.top);
PixelRect avaloniaBounds = bounds.ToPixelRect();
PixelRect avaloniaWorkArea = workingArea.ToPixelRect();
screens[index] =
new WinScreen(dpi / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1,
monitor);

6
src/Windows/Avalonia.Win32/SystemDialogImpl.cs

@ -24,7 +24,7 @@ namespace Avalonia.Win32
Guid clsid = dialog is OpenFileDialog ? UnmanagedMethods.ShellIds.OpenFileDialog : UnmanagedMethods.ShellIds.SaveFileDialog;
Guid iid = UnmanagedMethods.ShellIds.IFileDialog;
UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk);
UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out object unk);
var frm = (UnmanagedMethods.IFileDialog)unk;
var openDialog = dialog as OpenFileDialog;
@ -105,9 +105,9 @@ namespace Avalonia.Win32
var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero;
Guid clsid = UnmanagedMethods.ShellIds.OpenFileDialog;
Guid iid = UnmanagedMethods.ShellIds.IFileDialog;
Guid iid = UnmanagedMethods.ShellIds.IFileDialog;
UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk);
UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out object unk);
var frm = (UnmanagedMethods.IFileDialog)unk;
uint options;
frm.GetOptions(out options);

13
src/Windows/Avalonia.Win32/Win32TypeExtensions.cs

@ -0,0 +1,13 @@
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32
{
internal static class Win32TypeExtensions
{
public static PixelRect ToPixelRect(this RECT rect)
{
return new PixelRect(rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top);
}
}
}

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

@ -37,10 +37,14 @@ namespace Avalonia.Win32
{ WindowEdge.West, HitTestValues.HTLEFT }
};
private SavedWindowInfo _savedWindowInfo;
private bool _isFullScreenActive;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
#endif
private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE);
private readonly List<WindowImpl> _disabledBy;
private readonly TouchDevice _touchDevice;
private readonly MouseDevice _mouseDevice;
@ -82,7 +86,9 @@ namespace Avalonia.Win32
_windowProperties = new WindowProperties
{
ShowInTaskbar = false, IsResizable = true, Decorations = SystemDecorations.Full
ShowInTaskbar = false,
IsResizable = true,
Decorations = SystemDecorations.Full
};
_rendererLock = new ManagedDeferredRendererLock();
@ -538,27 +544,98 @@ namespace Avalonia.Win32
}
}
/// <summary>
/// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
/// Method must only be called from inside UpdateWindowProperties.
/// </summary>
/// <param name="fullscreen"></param>
private void SetFullScreen(bool fullscreen)
{
if (fullscreen)
{
GetWindowRect(_hwnd, out var windowRect);
_savedWindowInfo.WindowRect = windowRect;
var current = GetStyle();
var currentEx = GetExtendedStyle();
_savedWindowInfo.Style = current;
_savedWindowInfo.ExStyle = currentEx;
// Set new window style and size.
SetStyle(current & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false);
SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false);
// On expand, if we're given a window_rect, grow to it, otherwise do
// not resize.
MONITORINFO monitor_info = MONITORINFO.Create();
GetMonitorInfo(MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST), ref monitor_info);
var window_rect = monitor_info.rcMonitor.ToPixelRect();
SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y,
window_rect.Width, window_rect.Height,
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
_isFullScreenActive = true;
}
else
{
// Reset original window style and size. The multiple window size/moves
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
// repainted. Better-looking methods welcome.
_isFullScreenActive = false;
var windowStates = GetWindowStateStyles();
SetStyle((_savedWindowInfo.Style & ~WindowStateMask) | windowStates, false);
SetExtendedStyle(_savedWindowInfo.ExStyle, false);
// On restore, resize to the previous saved rect size.
var new_rect = _savedWindowInfo.WindowRect.ToPixelRect();
SetWindowPos(_hwnd, IntPtr.Zero, new_rect.X, new_rect.Y, new_rect.Width,
new_rect.Height,
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
UpdateWindowProperties(_windowProperties, true);
}
TaskBarList.MarkFullscreen(_hwnd, fullscreen);
}
private void ShowWindow(WindowState state)
{
ShowWindowCommand command;
var newWindowProperties = _windowProperties;
switch (state)
{
case WindowState.Minimized:
newWindowProperties.IsFullScreen = false;
command = ShowWindowCommand.Minimize;
break;
case WindowState.Maximized:
newWindowProperties.IsFullScreen = false;
command = ShowWindowCommand.Maximize;
break;
case WindowState.Normal:
newWindowProperties.IsFullScreen = false;
command = ShowWindowCommand.Restore;
break;
case WindowState.FullScreen:
newWindowProperties.IsFullScreen = true;
UpdateWindowProperties(newWindowProperties);
return;
default:
throw new ArgumentException("Invalid WindowState.");
}
UpdateWindowProperties(newWindowProperties);
UnmanagedMethods.ShowWindow(_hwnd, command);
if (state == WindowState.Maximized)
@ -590,22 +667,69 @@ namespace Avalonia.Win32
SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
}
}
}
private WindowStyles GetWindowStateStyles ()
{
return GetStyle() & WindowStateMask;
}
private WindowStyles GetStyle() => (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE);
private WindowStyles GetStyle()
{
if (_isFullScreenActive)
{
return _savedWindowInfo.Style;
}
else
{
return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE);
}
}
private WindowStyles GetExtendedStyle() => (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE);
private WindowStyles GetExtendedStyle()
{
if (_isFullScreenActive)
{
return _savedWindowInfo.ExStyle;
}
else
{
return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE);
}
}
private void SetStyle(WindowStyles style) => SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style);
private void SetStyle(WindowStyles style, bool save = true)
{
if (save)
{
_savedWindowInfo.Style = style;
}
private void SetExtendedStyle(WindowStyles style) => SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)style);
if (!_isFullScreenActive)
{
SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style);
}
}
private void SetExtendedStyle(WindowStyles style, bool save = true)
{
if (save)
{
_savedWindowInfo.ExStyle = style;
}
if (!_isFullScreenActive)
{
SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)style);
}
}
private void UpdateEnabled()
{
EnableWindow(_hwnd, _disabledBy.Count == 0);
}
private void UpdateWindowProperties(WindowProperties newProperties)
private void UpdateWindowProperties(WindowProperties newProperties, bool forceChanges = false)
{
var oldProperties = _windowProperties;
@ -613,7 +737,7 @@ namespace Avalonia.Win32
// according to the new values already.
_windowProperties = newProperties;
if (oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar)
if ((oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar) || forceChanges)
{
var exStyle = GetExtendedStyle();
@ -632,7 +756,7 @@ namespace Avalonia.Win32
// Otherwise it will still show in the taskbar.
}
if (oldProperties.IsResizable != newProperties.IsResizable)
if ((oldProperties.IsResizable != newProperties.IsResizable) || forceChanges)
{
var style = GetStyle();
@ -648,7 +772,12 @@ namespace Avalonia.Win32
SetStyle(style);
}
if (oldProperties.Decorations != newProperties.Decorations)
if (oldProperties.IsFullScreen != newProperties.IsFullScreen)
{
SetFullScreen(newProperties.IsFullScreen);
}
if ((oldProperties.Decorations != newProperties.Decorations) || forceChanges)
{
var style = GetStyle();
@ -663,30 +792,33 @@ namespace Avalonia.Win32
style &= ~fullDecorationFlags;
}
var margins = new MARGINS
SetStyle(style);
if (!_isFullScreenActive)
{
cyBottomHeight = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0
};
var margins = new MARGINS
{
cyBottomHeight = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0
};
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
GetClientRect(_hwnd, out var oldClientRect);
var oldClientRectOrigin = new POINT();
ClientToScreen(_hwnd, ref oldClientRectOrigin);
oldClientRect.Offset(oldClientRectOrigin);
GetClientRect(_hwnd, out var oldClientRect);
var oldClientRectOrigin = new POINT();
ClientToScreen(_hwnd, ref oldClientRectOrigin);
oldClientRect.Offset(oldClientRectOrigin);
SetStyle(style);
var newRect = oldClientRect;
var newRect = oldClientRect;
if (newProperties.Decorations == SystemDecorations.Full)
{
AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle());
}
if (newProperties.Decorations == SystemDecorations.Full)
{
AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle());
SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_FRAMECHANGED);
}
SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_FRAMECHANGED);
}
}
@ -713,11 +845,19 @@ namespace Avalonia.Win32
IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
private struct SavedWindowInfo
{
public WindowStyles Style { get; set; }
public WindowStyles ExStyle { get; set; }
public RECT WindowRect { get; set; }
};
private struct WindowProperties
{
public bool ShowInTaskbar;
public bool IsResizable;
public SystemDecorations Decorations;
public bool IsFullScreen;
}
}
}

Loading…
Cancel
Save