From 33daa820eb99c2e4abc113a9225c4c6c5670f4b0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2017 13:01:09 +0300 Subject: [PATCH 01/66] 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/66] [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/66] [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 4f902c455257549535306ad08c2fbb5f421c13d5 Mon Sep 17 00:00:00 2001 From: Matthijs ter Woord Date: Tue, 22 Aug 2017 18:17:36 +0200 Subject: [PATCH 04/66] Add IsPressed property on Button --- src/Avalonia.Controls/Button.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 78bbd836be..dee8969a17 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -68,6 +68,9 @@ namespace Avalonia.Controls public static readonly RoutedEvent ClickEvent = RoutedEvent.Register("Click", RoutingStrategies.Bubble); + public static readonly AvaloniaProperty IsPressedProperty = + AvaloniaProperty.RegisterDirect(nameof(IsPressed), b => b.IsPressed); + /// /// Initializes static members of the class. /// @@ -134,6 +137,12 @@ namespace Avalonia.Controls set { SetValue(IsDefaultProperty, value); } } + public bool IsPressed + { + get { return GetValue(IsPressedProperty); } + private set { SetValue(IsPressedProperty, value); } + } + /// protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { @@ -164,7 +173,7 @@ namespace Avalonia.Controls { RaiseClickEvent(); } - + IsPressed = true; e.Handled = true; } @@ -180,7 +189,7 @@ namespace Avalonia.Controls { RaiseClickEvent(); } - + IsPressed = false; e.Handled = true; } } @@ -223,6 +232,7 @@ namespace Avalonia.Controls { PseudoClasses.Add(":pressed"); e.Device.Capture(this); + IsPressed = true; e.Handled = true; if (ClickMode == ClickMode.Press) @@ -241,6 +251,7 @@ namespace Avalonia.Controls { e.Device.Capture(null); PseudoClasses.Remove(":pressed"); + IsPressed = false; e.Handled = true; if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) From bfd7de2427c26edeae56b38985f2741fc54aadba Mon Sep 17 00:00:00 2001 From: Matthijs ter Woord Date: Tue, 22 Aug 2017 18:37:08 +0200 Subject: [PATCH 05/66] IsPressed property should be a StyledProperty, otherwise stackoverflow occurs --- src/Avalonia.Controls/Button.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 964ffbbac4..a5ace095b6 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -72,8 +72,8 @@ namespace Avalonia.Controls private ICommand _command; - public static readonly AvaloniaProperty IsPressedProperty = - AvaloniaProperty.RegisterDirect(nameof(IsPressed), b => b.IsPressed); + public static readonly StyledProperty IsPressedProperty = + AvaloniaProperty.Register(nameof(IsPressed)); /// /// Initializes static members of the class. From 0c55a8fe357010c7fcfc008831397e310d6297a8 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Thu, 24 Aug 2017 20:33:02 +0300 Subject: [PATCH 06/66] Defer changes to dirty visuals --- .../Rendering/DirtyVisuals.cs | 91 ++++++++++++++++--- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs index f14f5bc912..895b1679b0 100644 --- a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs +++ b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs @@ -17,6 +17,8 @@ namespace Avalonia.Rendering { private SortedDictionary> _inner = new SortedDictionary>(); private Dictionary _index = new Dictionary(); + private List<(DeferredChange change, IVisual visual)> _deferredChanges = new List<(DeferredChange, IVisual)>(); + private bool _deferring; /// /// Gets the number of dirty visuals. @@ -29,10 +31,15 @@ namespace Avalonia.Rendering /// The dirty visual. public void Add(IVisual visual) { + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Add, visual)); + return; + } + var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot); - int existingDistance; - if (_index.TryGetValue(visual, out existingDistance)) + if (_index.TryGetValue(visual, out var existingDistance)) { if (distance == existingDistance) { @@ -43,9 +50,7 @@ namespace Avalonia.Rendering _index.Remove(visual); } - List list; - - if (!_inner.TryGetValue(distance, out list)) + if (!_inner.TryGetValue(distance, out var list)) { list = new List(); _inner.Add(distance, list); @@ -60,6 +65,12 @@ namespace Avalonia.Rendering /// public void Clear() { + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Clear, null)); + return; + } + _inner.Clear(); _index.Clear(); } @@ -68,19 +79,19 @@ namespace Avalonia.Rendering /// Removes a visual from the dirty list. /// /// The visual. - /// True if the visual was present in the list; otherwise false. - public bool Remove(IVisual visual) + public void Remove(IVisual visual) { - int distance; + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Remove, visual)); + return; + } - if (_index.TryGetValue(visual, out distance)) + if (_index.TryGetValue(visual, out var distance)) { _inner[distance].Remove(visual); _index.Remove(visual); - return true; } - - return false; } /// @@ -89,19 +100,69 @@ namespace Avalonia.Rendering /// A collection of visuals. public IEnumerator GetEnumerator() { - foreach (var i in _inner) + using (DeferChanges()) { - foreach (var j in i.Value) + foreach (var i in _inner) { - yield return j; + foreach (var j in i.Value) + { + yield return j; + } } } } + private DeferDisposer DeferChanges() + { + _deferring = true; + return new DeferDisposer(this); + } + + private void EndDefer() + { + if (!_deferring) return; + + _deferring = false; + + foreach (var change in _deferredChanges) + { + switch (change.change) + { + case DeferredChange.Add: + Add(change.visual); + break; + case DeferredChange.Remove: + Remove(change.visual); + break; + case DeferredChange.Clear: + Clear(); + break; + } + } + + _deferredChanges.Clear(); + } + /// /// Gets the dirty visuals, in ascending order of distance to their root. /// /// A collection of visuals. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private struct DeferDisposer : IDisposable + { + private DirtyVisuals _parent; + + internal DeferDisposer(DirtyVisuals parent) => _parent = parent; + + public void Dispose() => _parent?.EndDefer(); + } + + private enum DeferredChange + { + Add, + Remove, + Clear + } } } From f6ed69be28c162936a4152066bb3fea1436edffa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 20:08:38 +0200 Subject: [PATCH 07/66] StyleResource -> DynamicResource --- samples/ControlCatalog/Pages/BorderPage.xaml | 10 ++++----- samples/ControlCatalog/Pages/ButtonPage.xaml | 8 +++---- .../ControlCatalog/Pages/ContextMenuPage.xaml | 2 +- .../ControlCatalog/Pages/DropDownPage.xaml | 2 +- .../Pages/LayoutTransformControlPage.xaml | 8 +++---- samples/ControlCatalog/Pages/ToolTipPage.xaml | 2 +- samples/ControlCatalog/SideBar.xaml | 4 ++-- samples/RenderTest/Pages/ClippingPage.xaml | 2 +- samples/RenderTest/Pages/DrawingPage.xaml | 8 +++---- samples/RenderTest/SideBar.xaml | 4 ++-- src/Avalonia.Themes.Default/Button.xaml | 14 ++++++------ src/Avalonia.Themes.Default/CheckBox.xaml | 10 ++++----- src/Avalonia.Themes.Default/DropDown.xaml | 10 ++++----- .../EmbeddableControlRoot.xaml | 4 ++-- src/Avalonia.Themes.Default/GridSplitter.xaml | 8 +++---- src/Avalonia.Themes.Default/ListBox.xaml | 6 ++--- src/Avalonia.Themes.Default/MenuItem.xaml | 18 +++++++-------- src/Avalonia.Themes.Default/PopupRoot.xaml | 2 +- src/Avalonia.Themes.Default/ProgressBar.xaml | 4 ++-- src/Avalonia.Themes.Default/RadioButton.xaml | 10 ++++----- src/Avalonia.Themes.Default/ScrollBar.xaml | 4 ++-- src/Avalonia.Themes.Default/Separator.xaml | 4 ++-- src/Avalonia.Themes.Default/Slider.xaml | 6 ++--- src/Avalonia.Themes.Default/TabStripItem.xaml | 6 ++--- src/Avalonia.Themes.Default/TextBox.xaml | 22 +++++++++---------- src/Avalonia.Themes.Default/ToggleButton.xaml | 18 +++++++-------- src/Avalonia.Themes.Default/ToolTip.xaml | 6 ++--- src/Avalonia.Themes.Default/TreeView.xaml | 4 ++-- src/Avalonia.Themes.Default/TreeViewItem.xaml | 2 +- src/Avalonia.Themes.Default/Window.xaml | 4 ++-- .../Xaml/Style2.xaml | 6 ++--- 31 files changed, 109 insertions(+), 109 deletions(-) diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml index 9842745413..a81bd13ddd 100644 --- a/samples/ControlCatalog/Pages/BorderPage.xaml +++ b/samples/ControlCatalog/Pages/BorderPage.xaml @@ -7,22 +7,22 @@ Margin="0,16,0,0" HorizontalAlignment="Center" Gap="16"> - + Border - Border and Background - Rounded Corners - Rounded Corners diff --git a/samples/ControlCatalog/Pages/ButtonPage.xaml b/samples/ControlCatalog/Pages/ButtonPage.xaml index e595f27263..b5335ff15e 100644 --- a/samples/ControlCatalog/Pages/ButtonPage.xaml +++ b/samples/ControlCatalog/Pages/ButtonPage.xaml @@ -11,7 +11,7 @@ - + - - - + + + diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml index f029b1106f..3af823befc 100644 --- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml @@ -7,7 +7,7 @@ Margin="0,16,0,0" HorizontalAlignment="Center" Gap="16"> - diff --git a/samples/ControlCatalog/Pages/DropDownPage.xaml b/samples/ControlCatalog/Pages/DropDownPage.xaml index b392770060..0a7a88e331 100644 --- a/samples/ControlCatalog/Pages/DropDownPage.xaml +++ b/samples/ControlCatalog/Pages/DropDownPage.xaml @@ -14,7 +14,7 @@ - + Control Items diff --git a/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml b/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml index 850eab8818..b428cd1b9f 100644 --- a/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml +++ b/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml @@ -10,10 +10,10 @@ RowDefinitions="24,Auto,24" HorizontalAlignment="Center" VerticalAlignment="Center"> - - - - + + + + diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml index 29df11510c..0c55fa28e4 100644 --- a/samples/ControlCatalog/Pages/ToolTipPage.xaml +++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml @@ -7,7 +7,7 @@ Margin="0,16,0,0" HorizontalAlignment="Center" Gap="16"> - diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 950de71e2b..c03bc432d4 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -3,7 +3,7 @@ - + diff --git a/samples/RenderTest/Pages/ClippingPage.xaml b/samples/RenderTest/Pages/ClippingPage.xaml index 47e9d51e3c..238e3449de 100644 --- a/samples/RenderTest/Pages/ClippingPage.xaml +++ b/samples/RenderTest/Pages/ClippingPage.xaml @@ -5,7 +5,7 @@ Width="100" Height="100" Clip="M 58.625 0.07421875 C 50.305778 0.26687364 42.411858 7.0346526 41.806641 15.595703 C 42.446442 22.063923 39.707425 13.710754 36.982422 12.683594 C 29.348395 6.1821635 16.419398 8.4359222 11.480469 17.195312 C 6.0935256 25.476803 9.8118851 37.71125 18.8125 41.6875 C 9.1554771 40.62945 -0.070876925 49.146842 0.21679688 58.857422 C 0.21545578 60.872512 0.56758794 62.88911 1.2617188 64.78125 C 4.3821886 74.16708 16.298268 78.921772 25.03125 74.326172 C 28.266843 72.062552 26.298191 74.214838 25.414062 76.398438 C 21.407348 85.589198 27.295992 97.294293 37.097656 99.501953 C 46.864883 102.3541 57.82177 94.726518 58.539062 84.580078 C 58.142158 79.498998 59.307538 83.392694 61.207031 85.433594 C 67.532324 93.056874 80.440232 93.192029 86.882812 85.630859 C 93.836392 78.456939 92.396838 65.538666 84.115234 60.009766 C 79.783641 57.904836 83.569793 58.802369 86.375 58.193359 C 96.383335 56.457569 102.87506 44.824101 99.083984 35.394531 C 95.963498 26.008711 84.047451 21.254079 75.314453 25.849609 C 72.078834 28.113269 74.047517 25.960974 74.931641 23.777344 C 78.93827 14.586564 73.049722 2.8815081 63.248047 0.67382812 C 61.721916 0.22817968 60.165597 0.038541919 58.625 0.07421875 z "> - + diff --git a/samples/RenderTest/Pages/DrawingPage.xaml b/samples/RenderTest/Pages/DrawingPage.xaml index 81181e01fc..ee892774f0 100644 --- a/samples/RenderTest/Pages/DrawingPage.xaml +++ b/samples/RenderTest/Pages/DrawingPage.xaml @@ -49,7 +49,7 @@ BorderThickness="1" BorderBrush="Gray" Margin="5"> - + - @@ -76,7 +76,7 @@ BorderThickness="1" BorderBrush="Gray" Margin="5"> - @@ -91,7 +91,7 @@ BorderThickness="1" BorderBrush="Gray" Margin="5"> - diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml index 950de71e2b..c03bc432d4 100644 --- a/samples/RenderTest/SideBar.xaml +++ b/samples/RenderTest/SideBar.xaml @@ -3,7 +3,7 @@ - + diff --git a/src/Avalonia.Themes.Default/Button.xaml b/src/Avalonia.Themes.Default/Button.xaml index daa2973b21..908f293fa7 100644 --- a/src/Avalonia.Themes.Default/Button.xaml +++ b/src/Avalonia.Themes.Default/Button.xaml @@ -1,9 +1,9 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/CheckBox.xaml b/src/Avalonia.Themes.Default/CheckBox.xaml index 2fc19b2679..195ec098c9 100644 --- a/src/Avalonia.Themes.Default/CheckBox.xaml +++ b/src/Avalonia.Themes.Default/CheckBox.xaml @@ -1,8 +1,8 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/DropDown.xaml b/src/Avalonia.Themes.Default/DropDown.xaml index 5a3d44360c..0e8b409cfd 100644 --- a/src/Avalonia.Themes.Default/DropDown.xaml +++ b/src/Avalonia.Themes.Default/DropDown.xaml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml index edea0ddf64..f39720bb65 100644 --- a/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml +++ b/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml @@ -1,7 +1,7 @@ @@ -25,7 +25,7 @@ diff --git a/src/Avalonia.Themes.Default/ListBox.xaml b/src/Avalonia.Themes.Default/ListBox.xaml index 5aa23f5275..aa63a1b6c3 100644 --- a/src/Avalonia.Themes.Default/ListBox.xaml +++ b/src/Avalonia.Themes.Default/ListBox.xaml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml index 89f6f26c08..1520e12962 100644 --- a/src/Avalonia.Themes.Default/ScrollBar.xaml +++ b/src/Avalonia.Themes.Default/ScrollBar.xaml @@ -2,7 +2,7 @@ diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Slider.xaml index 4fa293fbc5..b6f3cff318 100644 --- a/src/Avalonia.Themes.Default/Slider.xaml +++ b/src/Avalonia.Themes.Default/Slider.xaml @@ -16,7 +16,7 @@ - + @@ -43,7 +43,7 @@ - + @@ -61,6 +61,6 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/TabStripItem.xaml b/src/Avalonia.Themes.Default/TabStripItem.xaml index cee1c5e460..7f7fef0a1a 100644 --- a/src/Avalonia.Themes.Default/TabStripItem.xaml +++ b/src/Avalonia.Themes.Default/TabStripItem.xaml @@ -1,8 +1,8 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml index 8a5a41845b..8acc97d663 100644 --- a/src/Avalonia.Themes.Default/TextBox.xaml +++ b/src/Avalonia.Themes.Default/TextBox.xaml @@ -1,8 +1,8 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/ToggleButton.xaml b/src/Avalonia.Themes.Default/ToggleButton.xaml index 0c8a3fa425..12d7daacda 100644 --- a/src/Avalonia.Themes.Default/ToggleButton.xaml +++ b/src/Avalonia.Themes.Default/ToggleButton.xaml @@ -1,9 +1,9 @@ \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/ToolTip.xaml b/src/Avalonia.Themes.Default/ToolTip.xaml index 46e297fbc2..f3e819c101 100644 --- a/src/Avalonia.Themes.Default/ToolTip.xaml +++ b/src/Avalonia.Themes.Default/ToolTip.xaml @@ -1,7 +1,7 @@ \ No newline at end of file From a9748ee901456432a25fc7e93011b58eeeeca850 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 27 Aug 2017 00:36:06 +0200 Subject: [PATCH 08/66] Removed StyleResource from docs. --- docs/tutorial/from-wpf.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/tutorial/from-wpf.md b/docs/tutorial/from-wpf.md index 0eb6e80327..d244fb076b 100644 --- a/docs/tutorial/from-wpf.md +++ b/docs/tutorial/from-wpf.md @@ -73,17 +73,6 @@ and includes `DirectProperty` for turning standard CLR properties into Avalonia properties. The common base class of `StyledProperty` and `DirectProperty` is `AvaloniaProperty`. -# Resources - -There is no `Resources` collection on controls in Avalonia, however `Style`s -do have a `Resources` collection for style-related resources. These can be -referred to using the `{StyleResource}` markup extension both inside and outside -styles. - -For non-style-related resources, we suggest defining them in code and referring -to them in markup using the `{Static}` markup extension. To read more about the reasoning for this, -see [this issue comment](https://github.com/AvaloniaUI/Avalonia/issues/462#issuecomment-191849723). - ## Grid Column and row definitions can be specified in Avalonia using strings, avoiding From 981cf5a2f158e34a1f28db10af3c3c988013d900 Mon Sep 17 00:00:00 2001 From: Matthijs ter Woord Date: Fri, 1 Sep 2017 15:17:09 +0200 Subject: [PATCH 09/66] Now we have .IsPressed property, have the PseudoClass method handle that for us. --- src/Avalonia.Controls/Button.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index a5ace095b6..aa486422fd 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -84,6 +84,7 @@ namespace Avalonia.Controls ClickEvent.AddClassHandler