Selaa lähdekoodia

Merge branch 'master' into features/keyboard-input

danwalmsley 7 vuotta sitten
vanhempi
sitoutus
6ebd22530e

+ 4 - 0
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -10,6 +10,7 @@
 		37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
 		37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
 		37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
+		5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
 		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
 		AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
 		AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
@@ -23,6 +24,7 @@
 		37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = "<group>"; };
 		37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = "<group>"; };
 		37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
+		5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
 		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
 		AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
 		AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = "<group>"; };
@@ -54,6 +56,7 @@
 		AB7A61E62147C814003C5833 = {
 			isa = PBXGroup;
 			children = (
+				5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
 				379A4506214D0F6500CC143D /* headers */,
 				AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
 				AB661C212148288600291242 /* common.h */,
@@ -144,6 +147,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */,
 				AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
 				37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
 				37A517B32159597E00FBA241 /* Screens.mm in Sources */,

+ 37 - 0
src/Avalonia.Native.OSX/clipboard.mm

@@ -0,0 +1,37 @@
+#include "common.h"
+
+class Clipboard : public ComSingleObject<IAvnClipboard, &IID_IAvnClipboard>
+{
+public:
+    virtual HRESULT GetText (void** retOut)
+    {
+        @autoreleasepool {
+            NSString *str = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
+            *retOut = (void *)str.UTF8String;
+        }
+        return S_OK;
+    }
+    
+    virtual HRESULT SetText (char* text)
+    {
+        @autoreleasepool {
+            NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
+            [pasteBoard clearContents];
+            [pasteBoard setString:@(text) forType:NSPasteboardTypeString];
+        }
+        return S_OK;
+    }
+
+    virtual HRESULT Clear()
+    {
+        @autoreleasepool {
+            [[NSPasteboard generalPasteboard] clearContents];
+        }
+        return S_OK;
+    }
+};
+
+extern IAvnClipboard* CreateClipboard()
+{
+    return new Clipboard();
+}

+ 1 - 0
src/Avalonia.Native.OSX/common.h

@@ -12,6 +12,7 @@ extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
 extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
 extern IAvnSystemDialogs* CreateSystemDialogs();
 extern IAvnScreens* CreateScreens();
+extern IAvnClipboard* CreateClipboard();
 
 extern NSPoint ToNSPoint (AvnPoint p);
 extern AvnPoint ToAvnPoint (NSPoint p);

+ 7 - 2
src/Avalonia.Native.OSX/main.mm

@@ -101,11 +101,17 @@ public:
         return  S_OK;
     }
     
-     virtual HRESULT CreateScreens (IAvnScreens** ppv)
+    virtual HRESULT CreateScreens (IAvnScreens** ppv)
     {
         *ppv = ::CreateScreens ();
         return S_OK;
     }
+
+    virtual HRESULT CreateClipboard(IAvnClipboard** ppv)
+    {
+        *ppv = ::CreateClipboard ();
+        return S_OK;
+    }
 };
 
 extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
@@ -113,7 +119,6 @@ extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
     return new AvaloniaNative();
 };
 
-
 NSPoint ToNSPoint (AvnPoint p)
 {
     @autoreleasepool

+ 5 - 0
src/Avalonia.Native.OSX/window.h

@@ -28,4 +28,9 @@ struct INSWindowHolder
     virtual AvnWindow* GetNSWindow () = 0;
 };
 
+struct IWindowStateChanged
+{
+    virtual void WindowStateChanged () = 0;
+};
+
 #endif /* window_h */

+ 148 - 5
src/Avalonia.Native.OSX/window.mm

@@ -45,6 +45,16 @@ public:
         return S_OK;
     }
     
+    virtual HRESULT Activate ()
+    {
+        if(Window != nullptr)
+        {
+            [Window makeKeyWindow];
+        }
+        
+        return S_OK;
+    }
+    
     virtual HRESULT SetTopMost (bool value)
     {
         [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel];
@@ -188,6 +198,11 @@ protected:
     {
         [Window setStyleMask:GetStyle()];
     }
+    
+    virtual void OnResized ()
+    {
+        
+    }
 };
 
 NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
@@ -245,10 +260,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
 - (void) drawFb: (AvnFramebuffer*) fb
 {
     auto colorSpace = CGColorSpaceCreateDeviceRGB();
-    auto bctx = CGBitmapContextCreate(fb->Data, fb->Width, fb->Height, 8, fb->Stride, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
-    auto image = CGBitmapContextCreateImage(bctx);
-    CGContextRelease(bctx);
-    CGColorSpaceRelease(colorSpace);
+    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];
     
@@ -257,6 +273,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     
     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];
 
