// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; using System.Text; using Avalonia.Controls.Platform; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.VisualTree; using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { /// /// The root window of a . /// public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost { private readonly TopLevel _parent; private IDisposable _presenterSubscription; private PopupPositionerParameters _positionerParameters; /// /// Initializes static members of the class. /// static PopupRoot() { BackgroundProperty.OverrideDefaultValue(typeof(PopupRoot), Brushes.White); } /// /// Initializes a new instance of the class. /// public PopupRoot(TopLevel parent, IPopupImpl impl) : this(parent, impl,null) { } /// /// Initializes a new instance of the class. /// /// /// The dependency resolver to use. If null the default dependency resolver will be used. /// public PopupRoot(TopLevel parent, IPopupImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { _parent = parent; } /// /// Gets the platform-specific window implementation. /// [CanBeNull] public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; /// /// Gets the parent control in the event route. /// /// /// Popup events are passed to their parent window. This facilitates this. /// IInteractive IInteractive.InteractiveParent => Parent; /// /// Gets the control that is hosting the popup root. /// IVisual IHostedVisualTreeRoot.Host => Parent; /// /// Gets the styling parent of the popup root. /// IStyleHost IStyleHost.StylingParent => Parent; /// public void Dispose() => PlatformImpl?.Dispose(); /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) { base.OnTemplateApplied(e); if (Parent?.TemplatedParent != null) { if (_presenterSubscription != null) { _presenterSubscription.Dispose(); _presenterSubscription = null; } Presenter?.ApplyTemplate(); Presenter?.GetObservable(ContentPresenter.ChildProperty) .Subscribe(SetTemplatedParentAndApplyChildTemplates); } } private void SetTemplatedParentAndApplyChildTemplates(IControl control) { if (control != null) { var templatedParent = Parent.TemplatedParent; if (control.TemplatedParent == null) { control.SetValue(TemplatedParentProperty, templatedParent); } control.ApplyTemplate(); if (!(control is IPresenter) && control.TemplatedParent == templatedParent) { foreach (IControl child in control.GetVisualChildren()) { SetTemplatedParentAndApplyChildTemplates(child); } } } } void UpdatePosition() { PlatformImpl?.PopupPositioner.Update(_positionerParameters); } public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, PopupPositioningEdge anchor = PopupPositioningEdge.None, PopupPositioningEdge gravity = PopupPositioningEdge.None) { _positionerParameters.ConfigurePosition(_parent, target, placement, offset, anchor, gravity); if (_positionerParameters.Size != default) UpdatePosition(); } public void SetContent(IControl control) => Content = control; IVisual IPopupHost.VisualRoot => this; public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, StyledProperty maxHeightProperty, StyledProperty topmostProperty) { var bindings = new List(); void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to])); Bind(WidthProperty, widthProperty); Bind(MinWidthProperty, minWidthProperty); Bind(MaxWidthProperty, maxWidthProperty); Bind(HeightProperty, heightProperty); Bind(MinHeightProperty, minHeightProperty); Bind(MaxHeightProperty, maxHeightProperty); Bind(TopmostProperty, topmostProperty); return Disposable.Create(() => { foreach (var x in bindings) x.Dispose(); }); } /// /// Carries out the arrange pass of the window. /// /// The final window size. /// The parameter unchanged. protected override Size ArrangeOverride(Size finalSize) { using (BeginAutoSizing()) { _positionerParameters.Size = finalSize; UpdatePosition(); } return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } } }