diff --git a/build/MicroCom.targets b/build/MicroCom.targets
index b48e377fd4..49d2cdce72 100644
--- a/build/MicroCom.targets
+++ b/build/MicroCom.targets
@@ -15,7 +15,8 @@
Inputs="@(AvnComIdl);$(MSBuildThisFileDirectory)../src/tools/MicroComGenerator/**/*.cs"
Outputs="%(AvnComIdl.OutputFile)">
-
+
diff --git a/native/Avalonia.Native/src/OSX/KeyTransform.mm b/native/Avalonia.Native/src/OSX/KeyTransform.mm
index 6b7d95b619..4817ad0ccf 100644
--- a/native/Avalonia.Native/src/OSX/KeyTransform.mm
+++ b/native/Avalonia.Native/src/OSX/KeyTransform.mm
@@ -222,6 +222,7 @@ std::map s_QwertyKeyMap =
{ 45, "n" },
{ 46, "m" },
{ 47, "." },
+ { 48, "\t" },
{ 49, " " },
{ 50, "`" },
{ 51, "" },
diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm
index 460c24ea3a..e1972b22f4 100644
--- a/native/Avalonia.Native/src/OSX/app.mm
+++ b/native/Avalonia.Native/src/OSX/app.mm
@@ -50,6 +50,12 @@ ComPtr _events;
_events->FilesOpened(array);
}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ return _events->TryShutdown() ? NSTerminateNow : NSTerminateCancel;
+}
+
@end
@interface AvnApplication : NSApplication
diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h
index b1f64bca88..3a54bd4b79 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/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 */
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 4be1419f78..14fe60ab0b 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -29,10 +29,12 @@ public:
IAvnMenu* _mainMenu;
bool _shown;
+ bool _inResize;
WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl)
{
_shown = false;
+ _inResize = false;
_mainMenu = nullptr;
BaseEvents = events;
_glContext = gl;
@@ -50,6 +52,7 @@ public:
[Window setBackingType:NSBackingStoreBuffered];
[Window setOpaque:false];
+ [Window setContentView: StandardContainer];
}
virtual HRESULT ObtainNSWindowHandle(void** ret) override
@@ -113,7 +116,7 @@ public:
return Window;
}
- virtual HRESULT Show(bool activate) override
+ virtual HRESULT Show(bool activate, bool isDialog) override
{
START_COM_CALL;
@@ -122,7 +125,6 @@ public:
SetPosition(lastPositionSet);
UpdateStyle();
- [Window setContentView: StandardContainer];
[Window setTitle:_lastTitle];
if(ShouldTakeFocusOnShow() && activate)
@@ -275,9 +277,17 @@ public:
}
}
- virtual HRESULT Resize(double x, double y) override
+ virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
{
+ if(_inResize)
+ {
+ return S_OK;
+ }
+
+ _inResize = true;
+
START_COM_CALL;
+ auto resizeBlock = ResizeScope(View, reason);
@autoreleasepool
{
@@ -304,13 +314,19 @@ public:
y = maxSize.height;
}
- if(!_shown)
+ @try
{
- BaseEvents->Resized(AvnSize{x,y});
+ if(!_shown)
+ {
+ BaseEvents->Resized(AvnSize{x,y}, reason);
+ }
+
+ [Window setContentSize:NSSize{x, y}];
+ }
+ @finally
+ {
+ _inResize = false;
}
-
- [StandardContainer setFrameSize:NSSize{x,y}];
- [Window setContentSize:NSSize{x, y}];
return S_OK;
}
@@ -562,6 +578,11 @@ public:
return S_OK;
}
+ virtual bool IsDialog()
+ {
+ return false;
+ }
+
protected:
virtual NSWindowStyleMask GetStyle()
{
@@ -592,6 +613,7 @@ private:
NSRect _preZoomSize;
bool _transitioningWindowState;
bool _isClientAreaExtended;
+ bool _isDialog;
AvnExtendClientAreaChromeHints _extendClientHints;
FORWARD_IUNKNOWN()
@@ -652,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();
@@ -690,6 +713,12 @@ private:
if(cparent == nullptr)
return E_INVALIDARG;
+ // If one tries to show a child window with a minimized parent window, then the parent window will be
+ // restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+ // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
+ if (cparent->WindowState() == Minimized)
+ cparent->SetWindowState(Normal);
+
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
UpdateStyle();
@@ -757,6 +786,7 @@ private:
}
_lastWindowState = state;
+ _actualWindowState = state;
WindowEvents->WindowStateChanged(state);
}
}
@@ -1180,6 +1210,7 @@ private:
}
_actualWindowState = _lastWindowState;
+ WindowEvents->WindowStateChanged(_actualWindowState);
}
@@ -1197,6 +1228,11 @@ private:
}
}
+ virtual bool IsDialog() override
+ {
+ return _isDialog;
+ }
+
protected:
virtual NSWindowStyleMask GetStyle() override
{
@@ -1276,6 +1312,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[_blurBehind setWantsLayer:true];
_blurBehind.hidden = true;
+ [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
[self addSubview:_blurBehind];
[self addSubview:_content];
@@ -1311,9 +1350,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_settingSize = true;
[super setFrameSize:newSize];
- [_blurBehind setFrameSize:newSize];
- [_content setFrameSize:newSize];
-
auto window = objc_cast([self window]);
// TODO get actual titlebar size
@@ -1329,6 +1365,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[_titleBarMaterial setFrame:tbar];
tbar.size.height = height < 1 ? 0 : 1;
[_titleBarUnderline setFrame:tbar];
+
_settingSize = false;
}
@@ -1356,6 +1393,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _lastKeyHandled;
AvnPixelSize _lastPixelSize;
NSObject* _renderTarget;
+ AvnPlatformResizeReason _resizeReason;
}
- (void)onClosed
@@ -1467,7 +1505,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);
}
}
@@ -1966,6 +2005,16 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
+- (AvnPlatformResizeReason)getResizeReason
+{
+ return _resizeReason;
+}
+
+- (void)setResizeReason:(AvnPlatformResizeReason)reason
+{
+ _resizeReason = reason;
+}
+
@end
@@ -1985,6 +2034,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_isExtended = value;
}
+-(bool) isDialog
+{
+ return _parent->IsDialog();
+}
+
-(double) getScaling
{
return _lastScaling;
@@ -2014,18 +2068,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
+(void)closeAll
{
- NSArray* windows = [NSArray arrayWithArray:[NSApp windows]];
- auto numWindows = [windows count];
-
- for(int i = 0; i < numWindows; i++)
- {
- auto window = (AvnWindow*)[windows objectAtIndex:i];
-
- if([window parentWindow] == nullptr) // Avalonia will handle the child windows.
- {
- [window performClose:nil];
- }
- }
+ [[NSApplication sharedApplication] terminate:self];
}
- (void)performClose:(id)sender
@@ -2174,7 +2217,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(uch);
+ if(ch == nil)
+ continue;
+ if (ch.isDialog)
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
}
-(BOOL)canBecomeMainWindow
@@ -2182,22 +2240,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return _canBecomeKeyAndMain;
}
--(bool) activateAppropriateChild: (bool)activating
-{
- for(NSWindow* uch in [self childWindows])
- {
- auto ch = objc_cast(uch);
- if(ch == nil)
- continue;
- [ch activateAppropriateChild:false];
- return FALSE;
- }
-
- if(!activating)
- [self makeKeyAndOrderFront:self];
- return TRUE;
-}
-
-(bool)shouldTryToHandleEvents
{
return _isEnabled;
@@ -2208,26 +2250,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];
}
@@ -2237,7 +2268,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
if(parent != nil)
{
[parent removeChildWindow:self];
- [parent activateAppropriateChild: false];
}
}
@@ -2372,13 +2402,14 @@ protected:
return NSWindowStyleMaskBorderless;
}
- virtual HRESULT Resize(double x, double y) override
+ virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
{
+ START_COM_CALL;
+
@autoreleasepool
{
if (Window != nullptr)
{
- [StandardContainer setFrameSize:NSSize{x,y}];
[Window setContentSize:NSSize{x, y}];
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml b/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml
deleted file mode 100644
index f0e079ad91..0000000000
--- a/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
- Context Flyout
- A right click Flyout that can be applied to any control.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml.cs b/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml.cs
deleted file mode 100644
index e64d4a2cdd..0000000000
--- a/samples/ControlCatalog/Pages/ContextFlyoutPage.axaml.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using ControlCatalog.ViewModels;
-using Avalonia.Interactivity;
-namespace ControlCatalog.Pages
-{
- public class ContextFlyoutPage : UserControl
- {
- private TextBox _textBox;
-
- public ContextFlyoutPage()
- {
- InitializeComponent();
-
- var vm = new ContextFlyoutPageViewModel();
- vm.View = this;
- DataContext = vm;
-
- _textBox = this.FindControl("TextBox");
-
- var cutButton = this.FindControl