@@ -617,6 +635,24 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     [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 WindowBaseImpl, public IAvnPopup
@@ -646,11 +682,13 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events)
     return ptr;
 }
 
-class WindowImpl : public WindowBaseImpl, public IAvnWindow
+class WindowImpl : public WindowBaseImpl, public IAvnWindow, public IWindowStateChanged
 {
 private:
     bool _canResize = true;
     bool _hasDecorations = true;
+    CGRect _lastUndecoratedFrame;
+    AvnWindowState _lastWindowState;
     
     BEGIN_INTERFACE_MAP()
     INHERIT_INTERFACE_MAP(WindowBaseImpl)
@@ -663,6 +701,39 @@ private:
         [Window setCanBecomeKeyAndMain];
     }
     
+    void WindowStateChanged ()
+    {
+        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)
     {
@@ -678,7 +749,79 @@ private:
         return S_OK;
     }
     
+    virtual HRESULT GetWindowState (AvnWindowState*ret)
+    {
+        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)
+    {
+        switch (state) {
+            case Maximized:
+                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 ()
+    {
+        auto windowState = [Window isMiniaturized] ? Minimized
+        : (IsZoomed() ? Maximized : Normal);
+        
+        if (windowState != _lastWindowState)
+        {
+            _lastWindowState = windowState;
+            
+            WindowEvents->WindowStateChanged(windowState);
+        }
+    }
+    
     virtual NSWindowStyleMask GetStyle()
     {
         unsigned long s = NSWindowStyleMaskBorderless;

BIN
src/Avalonia.Native/.Stubs.cs.swp


+ 1 - 1
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -70,7 +70,7 @@ namespace Avalonia.Native
                 .Bind<IMouseDevice>().ToConstant(MouseDevice)
                 .Bind<IPlatformSettings>().ToConstant(this)
                 .Bind<IWindowingPlatform>().ToConstant(this)
-                .Bind<IClipboard>().ToSingleton<ClipboardImpl>()
+                .Bind<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
                 .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))

+ 1 - 0
src/Avalonia.Native/Mappings.xml

@@ -23,5 +23,6 @@
         <map interface="*.Callback" callback="true" autogen-shadow="true"/>
         <map param=".*::.*::ppv" return="true"/>
         <map param=".*::.*::ret" return="true"/>
+        <map param=".*::.*::retOut" attribute="out" return="true"/>
     </mapping>
 </config>

+ 4 - 0
src/Avalonia.Native/PopupImpl.cs

@@ -28,6 +28,10 @@ namespace Avalonia.Native
             {
                 _parent = parent;
             }
+
+            void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
+            {
+            }
         }
     }
 }

+ 16 - 1
src/Avalonia.Native/Stubs.cs

@@ -1,27 +1,42 @@
 using System;
 using System.IO;
 using System.Threading.Tasks;
+using System.Runtime.InteropServices;
 using Avalonia.Controls;
 using Avalonia.Controls.Platform;
 using Avalonia.Input.Platform;
 using Avalonia.Platform;
+using Avalonia.Native.Interop;
 
 namespace Avalonia.Native
 { 
     class ClipboardImpl : IClipboard
     {
+        IAvnClipboard _native;
+
+        public ClipboardImpl(IAvnClipboard native)
+        {
+            _native = native;
+        }
+
         public Task ClearAsync()
         {
+            _native.Clear();
             return Task.CompletedTask;
         }
 
         public Task<string> GetTextAsync()
         {
-            return Task.FromResult<string>(null);
+            var outPtr = _native.GetText();
+            var text = Marshal.PtrToStringAnsi(outPtr);
+            return Task.FromResult(text);
         }
 
         public Task SetTextAsync(string text)
         {
+            _native.Clear();
+            if(text != null)
+                _native.SetText(text);
             return Task.CompletedTask;
         }
     }

+ 17 - 1
src/Avalonia.Native/WindowImpl.cs

@@ -22,6 +22,11 @@ namespace Avalonia.Native
             {
                 _parent = parent;
             }
+
+            void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
+            {
+                _parent.WindowStateChanged?.Invoke((WindowState)state);
+            }
         }
 
         public IAvnWindow Native => _native;
@@ -46,7 +51,18 @@ namespace Avalonia.Native
         {
         }
 
