A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1269 lines
29 KiB

// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
#include "window.h"
#include "KeyTransform.h"
#include "cursor.h"
#include <OpenGL/gl.h>
class SoftwareDrawingOperation
{
public:
void* Data = 0;
AvnFramebuffer Desc;
void Alloc(NSView* view)
{
auto logicalSize = [view frame].size;
auto pixelSize = [view convertSizeToBacking:logicalSize];
int w = pixelSize.width;
int h = pixelSize.height;
int stride = w * 4;
Data = malloc(h * stride);
Desc = {
.Data = Data,
.Stride = stride,
.Width = w,
.Height = h,
.PixelFormat = kAvnRgba8888,
.Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96}
};
}
void Dealloc()
{
if(Data != NULL)
{
free(Data);
Data = NULL;
}
}
~SoftwareDrawingOperation()
{
Dealloc();
}
};
class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
{
private:
NSCursor* cursor;
public:
FORWARD_IUNKNOWN()
virtual ~WindowBaseImpl()
{
NSDebugLog(@"~WindowBaseImpl()");
View = NULL;
Window = NULL;
}
AvnView* View;
AvnWindow* Window;
ComPtr<IAvnWindowBaseEvents> BaseEvents;
SoftwareDrawingOperation CurrentSwDrawingOperation;
AvnPoint lastPositionSet;
NSString* _lastTitle;
WindowBaseImpl(IAvnWindowBaseEvents* events)
{
BaseEvents = events;
View = [[AvnView alloc] initWithParent:this];
Window = [[AvnWindow alloc] initWithParent:this];
lastPositionSet.X = 100;
lastPositionSet.Y = 100;
_lastTitle = @"";
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
[Window setContentView: View];
}
virtual AvnWindow* GetNSWindow() override
{
return Window;
}
virtual HRESULT Show() override
{
@autoreleasepool
{
SetPosition(lastPositionSet);
UpdateStyle();
[Window makeKeyAndOrderFront:Window];
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
return S_OK;
}
}
virtual HRESULT Hide () override
{
@autoreleasepool
{
if(Window != nullptr)
{
[Window orderOut:Window];
[Window restoreParentWindow];
}
return S_OK;
}
}
virtual HRESULT Activate () override
{
@autoreleasepool
{
if(Window != nullptr)
{
[Window makeKeyWindow];
}
}
return S_OK;
}
virtual HRESULT SetTopMost (bool value) override
{
@autoreleasepool
{
[Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel];
return S_OK;
}
}
virtual HRESULT Close() override
{
@autoreleasepool
{
[Window close];
return S_OK;
}
}
virtual HRESULT GetClientSize(AvnSize* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
return E_POINTER;
auto frame = [View frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;
return S_OK;
}
}
virtual HRESULT GetMaxClientSize(AvnSize* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
return E_POINTER;
auto size = [NSScreen.screens objectAtIndex:0].frame.size;
ret->Height = size.height;
ret->Width = size.width;
return S_OK;
}
}
virtual HRESULT GetScaling (double* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
return E_POINTER;
if(Window == nullptr)
{
*ret = 1;
return S_OK;
}
*ret = [Window backingScaleFactor];
return S_OK;
}
}
virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override
{
@autoreleasepool
{
[Window setMinSize: ToNSSize(minSize)];
[Window setMaxSize: ToNSSize(maxSize)];
return S_OK;
}
}
virtual HRESULT Resize(double x, double y) override
{
@autoreleasepool
{
[Window setContentSize:NSSize{x, y}];
return S_OK;
}
}
virtual HRESULT Invalidate (AvnRect rect) override
{
@autoreleasepool
{
[View setNeedsDisplayInRect:[View frame]];
return S_OK;
}
}
virtual bool TryLock() override
{
@autoreleasepool
{
return [View lockFocusIfCanDraw] == YES;
}
}
virtual void Unlock() override
{
@autoreleasepool
{
[View unlockFocus];
}
}
virtual HRESULT BeginMoveDrag () override
{
@autoreleasepool
{
auto lastEvent = [View lastMouseDownEvent];
if(lastEvent == nullptr)
{
return S_OK;
}
[Window performWindowDragWithEvent:lastEvent];
return S_OK;
}
}
virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override
{
return S_OK;
}
virtual HRESULT GetPosition (AvnPoint* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
{
return E_POINTER;
}
auto frame = [Window frame];
ret->X = frame.origin.x;
ret->Y = frame.origin.y + frame.size.height;
*ret = ConvertPointY(*ret);
return S_OK;
}
}
virtual HRESULT SetPosition (AvnPoint point) override
{
@autoreleasepool
{
lastPositionSet = point;
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
return S_OK;
}
}
virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
{
return E_POINTER;
}
point = ConvertPointY(point);
auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)];
*ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
return S_OK;
}
}
virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override
{
@autoreleasepool
{
if(ret == nullptr)
{
return E_POINTER;
}
auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]);
auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint];
*ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
return S_OK;
}
}
virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override
{
[View setSwRenderedFrame: fb dispose: dispose];
return S_OK;
}
virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) override
{
if(![[NSThread currentThread] isMainThread])
return E_FAIL;
if(CurrentSwDrawingOperation.Data == NULL)
CurrentSwDrawingOperation.Alloc(View);
*ret = CurrentSwDrawingOperation.Desc;
return S_OK;
}
virtual HRESULT SetCursor(IAvnCursor* cursor) override
{
@autoreleasepool
{
Cursor* avnCursor = dynamic_cast<Cursor*>(cursor);
this->cursor = avnCursor->GetNative();
UpdateCursor();
return S_OK;
}
}
virtual void UpdateCursor()
{
[View resetCursorRects];
if (cursor != nil)
{
auto rect = [Window frame];
[View addCursorRect:rect cursor:cursor];
[cursor set];
}
}
virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override
{
if(View == NULL)
return E_FAIL;
*ppv = ::CreateGlRenderTarget(Window, View);
return S_OK;
}
protected:
virtual NSWindowStyleMask GetStyle()
{
return NSWindowStyleMaskBorderless;
}
void UpdateStyle()
{
[Window setStyleMask:GetStyle()];
}
virtual void OnResized ()
{
}
};
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{
private:
bool _canResize = true;
bool _hasDecorations = true;
CGRect _lastUndecoratedFrame;
AvnWindowState _lastWindowState;
FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP()
INHERIT_INTERFACE_MAP(WindowBaseImpl)
INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
END_INTERFACE_MAP()
virtual ~WindowImpl(){
NSDebugLog(@"~WindowImpl");
}
ComPtr<IAvnWindowEvents> WindowEvents;
WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
{
WindowEvents = events;
[Window setCanBecomeKeyAndMain];
}
virtual HRESULT Show () override
{
@autoreleasepool
{
if([Window parentWindow] != nil)
[[Window parentWindow] removeChildWindow:Window];
WindowBaseImpl::Show();
return SetWindowState(_lastWindowState);
}
}
virtual HRESULT ShowDialog (IAvnWindow* parent) override
{
@autoreleasepool
{
if(parent == nullptr)
return E_POINTER;
auto cparent = dynamic_cast<WindowImpl*>(parent);
if(cparent == nullptr)
return E_INVALIDARG;
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
WindowBaseImpl::Show();
return S_OK;
}
}
void WindowStateChanged () override
{
AvnWindowState state;
GetWindowState(&state);
WindowEvents->WindowStateChanged(state);
}
bool UndecoratedIsMaximized ()
{
return CGRectEqualToRect([Window frame], [Window screen].visibleFrame);
}
bool IsZoomed ()
{
return _hasDecorations ? [Window isZoomed] : UndecoratedIsMaximized();
}
void DoZoom()
{
if (_hasDecorations)
{
[Window performZoom:Window];
}
else
{
if (!UndecoratedIsMaximized())
{
_lastUndecoratedFrame = [Window frame];
}
[Window zoom:Window];
}
}
virtual HRESULT SetCanResize(bool value) override
{
@autoreleasepool
{
_canResize = value;
UpdateStyle();
return S_OK;
}
}
virtual HRESULT SetHasDecorations(bool value) override
{
@autoreleasepool
{
_hasDecorations = value;
UpdateStyle();
return S_OK;
}
}
virtual HRESULT SetTitle (void* utf8title) override
{
@autoreleasepool
{
_lastTitle = [NSString stringWithUTF8String:(const char*)utf8title];
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
return S_OK;
}
}
virtual HRESULT SetTitleBarColor(AvnColor color) override
{
@autoreleasepool
{
float a = (float)color.Alpha / 255.0f;
float r = (float)color.Red / 255.0f;
float g = (float)color.Green / 255.0f;
float b = (float)color.Blue / 255.0f;
auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
// Based on the titlebar color we have to choose either light or dark
// OSX doesnt let you set a foreground color for titlebar.
if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f)
{
[Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
}
else
{
[Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
}
[Window setTitlebarAppearsTransparent:true];
[Window setBackgroundColor:nscolor];
}
return S_OK;
}
virtual HRESULT GetWindowState (AvnWindowState*ret) override
{
@autoreleasepool
{
if(ret == nullptr)
{
return E_POINTER;
}
if([Window isMiniaturized])
{
*ret = Minimized;
return S_OK;
}
if([Window isZoomed])
{
*ret = Maximized;
return S_OK;
}
*ret = Normal;
return S_OK;
}
}
virtual HRESULT SetWindowState (AvnWindowState state) override
{
@autoreleasepool
{
_lastWindowState = state;
switch (state) {
case Maximized:
lastPositionSet.X = 0;
lastPositionSet.Y = 0;
if([Window isMiniaturized])
{
[Window deminiaturize:Window];
}
if(!IsZoomed())
{
DoZoom();
}
break;
case Minimized:
[Window miniaturize:Window];
break;
default:
if([Window isMiniaturized])
{
[Window deminiaturize:Window];
}
if(IsZoomed())
{
DoZoom();
}
break;
}
return S_OK;
}
}
protected:
virtual void OnResized () override
{
auto windowState = [Window isMiniaturized] ? Minimized
: (IsZoomed() ? Maximized : Normal);
if (windowState != _lastWindowState)
{
_lastWindowState = windowState;
WindowEvents->WindowStateChanged(windowState);
}
}
virtual NSWindowStyleMask GetStyle() override
{
unsigned long s = NSWindowStyleMaskBorderless;
if(_hasDecorations)
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
if(_canResize)
s = s | NSWindowStyleMaskResizable;
return s;
}
};
NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
@implementation AvnView
{
ComPtr<WindowBaseImpl> _parent;
ComPtr<IUnknown> _swRenderedFrame;
AvnFramebuffer _swRenderedFrameBuffer;
bool _queuedDisplayFromThread;
NSTrackingArea* _area;
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver;
NSEvent* _lastMouseDownEvent;
bool _lastKeyHandled;
}
- (void)dealloc
{
NSDebugLog(@"AvnView dealloc");
}
- (void)onClosed
{
_parent = NULL;
}
- (NSEvent*) lastMouseDownEvent
{
return _lastMouseDownEvent;
}
-(AvnView*) initWithParent: (WindowBaseImpl*) parent
{
self = [super init];
[self setWantsBestResolutionOpenGLSurface:true];
_parent = parent;
_area = nullptr;
return self;
}
- (BOOL)isOpaque
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return true;
}
- (BOOL)acceptsFirstMouse:(NSEvent *)event
{
return true;
}
- (BOOL)canBecomeKeyView
{
return true;
}
-(void)setFrameSize:(NSSize)newSize
{
[super setFrameSize:newSize];
if(_area != nullptr)
{
[self removeTrackingArea:_area];
_area = nullptr;
}
NSRect rect = NSZeroRect;
rect.size = newSize;
NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingEnabledDuringMouseDrag;
_area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr];
[self addTrackingArea:_area];
_parent->UpdateCursor();
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
}
- (void) drawFb: (AvnFramebuffer*) fb
{
auto colorSpace = CGColorSpaceCreateDeviceRGB();
auto dataProvider = CGDataProviderCreateWithData(NULL, fb->Data, fb->Height*fb->Stride, NULL);
auto image = CGImageCreate(fb->Width, fb->Height, 8, 32, fb->Stride, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast,
dataProvider, nullptr, false, kCGRenderingIntentDefault);
auto ctx = [NSGraphicsContext currentContext];
[ctx saveGraphicsState];
auto cgc = [ctx CGContext];
CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image);
CGImageRelease(image);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(dataProvider);
[ctx restoreGraphicsState];
}
- (void)drawRect:(NSRect)dirtyRect
{
_parent->BaseEvents->RunRenderPriorityJobs();
@synchronized (self) {
if(_swRenderedFrame != NULL)
{
[self drawFb: &_swRenderedFrameBuffer];
return;
}
}
auto swOp = &_parent->CurrentSwDrawingOperation;
_parent->BaseEvents->Paint();
if(swOp->Data != NULL)
[self drawFb: &swOp->Desc];
swOp->Dealloc();
return;
}
-(void) redrawSelf
{
@autoreleasepool
{
@synchronized(self)
{
if(!_queuedDisplayFromThread)
return;
_queuedDisplayFromThread = false;
}
[self setNeedsDisplayInRect:[self frame]];
[self display];
}
}
-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
{
@autoreleasepool {
@synchronized (self) {
_swRenderedFrame = dispose;
_swRenderedFrameBuffer = *fb;
if(!_queuedDisplayFromThread)
{
_queuedDisplayFromThread = true;
[self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes];
}
}
}
}
- (AvnPoint) translateLocalPoint:(AvnPoint)pt
{
pt.Y = [self bounds].size.height - pt.Y;
return pt;
}
- (AvnPoint)toAvnPoint:(CGPoint)p
{
AvnPoint result;
result.X = p.x;
result.Y = p.y;
return result;
}
- (void) viewDidChangeBackingProperties
{
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
[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
{
if([self ignoreUserInput])
return;
[self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
auto avnPoint = [self toAvnPoint:localPoint];
auto point = [self translateLocalPoint:avnPoint];
AvnVector delta;
if(type == Wheel)
{
delta.X = [event scrollingDeltaX] / 5;
delta.Y = [event scrollingDeltaY] / 5;
if(delta.X == 0 && delta.Y == 0)
{
return;
}
}
auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]];
[self becomeFirstResponder];
_parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
[super mouseMoved:event];
}
- (void)mouseMoved:(NSEvent *)event
{
[self mouseEvent:event withType:Move];
}
- (void)mouseDown:(NSEvent *)event
{
_isLeftPressed = true;
_lastMouseDownEvent = event;
[self mouseEvent:event withType:LeftButtonDown];
}
- (void)otherMouseDown:(NSEvent *)event
{
_isMiddlePressed = true;
_lastMouseDownEvent = event;
[self mouseEvent:event withType:MiddleButtonDown];
}
- (void)rightMouseDown:(NSEvent *)event
{
_isRightPressed = true;
_lastMouseDownEvent = event;
[self mouseEvent:event withType:RightButtonDown];
}
- (void)mouseUp:(NSEvent *)event
{
_isLeftPressed = false;
[self mouseEvent:event withType:LeftButtonUp];
}
- (void)otherMouseUp:(NSEvent *)event
{
_isMiddlePressed = false;
[self mouseEvent:event withType:MiddleButtonUp];
}
- (void)rightMouseUp:(NSEvent *)event
{
_isRightPressed = false;
[self mouseEvent:event withType:RightButtonUp];
}
- (void)mouseDragged:(NSEvent *)event
{
[self mouseEvent:event withType:Move];
[super mouseDragged:event];
}
- (void)otherMouseDragged:(NSEvent *)event
{
[self mouseEvent:event withType:Move];
[super otherMouseDragged:event];
}
- (void)rightMouseDragged:(NSEvent *)event
{
[self mouseEvent:event withType:Move];
[super rightMouseDragged:event];
}
- (void)scrollWheel:(NSEvent *)event
{
[self mouseEvent:event withType:Wheel];
[super scrollWheel:event];
}
- (void)mouseEntered:(NSEvent *)event
{
_isMouseOver = true;
[super mouseEntered:event];
}
- (void)mouseExited:(NSEvent *)event
{
_isMouseOver = false;
[self mouseEvent:event withType:LeaveWindow];
[super mouseExited:event];
}
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
{
if([self ignoreUserInput])
return;
auto key = s_KeyMap[[event keyCode]];
auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]];
_lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
}
- (BOOL)performKeyEquivalent:(NSEvent *)event
{
bool result = _lastKeyHandled;
_lastKeyHandled = false;
return result;
}
- (void)keyDown:(NSEvent *)event
{
[self keyboardEvent:event withType:KeyDown];
[[self inputContext] handleEvent:event];
[super keyDown:event];
}
- (void)keyUp:(NSEvent *)event
{
[self keyboardEvent:event withType:KeyUp];
[super keyUp:event];
}
- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
{
unsigned int rv = 0;
if (mod & NSEventModifierFlagControl)
rv |= Control;
if (mod & NSEventModifierFlagShift)
rv |= Shift;
if (mod & NSEventModifierFlagOption)
rv |= Alt;
if (mod & NSEventModifierFlagCommand)
rv |= Windows;
if (_isLeftPressed)
rv |= LeftMouseButton;
if (_isMiddlePressed)
rv |= MiddleMouseButton;
if (_isRightPressed)
rv |= RightMouseButton;
return (AvnInputModifiers)rv;
}
- (BOOL)hasMarkedText
{
return _lastKeyHandled;
}
- (NSRange)markedRange
{
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)selectedRange
{
return NSMakeRange(NSNotFound, 0);
}
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
}
- (void)unmarkText
{
}
- (NSArray<NSString *> *)validAttributesForMarkedText
{
return [NSArray new];
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
return [NSAttributedString new];
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
if(!_lastKeyHandled)
{
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
}
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point
{
return 0;
}
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
CGRect result;
return result;
}
@end
@implementation AvnWindow
{
ComPtr<WindowBaseImpl> _parent;
bool _canBecomeKeyAndMain;
bool _closed;
}
- (void)dealloc
{
NSDebugLog(@"AvnWindow dealloc");
}
- (void)pollModalSession:(nonnull NSModalSession)session
{
auto response = [NSApp runModalSession:session];
if(response == NSModalResponseContinue)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self pollModalSession:session];
});
}
else if (!_closed)
{
[self orderOut:self];
[NSApp endModalSession:session];
}
}
-(void) setCanBecomeKeyAndMain
{
_canBecomeKeyAndMain = true;
}
-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent
{
self = [super init];
[self setReleasedWhenClosed:false];
_parent = parent;
[self setDelegate:self];
return self;
}
- (BOOL)windowShouldClose:(NSWindow *)sender
{
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
if(window != nullptr)
{
return !window->WindowEvents->Closing();
}
return true;
}
- (void)windowWillClose:(NSNotification *)notification
{
_closed = true;
if(_parent)
{
ComPtr<WindowBaseImpl> parent = _parent;
_parent = NULL;
[self restoreParentWindow];
parent->BaseEvents->Closed();
[parent->View onClosed];
dispatch_async(dispatch_get_main_queue(), ^{
[self setContentView: nil];
});
}
}
-(BOOL)canBecomeKeyWindow
{
return _canBecomeKeyAndMain;
}
-(BOOL)canBecomeMainWindow
{
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
{
if([self activateAppropriateChild: true])
{
_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
{
return true;
}
-(void)resignKeyWindow
{
if(_parent)
_parent->BaseEvents->Deactivated();
[super resignKeyWindow];
}
- (void)windowDidMove:(NSNotification *)notification
{
AvnPoint position;
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position);
}
// TODO this breaks resizing.
/*- (void)windowDidResize:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}*/
@end
class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
{
private:
BEGIN_INTERFACE_MAP()
INHERIT_INTERFACE_MAP(WindowBaseImpl)
INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup)
END_INTERFACE_MAP()
virtual ~PopupImpl(){}
ComPtr<IAvnWindowEvents> WindowEvents;
PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
{
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
}
protected:
virtual NSWindowStyleMask GetStyle() override
{
return NSWindowStyleMaskBorderless;
}
virtual HRESULT Resize(double x, double y) override
{
@autoreleasepool
{
[Window setContentSize:NSSize{x, y}];
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
return S_OK;
}
}
};
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events)
{
@autoreleasepool
{
IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events));
return ptr;
}
}
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
{
@autoreleasepool
{
IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
return ptr;
}
}