From 33daa820eb99c2e4abc113a9225c4c6c5670f4b0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2017 13:01:09 +0300 Subject: [PATCH 01/14] Initial implementation of MonoMac backend --- .../Avalonia.MonoMac/Avalonia.MonoMac.csproj | 11 + src/OSX/Avalonia.MonoMac/Cursor.cs | 61 +++ .../Avalonia.MonoMac/EmulatedFramebuffer.cs | 74 ++++ src/OSX/Avalonia.MonoMac/Helpers.cs | 33 ++ src/OSX/Avalonia.MonoMac/KeyTransform.cs | 266 +++++++++++++ src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs | 77 ++++ .../PlatformThreadingInterface.cs | 65 ++++ src/OSX/Avalonia.MonoMac/PopupImpl.cs | 19 + src/OSX/Avalonia.MonoMac/Stubs.cs | 51 +++ src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 368 ++++++++++++++++++ src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs | 168 ++++++++ src/OSX/Avalonia.MonoMac/WindowImpl.cs | 92 +++++ 12 files changed, 1285 insertions(+) create mode 100644 src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj create mode 100644 src/OSX/Avalonia.MonoMac/Cursor.cs create mode 100644 src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs create mode 100644 src/OSX/Avalonia.MonoMac/Helpers.cs create mode 100644 src/OSX/Avalonia.MonoMac/KeyTransform.cs create mode 100644 src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs create mode 100644 src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs create mode 100644 src/OSX/Avalonia.MonoMac/PopupImpl.cs create mode 100644 src/OSX/Avalonia.MonoMac/Stubs.cs create mode 100644 src/OSX/Avalonia.MonoMac/TopLevelImpl.cs create mode 100644 src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs create mode 100644 src/OSX/Avalonia.MonoMac/WindowImpl.cs diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj new file mode 100644 index 0000000000..8ae7dc8470 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -0,0 +1,11 @@ + + + netcoreapp2.0;netstandard2.0;net461 + True + + + + + + + diff --git a/src/OSX/Avalonia.MonoMac/Cursor.cs b/src/OSX/Avalonia.MonoMac/Cursor.cs new file mode 100644 index 0000000000..8f5f2c2063 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/Cursor.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using Avalonia.Input; +using Avalonia.Platform; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + class Cursor : IPlatformHandle + { + + public NSCursor Native { get; } + + public IntPtr Handle => Native.Handle; + + public string HandleDescriptor => "NSCursor"; + + public Cursor(NSCursor native) + { + Native = native; + } + } + + class CursorFactoryStub : IStandardCursorFactory + { + Dictionary _cache; + public CursorFactoryStub() + { + //TODO: Load diagonal cursors from webkit + //See https://stackoverflow.com/q/10733228 + _cache = new Dictionary() + { + [StandardCursorType.Arrow] = NSCursor.ArrowCursor, + [StandardCursorType.AppStarting] = NSCursor.ArrowCursor, //TODO + [StandardCursorType.BottomLeftCorner] = NSCursor.CrosshairCursor, //TODO + [StandardCursorType.BottomRightCorner]= NSCursor.CrosshairCursor, //TODO + [StandardCursorType.BottomSize] = NSCursor.ResizeDownCursor, + [StandardCursorType.Cross] = NSCursor.CrosshairCursor, + [StandardCursorType.Hand] = NSCursor.PointingHandCursor, + [StandardCursorType.Help] = NSCursor.ContextualMenuCursor, + [StandardCursorType.Ibeam] = NSCursor.IBeamCursor, + [StandardCursorType.LeftSide] = NSCursor.ResizeLeftCursor, + [StandardCursorType.No] = NSCursor.OperationNotAllowedCursor, + [StandardCursorType.RightSide] = NSCursor.ResizeRightCursor, + [StandardCursorType.SizeAll] = NSCursor.CrosshairCursor, //TODO + [StandardCursorType.SizeNorthSouth] = NSCursor.ResizeUpDownCursor, + [StandardCursorType.SizeWestEast] = NSCursor.ResizeLeftRightCursor, + [StandardCursorType.TopLeftCorner] = NSCursor.CrosshairCursor, //TODO + [StandardCursorType.TopRightCorner] = NSCursor.CrosshairCursor, //TODO + [StandardCursorType.TopSide] = NSCursor.ResizeUpCursor, + [StandardCursorType.UpArrow] = NSCursor.ResizeUpCursor, + [StandardCursorType.Wait] = NSCursor.ArrowCursor, //TODO + }; + } + + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new Cursor(_cache[cursorType]); + } + } +} diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs new file mode 100644 index 0000000000..11cc1e0151 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs @@ -0,0 +1,74 @@ +using System; +using Avalonia.Platform; +using MonoMac.AppKit; +using System.Runtime.InteropServices; +using MonoMac.CoreGraphics; +using MonoMac.ImageIO; +using MonoMac.Foundation; + +namespace Avalonia.MonoMac +{ + class EmulatedFramebuffer : ILockedFramebuffer + { + [DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")] + extern static IntPtr CGBitmapContextCreate (IntPtr data, UIntPtr width, UIntPtr height, UIntPtr bitsPerComponent, + UIntPtr bytesPerRow, IntPtr colorSpace, uint bitmapInfo); + + public EmulatedFramebuffer(NSView view) + { + //TODO: Check if this is correct + var factor = view.Window.UserSpaceScaleFactor; + var frame = view.Frame; + Width = (int)(frame.Width * factor); + Height = (int)(frame.Height * factor); + RowBytes = Width * 4; + Dpi = new Size(96, 96) * factor; + Format = PixelFormat.Rgba8888; + Address = Marshal.AllocHGlobal(Height * RowBytes); + } + + public void Dispose() + { + if (Address == IntPtr.Zero) + return; + var nfo = (int)CGBitmapFlags.ByteOrder32Big | (int)CGImageAlphaInfo.PremultipliedLast; + + using (var colorSpace = CGColorSpace.CreateDeviceRGB()) + using (var bContext = new CGBitmapContext (Address, Width, Height, 8, Width * 4, + colorSpace, (CGImageAlphaInfo)nfo)) + using (var image = bContext.ToImage()) + using (var nscontext = NSGraphicsContext.CurrentContext) + using(var context = nscontext.GraphicsPort) + { + /* + var url = NSUrl.FromFilename("/tmp/wat.png"); + var dest = CGImageDestination.FromUrl(url, "public.png", 1); + dest.AddImage(image, new NSDictionary()); + dest.Close(); + dest.Dispose(); + + var buf = new byte[Height * RowBytes]; + Marshal.Copy(Address, buf, 0, buf.Length); + System.IO.File.WriteAllBytes("/tmp/wat.bin", buf); +*/ + // flip the image for CGContext.DrawImage + //context.TranslateCTM(0, Height); + //context.ScaleCTM(1, -1); + + context.SetFillColor(255, 255, 255, 255); + context.FillRect(new CGRect(0, 0, Width, Height)); + + context.DrawImage(new CGRect(0, 0, Width, Height), image); + } + Marshal.FreeHGlobal(Address); + Address = IntPtr.Zero; + } + + public IntPtr Address { get; private set; } + public int Width { get; } + public int Height { get; } + public int RowBytes { get; } + public Size Dpi { get; } + public PixelFormat Format { get; } + } +} diff --git a/src/OSX/Avalonia.MonoMac/Helpers.cs b/src/OSX/Avalonia.MonoMac/Helpers.cs new file mode 100644 index 0000000000..90fd88d725 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/Helpers.cs @@ -0,0 +1,33 @@ +using System; +using System.Runtime.InteropServices; +using MonoMac.AppKit; +using MonoMac.ObjCRuntime; +using MonoMac.CoreGraphics; +using MonoMac; +namespace Avalonia.MonoMac +{ + static class Helpers + { + public static Point ToAvaloniaPoint(this CGPoint point) => new Point(point.X, point.Y); + public static CGPoint ToMonoMacPoint(this Point point) => new CGPoint(point.X, point.Y); + public static Size ToAvaloniaSize(this CGSize size) => new Size(size.Width, size.Height); + public static CGSize ToMonoMacSize(this Size size) => new CGSize(size.Width, size.Height); + public static Rect ToAvaloniaRect(this CGRect rect) => new Rect(rect.Left, rect.Top, rect.Width, rect.Height); + public static CGRect ToMonoMacRect(this Rect rect) => new CGRect(rect.X, rect.Y, rect.Width, rect.Height); + + public static Point ConvertPointY(this Point pt) + { + var sw = NSScreen.Screens[0].Frame; + var t = Math.Max(sw.Top, sw.Bottom); + return pt.WithY(t - pt.Y); + } + + public static CGPoint ConvertPointY(this CGPoint pt) + { + var sw = NSScreen.Screens[0].Frame; + var t = Math.Max(sw.Top, sw.Bottom); + return new CGPoint(pt.X, t - pt.Y); + } + + } +} diff --git a/src/OSX/Avalonia.MonoMac/KeyTransform.cs b/src/OSX/Avalonia.MonoMac/KeyTransform.cs new file mode 100644 index 0000000000..930b6f7b81 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/KeyTransform.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using Avalonia.Input; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + public static class KeyTransform + { + // See /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h + private const int kVK_ANSI_A = 0x00; + private const int kVK_ANSI_S = 0x01; + private const int kVK_ANSI_D = 0x02; + private const int kVK_ANSI_F = 0x03; + private const int kVK_ANSI_H = 0x04; + private const int kVK_ANSI_G = 0x05; + private const int kVK_ANSI_Z = 0x06; + private const int kVK_ANSI_X = 0x07; + private const int kVK_ANSI_C = 0x08; + private const int kVK_ANSI_V = 0x09; + private const int kVK_ANSI_B = 0x0B; + private const int kVK_ANSI_Q = 0x0C; + private const int kVK_ANSI_W = 0x0D; + private const int kVK_ANSI_E = 0x0E; + private const int kVK_ANSI_R = 0x0F; + private const int kVK_ANSI_Y = 0x10; + private const int kVK_ANSI_T = 0x11; + private const int kVK_ANSI_1 = 0x12; + private const int kVK_ANSI_2 = 0x13; + private const int kVK_ANSI_3 = 0x14; + private const int kVK_ANSI_4 = 0x15; + private const int kVK_ANSI_6 = 0x16; + private const int kVK_ANSI_5 = 0x17; + private const int kVK_ANSI_Equal = 0x18; + private const int kVK_ANSI_9 = 0x19; + private const int kVK_ANSI_7 = 0x1A; + private const int kVK_ANSI_Minus = 0x1B; + private const int kVK_ANSI_8 = 0x1C; + private const int kVK_ANSI_0 = 0x1D; + private const int kVK_ANSI_RightBracket = 0x1E; + private const int kVK_ANSI_O = 0x1F; + private const int kVK_ANSI_U = 0x20; + private const int kVK_ANSI_LeftBracket = 0x21; + private const int kVK_ANSI_I = 0x22; + private const int kVK_ANSI_P = 0x23; + private const int kVK_ANSI_L = 0x25; + private const int kVK_ANSI_J = 0x26; + private const int kVK_ANSI_Quote = 0x27; + private const int kVK_ANSI_K = 0x28; + private const int kVK_ANSI_Semicolon = 0x29; + private const int kVK_ANSI_Backslash = 0x2A; + private const int kVK_ANSI_Comma = 0x2B; + private const int kVK_ANSI_Slash = 0x2C; + private const int kVK_ANSI_N = 0x2D; + private const int kVK_ANSI_M = 0x2E; + private const int kVK_ANSI_Period = 0x2F; + private const int kVK_ANSI_Grave = 0x32; + private const int kVK_ANSI_KeypadDecimal = 0x41; + private const int kVK_ANSI_KeypadMultiply = 0x43; + private const int kVK_ANSI_KeypadPlus = 0x45; + private const int kVK_ANSI_KeypadClear = 0x47; + private const int kVK_ANSI_KeypadDivide = 0x4B; + private const int kVK_ANSI_KeypadEnter = 0x4C; + private const int kVK_ANSI_KeypadMinus = 0x4E; + private const int kVK_ANSI_KeypadEquals = 0x51; + private const int kVK_ANSI_Keypad0 = 0x52; + private const int kVK_ANSI_Keypad1 = 0x53; + private const int kVK_ANSI_Keypad2 = 0x54; + private const int kVK_ANSI_Keypad3 = 0x55; + private const int kVK_ANSI_Keypad4 = 0x56; + private const int kVK_ANSI_Keypad5 = 0x57; + private const int kVK_ANSI_Keypad6 = 0x58; + private const int kVK_ANSI_Keypad7 = 0x59; + private const int kVK_ANSI_Keypad8 = 0x5B; + private const int kVK_ANSI_Keypad9 = 0x5C; + private const int kVK_Return = 0x24; + private const int kVK_Tab = 0x30; + private const int kVK_Space = 0x31; + private const int kVK_Delete = 0x33; + private const int kVK_Escape = 0x35; + private const int kVK_Command = 0x37; + private const int kVK_Shift = 0x38; + private const int kVK_CapsLock = 0x39; + private const int kVK_Option = 0x3A; + private const int kVK_Control = 0x3B; + private const int kVK_RightCommand = 0x36; + private const int kVK_RightShift = 0x3C; + private const int kVK_RightOption = 0x3D; + private const int kVK_RightControl = 0x3E; + private const int kVK_Function = 0x3F; + private const int kVK_F17 = 0x40; + private const int kVK_VolumeUp = 0x48; + private const int kVK_VolumeDown = 0x49; + private const int kVK_Mute = 0x4A; + private const int kVK_F18 = 0x4F; + private const int kVK_F19 = 0x50; + private const int kVK_F20 = 0x5A; + private const int kVK_F5 = 0x60; + private const int kVK_F6 = 0x61; + private const int kVK_F7 = 0x62; + private const int kVK_F3 = 0x63; + private const int kVK_F8 = 0x64; + private const int kVK_F9 = 0x65; + private const int kVK_F11 = 0x67; + private const int kVK_F13 = 0x69; + private const int kVK_F16 = 0x6A; + private const int kVK_F14 = 0x6B; + private const int kVK_F10 = 0x6D; + private const int kVK_F12 = 0x6F; + private const int kVK_F15 = 0x71; + private const int kVK_Help = 0x72; + private const int kVK_Home = 0x73; + private const int kVK_PageUp = 0x74; + private const int kVK_ForwardDelete = 0x75; + private const int kVK_F4 = 0x76; + private const int kVK_End = 0x77; + private const int kVK_F2 = 0x78; + private const int kVK_PageDown = 0x79; + private const int kVK_F1 = 0x7A; + private const int kVK_LeftArrow = 0x7B; + private const int kVK_RightArrow = 0x7C; + private const int kVK_DownArrow = 0x7D; + private const int kVK_UpArrow = 0x7E; + private const int kVK_ISO_Section = 0x0A; + private const int kVK_JIS_Yen = 0x5D; + private const int kVK_JIS_Underscore = 0x5E; + private const int kVK_JIS_KeypadComma = 0x5F; + private const int kVK_JIS_Eisu = 0x66; + private const int kVK_JIS_Kana = 0x68; + + //TODO: Map missing keys + static readonly Dictionary Keys = new Dictionary + { + [kVK_ANSI_A] = Key.A, + [kVK_ANSI_S] = Key.S, + [kVK_ANSI_D] = Key.D, + [kVK_ANSI_F] = Key.F, + [kVK_ANSI_H] = Key.H, + [kVK_ANSI_G] = Key.G, + [kVK_ANSI_Z] = Key.Z, + [kVK_ANSI_X] = Key.X, + [kVK_ANSI_C] = Key.C, + [kVK_ANSI_V] = Key.V, + [kVK_ANSI_B] = Key.B, + [kVK_ANSI_Q] = Key.Q, + [kVK_ANSI_W] = Key.W, + [kVK_ANSI_E] = Key.E, + [kVK_ANSI_R] = Key.R, + [kVK_ANSI_Y] = Key.Y, + [kVK_ANSI_T] = Key.T, + [kVK_ANSI_1] = Key.D1, + [kVK_ANSI_2] = Key.D2, + [kVK_ANSI_3] = Key.D3, + [kVK_ANSI_4] = Key.D4, + [kVK_ANSI_6] = Key.D6, + [kVK_ANSI_5] = Key.D5, + //[kVK_ANSI_Equal] = Key.?, + [kVK_ANSI_9] = Key.D9, + [kVK_ANSI_7] = Key.D7, + [kVK_ANSI_Minus] = Key.OemMinus, + [kVK_ANSI_8] = Key.D8, + [kVK_ANSI_0] = Key.D0, + [kVK_ANSI_RightBracket] = Key.OemCloseBrackets, + [kVK_ANSI_O] = Key.O, + [kVK_ANSI_U] = Key.U, + [kVK_ANSI_LeftBracket] = Key.OemOpenBrackets, + [kVK_ANSI_I] = Key.I, + [kVK_ANSI_P] = Key.P, + [kVK_ANSI_L] = Key.L, + [kVK_ANSI_J] = Key.J, + [kVK_ANSI_Quote] = Key.OemQuotes, + [kVK_ANSI_K] = Key.K, + [kVK_ANSI_Semicolon] = Key.OemSemicolon, + [kVK_ANSI_Backslash] = Key.OemBackslash, + [kVK_ANSI_Comma] = Key.OemComma, + //[kVK_ANSI_Slash] = Key.?, + [kVK_ANSI_N] = Key.N, + [kVK_ANSI_M] = Key.M, + [kVK_ANSI_Period] = Key.OemPeriod, + //[kVK_ANSI_Grave] = Key.?, + [kVK_ANSI_KeypadDecimal] = Key.Decimal, + [kVK_ANSI_KeypadMultiply] = Key.Multiply, + [kVK_ANSI_KeypadPlus] = Key.OemPlus, + [kVK_ANSI_KeypadClear] = Key.Clear, + [kVK_ANSI_KeypadDivide] = Key.Divide, + [kVK_ANSI_KeypadEnter] = Key.Enter, + [kVK_ANSI_KeypadMinus] = Key.OemMinus, + //[kVK_ANSI_KeypadEquals] = Key.?, + [kVK_ANSI_Keypad0] = Key.NumPad0, + [kVK_ANSI_Keypad1] = Key.NumPad1, + [kVK_ANSI_Keypad2] = Key.NumPad2, + [kVK_ANSI_Keypad3] = Key.NumPad3, + [kVK_ANSI_Keypad4] = Key.NumPad4, + [kVK_ANSI_Keypad5] = Key.NumPad5, + [kVK_ANSI_Keypad6] = Key.NumPad6, + [kVK_ANSI_Keypad7] = Key.NumPad7, + [kVK_ANSI_Keypad8] = Key.NumPad8, + [kVK_ANSI_Keypad9] = Key.NumPad9, + [kVK_Return] = Key.Return, + [kVK_Tab] = Key.Tab, + [kVK_Space] = Key.Space, + [kVK_Delete] = Key.Delete, + [kVK_Escape] = Key.Escape, + [kVK_Command] = Key.LWin, + [kVK_Shift] = Key.LeftShift, + [kVK_CapsLock] = Key.CapsLock, + [kVK_Option] = Key.LeftAlt, + [kVK_Control] = Key.LeftCtrl, + [kVK_RightCommand] = Key.RWin, + [kVK_RightShift] = Key.RightShift, + [kVK_RightOption] = Key.RightAlt, + [kVK_RightControl] = Key.RightCtrl, + //[kVK_Function] = Key.?, + [kVK_F17] = Key.F17, + [kVK_VolumeUp] = Key.VolumeUp, + [kVK_VolumeDown] = Key.VolumeDown, + [kVK_Mute] = Key.VolumeMute, + [kVK_F18] = Key.F18, + [kVK_F19] = Key.F19, + [kVK_F20] = Key.F20, + [kVK_F5] = Key.F5, + [kVK_F6] = Key.F6, + [kVK_F7] = Key.F7, + [kVK_F3] = Key.F3, + [kVK_F8] = Key.F8, + [kVK_F9] = Key.F9, + [kVK_F11] = Key.F11, + [kVK_F13] = Key.F13, + [kVK_F16] = Key.F16, + [kVK_F14] = Key.F14, + [kVK_F10] = Key.F10, + [kVK_F12] = Key.F12, + [kVK_F15] = Key.F15, + [kVK_Help] = Key.Help, + [kVK_Home] = Key.Home, + [kVK_PageUp] = Key.PageUp, + [kVK_ForwardDelete] = Key.Delete, + [kVK_F4] = Key.F4, + [kVK_End] = Key.End, + [kVK_F2] = Key.F2, + [kVK_PageDown] = Key.PageDown, + [kVK_F1] = Key.F1, + [kVK_LeftArrow] = Key.Left, + [kVK_RightArrow] = Key.Right, + [kVK_DownArrow] = Key.Down, + [kVK_UpArrow] = Key.Up, + /* + [kVK_ISO_Section] = Key.?, + [kVK_JIS_Yen] = Key.?, + [kVK_JIS_Underscore] = Key.?, + [kVK_JIS_KeypadComma] = Key.?, + [kVK_JIS_Eisu] = Key.?, + [kVK_JIS_Kana] = Key.? + */ + }; + + + public static Key? TransformKeyCode(ushort code) + { + Key rv; + if (Keys.TryGetValue(code, out rv)) + return rv; + return null; + } + } +} diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs new file mode 100644 index 0000000000..c3f07a2439 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -0,0 +1,77 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Platform; +using Avalonia.Rendering; +using MonoMac.Foundation; +using MonoMac.AppKit; +using System.Drawing; + +namespace Avalonia.MonoMac +{ + public class MonoMacPlatform : IWindowingPlatform, IPlatformSettings + { + internal static MonoMacPlatform Instance { get; private set; } + MouseDevice _mouseDevice = new MouseDevice(); + KeyboardDevice _keyboardDevice = new KeyboardDevice(); + NSApplication _app; + void DoInitialize() + { + AvaloniaLocator.CurrentMutable + .Bind().ToTransient() + .Bind().ToSingleton() + .Bind().ToConstant(_keyboardDevice) + .Bind().ToConstant(_mouseDevice) + .Bind().ToConstant(this) + .Bind().ToConstant(ImmediateRenderer.Factory) + .Bind().ToConstant(this) + .Bind().ToConstant(PlatformThreadingInterface.Instance); + + InitializeCocoaApp(); + } + + public static void Initialize() + { + Instance = new MonoMacPlatform(); + Instance.DoInitialize(); + + } + + void InitializeCocoaApp() + { + NSApplication.Init(); + _app = NSApplication.SharedApplication; + _app.ActivationPolicy = NSApplicationActivationPolicy.Regular; + + } + + + public Size DoubleClickSize => new Size(4, 4); + public TimeSpan DoubleClickTime => TimeSpan.FromSeconds(NSEvent.DoubleClickInterval); + + public IWindowImpl CreateWindow() => new WindowImpl(); + + public IEmbeddableWindowImpl CreateEmbeddableWindow() + { + throw new PlatformNotSupportedException(); + } + + public IPopupImpl CreatePopup() + { + return new PopupImpl(); + } + } +} + + +namespace Avalonia +{ + public static class MonoMacPlatformExtensions + { + public static AppBuilderBase UseMonoMac(this AppBuilderBase builder) where T : AppBuilderBase, new() + { + return builder.UseWindowingSubsystem(Avalonia.MonoMac.MonoMacPlatform.Initialize); + } + } +} + \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs new file mode 100644 index 0000000000..4d15a164e0 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading; +using Avalonia.Platform; +using MonoMac.AppKit; +using MonoMac.CoreGraphics; +using MonoMac.Foundation; + +namespace Avalonia.MonoMac +{ + class PlatformThreadingInterface : IPlatformThreadingInterface + { + private bool _signaled; + public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface(); + public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread; + + public event Action Signaled; + + public IDisposable StartTimer(TimeSpan interval, Action tick) + => NSTimer.CreateRepeatingScheduledTimer(interval, () => tick()); + + public void Signal() + { + lock (this) + { + if (_signaled) + return; + _signaled = true; + } + NSApplication.SharedApplication.BeginInvokeOnMainThread(() => + { + lock(this) + { + if (!_signaled) + return; + _signaled = false; + } + Signaled?.Invoke(); + }); + } + + + + public void RunLoop(CancellationToken cancellationToken) + { + NSApplication.SharedApplication.ActivateIgnoringOtherApps(true); + //NSApplication.SharedApplication.Run(); + + var app = NSApplication.SharedApplication; + cancellationToken.Register(() => + { + app.PostEvent(NSEvent.OtherEvent(NSEventType.ApplicationDefined, default(CGPoint), + default(NSEventModifierMask), 0, 0, null, 0, 0, 0), true); + }); + while (!cancellationToken.IsCancellationRequested) + { + var ev = app.NextEvent(NSEventMask.AnyEvent, NSDate.DistantPast, NSRunLoop.NSDefaultRunLoopMode, true); + if (ev != null) + { + app.SendEvent(ev); + ev.Dispose(); + } + } + } + } +} diff --git a/src/OSX/Avalonia.MonoMac/PopupImpl.cs b/src/OSX/Avalonia.MonoMac/PopupImpl.cs new file mode 100644 index 0000000000..0b1ee537f3 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/PopupImpl.cs @@ -0,0 +1,19 @@ +using System; +using Avalonia.Platform; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + class PopupImpl : WindowBaseImpl, IPopupImpl + { + public PopupImpl() + { + UpdateStyle(); + } + + protected override NSWindowStyle GetStyle() + { + return NSWindowStyle.Borderless; + } + } +} diff --git a/src/OSX/Avalonia.MonoMac/Stubs.cs b/src/OSX/Avalonia.MonoMac/Stubs.cs new file mode 100644 index 0000000000..ec7609fa4f --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/Stubs.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using Avalonia.Input; +using Avalonia.Platform; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + // OSX doesn't have a concept of *window* icon. + // Icons in the title bar are only shown if there is + // an opened file (on disk) associated with the current window + // see http://stackoverflow.com/a/7038671/2231814 + class IconLoader : IPlatformIconLoader + { + class IconStub : IWindowIconImpl + { + private readonly IBitmapImpl _bitmap; + + public IconStub(IBitmapImpl bitmap) + { + _bitmap = bitmap; + } + + public void Save(Stream outputStream) + { + _bitmap.Save(outputStream); + } + } + + public IWindowIconImpl LoadIcon(string fileName) + { + return new IconStub( + AvaloniaLocator.Current.GetService().LoadBitmap(fileName)); + } + + public IWindowIconImpl LoadIcon(Stream stream) + { + return new IconStub( + AvaloniaLocator.Current.GetService().LoadBitmap(stream)); + } + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + var ms = new MemoryStream(); + bitmap.Save(ms); + ms.Seek(0, SeekOrigin.Begin); + return LoadIcon(ms); + } + } + +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs new file mode 100644 index 0000000000..2854aba9c9 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -0,0 +1,368 @@ +using System; +using System.Collections.Generic; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.Controls.Platform.Surfaces; +using MonoMac.AppKit; + +using MonoMac.CoreGraphics; +using MonoMac.Foundation; +using MonoMac.ObjCRuntime; + +namespace Avalonia.MonoMac +{ + abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface + { + public TopLevelView View { get; } + public TopLevelImpl() + { + View = new TopLevelView(this); + } + + [Adopts("NSTextInputClient")] + public class TopLevelView : NSView + { + TopLevelImpl _tl; + bool _isLeftPressed, _isRightPressed, _isMiddlePressed; + private readonly IMouseDevice _mouse; + private readonly IKeyboardDevice _keyboard; + private NSTrackingArea _area; + private NSCursor _cursor; + public TopLevelView(TopLevelImpl tl) + { + _tl = tl; + _mouse = AvaloniaLocator.Current.GetService(); + _keyboard = AvaloniaLocator.Current.GetService(); + } + + public override bool ConformsToProtocol(IntPtr protocol) + { + var rv = base.ConformsToProtocol(protocol); + return rv; + } + + public override void DrawRect(CGRect dirtyRect) + { + _tl.Paint?.Invoke(dirtyRect.ToAvaloniaRect()); + } + + [Export("viewDidChangeBackingProperties:")] + public void ViewDidChangeBackingProperties() + { + _tl?.ScalingChanged?.Invoke(_tl.Scaling); + } + + void UpdateCursor() + { + ResetCursorRects(); + if (_cursor != null) + AddCursorRect(Frame, _cursor); + } + + static NSCursor ArrowCursor = NSCursor.ArrowCursor; + public void SetCursor(NSCursor cursor) + { + _cursor = cursor ?? ArrowCursor; + UpdateCursor(); + } + + public override void SetFrameSize(CGSize newSize) + { + base.SetFrameSize(newSize); + + if (_area != null) + { + RemoveTrackingArea(_area); + _area.Dispose(); ; + } + _area = new NSTrackingArea(new CGRect(default(CGPoint), newSize), + NSTrackingAreaOptions.ActiveAlways | + NSTrackingAreaOptions.MouseMoved | + NSTrackingAreaOptions.EnabledDuringMouseDrag, this, null); + AddTrackingArea(_area); + UpdateCursor(); + _tl?.Resized?.Invoke(_tl.ClientSize); + } + + InputModifiers GetModifiers(NSEventModifierMask mod) + { + var rv = new InputModifiers(); + if (mod.HasFlag(NSEventModifierMask.ControlKeyMask)) + rv |= InputModifiers.Control; + if (mod.HasFlag(NSEventModifierMask.ShiftKeyMask)) + rv |= InputModifiers.Shift; + if (mod.HasFlag(NSEventModifierMask.AlternateKeyMask)) + rv |= InputModifiers.Alt; + if (mod.HasFlag(NSEventModifierMask.CommandKeyMask)) + rv |= InputModifiers.Windows; + + if (_isLeftPressed) + rv |= InputModifiers.LeftMouseButton; + if (_isMiddlePressed) + rv |= InputModifiers.MiddleMouseButton; + if (_isRightPressed) + rv |= InputModifiers.RightMouseButton; + return rv; + } + + public Point TranslateLocalPoint(Point pt) => pt.WithY(Bounds.Height - pt.Y); + + Vector GetDelta(NSEvent ev) + { + var rv = new Vector(ev.ScrollingDeltaX, ev.ScrollingDeltaY); + //TODO: Verify if handling of HasPreciseScrollingDeltas + // is required (touchpad or magic-mouse is needed) + return rv; + } + + uint GetTimeStamp(NSEvent ev) => (uint)(ev.Timestamp * 1000); + + void MouseEvent(NSEvent ev, RawMouseEventType type) + { + BecomeFirstResponder(); + var loc = TranslateLocalPoint(ConvertPointToView(ev.LocationInWindow, this).ToAvaloniaPoint()); + var ts = GetTimeStamp(ev); + var mod = GetModifiers(ev.ModifierFlags); + if (type == RawMouseEventType.Wheel) + { + var delta = GetDelta(ev); + if (delta.X == 0 && delta.Y == 0) + return; + _tl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc, + delta, mod)); + } + else + _tl.Input?.Invoke(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod)); + } + + public override void MouseMoved(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.Move); + base.MouseMoved(theEvent); + } + + public override void MouseDragged(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.Move); + base.MouseDragged(theEvent); + } + + public override void OtherMouseDragged(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.Move); + base.OtherMouseDragged(theEvent); + } + + public override void RightMouseDragged(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.Move); + base.RightMouseDragged(theEvent); + } + + public NSEvent LastMouseDownEvent { get; private set; } + + public override void MouseDown(NSEvent theEvent) + { + _isLeftPressed = true; + LastMouseDownEvent = theEvent; + MouseEvent(theEvent, RawMouseEventType.LeftButtonDown); + LastMouseDownEvent = null; + base.MouseDown(theEvent); + } + + public override void RightMouseDown(NSEvent theEvent) + { + _isRightPressed = true; + MouseEvent(theEvent, RawMouseEventType.RightButtonDown); + base.RightMouseDown(theEvent); + } + + public override void OtherMouseDown(NSEvent theEvent) + { + _isMiddlePressed = true; + MouseEvent(theEvent, RawMouseEventType.MiddleButtonDown); + base.OtherMouseDown(theEvent); + } + + public override void MouseUp(NSEvent theEvent) + { + _isLeftPressed = false; + MouseEvent(theEvent, RawMouseEventType.LeftButtonUp); + base.MouseUp(theEvent); + } + + public override void RightMouseUp(NSEvent theEvent) + { + _isRightPressed = false; + MouseEvent(theEvent, RawMouseEventType.RightButtonUp); + base.RightMouseUp(theEvent); + } + + public override void OtherMouseUp(NSEvent theEvent) + { + _isMiddlePressed = false; + MouseEvent(theEvent, RawMouseEventType.MiddleButtonUp); + base.OtherMouseUp(theEvent); + } + + public override void ScrollWheel(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.Wheel); + base.ScrollWheel(theEvent); + } + + public override void MouseExited(NSEvent theEvent) + { + MouseEvent(theEvent, RawMouseEventType.LeaveWindow); + base.MouseExited(theEvent); + } + + void KeyboardEvent(RawKeyEventType type, NSEvent ev) + { + var code = KeyTransform.TransformKeyCode(ev.KeyCode); + if (!code.HasValue) + return; + _tl.Input?.Invoke(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev), + type, code.Value, GetModifiers(ev.ModifierFlags))); + } + + public override void KeyDown(NSEvent theEvent) + { + KeyboardEvent(RawKeyEventType.KeyDown, theEvent); + InputContext.HandleEvent(theEvent); + base.KeyDown(theEvent); + } + + public override void KeyUp(NSEvent theEvent) + { + KeyboardEvent(RawKeyEventType.KeyUp, theEvent); + base.KeyUp(theEvent); + } + + + + #region NSTextInputClient + + public override bool AcceptsFirstResponder() => true; + + public bool HasMarkedText + { + [Export("hasMarkedText")] + get { return false; } + } + + public NSRange MarkedRange + { + [Export("markedRange")] + get { return new NSRange(NSRange.NotFound, 0); } + } + + public NSRange SelectedRange + { + [Export("selectedRange")] + get { return new NSRange(NSRange.NotFound, 0); } + } + + [Export("setMarkedText:selectedRange:replacementRange:")] + public void SetMarkedText(NSString str, NSRange a1, NSRange a2) + { + + } + + [Export("unmarkText")] + public void UnmarkText() + { + + } + + public NSArray ValidAttributesForMarkedText + { + [Export("validAttributesForMarkedText")] + get + { + return new NSArray(); + } + } + + [Export("attributedSubstringForProposedRange:actualRange:")] + public NSAttributedString AttributedSubstringForProposedRange(NSRange range, IntPtr wat) + { + return new NSAttributedString(""); + } + + [Export("insertText:replacementRange:")] + public void InsertText(NSString str, NSRange range) + { + //TODO: timestamp + _tl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, 0, str.ToString())); + } + + [Export("characterIndexForPoint:")] + public uint CharacterIndexForPoint(CGPoint pt) + { + return 0; + } + + [Export("firstRectForCharacterRange:actualRange:")] + public CGRect FirstRectForCharacterRange(NSRange range, IntPtr wat) + { + return new CGRect(); + } + + #endregion + } + + public IInputRoot InputRoot { get; private set; } + + public abstract Size ClientSize { get; } + + public double Scaling + { + get + { + if (View.Window == null) + return 1; + return View.Window.BackingScaleFactor; + } + } + + public IEnumerable Surfaces => new[] { this }; + + #region Events + public Action Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action Closed { get; set; } + #endregion + + public virtual void Dispose() + { + Closed?.Invoke(); + Closed = null; + View.Dispose(); + } + + public void Invalidate(Rect rect) + { + View.SetNeedsDisplayInRect(View.Frame); + } + + public abstract Point PointToClient(Point point); + + public abstract Point PointToScreen(Point point); + + public void SetCursor(IPlatformHandle cursor) + { + View.SetCursor((cursor as Cursor)?.Native); + } + + public void SetInputRoot(IInputRoot inputRoot) + { + InputRoot = inputRoot; + } + + public ILockedFramebuffer Lock() => new EmulatedFramebuffer(View); + } +} diff --git a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs new file mode 100644 index 0000000000..9186d3ded5 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using MonoMac.AppKit; +using MonoMac.ObjCRuntime; + +namespace Avalonia.MonoMac +{ + class WindowBaseImpl : TopLevelImpl, IWindowBaseImpl + { + public CustomWindow Window { get; private set; } + + public WindowBaseImpl() + { + Window = new CustomWindow(this); + Window.StyleMask = NSWindowStyle.Titled; + Window.BackingType = NSBackingStore.Buffered; + Window.ContentView = View; + Window.Delegate = CreateWindowDelegate(); + } + + public class CustomWindow : NSWindow + { + readonly WindowBaseImpl _impl; + + public CustomWindow(WindowBaseImpl impl) + { + _impl = impl; + } + + public override void BecomeKeyWindow() + { + _impl.Activated?.Invoke(); + base.BecomeKeyWindow(); + } + + public override void ResignKeyWindow() + { + _impl.Deactivated?.Invoke(); + base.ResignKeyWindow(); + } + } + + protected virtual NSWindowDelegate CreateWindowDelegate() => new WindowBaseDelegate(this); + + public class WindowBaseDelegate : NSWindowDelegate + { + readonly WindowBaseImpl _impl; + + public WindowBaseDelegate(WindowBaseImpl impl) + { + _impl = impl; + } + + public override void DidMoved(global::MonoMac.Foundation.NSNotification notification) + { + _impl.PositionChanged?.Invoke(_impl.Position); + } + + public override void WillClose(global::MonoMac.Foundation.NSNotification notification) + { + _impl.Window.Dispose(); + _impl.Window = null; + _impl.Dispose(); + } + } + + + public Point Position + { + get + { + var pos = Window.Frame.ToAvaloniaRect().BottomLeft.ConvertPointY(); + //Console.WriteLine($"GET pos {pos}"); + return pos; + } + set + { + //Console.WriteLine($"SET pos {value}"); + Window.CascadeTopLeftFromPoint(value.ToMonoMacPoint().ConvertPointY()); + } + } + + + protected virtual NSWindowStyle GetStyle() + { + return NSWindowStyle.Borderless; + } + + protected void UpdateStyle() + { + Window.StyleMask = GetStyle(); + } + + + IPlatformHandle IWindowBaseImpl.Handle => new PlatformHandle(Window.Handle, "NSWindow"); + public Size MaxClientSize => NSScreen.Screens[0].Frame.ToAvaloniaRect().Size; + public Action PositionChanged { get; set; } + public Action Deactivated { get; set; } + public Action Activated { get; set; } + + public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize(); + + + public void Show() + { + Window.MakeKeyAndOrderFront(Window); + } + + public void Hide() + { + Window?.OrderOut(Window); + } + + + public void BeginMoveDrag() + { + var ev = View.LastMouseDownEvent; + if (ev == null) + return; + var handle = Selector.GetHandle("performWindowDragWithEvent:"); + Messaging.void_objc_msgSend_IntPtr(Window.Handle, handle, ev.Handle); + } + + public void BeginResizeDrag(WindowEdge edge) + { + //TODO: Intercept mouse events and implement resize drag manually + } + + public void Activate() + { + Window.MakeKeyWindow(); + } + + public void Resize(Size clientSize) + { + var pos = Position; + Window.SetContentSize(clientSize.ToMonoMacSize()); + Position = pos; + } + + public override Point PointToClient(Point point) + { + var cocoaScreenPoint = point.ToMonoMacPoint().ConvertPointY(); + var cocoaViewPoint = Window.ConvertScreenToBase(cocoaScreenPoint).ToAvaloniaPoint(); + return View.TranslateLocalPoint(cocoaViewPoint); + } + + public override Point PointToScreen(Point point) + { + var cocoaViewPoint = View.TranslateLocalPoint(point).ToMonoMacPoint(); + var cocoaScreenPoint = Window.ConvertBaseToScreen(cocoaViewPoint); + return cocoaScreenPoint.ConvertPointY().ToAvaloniaPoint(); + } + + + + public override void Dispose() + { + Window?.Close(); + Window?.Dispose(); + base.Dispose(); + } + } +} diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs new file mode 100644 index 0000000000..bef24cc6b1 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -0,0 +1,92 @@ +using System; +using Avalonia.Controls; +using Avalonia.Platform; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + class WindowImpl : WindowBaseImpl, IWindowImpl + { + bool _decorated = true; + public WindowImpl() + { + UpdateStyle(); + } + + public WindowState WindowState + { + get + { + if (Window.IsMiniaturized) + return WindowState.Minimized; + return WindowState.Normal; + + } + set + { + if (value == WindowState.Maximized) + { + if (Window.IsMiniaturized) + Window.Deminiaturize(Window); + if (!Window.IsZoomed) + Window.PerformZoom(Window); + } + else if (value.HasFlag(WindowState.Minimized)) + Window.Miniaturize(Window); + else + { + if (Window.IsMiniaturized) + Window.Deminiaturize(Window); + if (Window.IsZoomed) + Window.IsZoomed = false; + } + } + } + + public void SetIcon(IWindowIconImpl icon) + { + //No-OP, see http://stackoverflow.com/a/7038671/2231814 + } + + protected override NSWindowStyle GetStyle() + { + if (_decorated) + return NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled; + return NSWindowStyle.Borderless; + } + + public void SetSystemDecorations(bool enabled) + { + _decorated = true; + UpdateStyle(); + } + + public void SetTitle(string title) + { + Window.Title = title; + } + + class ModalDisposable : IDisposable + { + readonly WindowImpl impl; + + public ModalDisposable(WindowImpl impl) + { + this.impl = impl; + } + + public void Dispose() + { + impl.Window.OrderOut(impl.Window); + } + } + + public IDisposable ShowDialog() + { + //TODO: Investigate how to return immediately. + // May be add some magic to our run loop or something + NSApplication.SharedApplication.RunModalForWindow(Window); + return new ModalDisposable(this); + } + } +} From f13f017057af29ed380360b3ed36b578609bf286 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2017 13:11:05 +0300 Subject: [PATCH 02/14] [MONOMAC] Code cleanup --- src/OSX/Avalonia.MonoMac/.gitignore | 1 + src/OSX/Avalonia.MonoMac/Cursor.cs | 19 +- .../Avalonia.MonoMac/EmulatedFramebuffer.cs | 90 +++---- src/OSX/Avalonia.MonoMac/Helpers.cs | 30 +-- src/OSX/Avalonia.MonoMac/KeyTransform.cs | 241 +++++++++--------- src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs | 15 +- .../PlatformThreadingInterface.cs | 34 +-- src/OSX/Avalonia.MonoMac/PopupImpl.cs | 11 +- src/OSX/Avalonia.MonoMac/Stubs.cs | 10 +- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 99 ++++--- src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs | 70 ++--- src/OSX/Avalonia.MonoMac/WindowImpl.cs | 21 +- 12 files changed, 289 insertions(+), 352 deletions(-) create mode 100644 src/OSX/Avalonia.MonoMac/.gitignore diff --git a/src/OSX/Avalonia.MonoMac/.gitignore b/src/OSX/Avalonia.MonoMac/.gitignore new file mode 100644 index 0000000000..2d6e7a861f --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/.gitignore @@ -0,0 +1 @@ +Avalonia.MonoMac.sln diff --git a/src/OSX/Avalonia.MonoMac/Cursor.cs b/src/OSX/Avalonia.MonoMac/Cursor.cs index 8f5f2c2063..10445e62e2 100644 --- a/src/OSX/Avalonia.MonoMac/Cursor.cs +++ b/src/OSX/Avalonia.MonoMac/Cursor.cs @@ -21,9 +21,10 @@ namespace Avalonia.MonoMac } } - class CursorFactoryStub : IStandardCursorFactory - { + class CursorFactoryStub : IStandardCursorFactory + { Dictionary _cache; + public CursorFactoryStub() { //TODO: Load diagonal cursors from webkit @@ -33,8 +34,8 @@ namespace Avalonia.MonoMac [StandardCursorType.Arrow] = NSCursor.ArrowCursor, [StandardCursorType.AppStarting] = NSCursor.ArrowCursor, //TODO [StandardCursorType.BottomLeftCorner] = NSCursor.CrosshairCursor, //TODO - [StandardCursorType.BottomRightCorner]= NSCursor.CrosshairCursor, //TODO - [StandardCursorType.BottomSize] = NSCursor.ResizeDownCursor, + [StandardCursorType.BottomRightCorner] = NSCursor.CrosshairCursor, //TODO + [StandardCursorType.BottomSize] = NSCursor.ResizeDownCursor, [StandardCursorType.Cross] = NSCursor.CrosshairCursor, [StandardCursorType.Hand] = NSCursor.PointingHandCursor, [StandardCursorType.Help] = NSCursor.ContextualMenuCursor, @@ -50,12 +51,12 @@ namespace Avalonia.MonoMac [StandardCursorType.TopSide] = NSCursor.ResizeUpCursor, [StandardCursorType.UpArrow] = NSCursor.ResizeUpCursor, [StandardCursorType.Wait] = NSCursor.ArrowCursor, //TODO - }; + }; } public IPlatformHandle GetCursor(StandardCursorType cursorType) - { + { return new Cursor(_cache[cursorType]); - } - } -} + } + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs index 11cc1e0151..e524a706cc 100644 --- a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs +++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs @@ -3,72 +3,50 @@ using Avalonia.Platform; using MonoMac.AppKit; using System.Runtime.InteropServices; using MonoMac.CoreGraphics; -using MonoMac.ImageIO; -using MonoMac.Foundation; namespace Avalonia.MonoMac { class EmulatedFramebuffer : ILockedFramebuffer - { - [DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")] - extern static IntPtr CGBitmapContextCreate (IntPtr data, UIntPtr width, UIntPtr height, UIntPtr bitsPerComponent, - UIntPtr bytesPerRow, IntPtr colorSpace, uint bitmapInfo); - - public EmulatedFramebuffer(NSView view) - { + { + public EmulatedFramebuffer(NSView view) + { //TODO: Check if this is correct var factor = view.Window.UserSpaceScaleFactor; - var frame = view.Frame; - Width = (int)(frame.Width * factor); - Height = (int)(frame.Height * factor); - RowBytes = Width * 4; - Dpi = new Size(96, 96) * factor; + var frame = view.Frame; + Width = (int) (frame.Width * factor); + Height = (int) (frame.Height * factor); + RowBytes = Width * 4; + Dpi = new Size(96, 96) * factor; Format = PixelFormat.Rgba8888; - Address = Marshal.AllocHGlobal(Height * RowBytes); - } + Address = Marshal.AllocHGlobal(Height * RowBytes); + } - public void Dispose() - { - if (Address == IntPtr.Zero) - return; - var nfo = (int)CGBitmapFlags.ByteOrder32Big | (int)CGImageAlphaInfo.PremultipliedLast; + public void Dispose() + { + if (Address == IntPtr.Zero) + return; + var nfo = (int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast; using (var colorSpace = CGColorSpace.CreateDeviceRGB()) - using (var bContext = new CGBitmapContext (Address, Width, Height, 8, Width * 4, - colorSpace, (CGImageAlphaInfo)nfo)) - using (var image = bContext.ToImage()) + using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4, + colorSpace, (CGImageAlphaInfo) nfo)) + using (var image = bContext.ToImage()) using (var nscontext = NSGraphicsContext.CurrentContext) - using(var context = nscontext.GraphicsPort) - { - /* - var url = NSUrl.FromFilename("/tmp/wat.png"); - var dest = CGImageDestination.FromUrl(url, "public.png", 1); - dest.AddImage(image, new NSDictionary()); - dest.Close(); - dest.Dispose(); - - var buf = new byte[Height * RowBytes]; - Marshal.Copy(Address, buf, 0, buf.Length); - System.IO.File.WriteAllBytes("/tmp/wat.bin", buf); -*/ - // flip the image for CGContext.DrawImage - //context.TranslateCTM(0, Height); - //context.ScaleCTM(1, -1); - + using (var context = nscontext.GraphicsPort) + { context.SetFillColor(255, 255, 255, 255); context.FillRect(new CGRect(0, 0, Width, Height)); - - context.DrawImage(new CGRect(0, 0, Width, Height), image); - } - Marshal.FreeHGlobal(Address); - Address = IntPtr.Zero; - } - - public IntPtr Address { get; private set; } - public int Width { get; } - public int Height { get; } - public int RowBytes { get; } - public Size Dpi { get; } - public PixelFormat Format { get; } - } -} + context.DrawImage(new CGRect(0, 0, Width, Height), image); + } + Marshal.FreeHGlobal(Address); + Address = IntPtr.Zero; + } + + public IntPtr Address { get; private set; } + public int Width { get; } + public int Height { get; } + public int RowBytes { get; } + public Size Dpi { get; } + public PixelFormat Format { get; } + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/Helpers.cs b/src/OSX/Avalonia.MonoMac/Helpers.cs index 90fd88d725..b4e3f7e0b7 100644 --- a/src/OSX/Avalonia.MonoMac/Helpers.cs +++ b/src/OSX/Avalonia.MonoMac/Helpers.cs @@ -1,9 +1,7 @@ using System; -using System.Runtime.InteropServices; -using MonoMac.AppKit; -using MonoMac.ObjCRuntime; -using MonoMac.CoreGraphics; -using MonoMac; + using MonoMac.AppKit; + using MonoMac.CoreGraphics; + namespace Avalonia.MonoMac { static class Helpers @@ -15,19 +13,19 @@ namespace Avalonia.MonoMac public static Rect ToAvaloniaRect(this CGRect rect) => new Rect(rect.Left, rect.Top, rect.Width, rect.Height); public static CGRect ToMonoMacRect(this Rect rect) => new CGRect(rect.X, rect.Y, rect.Width, rect.Height); - public static Point ConvertPointY(this Point pt) - { - var sw = NSScreen.Screens[0].Frame; - var t = Math.Max(sw.Top, sw.Bottom); - return pt.WithY(t - pt.Y); - } + public static Point ConvertPointY(this Point pt) + { + var sw = NSScreen.Screens[0].Frame; + var t = Math.Max(sw.Top, sw.Bottom); + return pt.WithY(t - pt.Y); + } - public static CGPoint ConvertPointY(this CGPoint pt) - { - var sw = NSScreen.Screens[0].Frame; - var t = Math.Max(sw.Top, sw.Bottom); + public static CGPoint ConvertPointY(this CGPoint pt) + { + var sw = NSScreen.Screens[0].Frame; + var t = Math.Max(sw.Top, sw.Bottom); return new CGPoint(pt.X, t - pt.Y); - } + } } } diff --git a/src/OSX/Avalonia.MonoMac/KeyTransform.cs b/src/OSX/Avalonia.MonoMac/KeyTransform.cs index 930b6f7b81..6d4b58031e 100644 --- a/src/OSX/Avalonia.MonoMac/KeyTransform.cs +++ b/src/OSX/Avalonia.MonoMac/KeyTransform.cs @@ -1,14 +1,14 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Avalonia.Input; -using MonoMac.AppKit; namespace Avalonia.MonoMac { public static class KeyTransform { - // See /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h - private const int kVK_ANSI_A = 0x00; + // See /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h + // ReSharper disable InconsistentNaming + // ReSharper disable UnusedMember.Local + private const int kVK_ANSI_A = 0x00; private const int kVK_ANSI_S = 0x01; private const int kVK_ANSI_D = 0x02; private const int kVK_ANSI_F = 0x03; @@ -127,130 +127,131 @@ namespace Avalonia.MonoMac private const int kVK_JIS_KeypadComma = 0x5F; private const int kVK_JIS_Eisu = 0x66; private const int kVK_JIS_Kana = 0x68; - + // ReSharper restore UnusedMember.Local + // ReSharper restore InconsistentNaming //TODO: Map missing keys static readonly Dictionary Keys = new Dictionary { - [kVK_ANSI_A] = Key.A, - [kVK_ANSI_S] = Key.S, - [kVK_ANSI_D] = Key.D, - [kVK_ANSI_F] = Key.F, - [kVK_ANSI_H] = Key.H, - [kVK_ANSI_G] = Key.G, - [kVK_ANSI_Z] = Key.Z, - [kVK_ANSI_X] = Key.X, - [kVK_ANSI_C] = Key.C, - [kVK_ANSI_V] = Key.V, - [kVK_ANSI_B] = Key.B, - [kVK_ANSI_Q] = Key.Q, - [kVK_ANSI_W] = Key.W, - [kVK_ANSI_E] = Key.E, - [kVK_ANSI_R] = Key.R, - [kVK_ANSI_Y] = Key.Y, - [kVK_ANSI_T] = Key.T, + [kVK_ANSI_A] = Key.A, + [kVK_ANSI_S] = Key.S, + [kVK_ANSI_D] = Key.D, + [kVK_ANSI_F] = Key.F, + [kVK_ANSI_H] = Key.H, + [kVK_ANSI_G] = Key.G, + [kVK_ANSI_Z] = Key.Z, + [kVK_ANSI_X] = Key.X, + [kVK_ANSI_C] = Key.C, + [kVK_ANSI_V] = Key.V, + [kVK_ANSI_B] = Key.B, + [kVK_ANSI_Q] = Key.Q, + [kVK_ANSI_W] = Key.W, + [kVK_ANSI_E] = Key.E, + [kVK_ANSI_R] = Key.R, + [kVK_ANSI_Y] = Key.Y, + [kVK_ANSI_T] = Key.T, [kVK_ANSI_1] = Key.D1, - [kVK_ANSI_2] = Key.D2, - [kVK_ANSI_3] = Key.D3, - [kVK_ANSI_4] = Key.D4, - [kVK_ANSI_6] = Key.D6, - [kVK_ANSI_5] = Key.D5, - //[kVK_ANSI_Equal] = Key.?, - [kVK_ANSI_9] = Key.D9, - [kVK_ANSI_7] = Key.D7, - [kVK_ANSI_Minus] = Key.OemMinus, - [kVK_ANSI_8] = Key.D8, - [kVK_ANSI_0] = Key.D0, + [kVK_ANSI_2] = Key.D2, + [kVK_ANSI_3] = Key.D3, + [kVK_ANSI_4] = Key.D4, + [kVK_ANSI_6] = Key.D6, + [kVK_ANSI_5] = Key.D5, + //[kVK_ANSI_Equal] = Key.?, + [kVK_ANSI_9] = Key.D9, + [kVK_ANSI_7] = Key.D7, + [kVK_ANSI_Minus] = Key.OemMinus, + [kVK_ANSI_8] = Key.D8, + [kVK_ANSI_0] = Key.D0, [kVK_ANSI_RightBracket] = Key.OemCloseBrackets, - [kVK_ANSI_O] = Key.O, - [kVK_ANSI_U] = Key.U, + [kVK_ANSI_O] = Key.O, + [kVK_ANSI_U] = Key.U, [kVK_ANSI_LeftBracket] = Key.OemOpenBrackets, - [kVK_ANSI_I] = Key.I, - [kVK_ANSI_P] = Key.P, - [kVK_ANSI_L] = Key.L, - [kVK_ANSI_J] = Key.J, - [kVK_ANSI_Quote] = Key.OemQuotes, - [kVK_ANSI_K] = Key.K, - [kVK_ANSI_Semicolon] = Key.OemSemicolon, - [kVK_ANSI_Backslash] = Key.OemBackslash, - [kVK_ANSI_Comma] = Key.OemComma, - //[kVK_ANSI_Slash] = Key.?, - [kVK_ANSI_N] = Key.N, - [kVK_ANSI_M] = Key.M, - [kVK_ANSI_Period] = Key.OemPeriod, - //[kVK_ANSI_Grave] = Key.?, - [kVK_ANSI_KeypadDecimal] = Key.Decimal, - [kVK_ANSI_KeypadMultiply] = Key.Multiply, - [kVK_ANSI_KeypadPlus] = Key.OemPlus, - [kVK_ANSI_KeypadClear] = Key.Clear, - [kVK_ANSI_KeypadDivide] = Key.Divide, - [kVK_ANSI_KeypadEnter] = Key.Enter, - [kVK_ANSI_KeypadMinus] = Key.OemMinus, - //[kVK_ANSI_KeypadEquals] = Key.?, - [kVK_ANSI_Keypad0] = Key.NumPad0, - [kVK_ANSI_Keypad1] = Key.NumPad1, - [kVK_ANSI_Keypad2] = Key.NumPad2, - [kVK_ANSI_Keypad3] = Key.NumPad3, - [kVK_ANSI_Keypad4] = Key.NumPad4, - [kVK_ANSI_Keypad5] = Key.NumPad5, - [kVK_ANSI_Keypad6] = Key.NumPad6, - [kVK_ANSI_Keypad7] = Key.NumPad7, - [kVK_ANSI_Keypad8] = Key.NumPad8, - [kVK_ANSI_Keypad9] = Key.NumPad9, - [kVK_Return] = Key.Return, - [kVK_Tab] = Key.Tab, - [kVK_Space] = Key.Space, - [kVK_Delete] = Key.Delete, - [kVK_Escape] = Key.Escape, + [kVK_ANSI_I] = Key.I, + [kVK_ANSI_P] = Key.P, + [kVK_ANSI_L] = Key.L, + [kVK_ANSI_J] = Key.J, + [kVK_ANSI_Quote] = Key.OemQuotes, + [kVK_ANSI_K] = Key.K, + [kVK_ANSI_Semicolon] = Key.OemSemicolon, + [kVK_ANSI_Backslash] = Key.OemBackslash, + [kVK_ANSI_Comma] = Key.OemComma, + //[kVK_ANSI_Slash] = Key.?, + [kVK_ANSI_N] = Key.N, + [kVK_ANSI_M] = Key.M, + [kVK_ANSI_Period] = Key.OemPeriod, + //[kVK_ANSI_Grave] = Key.?, + [kVK_ANSI_KeypadDecimal] = Key.Decimal, + [kVK_ANSI_KeypadMultiply] = Key.Multiply, + [kVK_ANSI_KeypadPlus] = Key.OemPlus, + [kVK_ANSI_KeypadClear] = Key.Clear, + [kVK_ANSI_KeypadDivide] = Key.Divide, + [kVK_ANSI_KeypadEnter] = Key.Enter, + [kVK_ANSI_KeypadMinus] = Key.OemMinus, + //[kVK_ANSI_KeypadEquals] = Key.?, + [kVK_ANSI_Keypad0] = Key.NumPad0, + [kVK_ANSI_Keypad1] = Key.NumPad1, + [kVK_ANSI_Keypad2] = Key.NumPad2, + [kVK_ANSI_Keypad3] = Key.NumPad3, + [kVK_ANSI_Keypad4] = Key.NumPad4, + [kVK_ANSI_Keypad5] = Key.NumPad5, + [kVK_ANSI_Keypad6] = Key.NumPad6, + [kVK_ANSI_Keypad7] = Key.NumPad7, + [kVK_ANSI_Keypad8] = Key.NumPad8, + [kVK_ANSI_Keypad9] = Key.NumPad9, + [kVK_Return] = Key.Return, + [kVK_Tab] = Key.Tab, + [kVK_Space] = Key.Space, + [kVK_Delete] = Key.Delete, + [kVK_Escape] = Key.Escape, [kVK_Command] = Key.LWin, - [kVK_Shift] = Key.LeftShift, - [kVK_CapsLock] = Key.CapsLock, + [kVK_Shift] = Key.LeftShift, + [kVK_CapsLock] = Key.CapsLock, [kVK_Option] = Key.LeftAlt, - [kVK_Control] = Key.LeftCtrl, - [kVK_RightCommand] = Key.RWin, - [kVK_RightShift] = Key.RightShift, - [kVK_RightOption] = Key.RightAlt, - [kVK_RightControl] = Key.RightCtrl, - //[kVK_Function] = Key.?, - [kVK_F17] = Key.F17, - [kVK_VolumeUp] = Key.VolumeUp, - [kVK_VolumeDown] = Key.VolumeDown, - [kVK_Mute] = Key.VolumeMute, - [kVK_F18] = Key.F18, - [kVK_F19] = Key.F19, - [kVK_F20] = Key.F20, - [kVK_F5] = Key.F5, - [kVK_F6] = Key.F6, - [kVK_F7] = Key.F7, - [kVK_F3] = Key.F3, - [kVK_F8] = Key.F8, - [kVK_F9] = Key.F9, - [kVK_F11] = Key.F11, - [kVK_F13] = Key.F13, - [kVK_F16] = Key.F16, - [kVK_F14] = Key.F14, - [kVK_F10] = Key.F10, - [kVK_F12] = Key.F12, - [kVK_F15] = Key.F15, - [kVK_Help] = Key.Help, - [kVK_Home] = Key.Home, - [kVK_PageUp] = Key.PageUp, - [kVK_ForwardDelete] = Key.Delete, - [kVK_F4] = Key.F4, - [kVK_End] = Key.End, - [kVK_F2] = Key.F2, - [kVK_PageDown] = Key.PageDown, - [kVK_F1] = Key.F1, - [kVK_LeftArrow] = Key.Left, - [kVK_RightArrow] = Key.Right, - [kVK_DownArrow] = Key.Down, - [kVK_UpArrow] = Key.Up, + [kVK_Control] = Key.LeftCtrl, + [kVK_RightCommand] = Key.RWin, + [kVK_RightShift] = Key.RightShift, + [kVK_RightOption] = Key.RightAlt, + [kVK_RightControl] = Key.RightCtrl, + //[kVK_Function] = Key.?, + [kVK_F17] = Key.F17, + [kVK_VolumeUp] = Key.VolumeUp, + [kVK_VolumeDown] = Key.VolumeDown, + [kVK_Mute] = Key.VolumeMute, + [kVK_F18] = Key.F18, + [kVK_F19] = Key.F19, + [kVK_F20] = Key.F20, + [kVK_F5] = Key.F5, + [kVK_F6] = Key.F6, + [kVK_F7] = Key.F7, + [kVK_F3] = Key.F3, + [kVK_F8] = Key.F8, + [kVK_F9] = Key.F9, + [kVK_F11] = Key.F11, + [kVK_F13] = Key.F13, + [kVK_F16] = Key.F16, + [kVK_F14] = Key.F14, + [kVK_F10] = Key.F10, + [kVK_F12] = Key.F12, + [kVK_F15] = Key.F15, + [kVK_Help] = Key.Help, + [kVK_Home] = Key.Home, + [kVK_PageUp] = Key.PageUp, + [kVK_ForwardDelete] = Key.Delete, + [kVK_F4] = Key.F4, + [kVK_End] = Key.End, + [kVK_F2] = Key.F2, + [kVK_PageDown] = Key.PageDown, + [kVK_F1] = Key.F1, + [kVK_LeftArrow] = Key.Left, + [kVK_RightArrow] = Key.Right, + [kVK_DownArrow] = Key.Down, + [kVK_UpArrow] = Key.Up, /* - [kVK_ISO_Section] = Key.?, - [kVK_JIS_Yen] = Key.?, - [kVK_JIS_Underscore] = Key.?, - [kVK_JIS_KeypadComma] = Key.?, - [kVK_JIS_Eisu] = Key.?, - [kVK_JIS_Kana] = Key.? + [kVK_ISO_Section] = Key.?, + [kVK_JIS_Yen] = Key.?, + [kVK_JIS_Underscore] = Key.?, + [kVK_JIS_KeypadComma] = Key.?, + [kVK_JIS_Eisu] = Key.?, + [kVK_JIS_Kana] = Key.? */ }; diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs index c3f07a2439..4ffb11ab94 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -3,17 +3,15 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Platform; using Avalonia.Rendering; -using MonoMac.Foundation; using MonoMac.AppKit; -using System.Drawing; namespace Avalonia.MonoMac { public class MonoMacPlatform : IWindowingPlatform, IPlatformSettings { internal static MonoMacPlatform Instance { get; private set; } - MouseDevice _mouseDevice = new MouseDevice(); - KeyboardDevice _keyboardDevice = new KeyboardDevice(); + readonly MouseDevice _mouseDevice = new MouseDevice(); + readonly KeyboardDevice _keyboardDevice = new KeyboardDevice(); NSApplication _app; void DoInitialize() { @@ -46,8 +44,8 @@ namespace Avalonia.MonoMac } - public Size DoubleClickSize => new Size(4, 4); - public TimeSpan DoubleClickTime => TimeSpan.FromSeconds(NSEvent.DoubleClickInterval); + public Size DoubleClickSize => new Size(4, 4); + public TimeSpan DoubleClickTime => TimeSpan.FromSeconds(NSEvent.DoubleClickInterval); public IWindowImpl CreateWindow() => new WindowImpl(); @@ -70,8 +68,7 @@ namespace Avalonia { public static AppBuilderBase UseMonoMac(this AppBuilderBase builder) where T : AppBuilderBase, new() { - return builder.UseWindowingSubsystem(Avalonia.MonoMac.MonoMacPlatform.Initialize); + return builder.UseWindowingSubsystem(MonoMac.MonoMacPlatform.Initialize); } } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs index 4d15a164e0..d926785229 100644 --- a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs +++ b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs @@ -9,26 +9,26 @@ namespace Avalonia.MonoMac { class PlatformThreadingInterface : IPlatformThreadingInterface { - private bool _signaled; - public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface(); - public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread; + private bool _signaled; + public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface(); + public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread; - public event Action Signaled; + public event Action Signaled; public IDisposable StartTimer(TimeSpan interval, Action tick) - => NSTimer.CreateRepeatingScheduledTimer(interval, () => tick()); + => NSTimer.CreateRepeatingScheduledTimer(interval, () => tick()); - public void Signal() - { - lock (this) - { - if (_signaled) - return; - _signaled = true; - } + public void Signal() + { + lock (this) + { + if (_signaled) + return; + _signaled = true; + } NSApplication.SharedApplication.BeginInvokeOnMainThread(() => { - lock(this) + lock (this) { if (!_signaled) return; @@ -36,7 +36,7 @@ namespace Avalonia.MonoMac } Signaled?.Invoke(); }); - } + } @@ -49,7 +49,7 @@ namespace Avalonia.MonoMac cancellationToken.Register(() => { app.PostEvent(NSEvent.OtherEvent(NSEventType.ApplicationDefined, default(CGPoint), - default(NSEventModifierMask), 0, 0, null, 0, 0, 0), true); + default(NSEventModifierMask), 0, 0, null, 0, 0, 0), true); }); while (!cancellationToken.IsCancellationRequested) { @@ -62,4 +62,4 @@ namespace Avalonia.MonoMac } } } -} +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/PopupImpl.cs b/src/OSX/Avalonia.MonoMac/PopupImpl.cs index 0b1ee537f3..ba4b7f0eac 100644 --- a/src/OSX/Avalonia.MonoMac/PopupImpl.cs +++ b/src/OSX/Avalonia.MonoMac/PopupImpl.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Platform; +using Avalonia.Platform; using MonoMac.AppKit; namespace Avalonia.MonoMac @@ -11,9 +10,9 @@ namespace Avalonia.MonoMac UpdateStyle(); } - protected override NSWindowStyle GetStyle() - { + protected override NSWindowStyle GetStyle() + { return NSWindowStyle.Borderless; - } + } } -} +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/Stubs.cs b/src/OSX/Avalonia.MonoMac/Stubs.cs index ec7609fa4f..f3ff6a260c 100644 --- a/src/OSX/Avalonia.MonoMac/Stubs.cs +++ b/src/OSX/Avalonia.MonoMac/Stubs.cs @@ -1,8 +1,5 @@ -using System; -using System.IO; -using Avalonia.Input; +using System.IO; using Avalonia.Platform; -using MonoMac.AppKit; namespace Avalonia.MonoMac { @@ -35,8 +32,8 @@ namespace Avalonia.MonoMac public IWindowIconImpl LoadIcon(Stream stream) { - return new IconStub( - AvaloniaLocator.Current.GetService().LoadBitmap(stream)); + return new IconStub( + AvaloniaLocator.Current.GetService().LoadBitmap(stream)); } public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) @@ -47,5 +44,4 @@ namespace Avalonia.MonoMac return LoadIcon(ms); } } - } \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 2854aba9c9..78c8434974 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -15,7 +15,8 @@ namespace Avalonia.MonoMac abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface { public TopLevelView View { get; } - public TopLevelImpl() + + protected TopLevelImpl() { View = new TopLevelView(this); } @@ -29,12 +30,13 @@ namespace Avalonia.MonoMac private readonly IKeyboardDevice _keyboard; private NSTrackingArea _area; private NSCursor _cursor; + public TopLevelView(TopLevelImpl tl) { _tl = tl; _mouse = AvaloniaLocator.Current.GetService(); _keyboard = AvaloniaLocator.Current.GetService(); - } + } public override bool ConformsToProtocol(IntPtr protocol) { @@ -60,7 +62,8 @@ namespace Avalonia.MonoMac AddCursorRect(Frame, _cursor); } - static NSCursor ArrowCursor = NSCursor.ArrowCursor; + static readonly NSCursor ArrowCursor = NSCursor.ArrowCursor; + public void SetCursor(NSCursor cursor) { _cursor = cursor ?? ArrowCursor; @@ -74,12 +77,12 @@ namespace Avalonia.MonoMac if (_area != null) { RemoveTrackingArea(_area); - _area.Dispose(); ; + _area.Dispose(); } _area = new NSTrackingArea(new CGRect(default(CGPoint), newSize), - NSTrackingAreaOptions.ActiveAlways | - NSTrackingAreaOptions.MouseMoved | - NSTrackingAreaOptions.EnabledDuringMouseDrag, this, null); + NSTrackingAreaOptions.ActiveAlways | + NSTrackingAreaOptions.MouseMoved | + NSTrackingAreaOptions.EnabledDuringMouseDrag, this, null); AddTrackingArea(_area); UpdateCursor(); _tl?.Resized?.Invoke(_tl.ClientSize); @@ -116,7 +119,7 @@ namespace Avalonia.MonoMac return rv; } - uint GetTimeStamp(NSEvent ev) => (uint)(ev.Timestamp * 1000); + uint GetTimeStamp(NSEvent ev) => (uint) (ev.Timestamp * 1000); void MouseEvent(NSEvent ev, RawMouseEventType type) { @@ -127,10 +130,12 @@ namespace Avalonia.MonoMac if (type == RawMouseEventType.Wheel) { var delta = GetDelta(ev); + // ReSharper disable CompareOfFloatsByEqualityOperator if (delta.X == 0 && delta.Y == 0) return; + // ReSharper restore CompareOfFloatsByEqualityOperator _tl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc, - delta, mod)); + delta, mod)); } else _tl.Input?.Invoke(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod)); @@ -224,7 +229,7 @@ namespace Avalonia.MonoMac if (!code.HasValue) return; _tl.Input?.Invoke(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev), - type, code.Value, GetModifiers(ev.ModifierFlags))); + type, code.Value, GetModifiers(ev.ModifierFlags))); } public override void KeyDown(NSEvent theEvent) @@ -246,43 +251,36 @@ namespace Avalonia.MonoMac public override bool AcceptsFirstResponder() => true; - public bool HasMarkedText + public bool HasMarkedText { - [Export("hasMarkedText")] - get { return false; } + [Export("hasMarkedText")] get => false; } - public NSRange MarkedRange - { - [Export("markedRange")] - get { return new NSRange(NSRange.NotFound, 0); } - } + public NSRange MarkedRange + { + [Export("markedRange")] get => new NSRange(NSRange.NotFound, 0); + } public NSRange SelectedRange - { - [Export("selectedRange")] - get { return new NSRange(NSRange.NotFound, 0); } - } + { + [Export("selectedRange")] get => new NSRange(NSRange.NotFound, 0); + } [Export("setMarkedText:selectedRange:replacementRange:")] public void SetMarkedText(NSString str, NSRange a1, NSRange a2) { - + } [Export("unmarkText")] public void UnmarkText() { - + } public NSArray ValidAttributesForMarkedText { - [Export("validAttributesForMarkedText")] - get - { - return new NSArray(); - } + [Export("validAttributesForMarkedText")] get => new NSArray(); } [Export("attributedSubstringForProposedRange:actualRange:")] @@ -310,14 +308,14 @@ namespace Avalonia.MonoMac return new CGRect(); } - #endregion - } + #endregion + } public IInputRoot InputRoot { get; private set; } public abstract Size ClientSize { get; } - public double Scaling + public double Scaling { get { @@ -327,42 +325,35 @@ namespace Avalonia.MonoMac } } - public IEnumerable Surfaces => new[] { this }; + public IEnumerable Surfaces => new[] {this}; + + #region Events - #region Events - public Action Input { get; set; } - public Action Paint { get; set; } - public Action Resized { get; set; } - public Action ScalingChanged { get; set; } - public Action Closed { get; set; } - #endregion + public Action Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action Closed { get; set; } - public virtual void Dispose() + #endregion + + public virtual void Dispose() { Closed?.Invoke(); Closed = null; View.Dispose(); } - public void Invalidate(Rect rect) - { - View.SetNeedsDisplayInRect(View.Frame); - } + public void Invalidate(Rect rect) => View.SetNeedsDisplayInRect(View.Frame); public abstract Point PointToClient(Point point); public abstract Point PointToScreen(Point point); - public void SetCursor(IPlatformHandle cursor) - { - View.SetCursor((cursor as Cursor)?.Native); - } + public void SetCursor(IPlatformHandle cursor) => View.SetCursor((cursor as Cursor)?.Native); - public void SetInputRoot(IInputRoot inputRoot) - { - InputRoot = inputRoot; - } + public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot; public ILockedFramebuffer Lock() => new EmulatedFramebuffer(View); } -} +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs index 9186d3ded5..cd53cb5dc0 100644 --- a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Input.Raw; using Avalonia.Platform; using MonoMac.AppKit; using MonoMac.ObjCRuntime; @@ -13,14 +10,17 @@ namespace Avalonia.MonoMac { public CustomWindow Window { get; private set; } - public WindowBaseImpl() - { - Window = new CustomWindow(this); - Window.StyleMask = NSWindowStyle.Titled; - Window.BackingType = NSBackingStore.Buffered; - Window.ContentView = View; - Window.Delegate = CreateWindowDelegate(); - } + public WindowBaseImpl() + { + Window = new CustomWindow(this) + { + StyleMask = NSWindowStyle.Titled, + BackingType = NSBackingStore.Buffered, + ContentView = View, + // ReSharper disable once VirtualMemberCallInConstructor + Delegate = CreateWindowDelegate() + }; + } public class CustomWindow : NSWindow { @@ -71,49 +71,28 @@ namespace Avalonia.MonoMac public Point Position { - get - { - var pos = Window.Frame.ToAvaloniaRect().BottomLeft.ConvertPointY(); - //Console.WriteLine($"GET pos {pos}"); - return pos; - } - set - { - //Console.WriteLine($"SET pos {value}"); - Window.CascadeTopLeftFromPoint(value.ToMonoMacPoint().ConvertPointY()); - } + get => Window.Frame.ToAvaloniaRect().BottomLeft.ConvertPointY(); + set => Window.CascadeTopLeftFromPoint(value.ToMonoMacPoint().ConvertPointY()); } - protected virtual NSWindowStyle GetStyle() - { - return NSWindowStyle.Borderless; - } + protected virtual NSWindowStyle GetStyle() => NSWindowStyle.Borderless; - protected void UpdateStyle() - { - Window.StyleMask = GetStyle(); - } + protected void UpdateStyle() => Window.StyleMask = GetStyle(); IPlatformHandle IWindowBaseImpl.Handle => new PlatformHandle(Window.Handle, "NSWindow"); public Size MaxClientSize => NSScreen.Screens[0].Frame.ToAvaloniaRect().Size; - public Action PositionChanged { get; set; } - public Action Deactivated { get; set; } - public Action Activated { get; set; } + public Action PositionChanged { get; set; } + public Action Deactivated { get; set; } + public Action Activated { get; set; } - public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize(); + public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize(); - public void Show() - { - Window.MakeKeyAndOrderFront(Window); - } + public void Show() => Window.MakeKeyAndOrderFront(Window); - public void Hide() - { - Window?.OrderOut(Window); - } + public void Hide() => Window?.OrderOut(Window); public void BeginMoveDrag() @@ -130,10 +109,7 @@ namespace Avalonia.MonoMac //TODO: Intercept mouse events and implement resize drag manually } - public void Activate() - { - Window.MakeKeyWindow(); - } + public void Activate() => Window.MakeKeyWindow(); public void Resize(Size clientSize) { @@ -162,7 +138,7 @@ namespace Avalonia.MonoMac { Window?.Close(); Window?.Dispose(); - base.Dispose(); + base.Dispose(); } } } diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index bef24cc6b1..dc6cca9979 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -8,6 +8,7 @@ namespace Avalonia.MonoMac class WindowImpl : WindowBaseImpl, IWindowImpl { bool _decorated = true; + public WindowImpl() { UpdateStyle(); @@ -45,13 +46,14 @@ namespace Avalonia.MonoMac public void SetIcon(IWindowIconImpl icon) { - //No-OP, see http://stackoverflow.com/a/7038671/2231814 - } + //No-OP, see http://stackoverflow.com/a/7038671/2231814 + } protected override NSWindowStyle GetStyle() { if (_decorated) - return NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled; + return NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | + NSWindowStyle.Titled; return NSWindowStyle.Borderless; } @@ -61,23 +63,20 @@ namespace Avalonia.MonoMac UpdateStyle(); } - public void SetTitle(string title) - { - Window.Title = title; - } + public void SetTitle(string title) => Window.Title = title; class ModalDisposable : IDisposable { - readonly WindowImpl impl; + readonly WindowImpl _impl; public ModalDisposable(WindowImpl impl) { - this.impl = impl; + _impl = impl; } public void Dispose() { - impl.Window.OrderOut(impl.Window); + _impl.Window.OrderOut(_impl.Window); } } @@ -89,4 +88,4 @@ namespace Avalonia.MonoMac return new ModalDisposable(this); } } -} +} \ No newline at end of file From f7011a3fd26a1bdeb9f2db9ae8d7c08f4c5dd825 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2017 22:47:19 +0300 Subject: [PATCH 03/14] [MONOMAC] Fixed retina support --- src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs index e524a706cc..0f33cdfa84 100644 --- a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs +++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs @@ -8,15 +8,15 @@ namespace Avalonia.MonoMac { class EmulatedFramebuffer : ILockedFramebuffer { + private readonly CGSize _logicalSize; public EmulatedFramebuffer(NSView view) { - //TODO: Check if this is correct - var factor = view.Window.UserSpaceScaleFactor; - var frame = view.Frame; - Width = (int) (frame.Width * factor); - Height = (int) (frame.Height * factor); + _logicalSize = view.Frame.Size; + var pixelSize = view.ConvertSizeToBacking(_logicalSize); + Width = (int)pixelSize.Width; + Height = (int)pixelSize.Height; RowBytes = Width * 4; - Dpi = new Size(96, 96) * factor; + Dpi = new Size(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height); Format = PixelFormat.Rgba8888; Address = Marshal.AllocHGlobal(Height * RowBytes); } @@ -35,8 +35,8 @@ namespace Avalonia.MonoMac using (var context = nscontext.GraphicsPort) { context.SetFillColor(255, 255, 255, 255); - context.FillRect(new CGRect(0, 0, Width, Height)); - context.DrawImage(new CGRect(0, 0, Width, Height), image); + context.FillRect(new CGRect(default(CGPoint), _logicalSize)); + context.DrawImage(new CGRect(default(CGPoint), _logicalSize), image); } Marshal.FreeHGlobal(Address); Address = IntPtr.Zero; From b3f3c010c7ab22ac0a46a6272f438eaa5df49a1a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 8 Sep 2017 17:57:27 +0300 Subject: [PATCH 04/14] [MONOMAC] Updated code to new APIs and added it to solution --- Avalonia.sln | 52 ++++++++++++++++++- samples/ControlCatalog.NetCore/Program.cs | 13 ++++- .../Avalonia.DotNetCoreRuntime.csproj | 1 + src/OSX/Avalonia.MonoMac/.gitignore | 1 - .../Avalonia.MonoMac/Avalonia.MonoMac.csproj | 14 +++-- .../Avalonia.MonoMac/EmulatedFramebuffer.cs | 4 +- src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs | 7 ++- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 13 ++--- src/OSX/Avalonia.MonoMac/WindowImpl.cs | 5 ++ 9 files changed, 90 insertions(+), 20 deletions(-) delete mode 100644 src/OSX/Avalonia.MonoMac/.gitignore diff --git a/Avalonia.sln b/Avalonia.sln index 391d02382e..f1dec39fde 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject @@ -129,7 +129,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInteropTest", "sampl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GtkInteropDemo", "samples\interop\GtkInteropDemo\GtkInteropDemo.csproj", "{BD7F352C-6DC1-4740-BAF2-2D34A038728C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}" EndProject @@ -185,6 +185,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OSX", "OSX", "{A59C4C0A-64DF-4621-B450-2BA00D6F61E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MonoMac", "src\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj", "{CBFD5788-567D-401B-9DFA-74E4224025A0}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -2514,6 +2518,46 @@ Global {E1582370-37B3-403C-917F-8209551B1634}.Release|Mono.Build.0 = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|x86.ActiveCfg = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|x86.Build.0 = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Mono.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|x86.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|x86.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Mono.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Mono.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|x86.Build.0 = Debug|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.Build.0 = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.ActiveCfg = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.Build.0 = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Mono.ActiveCfg = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Mono.Build.0 = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.ActiveCfg = Release|Any CPU + {CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2571,5 +2615,9 @@ Global {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} + {CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} EndGlobalSection EndGlobal diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 7c67a239e6..b959e7eba3 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -1,10 +1,11 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using Avalonia; namespace ControlCatalog.NetCore { - class Program + static class Program { static void Main(string[] args) { @@ -15,10 +16,18 @@ namespace ControlCatalog.NetCore }); else AppBuilder.Configure() - .UsePlatformDetect() + .CustomPlatformDetect() .Start(); } + static AppBuilder CustomPlatformDetect(this AppBuilder builder) + { + //This is needed because we still aren't ready to have MonoMac backend as default one + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return builder.UseSkia().UseMonoMac(); + return builder.UsePlatformDetect(); + } + static void ConsoleSilencer() { Console.CursorVisible = false; diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj index 5c72d433ce..eade213c4c 100644 --- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj +++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj @@ -16,6 +16,7 @@ + diff --git a/src/OSX/Avalonia.MonoMac/.gitignore b/src/OSX/Avalonia.MonoMac/.gitignore deleted file mode 100644 index 2d6e7a861f..0000000000 --- a/src/OSX/Avalonia.MonoMac/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Avalonia.MonoMac.sln diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj index 8ae7dc8470..80a0b661d8 100644 --- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -1,11 +1,19 @@ - netcoreapp2.0;netstandard2.0;net461 + netstandard2.0 True - - + + + + + + + + + + diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs index 0f33cdfa84..1ecfa0eb7d 100644 --- a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs +++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs @@ -16,7 +16,7 @@ namespace Avalonia.MonoMac Width = (int)pixelSize.Width; Height = (int)pixelSize.Height; RowBytes = Width * 4; - Dpi = new Size(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height); + Dpi = new Vector(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height); Format = PixelFormat.Rgba8888; Address = Marshal.AllocHGlobal(Height * RowBytes); } @@ -46,7 +46,7 @@ namespace Avalonia.MonoMac public int Width { get; } public int Height { get; } public int RowBytes { get; } - public Size Dpi { get; } + public Vector Dpi { get; } public PixelFormat Format { get; } } } \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs index 4ffb11ab94..114a828a77 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -10,7 +10,7 @@ namespace Avalonia.MonoMac public class MonoMacPlatform : IWindowingPlatform, IPlatformSettings { internal static MonoMacPlatform Instance { get; private set; } - readonly MouseDevice _mouseDevice = new MouseDevice(); + internal readonly MouseDevice MouseDevice = new MouseDevice(); readonly KeyboardDevice _keyboardDevice = new KeyboardDevice(); NSApplication _app; void DoInitialize() @@ -19,9 +19,8 @@ namespace Avalonia.MonoMac .Bind().ToTransient() .Bind().ToSingleton() .Bind().ToConstant(_keyboardDevice) - .Bind().ToConstant(_mouseDevice) + .Bind().ToConstant(MouseDevice) .Bind().ToConstant(this) - .Bind().ToConstant(ImmediateRenderer.Factory) .Bind().ToConstant(this) .Bind().ToConstant(PlatformThreadingInterface.Instance); @@ -66,7 +65,7 @@ namespace Avalonia { public static class MonoMacPlatformExtensions { - public static AppBuilderBase UseMonoMac(this AppBuilderBase builder) where T : AppBuilderBase, new() + public static T UseMonoMac(this T builder) where T : AppBuilderBase, new() { return builder.UseWindowingSubsystem(MonoMac.MonoMacPlatform.Initialize); } diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 78c8434974..c2a5e15b44 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Rendering; using MonoMac.AppKit; using MonoMac.CoreGraphics; @@ -15,7 +16,7 @@ namespace Avalonia.MonoMac abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface { public TopLevelView View { get; } - + private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService(); protected TopLevelImpl() { View = new TopLevelView(this); @@ -326,17 +327,15 @@ namespace Avalonia.MonoMac } public IEnumerable Surfaces => new[] {this}; - - #region Events - + public IMouseDevice MouseDevice => _mouse; + public Action Input { get; set; } public Action Paint { get; set; } public Action Resized { get; set; } public Action ScalingChanged { get; set; } public Action Closed { get; set; } - #endregion - + public virtual void Dispose() { Closed?.Invoke(); @@ -344,6 +343,8 @@ namespace Avalonia.MonoMac View.Dispose(); } + public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root); + public void Invalidate(Rect rect) => View.SetNeedsDisplayInRect(View.Frame); public abstract Point PointToClient(Point point); diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index dc6cca9979..28dbc62914 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -49,6 +49,11 @@ namespace Avalonia.MonoMac //No-OP, see http://stackoverflow.com/a/7038671/2231814 } + public void ShowTaskbarIcon(bool value) + { + //No-OP, there is no such this as taskbar in OSX + } + protected override NSWindowStyle GetStyle() { if (_decorated) From 3f466547845c54bedf7135799eefd9acf41ca85b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 8 Sep 2017 18:21:44 +0300 Subject: [PATCH 05/14] Added Avalonia.MonoMac package --- build/MonoMac.props | 5 +++++ packages.cake | 14 ++++++++++++++ src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj | 1 + 3 files changed, 20 insertions(+) create mode 100644 build/MonoMac.props diff --git a/build/MonoMac.props b/build/MonoMac.props new file mode 100644 index 0000000000..452177ca5d --- /dev/null +++ b/build/MonoMac.props @@ -0,0 +1,5 @@ + + + + + diff --git a/packages.cake b/packages.cake index 2c0298b669..6c2d0f97f0 100644 --- a/packages.cake +++ b/packages.cake @@ -431,6 +431,20 @@ public class Packages BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot }, + new NuGetPackSettings() + { + Id = "Avalonia.MonoMac", + Dependencies = new DependencyBuilder(this) + { + new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version } + }.Dep("MonoMac.NetStandard").ToArray(), + Files = new [] + { + new NuSpecContent { Source = "netstandard2.0/Avalonia.MonoMac.dll", Target = "lib/netstandard2.0" }, + }, + BasePath = context.Directory("./src/OSX/Avalonia.MonoMac/bin/" + parameters.DirSuffix), + OutputDirectory = parameters.NugetRoot + }, /////////////////////////////////////////////////////////////////////////////// // Avalonia.Desktop /////////////////////////////////////////////////////////////////////////////// diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj index 80a0b661d8..43816fc801 100644 --- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -16,4 +16,5 @@ + From eddabf7d540606cc3d7817df3b24458c8847c93f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 9 Sep 2017 03:01:04 +0300 Subject: [PATCH 06/14] Added "System dialogs" page for ControlCatalog --- samples/ControlCatalog/ControlCatalog.csproj | 6 +++ samples/ControlCatalog/MainView.xaml | 2 +- samples/ControlCatalog/MainView.xaml.cs | 9 ++++ samples/ControlCatalog/MainWindow.xaml.cs | 2 +- samples/ControlCatalog/Pages/DialogsPage.xaml | 11 +++++ .../ControlCatalog/Pages/DialogsPage.xaml.cs | 42 +++++++++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 samples/ControlCatalog/Pages/DialogsPage.xaml create mode 100644 samples/ControlCatalog/Pages/DialogsPage.xaml.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 99ddce0757..4e2dfb9969 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -32,6 +32,9 @@ Designer + + Designer + Designer @@ -86,6 +89,9 @@ MainWindow.xaml + + DialogsPage.xaml + BorderPage.xaml diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 7dd026c124..6c432a90ac 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -1,7 +1,7 @@ - + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 2e1f7d38b2..f065be55aa 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -1,6 +1,9 @@ +using System.Collections; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Platform; +using ControlCatalog.Pages; namespace ControlCatalog { @@ -9,6 +12,12 @@ namespace ControlCatalog public MainView() { this.InitializeComponent(); + if (AvaloniaLocator.Current.GetService().GetRuntimeInfo().IsDesktop) + ((IList) this.FindControl("Sidebar").Items).Add(new TabItem() + { + Header = "System dialogs", + Content = new DialogsPage() + }); } private void InitializeComponent() diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 65c37c898b..ed0bffdf10 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -11,7 +11,7 @@ namespace ControlCatalog { this.InitializeComponent(); this.AttachDevTools(); - Renderer.DrawDirtyRects = Renderer.DrawFps = true; + //Renderer.DrawDirtyRects = Renderer.DrawFps = true; } private void InitializeComponent() diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml new file mode 100644 index 0000000000..12be105469 --- /dev/null +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -0,0 +1,11 @@ + + + + + + + + Modal to window + + + \ No newline at end of file diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs new file mode 100644 index 0000000000..e9c95c20e4 --- /dev/null +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -0,0 +1,42 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +#pragma warning disable 4014 + +namespace ControlCatalog.Pages +{ + public class DialogsPage : UserControl + { + public DialogsPage() + { + this.InitializeComponent(); + this.FindControl + + + + + + Hello world! + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs new file mode 100644 index 0000000000..4a11e45519 --- /dev/null +++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs @@ -0,0 +1,53 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using System; +using Avalonia.Input; + +namespace ControlCatalog +{ + public class DecoratedWindow : Window + { + public DecoratedWindow() + { + this.InitializeComponent(); + this.AttachDevTools(); + } + + void SetupSide(string name, StandardCursorType cursor, WindowEdge edge) + { + var ctl = this.FindControl(name); + ctl.Cursor = new Cursor(cursor); + ctl.PointerPressed += delegate + { + PlatformImpl.BeginResizeDrag(edge); + }; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + this.FindControl("TitleBar").PointerPressed += delegate + { + PlatformImpl.BeginMoveDrag(); + }; + SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West); + SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East); + SetupSide("Top", StandardCursorType.TopSide, WindowEdge.South); + SetupSide("Bottom", StandardCursorType.BottomSize, WindowEdge.North); + SetupSide("TopLeft", StandardCursorType.TopLeftCorner, WindowEdge.NorthWest); + SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast); + SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest); + SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast); + this.FindControl \ No newline at end of file diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index e9c95c20e4..94364da698 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -30,6 +30,10 @@ namespace ControlCatalog.Pages Title = "Select folder" }.ShowAsync(GetWindow()); }; + this.FindControl