-        public WindowState WindowState { get; set; } = WindowState.Normal;
+        public WindowState WindowState
+        {
+            get
+            {
+                return (WindowState)_native.GetWindowState();
+            }
+            set
+            {
+                _native.SetWindowState((AvnWindowState)value);
+            }
+        }
+
         public Action<WindowState> WindowStateChanged { get; set; }
 
         public void ShowTaskbarIcon(bool value)

+ 10 - 2
src/Avalonia.Native/WindowImplBase.cs

@@ -20,6 +20,7 @@ namespace Avalonia.Native
         private readonly IMouseDevice _mouse;
         private readonly IKeyboardDevice _keyboard;
         private Size _savedLogicalSize;
+        private Size _lastRenderedLogicalSize;
         private double _savedScaling;
 
         public WindowBaseImpl()
@@ -62,6 +63,7 @@ namespace Avalonia.Native
                         if (_native == null)
                             return false;
                         cb(_native);
+                        _lastRenderedLogicalSize = _savedLogicalSize;
                         return true;
                     }
                 }, (int)w, (int)h, new Vector(dpi, dpi));
@@ -133,6 +135,11 @@ namespace Avalonia.Native
                 _parent.Resized?.Invoke(s);
             }
 
+            void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
+            {
+                _parent.PositionChanged?.Invoke(position.ToAvaloniaPoint());
+            }
+
             void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
             {
                 _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
@@ -151,7 +158,8 @@ namespace Avalonia.Native
 
             void IAvnWindowBaseEvents.RunRenderPriorityJobs()
             {
-                if (_parent._deferredRendering)
+                if (_parent._deferredRendering 
+                    && _parent._lastRenderedLogicalSize != _parent.ClientSize)
                     // Hack to trigger Paint event on the renderer
                     _parent.Paint?.Invoke(new Rect());
                 Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
@@ -160,7 +168,7 @@ namespace Avalonia.Native
 
         public void Activate()
         {
-        
+            _native.Activate();
         }
 
         public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)

+ 21 - 2
src/headers/avalonia-native.h

@@ -11,6 +11,7 @@ struct IAvnPlatformThreadingInterface;
 struct IAvnSystemDialogEvents;
 struct IAvnSystemDialogs;
 struct IAvnScreens;
+struct IAvnClipboard;
 
 struct AvnSize
 {
@@ -88,6 +89,13 @@ enum AvnInputModifiers
     MiddleMouseButton = 64
 };
 
+enum AvnWindowState
+{
+    Normal,
+    Minimized,
+    Maximized,
+};
+
 AVNCOM(IAvaloniaNativeFactory, 01) : virtual IUnknown
 {
 public:
@@ -98,6 +106,7 @@ public:
     virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0;
     virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
     virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
+    virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
 };
 
 AVNCOM(IAvnWindowBase, 02) : virtual IUnknown
@@ -105,6 +114,7 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown
     virtual HRESULT Show() = 0;
     virtual HRESULT Hide () = 0;
     virtual HRESULT Close() = 0;
+    virtual HRESULT Activate () = 0;
     virtual HRESULT GetClientSize(AvnSize*ret) = 0;
     virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0;
     virtual HRESULT GetScaling(double*ret)=0;
@@ -128,6 +138,8 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
 {
     virtual HRESULT SetCanResize(bool value) = 0;
     virtual HRESULT SetHasDecorations(bool value) = 0;
+    virtual HRESULT SetWindowState(AvnWindowState state) = 0;
+    virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
 };
 
 AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
@@ -137,6 +149,7 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
     virtual void Activated() = 0;
     virtual void Deactivated() = 0;
     virtual void Resized(const AvnSize& size) = 0;
+    virtual void PositionChanged (AvnPoint position) = 0;
     virtual void RawMouseEvent (AvnRawMouseEventType type,
                                 unsigned int timeStamp,
                                 AvnInputModifiers modifiers,
@@ -150,7 +163,7 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
 
 AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
 {
-
+    virtual void WindowStateChanged (AvnWindowState state) = 0;
 };
 
 AVNCOM(IAvnMacOptions, 07) : virtual IUnknown
@@ -217,7 +230,13 @@ AVNCOM(IAvnScreens, 0e) : virtual IUnknown
 {
     virtual HRESULT GetScreenCount (int* ret) = 0;
     virtual HRESULT GetScreen (int index, AvnScreen* ret) = 0;
-    
+};
+
+AVNCOM(IAvnClipboard, 0f) : virtual IUnknown
+{
+    virtual HRESULT GetText (void** retOut) = 0;
+    virtual HRESULT SetText (char* text) = 0;
+    virtual HRESULT Clear() = 0;
 };
 
 extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();