TopLevel.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Reactive.Disposables;
  5. using System.Reactive.Linq;
  6. using Avalonia.Controls.Platform;
  7. using Avalonia.Controls.Primitives;
  8. using Avalonia.Input;
  9. using Avalonia.Input.Raw;
  10. using Avalonia.Layout;
  11. using Avalonia.Logging;
  12. using Avalonia.Platform;
  13. using Avalonia.Rendering;
  14. using Avalonia.Styling;
  15. using Avalonia.VisualTree;
  16. using JetBrains.Annotations;
  17. namespace Avalonia.Controls
  18. {
  19. /// <summary>
  20. /// Base class for top-level widgets.
  21. /// </summary>
  22. /// <remarks>
  23. /// This class acts as a base for top level widget.
  24. /// It handles scheduling layout, styling and rendering as well as
  25. /// tracking the widget's <see cref="ClientSize"/>.
  26. /// </remarks>
  27. public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot
  28. {
  29. /// <summary>
  30. /// Defines the <see cref="ClientSize"/> property.
  31. /// </summary>
  32. public static readonly DirectProperty<TopLevel, Size> ClientSizeProperty =
  33. AvaloniaProperty.RegisterDirect<TopLevel, Size>(nameof(ClientSize), o => o.ClientSize);
  34. /// <summary>
  35. /// Defines the <see cref="IInputRoot.PointerOverElement"/> property.
  36. /// </summary>
  37. public static readonly StyledProperty<IInputElement> PointerOverElementProperty =
  38. AvaloniaProperty.Register<TopLevel, IInputElement>(nameof(IInputRoot.PointerOverElement));
  39. private readonly IInputManager _inputManager;
  40. private readonly IAccessKeyHandler _accessKeyHandler;
  41. private readonly IKeyboardNavigationHandler _keyboardNavigationHandler;
  42. private readonly IApplicationLifecycle _applicationLifecycle;
  43. private readonly IPlatformRenderInterface _renderInterface;
  44. private Size _clientSize;
  45. /// <summary>
  46. /// Initializes static members of the <see cref="TopLevel"/> class.
  47. /// </summary>
  48. static TopLevel()
  49. {
  50. AffectsMeasure(ClientSizeProperty);
  51. }
  52. /// <summary>
  53. /// Initializes a new instance of the <see cref="TopLevel"/> class.
  54. /// </summary>
  55. /// <param name="impl">The platform-specific window implementation.</param>
  56. public TopLevel(ITopLevelImpl impl)
  57. : this(impl, AvaloniaLocator.Current)
  58. {
  59. }
  60. /// <summary>
  61. /// Initializes a new instance of the <see cref="TopLevel"/> class.
  62. /// </summary>
  63. /// <param name="impl">The platform-specific window implementation.</param>
  64. /// <param name="dependencyResolver">
  65. /// The dependency resolver to use. If null the default dependency resolver will be used.
  66. /// </param>
  67. public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver dependencyResolver)
  68. {
  69. if (impl == null)
  70. {
  71. throw new InvalidOperationException(
  72. "Could not create window implementation: maybe no windowing subsystem was initialized?");
  73. }
  74. PlatformImpl = impl;
  75. dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
  76. var styler = TryGetService<IStyler>(dependencyResolver);
  77. _accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
  78. _inputManager = TryGetService<IInputManager>(dependencyResolver);
  79. _keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
  80. _applicationLifecycle = TryGetService<IApplicationLifecycle>(dependencyResolver);
  81. _renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
  82. var renderLoop = TryGetService<IRenderLoop>(dependencyResolver);
  83. Renderer = impl.CreateRenderer(this);
  84. impl.SetInputRoot(this);
  85. impl.Closed = HandleClosed;
  86. impl.Input = HandleInput;
  87. impl.Paint = HandlePaint;
  88. impl.Resized = HandleResized;
  89. impl.ScalingChanged = HandleScalingChanged;
  90. _keyboardNavigationHandler?.SetOwner(this);
  91. _accessKeyHandler?.SetOwner(this);
  92. styler?.ApplyStyles(this);
  93. ClientSize = impl.ClientSize;
  94. this.GetObservable(PointerOverElementProperty)
  95. .Select(
  96. x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
  97. .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
  98. if (_applicationLifecycle != null)
  99. {
  100. _applicationLifecycle.OnExit += OnApplicationExiting;
  101. }
  102. }
  103. /// <summary>
  104. /// Fired when the window is closed.
  105. /// </summary>
  106. public event EventHandler Closed;
  107. /// <summary>
  108. /// Gets or sets the client size of the window.
  109. /// </summary>
  110. public Size ClientSize
  111. {
  112. get { return _clientSize; }
  113. protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
  114. }
  115. /// <summary>
  116. /// Gets the platform-specific window implementation.
  117. /// </summary>
  118. [CanBeNull]
  119. public ITopLevelImpl PlatformImpl { get; private set; }
  120. /// <summary>
  121. /// Gets the renderer for the window.
  122. /// </summary>
  123. public IRenderer Renderer { get; private set; }
  124. /// <summary>
  125. /// Gets the access key handler for the window.
  126. /// </summary>
  127. IAccessKeyHandler IInputRoot.AccessKeyHandler => _accessKeyHandler;
  128. /// <summary>
  129. /// Gets or sets the keyboard navigation handler for the window.
  130. /// </summary>
  131. IKeyboardNavigationHandler IInputRoot.KeyboardNavigationHandler => _keyboardNavigationHandler;
  132. /// <summary>
  133. /// Gets or sets the input element that the pointer is currently over.
  134. /// </summary>
  135. IInputElement IInputRoot.PointerOverElement
  136. {
  137. get { return GetValue(PointerOverElementProperty); }
  138. set { SetValue(PointerOverElementProperty, value); }
  139. }
  140. /// <inheritdoc/>
  141. IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice;
  142. /// <summary>
  143. /// Gets or sets a value indicating whether access keys are shown in the window.
  144. /// </summary>
  145. bool IInputRoot.ShowAccessKeys
  146. {
  147. get { return GetValue(AccessText.ShowAccessKeyProperty); }
  148. set { SetValue(AccessText.ShowAccessKeyProperty, value); }
  149. }
  150. /// <inheritdoc/>
  151. Size ILayoutRoot.MaxClientSize => Size.Infinity;
  152. /// <inheritdoc/>
  153. double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
  154. /// <inheritdoc/>
  155. double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1;
  156. IStyleHost IStyleHost.StylingParent
  157. {
  158. get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }
  159. }
  160. IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget();
  161. /// <inheritdoc/>
  162. protected virtual IRenderTarget CreateRenderTarget()
  163. {
  164. if(PlatformImpl == null)
  165. throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)");
  166. return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
  167. }
  168. /// <inheritdoc/>
  169. void IRenderRoot.Invalidate(Rect rect)
  170. {
  171. PlatformImpl?.Invalidate(rect);
  172. }
  173. /// <inheritdoc/>
  174. Point IRenderRoot.PointToClient(Point p)
  175. {
  176. return PlatformImpl?.PointToClient(p) ?? default(Point);
  177. }
  178. /// <inheritdoc/>
  179. Point IRenderRoot.PointToScreen(Point p)
  180. {
  181. return PlatformImpl?.PointToScreen(p) ?? default(Point);
  182. }
  183. /// <summary>
  184. /// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>.
  185. /// </summary>
  186. /// <param name="rect">The dirty area.</param>
  187. protected virtual void HandlePaint(Rect rect)
  188. {
  189. Renderer?.Paint(rect);
  190. }
  191. /// <summary>
  192. /// Handles a closed notification from <see cref="ITopLevelImpl.Closed"/>.
  193. /// </summary>
  194. protected virtual void HandleClosed()
  195. {
  196. PlatformImpl = null;
  197. Closed?.Invoke(this, EventArgs.Empty);
  198. Renderer?.Dispose();
  199. Renderer = null;
  200. _applicationLifecycle.OnExit -= OnApplicationExiting;
  201. }
  202. /// <summary>
  203. /// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
  204. /// </summary>
  205. /// <param name="clientSize">The new client size.</param>
  206. protected virtual void HandleResized(Size clientSize)
  207. {
  208. ClientSize = clientSize;
  209. Width = clientSize.Width;
  210. Height = clientSize.Height;
  211. LayoutManager.Instance.ExecuteLayoutPass();
  212. Renderer?.Resized(clientSize);
  213. }
  214. /// <summary>
  215. /// Handles a window scaling change notification from
  216. /// <see cref="ITopLevelImpl.ScalingChanged"/>.
  217. /// </summary>
  218. /// <param name="scaling">The window scaling.</param>
  219. protected virtual void HandleScalingChanged(double scaling)
  220. {
  221. foreach (ILayoutable control in this.GetSelfAndVisualDescendants())
  222. {
  223. control.InvalidateMeasure();
  224. }
  225. }
  226. /// <inheritdoc/>
  227. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  228. {
  229. base.OnAttachedToVisualTree(e);
  230. throw new InvalidOperationException(
  231. $"Control '{GetType().Name}' is a top level control and cannot be added as a child.");
  232. }
  233. /// <summary>
  234. /// Tries to get a service from an <see cref="IAvaloniaDependencyResolver"/>, logging a
  235. /// warning if not found.
  236. /// </summary>
  237. /// <typeparam name="T">The service type.</typeparam>
  238. /// <param name="resolver">The resolver.</param>
  239. /// <returns>The service.</returns>
  240. private T TryGetService<T>(IAvaloniaDependencyResolver resolver) where T : class
  241. {
  242. var result = resolver.GetService<T>();
  243. if (result == null)
  244. {
  245. Logger.Warning(
  246. LogArea.Control,
  247. this,
  248. "Could not create {Service} : maybe Application.RegisterServices() wasn't called?",
  249. typeof(T));
  250. }
  251. return result;
  252. }
  253. private void OnApplicationExiting(object sender, EventArgs args)
  254. {
  255. HandleApplicationExiting();
  256. }
  257. /// <summary>
  258. /// Handles the application exiting, either from the last window closing, or a call to <see cref="IApplicationLifecycle.Exit"/>.
  259. /// </summary>
  260. protected virtual void HandleApplicationExiting()
  261. {
  262. }
  263. /// <summary>
  264. /// Handles input from <see cref="ITopLevelImpl.Input"/>.
  265. /// </summary>
  266. /// <param name="e">The event args.</param>
  267. private void HandleInput(RawInputEventArgs e)
  268. {
  269. _inputManager.ProcessInput(e);
  270. }
  271. }
  272. }