Browse Source

Make toplevels responsible of creating popups

pull/2778/head
Nikita Tsukanov 7 years ago
parent
commit
35f64af761
  1. 5
      src/Android/Avalonia.Android/AndroidPlatform.cs
  2. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  3. 1
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  4. 2
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  5. 1
      src/Avalonia.Controls/Platform/IWindowingPlatform.cs
  6. 5
      src/Avalonia.Controls/Platform/PlatformManager.cs
  7. 37
      src/Avalonia.Controls/Primitives/Popup.cs
  8. 8
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  9. 3
      src/Avalonia.Controls/ToolTip.cs
  10. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  11. 5
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  12. 6
      src/Avalonia.Native/PopupImpl.cs
  13. 5
      src/Avalonia.Native/WindowImpl.cs
  14. 3
      src/Avalonia.Native/WindowImplBase.cs
  15. 7
      src/Avalonia.X11/X11Platform.cs
  16. 10
      src/Avalonia.X11/X11Window.cs
  17. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  18. 2
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  19. 5
      src/Windows/Avalonia.Win32/Win32Platform.cs
  20. 2
      src/iOS/Avalonia.iOS/TopLevelImpl.cs
  21. 12
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

5
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -71,10 +71,5 @@ namespace Avalonia.Android
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public IPopupImpl CreatePopup()
{
return new PopupImpl();
}
} }
} }

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -191,6 +191,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
} }
} }
public IPopupImpl CreatePopup() => null;
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface); ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface);
} }
} }

1
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -61,5 +61,6 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action Closed { get; set; } public Action Closed { get; set; }
public abstract IMouseDevice MouseDevice { get; } public abstract IMouseDevice MouseDevice { get; }
public IPopupImpl CreatePopup() => null;
} }
} }

2
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -107,5 +107,7 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
[CanBeNull] [CanBeNull]
IMouseDevice MouseDevice { get; } IMouseDevice MouseDevice { get; }
IPopupImpl CreatePopup();
} }
} }

1
src/Avalonia.Controls/Platform/IWindowingPlatform.cs

@ -4,6 +4,5 @@ namespace Avalonia.Platform
{ {
IWindowImpl CreateWindow(); IWindowImpl CreateWindow();
IEmbeddableWindowImpl CreateEmbeddableWindow(); IEmbeddableWindowImpl CreateEmbeddableWindow();
IPopupImpl CreatePopup();
} }
} }

5
src/Avalonia.Controls/Platform/PlatformManager.cs

@ -41,10 +41,5 @@ namespace Avalonia.Controls.Platform
throw new Exception("Could not CreateEmbeddableWindow(): IWindowingPlatform is not registered."); throw new Exception("Could not CreateEmbeddableWindow(): IWindowingPlatform is not registered.");
return platform.CreateEmbeddableWindow(); return platform.CreateEmbeddableWindow();
} }
public static IPopupImpl CreatePopup()
{
return AvaloniaLocator.Current.GetService<IWindowingPlatform>().CreatePopup();
}
} }
} }

37
src/Avalonia.Controls/Primitives/Popup.cs

@ -218,9 +218,16 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
public void Open() public void Open()
{ {
if (PlacementTarget == null)
throw new InvalidOperationException("It's not valid to show a popup without a PlacementTarget");
if (_topLevel == null && PlacementTarget != null)
_topLevel = PlacementTarget.GetSelfAndLogicalAncestors().First(x => x is TopLevel) as TopLevel;
if (_popupRoot == null) if (_popupRoot == null)
{ {
_popupRoot = new PopupRoot(DependencyResolver) _popupRoot = new PopupRoot(_topLevel, DependencyResolver)
{ {
[~ContentControl.ContentProperty] = this[~ChildProperty], [~ContentControl.ContentProperty] = this[~ChildProperty],
[~WidthProperty] = this[~WidthProperty], [~WidthProperty] = this[~WidthProperty],
@ -236,30 +243,22 @@ namespace Avalonia.Controls.Primitives
_popupRoot.Position = GetPosition(); _popupRoot.Position = GetPosition();
if (_topLevel == null && PlacementTarget != null) var window = _topLevel as Window;
if (window != null)
{ {
_topLevel = PlacementTarget.GetSelfAndLogicalAncestors().First(x => x is TopLevel) as TopLevel; window.Deactivated += WindowDeactivated;
} }
else
if (_topLevel != null)
{ {
var window = _topLevel as Window; var parentPopuproot = _topLevel as PopupRoot;
if (window != null) if (parentPopuproot?.Parent is Popup popup)
{ {
window.Deactivated += WindowDeactivated; popup.Closed += ParentClosed;
} }
else
{
var parentPopuproot = _topLevel as PopupRoot;
if (parentPopuproot?.Parent is Popup popup)
{
popup.Closed += ParentClosed;
}
}
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
_nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
} }
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
_nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
PopupRootCreated?.Invoke(this, EventArgs.Empty); PopupRootCreated?.Invoke(this, EventArgs.Empty);
_popupRoot.Show(); _popupRoot.Show();

8
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -31,8 +31,8 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PopupRoot"/> class. /// Initializes a new instance of the <see cref="PopupRoot"/> class.
/// </summary> /// </summary>
public PopupRoot() public PopupRoot(TopLevel parent)
: this(null) : this(parent, null)
{ {
} }
@ -42,8 +42,8 @@ namespace Avalonia.Controls.Primitives
/// <param name="dependencyResolver"> /// <param name="dependencyResolver">
/// The dependency resolver to use. If null the default dependency resolver will be used. /// The dependency resolver to use. If null the default dependency resolver will be used.
/// </param> /// </param>
public PopupRoot(IAvaloniaDependencyResolver dependencyResolver) public PopupRoot(TopLevel parent, IAvaloniaDependencyResolver dependencyResolver)
: base(PlatformManager.CreatePopup(), dependencyResolver) : base(parent.PlatformImpl.CreatePopup(), dependencyResolver)
{ {
} }

3
src/Avalonia.Controls/ToolTip.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -234,7 +235,7 @@ namespace Avalonia.Controls
{ {
Close(); Close();
_popup = new PopupRoot { Content = this, }; _popup = new PopupRoot((TopLevel)control.GetVisualRoot()) {Content = this};
((ISetLogicalParent)_popup).SetParent(control); ((ISetLogicalParent)_popup).SetParent(control);
_popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup, _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
GetHorizontalOffset(control), GetVerticalOffset(control)); GetHorizontalOffset(control), GetVerticalOffset(control));

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -29,6 +29,8 @@ namespace Avalonia.DesignerSupport.Remote
public Func<bool> Closing { get; set; } public Func<bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice(); public IMouseDevice MouseDevice { get; } = new MouseDevice();
public IPopupImpl CreatePopup() => null;
public PixelPoint Position { get; set; } public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; } public Action<PixelPoint> PositionChanged { get; set; }
public WindowState WindowState { get; set; } public WindowState WindowState { get; set; }

5
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -97,11 +97,6 @@ namespace Avalonia.Native
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IPopupImpl CreatePopup()
{
return new PopupImpl(_factory, _options);
}
} }
public class AvaloniaNativeMacOptions public class AvaloniaNativeMacOptions

6
src/Avalonia.Native/PopupImpl.cs

@ -9,8 +9,12 @@ namespace Avalonia.Native
{ {
public class PopupImpl : WindowBaseImpl, IPopupImpl public class PopupImpl : WindowBaseImpl, IPopupImpl
{ {
private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts;
public PopupImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts) public PopupImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts)
{ {
_factory = factory;
_opts = opts;
using (var e = new PopupEvents(this)) using (var e = new PopupEvents(this))
{ {
Init(factory.CreatePopup(e), factory.CreateScreens()); Init(factory.CreatePopup(e), factory.CreateScreens());
@ -35,5 +39,7 @@ namespace Avalonia.Native
{ {
} }
} }
public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts);
} }
} }

5
src/Avalonia.Native/WindowImpl.cs

@ -11,9 +11,13 @@ namespace Avalonia.Native
{ {
public class WindowImpl : WindowBaseImpl, IWindowImpl public class WindowImpl : WindowBaseImpl, IWindowImpl
{ {
private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts;
IAvnWindow _native; IAvnWindow _native;
public WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts) public WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts)
{ {
_factory = factory;
_opts = opts;
using (var e = new WindowEvents(this)) using (var e = new WindowEvents(this))
{ {
Init(_native = factory.CreateWindow(e), factory.CreateScreens()); Init(_native = factory.CreateWindow(e), factory.CreateScreens());
@ -100,5 +104,6 @@ namespace Avalonia.Native
} }
public Func<bool> Closing { get; set; } public Func<bool> Closing { get; set; }
public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts);
} }
} }

3
src/Avalonia.Native/WindowImplBase.cs

@ -15,7 +15,7 @@ using Avalonia.Threading;
namespace Avalonia.Native namespace Avalonia.Native
{ {
public class WindowBaseImpl : IWindowBaseImpl, public abstract class WindowBaseImpl : IWindowBaseImpl,
IFramebufferPlatformSurface IFramebufferPlatformSurface
{ {
IInputRoot _inputRoot; IInputRoot _inputRoot;
@ -91,6 +91,7 @@ namespace Avalonia.Native
public Action<Size> Resized { get; set; } public Action<Size> Resized { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public IMouseDevice MouseDevice => AvaloniaNativePlatform.MouseDevice; public IMouseDevice MouseDevice => AvaloniaNativePlatform.MouseDevice;
public abstract IPopupImpl CreatePopup();
class FramebufferWrapper : ILockedFramebuffer class FramebufferWrapper : ILockedFramebuffer

7
src/Avalonia.X11/X11Platform.cs

@ -74,18 +74,13 @@ namespace Avalonia.X11
public IntPtr Display { get; set; } public IntPtr Display { get; set; }
public IWindowImpl CreateWindow() public IWindowImpl CreateWindow()
{ {
return new X11Window(this, false); return new X11Window(this, null);
} }
public IEmbeddableWindowImpl CreateEmbeddableWindow() public IEmbeddableWindowImpl CreateEmbeddableWindow()
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public IPopupImpl CreatePopup()
{
return new X11Window(this, true);
}
} }
} }

10
src/Avalonia.X11/X11Window.cs

@ -21,6 +21,7 @@ namespace Avalonia.X11
unsafe class X11Window : IWindowImpl, IPopupImpl, IXI2Client unsafe class X11Window : IWindowImpl, IPopupImpl, IXI2Client
{ {
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
private readonly IWindowImpl _popupParent;
private readonly bool _popup; private readonly bool _popup;
private readonly X11Info _x11; private readonly X11Info _x11;
private bool _invalidated; private bool _invalidated;
@ -47,10 +48,10 @@ namespace Avalonia.X11
private readonly Queue<InputEventContainer> _inputQueue = new Queue<InputEventContainer>(); private readonly Queue<InputEventContainer> _inputQueue = new Queue<InputEventContainer>();
private InputEventContainer _lastEvent; private InputEventContainer _lastEvent;
private bool _useRenderWindow = false; private bool _useRenderWindow = false;
public X11Window(AvaloniaX11Platform platform, bool popup) public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
{ {
_platform = platform; _platform = platform;
_popup = popup; _popup = popupParent != null;
_x11 = platform.Info; _x11 = platform.Info;
_mouse = platform.MouseDevice; _mouse = platform.MouseDevice;
_keyboard = platform.KeyboardDevice; _keyboard = platform.KeyboardDevice;
@ -66,7 +67,7 @@ namespace Avalonia.X11
| SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore
| SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
if (popup) if (_popup)
{ {
attr.override_redirect = true; attr.override_redirect = true;
valueMask |= SetWindowValuemask.OverrideRedirect; valueMask |= SetWindowValuemask.OverrideRedirect;
@ -793,7 +794,8 @@ namespace Avalonia.X11
} }
public IMouseDevice MouseDevice => _mouse; public IMouseDevice MouseDevice => _mouse;
public IPopupImpl CreatePopup() => new X11Window(_platform, this);
public void Activate() public void Activate()
{ {
if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero) if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero)

2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -59,6 +59,8 @@ namespace Avalonia.LinuxFramebuffer
public Size ClientSize => ScaledSize; public Size ClientSize => ScaledSize;
public IMouseDevice MouseDevice => new MouseDevice(); public IMouseDevice MouseDevice => new MouseDevice();
public IPopupImpl CreatePopup() => null;
public double Scaling => 1; public double Scaling => 1;
public IEnumerable<object> Surfaces => new object[] {_outputBackend}; public IEnumerable<object> Surfaces => new object[] {_outputBackend};
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs> Input { get; set; }

2
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -240,5 +240,7 @@ namespace Avalonia.Win32.Interop.Wpf
return new Vector(1, 1); return new Vector(1, 1);
return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22); return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22);
} }
IPopupImpl CreatePopup() => null;
} }
} }

5
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -210,11 +210,6 @@ namespace Avalonia.Win32
return embedded; return embedded;
} }
public IPopupImpl CreatePopup()
{
return new PopupImpl();
}
public IWindowIconImpl LoadIcon(string fileName) public IWindowIconImpl LoadIcon(string fileName)
{ {
using (var stream = File.OpenRead(fileName)) using (var stream = File.OpenRead(fileName))

2
src/iOS/Avalonia.iOS/TopLevelImpl.cs

@ -134,5 +134,7 @@ namespace Avalonia.iOS
} }
public ILockedFramebuffer Lock() => new EmulatedFramebuffer(this); public ILockedFramebuffer Lock() => new EmulatedFramebuffer(this);
public IPopupImpl CreatePopup() => null;
} }
} }

12
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -21,7 +21,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{ {
using (UnitTestApplication.Start(TestServices.StyledWindow)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var target = CreateTarget(); var target = CreateTarget(new Window());
Assert.True(((ILogical)target).IsAttachedToLogicalTree); Assert.True(((ILogical)target).IsAttachedToLogicalTree);
} }
@ -32,7 +32,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{ {
using (UnitTestApplication.Start(TestServices.StyledWindow)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var target = CreateTarget(); var target = CreateTarget(new Window());
Assert.True(target.Presenter.IsAttachedToLogicalTree); Assert.True(target.Presenter.IsAttachedToLogicalTree);
} }
@ -63,8 +63,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (UnitTestApplication.Start(TestServices.StyledWindow)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var child = new Decorator(); var child = new Decorator();
var target = CreateTarget();
var window = new Window(); var window = new Window();
var target = CreateTarget(window);
var detachedCount = 0; var detachedCount = 0;
var attachedCount = 0; var attachedCount = 0;
@ -88,8 +88,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (UnitTestApplication.Start(TestServices.StyledWindow)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var child = new Decorator(); var child = new Decorator();
var target = CreateTarget();
var window = new Window(); var window = new Window();
var target = CreateTarget(window);
var detachedCount = 0; var detachedCount = 0;
var attachedCount = 0; var attachedCount = 0;
@ -130,9 +130,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
} }
} }
private PopupRoot CreateTarget() private PopupRoot CreateTarget(TopLevel popupParent)
{ {
var result = new PopupRoot var result = new PopupRoot(popupParent)
{ {
Template = new FuncControlTemplate<PopupRoot>((parent, scope) => Template = new FuncControlTemplate<PopupRoot>((parent, scope) =>
new ContentPresenter new ContentPresenter

Loading…
Cancel
Save