// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Controls { using System; using System.Reactive.Linq; using Perspex.Input; using Perspex.Input.Raw; using Perspex.Layout; using Perspex.Media; using Perspex.Platform; using Perspex.Rendering; using Perspex.Styling; using Perspex.Threading; using Splat; public class Window : ContentControl, ILayoutRoot, IRenderRoot, ICloseable, IFocusScope { public static readonly PerspexProperty ClientSizeProperty = PerspexProperty.Register("ClientSize"); public static readonly PerspexProperty TitleProperty = PerspexProperty.Register("Title", "Window"); private IWindowImpl impl; private Dispatcher dispatcher; private IRenderer renderer; private IInputManager inputManager; static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); } public Window() { IPlatformRenderInterface renderInterface = Locator.Current.GetService(); this.impl = Locator.Current.GetService(); this.inputManager = Locator.Current.GetService(); if (this.impl == null) { throw new InvalidOperationException( "Could not create window implementation: maybe no windowing subsystem was initialized?"); } if (this.inputManager == null) { throw new InvalidOperationException( "Could not create input manager: maybe Application.RegisterServices() wasn't called?"); } this.impl.SetOwner(this); this.impl.Activated = this.HandleActivated; this.impl.Closed = this.HandleClosed; this.impl.Input = this.HandleInput; this.impl.Paint = this.HandlePaint; this.impl.Resized = this.HandleResized; Size clientSize = this.ClientSize = this.impl.ClientSize; this.dispatcher = Dispatcher.UIThread; this.renderer = renderInterface.CreateRenderer(this.impl.Handle, clientSize.Width, clientSize.Height); this.LayoutManager = new LayoutManager(this); this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded()); this.RenderManager = new RenderManager(); this.RenderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded()); this.GetObservable(TitleProperty).Subscribe(s => this.impl.SetTitle(s)); IStyler styler = Locator.Current.GetService(); styler.ApplyStyles(this); } public event EventHandler Activated; public event EventHandler Closed; public Size ClientSize { get { return this.GetValue(ClientSizeProperty); } set { this.SetValue(ClientSizeProperty, value); } } public string Title { get { return this.GetValue(TitleProperty); } set { this.SetValue(TitleProperty, value); } } public ILayoutManager LayoutManager { get; private set; } public IRenderManager RenderManager { get; private set; } public void Show() { this.impl.Show(); this.LayoutPass(); } private void HandleActivated() { if (this.Activated != null) { this.Activated(this, EventArgs.Empty); } FocusManager.Instance.SetFocusScope(this); } private void HandleClosed() { if (this.Closed != null) { this.Closed(this, EventArgs.Empty); } } private void HandleInput(RawInputEventArgs e) { this.inputManager.Process(e); } private void HandleLayoutNeeded() { this.dispatcher.InvokeAsync(this.LayoutPass, DispatcherPriority.Render); } private void HandleRenderNeeded() { this.dispatcher.InvokeAsync(this.RenderVisualTree, DispatcherPriority.Render); } private void HandlePaint(Rect rect, IPlatformHandle handle) { this.renderer.Render(this, handle); this.RenderManager.RenderFinished(); } private void HandleResized(Size size) { this.ClientSize = size; this.renderer.Resize((int)size.Width, (int)size.Height); this.LayoutManager.ExecuteLayoutPass(); this.impl.Invalidate(new Rect(this.ClientSize)); } private void LayoutPass() { this.LayoutManager.ExecuteLayoutPass(); this.impl.Invalidate(new Rect(this.ClientSize)); } private void RenderVisualTree() { if (!this.LayoutManager.LayoutQueued) { this.impl.Invalidate(new Rect(this.ClientSize)); } } } }