TopLevelImpl.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. using System;
  2. using System.Collections.Generic;
  3. using Avalonia.Input;
  4. using Avalonia.Input.Raw;
  5. using Avalonia.Platform;
  6. using Avalonia.Controls.Platform.Surfaces;
  7. using Avalonia.Rendering;
  8. using Avalonia.Threading;
  9. using MonoMac.AppKit;
  10. using MonoMac.CoreFoundation;
  11. using MonoMac.CoreGraphics;
  12. using MonoMac.Foundation;
  13. using MonoMac.ObjCRuntime;
  14. namespace Avalonia.MonoMac
  15. {
  16. abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface
  17. {
  18. public TopLevelView View { get; }
  19. private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
  20. protected TopLevelImpl()
  21. {
  22. View = new TopLevelView(this);
  23. }
  24. protected virtual void OnInput(RawInputEventArgs args)
  25. {
  26. Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
  27. Input?.Invoke(args);
  28. }
  29. [Adopts("NSTextInputClient")]
  30. public class TopLevelView : NSView
  31. {
  32. TopLevelImpl _tl;
  33. bool _isLeftPressed, _isRightPressed, _isMiddlePressed;
  34. private readonly IMouseDevice _mouse;
  35. private readonly IKeyboardDevice _keyboard;
  36. private NSTrackingArea _area;
  37. private NSCursor _cursor;
  38. private bool _nonUiRedrawQueued;
  39. public CGSize PixelSize { get; set; }
  40. public CGSize LogicalSize { get; set; }
  41. private SavedImage _backBuffer;
  42. public object SyncRoot { get; } = new object();
  43. public TopLevelView(TopLevelImpl tl)
  44. {
  45. _tl = tl;
  46. _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
  47. _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
  48. }
  49. protected override void Dispose(bool disposing)
  50. {
  51. if (disposing)
  52. {
  53. _backBuffer?.Dispose();
  54. _backBuffer = null;
  55. }
  56. base.Dispose(disposing);
  57. }
  58. public override bool ConformsToProtocol(IntPtr protocol)
  59. {
  60. var rv = base.ConformsToProtocol(protocol);
  61. return rv;
  62. }
  63. public override bool IsOpaque => false;
  64. public override void DrawRect(CGRect dirtyRect)
  65. {
  66. lock (SyncRoot)
  67. _nonUiRedrawQueued = false;
  68. Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
  69. lock (SyncRoot)
  70. {
  71. if (_backBuffer != null)
  72. {
  73. using (var context = NSGraphicsContext.CurrentContext.GraphicsPort)
  74. {
  75. context.SetFillColor(255, 255, 255, 255);
  76. context.FillRect(new CGRect(default(CGPoint), LogicalSize));
  77. context.TranslateCTM(0, LogicalSize.Height - _backBuffer.LogicalSize.Height);
  78. context.DrawImage(new CGRect(default(CGPoint), _backBuffer.LogicalSize), _backBuffer.Image);
  79. context.Flush();
  80. NSGraphicsContext.CurrentContext.FlushGraphics();
  81. }
  82. }
  83. }
  84. _tl.Paint?.Invoke(dirtyRect.ToAvaloniaRect());
  85. }
  86. public void SetBackBufferImage(SavedImage image)
  87. {
  88. lock (SyncRoot)
  89. {
  90. _backBuffer?.Dispose();
  91. _backBuffer = image;
  92. if (image == null)
  93. return;
  94. if (_nonUiRedrawQueued)
  95. return;
  96. _nonUiRedrawQueued = true;
  97. Dispatcher.UIThread.Post(
  98. () =>
  99. {
  100. lock (SyncRoot)
  101. {
  102. if (!_nonUiRedrawQueued)
  103. return;
  104. _nonUiRedrawQueued = false;
  105. }
  106. SetNeedsDisplayInRect(Frame);
  107. Display();
  108. }, DispatcherPriority.Render);
  109. }
  110. }
  111. [Export("viewDidChangeBackingProperties:")]
  112. public void ViewDidChangeBackingProperties()
  113. {
  114. _tl?.ScalingChanged?.Invoke(_tl.Scaling);
  115. }
  116. void UpdateCursor()
  117. {
  118. ResetCursorRects();
  119. if (_cursor != null)
  120. AddCursorRect(Frame, _cursor);
  121. }
  122. static readonly NSCursor ArrowCursor = NSCursor.ArrowCursor;
  123. public void SetCursor(NSCursor cursor)
  124. {
  125. _cursor = cursor ?? ArrowCursor;
  126. UpdateCursor();
  127. }
  128. public override void SetFrameSize(CGSize newSize)
  129. {
  130. lock (SyncRoot)
  131. {
  132. base.SetFrameSize(newSize);
  133. LogicalSize = Frame.Size;
  134. PixelSize = ConvertSizeToBacking(LogicalSize);
  135. }
  136. if (_area != null)
  137. {
  138. RemoveTrackingArea(_area);
  139. _area.Dispose();
  140. }
  141. _area = new NSTrackingArea(new CGRect(default(CGPoint), newSize),
  142. NSTrackingAreaOptions.ActiveAlways |
  143. NSTrackingAreaOptions.MouseMoved |
  144. NSTrackingAreaOptions.EnabledDuringMouseDrag, this, null);
  145. AddTrackingArea(_area);
  146. UpdateCursor();
  147. _tl?.Resized?.Invoke(_tl.ClientSize);
  148. Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
  149. }
  150. InputModifiers GetModifiers(NSEventModifierMask mod)
  151. {
  152. var rv = new InputModifiers();
  153. if (mod.HasFlag(NSEventModifierMask.ControlKeyMask))
  154. rv |= InputModifiers.Control;
  155. if (mod.HasFlag(NSEventModifierMask.ShiftKeyMask))
  156. rv |= InputModifiers.Shift;
  157. if (mod.HasFlag(NSEventModifierMask.AlternateKeyMask))
  158. rv |= InputModifiers.Alt;
  159. if (mod.HasFlag(NSEventModifierMask.CommandKeyMask))
  160. rv |= InputModifiers.Windows;
  161. if (_isLeftPressed)
  162. rv |= InputModifiers.LeftMouseButton;
  163. if (_isMiddlePressed)
  164. rv |= InputModifiers.MiddleMouseButton;
  165. if (_isRightPressed)
  166. rv |= InputModifiers.RightMouseButton;
  167. return rv;
  168. }
  169. public Point TranslateLocalPoint(Point pt) => pt.WithY(Bounds.Height - pt.Y);
  170. Vector GetDelta(NSEvent ev)
  171. {
  172. var rv = new Vector(ev.ScrollingDeltaX, ev.ScrollingDeltaY);
  173. //TODO: Verify if handling of HasPreciseScrollingDeltas
  174. // is required (touchpad or magic-mouse is needed)
  175. return rv;
  176. }
  177. uint GetTimeStamp(NSEvent ev) => (uint) (ev.Timestamp * 1000);
  178. void MouseEvent(NSEvent ev, RawMouseEventType type)
  179. {
  180. BecomeFirstResponder();
  181. var loc = TranslateLocalPoint(ConvertPointToView(ev.LocationInWindow, this).ToAvaloniaPoint());
  182. var ts = GetTimeStamp(ev);
  183. var mod = GetModifiers(ev.ModifierFlags);
  184. if (type == RawMouseEventType.Wheel)
  185. {
  186. var delta = GetDelta(ev);
  187. // ReSharper disable CompareOfFloatsByEqualityOperator
  188. if (delta.X == 0 && delta.Y == 0)
  189. return;
  190. // ReSharper restore CompareOfFloatsByEqualityOperator
  191. _tl.OnInput(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc,
  192. delta, mod));
  193. }
  194. else
  195. _tl.OnInput(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod));
  196. }
  197. public override void MouseMoved(NSEvent theEvent)
  198. {
  199. MouseEvent(theEvent, RawMouseEventType.Move);
  200. base.MouseMoved(theEvent);
  201. }
  202. public override void MouseDragged(NSEvent theEvent)
  203. {
  204. MouseEvent(theEvent, RawMouseEventType.Move);
  205. base.MouseDragged(theEvent);
  206. }
  207. public override void OtherMouseDragged(NSEvent theEvent)
  208. {
  209. MouseEvent(theEvent, RawMouseEventType.Move);
  210. base.OtherMouseDragged(theEvent);
  211. }
  212. public override void RightMouseDragged(NSEvent theEvent)
  213. {
  214. MouseEvent(theEvent, RawMouseEventType.Move);
  215. base.RightMouseDragged(theEvent);
  216. }
  217. public NSEvent LastMouseDownEvent { get; private set; }
  218. public override void MouseDown(NSEvent theEvent)
  219. {
  220. _isLeftPressed = true;
  221. LastMouseDownEvent = theEvent;
  222. MouseEvent(theEvent, RawMouseEventType.LeftButtonDown);
  223. LastMouseDownEvent = null;
  224. base.MouseDown(theEvent);
  225. }
  226. public override void RightMouseDown(NSEvent theEvent)
  227. {
  228. _isRightPressed = true;
  229. MouseEvent(theEvent, RawMouseEventType.RightButtonDown);
  230. base.RightMouseDown(theEvent);
  231. }
  232. public override void OtherMouseDown(NSEvent theEvent)
  233. {
  234. _isMiddlePressed = true;
  235. MouseEvent(theEvent, RawMouseEventType.MiddleButtonDown);
  236. base.OtherMouseDown(theEvent);
  237. }
  238. public override void MouseUp(NSEvent theEvent)
  239. {
  240. _isLeftPressed = false;
  241. MouseEvent(theEvent, RawMouseEventType.LeftButtonUp);
  242. base.MouseUp(theEvent);
  243. }
  244. public override void RightMouseUp(NSEvent theEvent)
  245. {
  246. _isRightPressed = false;
  247. MouseEvent(theEvent, RawMouseEventType.RightButtonUp);
  248. base.RightMouseUp(theEvent);
  249. }
  250. public override void OtherMouseUp(NSEvent theEvent)
  251. {
  252. _isMiddlePressed = false;
  253. MouseEvent(theEvent, RawMouseEventType.MiddleButtonUp);
  254. base.OtherMouseUp(theEvent);
  255. }
  256. public override void ScrollWheel(NSEvent theEvent)
  257. {
  258. MouseEvent(theEvent, RawMouseEventType.Wheel);
  259. base.ScrollWheel(theEvent);
  260. }
  261. public override void MouseExited(NSEvent theEvent)
  262. {
  263. MouseEvent(theEvent, RawMouseEventType.LeaveWindow);
  264. base.MouseExited(theEvent);
  265. }
  266. void KeyboardEvent(RawKeyEventType type, NSEvent ev)
  267. {
  268. var code = KeyTransform.TransformKeyCode(ev.KeyCode);
  269. if (!code.HasValue)
  270. return;
  271. _tl.OnInput(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev),
  272. type, code.Value, GetModifiers(ev.ModifierFlags)));
  273. }
  274. public override void KeyDown(NSEvent theEvent)
  275. {
  276. KeyboardEvent(RawKeyEventType.KeyDown, theEvent);
  277. InputContext.HandleEvent(theEvent);
  278. base.KeyDown(theEvent);
  279. }
  280. public override void KeyUp(NSEvent theEvent)
  281. {
  282. KeyboardEvent(RawKeyEventType.KeyUp, theEvent);
  283. base.KeyUp(theEvent);
  284. }
  285. #region NSTextInputClient
  286. public override bool AcceptsFirstResponder() => true;
  287. public bool HasMarkedText
  288. {
  289. [Export("hasMarkedText")] get => false;
  290. }
  291. public NSRange MarkedRange
  292. {
  293. [Export("markedRange")] get => new NSRange(NSRange.NotFound, 0);
  294. }
  295. public NSRange SelectedRange
  296. {
  297. [Export("selectedRange")] get => new NSRange(NSRange.NotFound, 0);
  298. }
  299. [Export("setMarkedText:selectedRange:replacementRange:")]
  300. public void SetMarkedText(NSString str, NSRange a1, NSRange a2)
  301. {
  302. }
  303. [Export("unmarkText")]
  304. public void UnmarkText()
  305. {
  306. }
  307. public NSArray ValidAttributesForMarkedText
  308. {
  309. [Export("validAttributesForMarkedText")] get => new NSArray();
  310. }
  311. [Export("attributedSubstringForProposedRange:actualRange:")]
  312. public NSAttributedString AttributedSubstringForProposedRange(NSRange range, IntPtr wat)
  313. {
  314. return new NSAttributedString("");
  315. }
  316. [Export("insertText:replacementRange:")]
  317. public void InsertText(NSString str, NSRange range)
  318. {
  319. //TODO: timestamp
  320. _tl.OnInput(new RawTextInputEventArgs(_keyboard, 0, str.ToString()));
  321. }
  322. [Export("characterIndexForPoint:")]
  323. public uint CharacterIndexForPoint(CGPoint pt)
  324. {
  325. return 0;
  326. }
  327. [Export("firstRectForCharacterRange:actualRange:")]
  328. public CGRect FirstRectForCharacterRange(NSRange range, IntPtr wat)
  329. {
  330. return new CGRect();
  331. }
  332. #endregion
  333. }
  334. public IInputRoot InputRoot { get; private set; }
  335. public abstract Size ClientSize { get; }
  336. public double Scaling
  337. {
  338. get
  339. {
  340. if (View.Window == null)
  341. return 1;
  342. return View.Window.BackingScaleFactor;
  343. }
  344. }
  345. public IEnumerable<object> Surfaces => new[] {this};
  346. public IMouseDevice MouseDevice => _mouse;
  347. public Action<RawInputEventArgs> Input { get; set; }
  348. public Action<Rect> Paint { get; set; }
  349. public Action<Size> Resized { get; set; }
  350. public Action<double> ScalingChanged { get; set; }
  351. public Action Closed { get; set; }
  352. public virtual void Dispose()
  353. {
  354. Closed?.Invoke();
  355. Closed = null;
  356. View.Dispose();
  357. }
  358. public IRenderer CreateRenderer(IRenderRoot root) =>
  359. MonoMacPlatform.UseDeferredRendering
  360. ? new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>())
  361. : (IRenderer) new ImmediateRenderer(root);
  362. public void Invalidate(Rect rect)
  363. {
  364. if (!MonoMacPlatform.UseDeferredRendering)
  365. View.SetNeedsDisplayInRect(View.Frame);
  366. }
  367. public abstract Point PointToClient(Point point);
  368. public abstract Point PointToScreen(Point point);
  369. public void SetCursor(IPlatformHandle cursor) => View.SetCursor((cursor as Cursor)?.Native);
  370. public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot;
  371. public ILockedFramebuffer Lock() => new EmulatedFramebuffer(View);
  372. }
  373. }