Browse Source

[OSX] Parent-modal dialogs

pull/2091/head
Nikita Tsukanov 7 years ago
parent
commit
986d2e4d84
  1. 2
      native/Avalonia.Native/inc/avalonia-native.h
  2. 9
      native/Avalonia.Native/src/OSX/common.h
  3. 2
      native/Avalonia.Native/src/OSX/window.h
  4. 121
      native/Avalonia.Native/src/OSX/window.mm
  5. 1
      samples/ControlCatalog/Pages/DialogsPage.xaml
  6. 6
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  7. 2
      src/Avalonia.Native/WindowImpl.cs

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

@ -207,7 +207,7 @@ AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
{ {
virtual HRESULT ShowDialog (IUnknown**ppv) = 0; virtual HRESULT ShowDialog (IAvnWindow* parent) = 0;
virtual HRESULT SetCanResize(bool value) = 0; virtual HRESULT SetCanResize(bool value) = 0;
virtual HRESULT SetHasDecorations(bool value) = 0; virtual HRESULT SetHasDecorations(bool value) = 0;
virtual HRESULT SetTitle (const char* title) = 0; virtual HRESULT SetTitle (const char* title) = 0;

9
native/Avalonia.Native/src/OSX/common.h

@ -31,4 +31,13 @@ extern NSSize ToNSSize (AvnSize s);
#define NSDebugLog(...) (void)0 #define NSDebugLog(...) (void)0
#endif #endif
template<typename T> inline T* objc_cast(id from) {
if(from == nil)
return nil;
if ([from isKindOfClass:[T class]]) {
return static_cast<T*>(from);
}
return nil;
}
#endif #endif

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

@ -18,6 +18,8 @@ class WindowBaseImpl;
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(void) setCanBecomeKeyAndMain; -(void) setCanBecomeKeyAndMain;
-(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents;
@end @end
struct INSWindowHolder struct INSWindowHolder

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

@ -109,6 +109,7 @@ public:
if(Window != nullptr) if(Window != nullptr)
{ {
[Window orderOut:Window]; [Window orderOut:Window];
[Window restoreParentWindow];
} }
return S_OK; return S_OK;
@ -392,30 +393,6 @@ protected:
} }
}; };
class ModalDisposable : public ComUnknownObject
{
NSModalSession _session;
AvnWindow* _window;
void Dispose ()
{
[_window orderOut:_window];
[NSApp endModalSession:_session];
}
public:
ModalDisposable(AvnWindow* window, NSModalSession session)
{
_session = session;
_window = window;
}
virtual ~ModalDisposable()
{
Dispose();
}
};
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{ {
private: private:
@ -444,32 +421,27 @@ private:
{ {
@autoreleasepool @autoreleasepool
{ {
if([Window parentWindow] != nil)
[[Window parentWindow] removeChildWindow:Window];
WindowBaseImpl::Show(); WindowBaseImpl::Show();
return SetWindowState(_lastWindowState); return SetWindowState(_lastWindowState);
} }
} }
virtual HRESULT ShowDialog (IUnknown**ppv) override virtual HRESULT ShowDialog (IAvnWindow* parent) override
{ {
@autoreleasepool @autoreleasepool
{ {
if(ppv == nullptr) if(parent == nullptr)
{
return E_POINTER; return E_POINTER;
}
auto cparent = dynamic_cast<WindowImpl*>(parent);
auto session = [NSApp beginModalSessionForWindow:Window]; if(cparent == nullptr)
auto disposable = new ModalDisposable(Window, session); return E_INVALIDARG;
*ppv = disposable;
SetPosition(lastPositionSet);
UpdateStyle();
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
[Window pollModalSession:session]; [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
WindowBaseImpl::Show();
return S_OK; return S_OK;
} }
@ -843,8 +815,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[super viewDidChangeBackingProperties]; [super viewDidChangeBackingProperties];
} }
- (bool) ignoreUserInput
{
auto parentWindow = objc_cast<AvnWindow>([self window]);
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
return TRUE;
return FALSE;
}
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
{ {
if([self ignoreUserInput])
return;
[self becomeFirstResponder]; [self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
auto avnPoint = [self toAvnPoint:localPoint]; auto avnPoint = [self toAvnPoint:localPoint];
@ -952,7 +935,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
} }
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
{ {
if([self ignoreUserInput])
return;
auto key = s_KeyMap[[event keyCode]]; auto key = s_KeyMap[[event keyCode]];
auto timestamp = [event timestamp] * 1000; auto timestamp = [event timestamp] * 1000;
@ -1121,13 +1106,13 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{ {
ComPtr<WindowBaseImpl> parent = _parent; ComPtr<WindowBaseImpl> parent = _parent;
_parent = NULL; _parent = NULL;
[self restoreParentWindow];
parent->BaseEvents->Closed(); parent->BaseEvents->Closed();
[parent->View onClosed]; [parent->View onClosed];
[self setContentView: nil]; [self setContentView: nil];
} }
} }
-(BOOL)canBecomeKeyWindow -(BOOL)canBecomeKeyWindow
{ {
return _canBecomeKeyAndMain; return _canBecomeKeyAndMain;
@ -1138,12 +1123,62 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return _canBecomeKeyAndMain; 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
{
for(NSWindow* uch in [self childWindows])
{
auto ch = objc_cast<AvnWindow>(uch);
if(ch == nil)
continue;
return FALSE;
}
return TRUE;
}
-(void)makeKeyWindow
{
if([self activateAppropriateChild: true])
{
[super makeKeyWindow];
}
}
-(void)becomeKeyWindow -(void)becomeKeyWindow
{ {
_parent->BaseEvents->Activated(); if([self activateAppropriateChild: true])
[super becomeKeyWindow]; {
_parent->BaseEvents->Activated();
[super becomeKeyWindow];
}
} }
-(void) restoreParentWindow;
{
auto parent = objc_cast<AvnWindow>([self parentWindow]);
if(parent != nil)
{
[parent removeChildWindow:self];
[parent activateAppropriateChild: false];
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
{ {
return true; return true;

1
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -4,5 +4,6 @@
<Button Name="SaveFile">Save File</Button> <Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button> <Button Name="SelectFolder">Select Folder</Button>
<Button Name="DecoratedWindow">Decorated window</Button> <Button Name="DecoratedWindow">Decorated window</Button>
<Button Name="Dialog">Dialog</Button>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -34,6 +34,12 @@ namespace ControlCatalog.Pages
{ {
new DecoratedWindow().ShowDialog(GetWindow()); new DecoratedWindow().ShowDialog(GetWindow());
}; };
this.FindControl<Button>("Dialog").Click += delegate
{
new MainWindow().ShowDialog(GetWindow());
};
} }
Window GetWindow() => (Window)this.VisualRoot; Window GetWindow() => (Window)this.VisualRoot;

2
src/Avalonia.Native/WindowImpl.cs

@ -48,7 +48,7 @@ namespace Avalonia.Native
public void ShowDialog(IWindowImpl window) public void ShowDialog(IWindowImpl window)
{ {
_native.ShowDialog(); _native.ShowDialog(((WindowImpl)window).Native);
} }
public void CanResize(bool value) public void CanResize(bool value)

Loading…
Cancel
Save