Browse Source

Merge branch 'master' into scenegraph

Conflicts:
	src/Gtk/Avalonia.Gtk/TopLevelImpl.cs
	tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
418effebc1
  1. 39
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  2. 45
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  3. 5
      src/Avalonia.Controls/AppBuilderBase.cs
  4. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  5. 4
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  6. 12
      src/Avalonia.Controls/Menu.cs
  7. 2
      src/Avalonia.Controls/Platform/IEmbeddableWindowImpl.cs
  8. 2
      src/Avalonia.Controls/Platform/IPopupImpl.cs
  9. 78
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  10. 69
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  11. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  12. 2
      src/Avalonia.Controls/Platform/PlatformManager.cs
  13. 12
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  14. 10
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  16. 159
      src/Avalonia.Controls/TopLevel.cs
  17. 2
      src/Avalonia.Controls/Window.cs
  18. 194
      src/Avalonia.Controls/WindowBase.cs
  19. 3
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  20. 2
      src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
  21. 34
      src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs
  22. 4
      src/Gtk/Avalonia.Gtk/FramebufferManager.cs
  23. 4
      src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs
  24. 86
      src/Gtk/Avalonia.Gtk/TopLevelImpl.cs
  25. 29
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  26. 2
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  27. 4
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  28. 2
      src/Gtk/Avalonia.Gtk3/PopupImpl.cs
  29. 4
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  30. 17
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  31. 2
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  32. 2
      src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
  33. 2
      src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs
  34. 10
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  35. 4
      src/Windows/Avalonia.Win32/Win32Platform.cs
  36. 30
      src/Windows/Avalonia.Win32/WindowImpl.cs
  37. 66
      src/iOS/Avalonia.iOS/TopLevelImpl.cs
  38. 1
      tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
  39. 98
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  40. 126
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  41. 5
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

39
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -10,6 +10,7 @@ using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Controls;
using Avalonia.Platform;
namespace Avalonia.Android.Platform.SkiaPlatform
@ -18,24 +19,25 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
private Point _position;
private bool _isAdded;
Action IWindowBaseImpl.Activated { get; set; }
public Action<Point> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public PopupImpl() : base(ActivityTracker.Current, true)
{
}
private Size _clientSize = new Size(1, 1);
public override Size ClientSize
public void Resize(Size value)
{
get { return base.ClientSize; }
set
{
if(View == null)
return;
_clientSize = value;
UpdateParams();
}
if (View == null)
return;
_clientSize = value;
UpdateParams();
}
public override Point Position
public Point Position
{
get { return _position; }
set
@ -87,5 +89,22 @@ namespace Avalonia.Android.Platform.SkiaPlatform
Hide();
base.Dispose();
}
public void Activate()
{
}
public void BeginMoveDrag()
{
//Not supported
}
public void BeginResizeDrag(WindowEdge edge)
{
//Not supported
}
}
}

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

@ -67,8 +67,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Size MaxClientSize { get; protected set; }
@ -79,29 +77,17 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<double> ScalingChanged { get; set; }
public Action<Point> PositionChanged { get; set; }
public View View => _view;
Action ITopLevelImpl.Activated { get; set; }
IPlatformHandle ITopLevelImpl.Handle => _view;
public IPlatformHandle Handle => _view;
public IEnumerable<object> Surfaces => new object[] {this};
public void Activate()
{
}
public virtual void Hide()
{
_view.Visibility = ViewStates.Invisible;
}
public void SetSystemDecorations(bool enabled)
{
}
public void Invalidate(Rect rect)
{
if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
@ -126,40 +112,19 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
InputRoot = inputRoot;
}
public void SetTitle(string title)
{
}
public virtual void Show()
{
_view.Visibility = ViewStates.Visible;
}
public void BeginMoveDrag()
{
//Not supported
}
public void BeginResizeDrag(WindowEdge edge)
{
//Not supported
}
public virtual Point Position { get; set; }
public double Scaling => 1;
void Draw()
{
Paint?.Invoke(new Rect(new Point(0, 0), ClientSize));
}
public void SetIcon(IWindowIconImpl icon)
{
// No window icons for mobile platforms
}
public virtual void Dispose()
{
_view.Dispose();

5
src/Avalonia.Controls/AppBuilderBase.cs

@ -115,13 +115,16 @@ namespace Avalonia.Controls
/// Starts the application with an instance of <typeparamref name="TMainWindow"/>.
/// </summary>
/// <typeparam name="TMainWindow">The window type.</typeparam>
public void Start<TMainWindow>()
/// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
public void Start<TMainWindow>(Func<object> dataContextProvider = null)
where TMainWindow : Window, new()
{
Setup();
BeforeStartCallback(Self);
var window = new TMainWindow();
if (dataContextProvider != null)
window.DataContext = dataContextProvider();
window.Show();
Instance.Run(window);
}

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -57,6 +57,7 @@
<Compile Include="HotkeyManager.cs" />
<Compile Include="IApplicationLifecycle.cs" />
<Compile Include="IScrollable.cs" />
<Compile Include="Platform\IWindowBaseImpl.cs" />
<Compile Include="Platform\Surfaces\IFramebufferPlatformSurface.cs" />
<Compile Include="Platform\Surfaces\ILockedFramebuffer.cs" />
<Compile Include="Platform\Surfaces\PixelFormat.cs" />
@ -65,6 +66,7 @@
<Compile Include="Platform\IEmbeddableWindowImpl.cs" />
<Compile Include="Platform\ExportAvaloniaModuleAttribute.cs" />
<Compile Include="Platform\ExportWindowingSubsystemAttribute.cs" />
<Compile Include="WindowBase.cs" />
<Compile Include="WindowIcon.cs" />
<Compile Include="IPseudoClasses.cs" />
<Compile Include="DropDownItem.cs" />

4
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -11,12 +11,11 @@ namespace Avalonia.Controls.Embedding
{
public EmbeddableControlRoot(IEmbeddableWindowImpl impl) : base(impl)
{
PlatformImpl.Show();
}
public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableWindow())
{
PlatformImpl.Show();
}
public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
@ -25,7 +24,6 @@ namespace Avalonia.Controls.Embedding
{
EnsureInitialized();
ApplyTemplate();
PlatformImpl.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
}

12
src/Avalonia.Controls/Menu.cs

@ -103,9 +103,11 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTree(e);
var topLevel = e.Root as TopLevel;
var topLevel = (TopLevel)e.Root;
var window = e.Root as Window;
topLevel.Deactivated += Deactivated;
if (window != null)
window.Deactivated += Deactivated;
var pointerPress = topLevel.AddHandler(
PointerPressedEvent,
@ -114,7 +116,11 @@ namespace Avalonia.Controls
_subscription = new CompositeDisposable(
pointerPress,
Disposable.Create(() => topLevel.Deactivated -= Deactivated),
Disposable.Create(() =>
{
if (window != null)
window.Deactivated -= Deactivated;
}),
InputManager.Instance.Process.Subscribe(ListenForNonClientClick));
var inputRoot = e.Root as IInputRoot;

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

@ -8,7 +8,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific embeddable window implementation.
/// </summary>
public interface IEmbeddableWindowImpl : IWindowImpl
public interface IEmbeddableWindowImpl : ITopLevelImpl
{
event Action LostFocus;
}

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

@ -6,7 +6,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific popup window implementation.
/// </summary>
public interface IPopupImpl : ITopLevelImpl
public interface IPopupImpl : IWindowBaseImpl
{
}

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

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -19,25 +18,15 @@ namespace Avalonia.Platform
public interface ITopLevelImpl : IDisposable
{
/// <summary>
/// Gets or sets the client size of the window.
/// Gets the client size of the toplevel.
/// </summary>
Size ClientSize { get; set; }
Size ClientSize { get; }
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
Size MaxClientSize { get; }
/// <summary>
/// Gets the scaling factor for the window.
/// Gets the scaling factor for the toplevel.
/// </summary>
double Scaling { get; }
/// <summary>
/// Gets the platform window handle.
/// </summary>
IPlatformHandle Handle { get; }
/// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary>
@ -51,57 +40,32 @@ namespace Avalonia.Platform
IEnumerable<object> Surfaces { get; }
/// <summary>
/// Gets or sets a method called when the window is activated (receives focus).
/// </summary>
Action Activated { get; set; }
/// <summary>
/// Gets or sets a method called when the window is closed.
/// </summary>
Action Closed { get; set; }
/// <summary>
/// Gets or sets a method called when the window is deactivated (loses focus).
/// </summary>
Action Deactivated { get; set; }
/// <summary>
/// Gets or sets a method called when the window receives input.
/// Gets or sets a method called when the toplevel receives input.
/// </summary>
Action<RawInputEventArgs> Input { get; set; }
/// <summary>
/// Gets or sets a method called when the window requires painting.
/// Gets or sets a method called when the toplevel requires painting.
/// </summary>
Action<Rect> Paint { get; set; }
/// <summary>
/// Gets or sets a method called when the window is resized.
/// Gets or sets a method called when the toplevel is resized.
/// </summary>
Action<Size> Resized { get; set; }
/// <summary>
/// Gets or sets a method called when the window's scaling changes.
/// Gets or sets a method called when the toplevel's scaling changes.
/// </summary>
Action<double> ScalingChanged { get; set; }
/// <summary>
/// Gets or sets a method called when the window's position changes.
/// </summary>
Action<Point> PositionChanged { get; set; }
/// <summary>
/// Activates the window.
/// </summary>
void Activate();
/// <summary>
/// Invalidates a rect on the window.
/// Invalidates a rect on the toplevel.
/// </summary>
void Invalidate(Rect rect);
/// <summary>
/// Sets the <see cref="IInputRoot"/> for the window.
/// Sets the <see cref="IInputRoot"/> for the toplevel.
/// </summary>
void SetInputRoot(IInputRoot inputRoot);
@ -120,32 +84,14 @@ namespace Avalonia.Platform
Point PointToScreen(Point point);
/// <summary>
/// Sets the cursor associated with the window.
/// Sets the cursor associated with the toplevel.
/// </summary>
/// <param name="cursor">The cursor. Use null for default cursor</param>
void SetCursor(IPlatformHandle cursor);
/// <summary>
/// Shows the toplevel.
/// </summary>
void Show();
/// <summary>
/// Hides the window.
/// </summary>
void Hide();
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary>
void BeginMoveDrag();
/// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
/// Gets or sets a method called when the underlying implementation is destroyed.
/// </summary>
void BeginResizeDrag(WindowEdge edge);
Point Position { get; set; }
Action Closed { get; set; }
}
}

69
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -0,0 +1,69 @@
using System;
using Avalonia.Controls;
namespace Avalonia.Platform
{
public interface IWindowBaseImpl : ITopLevelImpl
{
/// <summary>
/// Shows the toplevel.
/// </summary>
void Show();
/// <summary>
/// Hides the window.
/// </summary>
void Hide();
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary>
void BeginMoveDrag();
/// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
/// </summary>
void BeginResizeDrag(WindowEdge edge);
/// <summary>
/// Gets position of the window relatively to the screen
/// </summary>
Point Position { get; set; }
/// <summary>
/// Gets or sets a method called when the window's position changes.
/// </summary>
Action<Point> PositionChanged { get; set; }
/// <summary>
/// Activates the window.
/// </summary>
void Activate();
/// <summary>
/// Gets or sets a method called when the window is deactivated (loses focus).
/// </summary>
Action Deactivated { get; set; }
/// <summary>
/// Gets or sets a method called when the window is activated (receives focus).
/// </summary>
Action Activated { get; set; }
/// <summary>
/// Gets the platform window handle.
/// </summary>
IPlatformHandle Handle { get; }
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
Size MaxClientSize { get; }
/// <summary>
/// Sets the client size of the toplevel.
/// </summary>
void Resize(Size clientSize);
}
}

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

@ -9,7 +9,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific window implementation.
/// </summary>
public interface IWindowImpl : ITopLevelImpl
public interface IWindowImpl : IWindowBaseImpl
{
/// <summary>
/// Gets or sets the minimized/maximized state of the window.

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

@ -31,7 +31,7 @@ namespace Avalonia.Controls.Platform
throw new Exception("Could not CreateWindow(): IWindowingPlatform is not registered.");
}
return s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow();
return s_designerMode ? (IWindowImpl)platform.CreateEmbeddableWindow() : platform.CreateWindow();
}
public static IEmbeddableWindowImpl CreateEmbeddableWindow()

12
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -97,7 +97,9 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
public override Size MeasureOverride(Size availableSize)
{
var window = Owner.GetVisualRoot() as TopLevel;
var visualRoot = Owner.GetVisualRoot();
var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize
?? (visualRoot as TopLevel)?.ClientSize;
// If infinity is passed as the available size and we're virtualized then we need to
// fill the available space, but to do that we *don't* want to materialize all our
@ -107,9 +109,9 @@ namespace Avalonia.Controls.Presenters
{
if (availableSize.Height == double.PositiveInfinity)
{
if (window != null)
if (maxAvailableSize.HasValue)
{
availableSize = availableSize.WithHeight(window.PlatformImpl.MaxClientSize.Height);
availableSize = availableSize.WithHeight(maxAvailableSize.Value.Height);
}
}
@ -119,9 +121,9 @@ namespace Avalonia.Controls.Presenters
{
if (availableSize.Width == double.PositiveInfinity)
{
if (window != null)
if (maxAvailableSize.HasValue)
{
availableSize = availableSize.WithWidth(window.PlatformImpl.MaxClientSize.Width);
availableSize = availableSize.WithWidth(maxAvailableSize.Value.Width);
}
}

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

@ -214,7 +214,9 @@ namespace Avalonia.Controls.Primitives
if (_topLevel != null)
{
_topLevel.Deactivated += TopLevelDeactivated;
var window = _topLevel as Window;
if (window != null)
window.Deactivated += WindowDeactivated;
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
_nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
}
@ -240,7 +242,9 @@ namespace Avalonia.Controls.Primitives
if (_topLevel != null)
{
_topLevel.RemoveHandler(PointerPressedEvent, PointerPressedOutside);
_topLevel.Deactivated -= TopLevelDeactivated;
var window = _topLevel as Window;
if (window != null)
window.Deactivated -= WindowDeactivated;
_nonClientListener?.Dispose();
_nonClientListener = null;
}
@ -382,7 +386,7 @@ namespace Avalonia.Controls.Primitives
}
}
private void TopLevelDeactivated(object sender, EventArgs e)
private void WindowDeactivated(object sender, EventArgs e)
{
if (!StaysOpen)
{

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

@ -15,7 +15,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// The root window of a <see cref="Popup"/>.
/// </summary>
public class PopupRoot : TopLevel, IInteractive, IHostedVisualTreeRoot, IDisposable
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable
{
private IDisposable _presenterSubscription;

159
src/Avalonia.Controls/TopLevel.cs

@ -18,12 +18,12 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Base class for top-level windows.
/// Base class for top-level widgets.
/// </summary>
/// <remarks>
/// This class acts as a base for top level windows such as <see cref="Window"/> and
/// <see cref="PopupRoot"/>. It handles scheduling layout, styling and rendering as well as
/// tracking the window <see cref="ClientSize"/> and <see cref="IsActive"/> state.
/// This class acts as a base for top level widget.
/// It handles scheduling layout, styling and rendering as well as
/// tracking the widget's <see cref="ClientSize"/>.
/// </remarks>
public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot
{
@ -33,12 +33,6 @@ namespace Avalonia.Controls
public static readonly DirectProperty<TopLevel, Size> ClientSizeProperty =
AvaloniaProperty.RegisterDirect<TopLevel, Size>(nameof(ClientSize), o => o.ClientSize);
/// <summary>
/// Defines the <see cref="IsActive"/> property.
/// </summary>
public static readonly DirectProperty<TopLevel, bool> IsActiveProperty =
AvaloniaProperty.RegisterDirect<TopLevel, bool>(nameof(IsActive), o => o.IsActive);
/// <summary>
/// Defines the <see cref="IInputRoot.PointerOverElement"/> property.
/// </summary>
@ -51,7 +45,6 @@ namespace Avalonia.Controls
private readonly IApplicationLifecycle _applicationLifecycle;
private readonly IPlatformRenderInterface _renderInterface;
private Size _clientSize;
private bool _isActive;
/// <summary>
/// Initializes static members of the <see cref="TopLevel"/> class.
@ -101,21 +94,20 @@ namespace Avalonia.Controls
Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
PlatformImpl.SetInputRoot(this);
PlatformImpl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated;
PlatformImpl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput;
PlatformImpl.Paint = Renderer != null ? (Action<Rect>)Renderer.Render : null;
PlatformImpl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged;
PlatformImpl.PositionChanged = HandlePositionChanged;
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this);
ClientSize = PlatformImpl.ClientSize;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.ClientSize = x);
this.GetObservable(PointerOverElementProperty)
.Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
@ -127,51 +119,18 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Fired when the window is activated.
/// </summary>
public event EventHandler Activated;
/// <summary>
/// Fired when the window is closed.
/// </summary>
public event EventHandler Closed;
/// <summary>
/// Fired when the window is deactivated.
/// </summary>
public event EventHandler Deactivated;
/// <summary>
/// Fired when the window position is changed.
/// </summary>
public event EventHandler<PointEventArgs> PositionChanged;
/// <summary>
/// Gets or sets the client size of the window.
/// </summary>
public Size ClientSize
{
get { return _clientSize; }
private set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
}
/// <summary>
/// Gets a value that indicates whether the window is active.
/// </summary>
public bool IsActive
{
get { return _isActive; }
private set { SetAndRaise(IsActiveProperty, ref _isActive, value); }
}
/// <summary>
/// Gets or sets the window position in screen coordinates.
/// </summary>
public Point Position
{
get { return PlatformImpl.Position; }
set { PlatformImpl.Position = value; }
protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
}
/// <summary>
@ -229,15 +188,6 @@ namespace Avalonia.Controls
get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }
}
/// <summary>
/// Whether an auto-size operation is in progress.
/// </summary>
protected bool AutoSizing
{
get;
private set;
}
/// <inheritdoc/>
IRenderTarget IRenderRoot.CreateRenderTarget(IVisualBrushRenderer visualBrushRenderer)
{
@ -262,43 +212,6 @@ namespace Avalonia.Controls
return PlatformImpl.PointToScreen(p);
}
/// <summary>
/// Activates the window.
/// </summary>
public void Activate()
{
PlatformImpl.Activate();
}
/// <summary>
/// Begins an auto-resize operation.
/// </summary>
/// <returns>A disposable used to finish the operation.</returns>
/// <remarks>
/// When an auto-resize operation is in progress any resize events received will not be
/// cause the new size to be written to the <see cref="Layoutable.Width"/> and
/// <see cref="Layoutable.Height"/> properties.
/// </remarks>
protected IDisposable BeginAutoSizing()
{
AutoSizing = true;
return Disposable.Create(() => AutoSizing = false);
}
/// <summary>
/// Carries out the arrange pass of the window.
/// </summary>
/// <param name="finalSize">The final window size.</param>
/// <returns>The <paramref name="finalSize"/> parameter unchanged.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
using (BeginAutoSizing())
{
PlatformImpl.ClientSize = finalSize;
}
return base.ArrangeOverride(PlatformImpl.ClientSize);
}
/// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
@ -306,13 +219,9 @@ namespace Avalonia.Controls
/// <param name="clientSize">The new client size.</param>
protected virtual void HandleResized(Size clientSize)
{
if (!AutoSizing)
{
Width = clientSize.Width;
Height = clientSize.Height;
}
ClientSize = clientSize;
Width = clientSize.Width;
Height = clientSize.Height;
LayoutManager.Instance.ExecuteLayoutPass();
PlatformImpl.Invalidate(new Rect(clientSize));
}
@ -362,23 +271,6 @@ namespace Avalonia.Controls
return result;
}
/// <summary>
/// Handles an activated notification from <see cref="ITopLevelImpl.Activated"/>.
/// </summary>
private void HandleActivated()
{
Activated?.Invoke(this, EventArgs.Empty);
var scope = this as IFocusScope;
if (scope != null)
{
FocusManager.Instance.SetFocusScope(scope);
}
IsActive = true;
}
/// <summary>
/// Handles a closed notification from <see cref="ITopLevelImpl.Closed"/>.
/// </summary>
@ -403,16 +295,6 @@ namespace Avalonia.Controls
{
}
/// <summary>
/// Handles a deactivated notification from <see cref="ITopLevelImpl.Deactivated"/>.
/// </summary>
private void HandleDeactivated()
{
IsActive = false;
Deactivated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Handles input from <see cref="ITopLevelImpl.Input"/>.
/// </summary>
@ -421,26 +303,5 @@ namespace Avalonia.Controls
{
_inputManager.ProcessInput(e);
}
/// <summary>
/// Handles a window position change notification from
/// <see cref="ITopLevelImpl.PositionChanged"/>.
/// </summary>
/// <param name="pos">The window position.</param>
private void HandlePositionChanged(Point pos)
{
PositionChanged?.Invoke(this, new PointEventArgs(pos));
}
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary>
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag();
/// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
/// </summary>
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge);
}
}

2
src/Avalonia.Controls/Window.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls
/// <summary>
/// A top-level window.
/// </summary>
public class Window : TopLevel, IStyleable, IFocusScope, ILayoutRoot, INameScope
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
{
private static IList<Window> s_windows = new List<Window>();

194
src/Avalonia.Controls/WindowBase.cs

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Platform;
namespace Avalonia.Controls
{
/// <summary>
/// Base class for top-level windows.
/// </summary>
/// <remarks>
/// This class acts as a base for top level windows such as <see cref="Window"/> and
/// <see cref="PopupRoot"/>. It handles scheduling layout, styling and rendering as well as
/// tracking the window <see cref="TopLevel.ClientSize"/> and <see cref="IsActive"/> state.
/// </remarks>
public class WindowBase : TopLevel
{
/// <summary>
/// Defines the <see cref="IsActive"/> property.
/// </summary>
public static readonly DirectProperty<WindowBase, bool> IsActiveProperty =
AvaloniaProperty.RegisterDirect<WindowBase, bool>(nameof(IsActive), o => o.IsActive);
private bool _isActive;
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
{
}
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
{
PlatformImpl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated;
PlatformImpl.PositionChanged = HandlePositionChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x));
}
/// <summary>
/// Fired when the window is activated.
/// </summary>
public event EventHandler Activated;
/// <summary>
/// Fired when the window is deactivated.
/// </summary>
public event EventHandler Deactivated;
/// <summary>
/// Fired when the window position is changed.
/// </summary>
public event EventHandler<PointEventArgs> PositionChanged;
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
/// <summary>
/// Gets a value that indicates whether the window is active.
/// </summary>
public bool IsActive
{
get { return _isActive; }
private set { SetAndRaise(IsActiveProperty, ref _isActive, value); }
}
/// <summary>
/// Gets or sets the window position in screen coordinates.
/// </summary>
public Point Position
{
get { return PlatformImpl.Position; }
set { PlatformImpl.Position = value; }
}
/// <summary>
/// Whether an auto-size operation is in progress.
/// </summary>
protected bool AutoSizing
{
get;
private set;
}
/// <summary>
/// Activates the window.
/// </summary>
public void Activate()
{
PlatformImpl.Activate();
}
/// <summary>
/// Begins an auto-resize operation.
/// </summary>
/// <returns>A disposable used to finish the operation.</returns>
/// <remarks>
/// When an auto-resize operation is in progress any resize events received will not be
/// cause the new size to be written to the <see cref="Layoutable.Width"/> and
/// <see cref="Layoutable.Height"/> properties.
/// </remarks>
protected IDisposable BeginAutoSizing()
{
AutoSizing = true;
return Disposable.Create(() => AutoSizing = false);
}
/// <summary>
/// Carries out the arrange pass of the window.
/// </summary>
/// <param name="finalSize">The final window size.</param>
/// <returns>The <paramref name="finalSize"/> parameter unchanged.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
using (BeginAutoSizing())
{
PlatformImpl.Resize(finalSize);
}
return base.ArrangeOverride(PlatformImpl.ClientSize);
}
/// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary>
/// <param name="clientSize">The new client size.</param>
protected override void HandleResized(Size clientSize)
{
if (!AutoSizing)
{
Width = clientSize.Width;
Height = clientSize.Height;
}
ClientSize = clientSize;
LayoutManager.Instance.ExecuteLayoutPass();
PlatformImpl.Invalidate(new Rect(clientSize));
}
/// <summary>
/// Handles a window position change notification from
/// <see cref="IWindowBaseImpl.PositionChanged"/>.
/// </summary>
/// <param name="pos">The window position.</param>
private void HandlePositionChanged(Point pos)
{
PositionChanged?.Invoke(this, new PointEventArgs(pos));
}
/// <summary>
/// Handles an activated notification from <see cref="IWindowBaseImpl.Activated"/>.
/// </summary>
private void HandleActivated()
{
Activated?.Invoke(this, EventArgs.Empty);
var scope = this as IFocusScope;
if (scope != null)
{
FocusManager.Instance.SetFocusScope(scope);
}
IsActive = true;
}
/// <summary>
/// Handles a deactivated notification from <see cref="IWindowBaseImpl.Deactivated"/>.
/// </summary>
private void HandleDeactivated()
{
IsActive = false;
Deactivated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary>
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag();
/// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
/// </summary>
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge);
}
}

3
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -75,8 +75,7 @@ namespace Avalonia.DesignerSupport
private static void SetScalingFactor(double factor)
{
PlatformManager.SetDesignerScalingFactor(factor);
if (s_currentWindow != null)
s_currentWindow.PlatformImpl.ClientSize = s_currentWindow.ClientSize;
s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize);
}
static Window s_currentWindow;

2
src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj

@ -61,7 +61,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GtkPlatform.cs" />
<Compile Include="WindowImpl.cs" />
<Compile Include="WindowImplBase.cs" />
<Compile Include="TopLevelImpl.cs" />
<Compile Include="Input\GtkKeyboardDevice.cs" />
<Compile Include="Input\GtkMouseDevice.cs" />
<Compile Include="Windows.cs" />

34
src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs

@ -12,7 +12,7 @@ using WindowEdge = Avalonia.Controls.WindowEdge;
namespace Avalonia.Gtk
{
class EmbeddableImpl : WindowImplBase, IEmbeddableWindowImpl
class EmbeddableImpl : TopLevelImpl, IEmbeddableWindowImpl
{
#pragma warning disable CS0067 // Method not used
public event Action LostFocus;
@ -36,37 +36,7 @@ namespace Avalonia.Gtk
public override Size ClientSize
{
get { return new Size(Widget.Allocation.Width, Widget.Allocation.Height); }
set {}
}
//Stubs are needed for future GTK designer embedding support
public override void SetTitle(string title)
{
}
public override IDisposable ShowDialog() => Disposable.Create(() => { });
public override void SetSystemDecorations(bool enabled)
{
}
public override void SetIcon(IWindowIconImpl icon)
{
}
public override void BeginMoveDrag()
{
}
public override void BeginResizeDrag(WindowEdge edge)
{
}
public override Point Position
{
get { return new Point(); }
set {}
}
}
}

4
src/Gtk/Avalonia.Gtk/FramebufferManager.cs

@ -5,10 +5,10 @@ namespace Avalonia.Gtk
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly WindowImplBase _window;
private readonly TopLevelImpl _window;
private SurfaceFramebuffer _fb;
public FramebufferManager(WindowImplBase window)
public FramebufferManager(TopLevelImpl window)
{
_window = window;
}

4
src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs

@ -15,7 +15,7 @@ namespace Avalonia.Gtk
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
var tcs = new TaskCompletionSource<string[]>();
var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent)?.Widget.Toplevel as Window,
var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((TopLevelImpl)parent)?.Widget.Toplevel as Window,
dialog is OpenFileDialog
? FileChooserAction.Open
: FileChooserAction.Save,
@ -57,7 +57,7 @@ namespace Avalonia.Gtk
public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
var tcs = new TaskCompletionSource<string>();
var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent)?.Widget.Toplevel as Window,
var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((TopLevelImpl)parent)?.Widget.Toplevel as Window,
FileChooserAction.SelectFolder,
"Cancel", ResponseType.Cancel,
"Select Folder", ResponseType.Accept)

86
src/Gtk/Avalonia.Gtk/WindowImplBase.cs → src/Gtk/Avalonia.Gtk/TopLevelImpl.cs

@ -15,10 +15,10 @@ namespace Avalonia.Gtk
{
using Gtk = global::Gtk;
public abstract class WindowImplBase : IWindowImpl
public abstract class TopLevelImpl : ITopLevelImpl
{
private IInputRoot _inputRoot;
protected Gtk.Widget _window;
private Gtk.Widget _widget;
private FramebufferManager _framebuffer;
private Gtk.IMContext _imContext;
@ -27,46 +27,43 @@ namespace Avalonia.Gtk
private static readonly Gdk.Cursor DefaultCursor = new Gdk.Cursor(CursorType.LeftPtr);
protected WindowImplBase(Gtk.Widget window)
protected TopLevelImpl(Gtk.Widget window)
{
_window = window;
_widget = window;
_framebuffer = new FramebufferManager(this);
Init();
}
void Init()
{
Handle = _window as IPlatformHandle;
_window.Events = EventMask.AllEventsMask;
Handle = _widget as IPlatformHandle;
_widget.Events = EventMask.AllEventsMask;
_imContext = new Gtk.IMMulticontext();
_imContext.Commit += ImContext_Commit;
_window.Realized += OnRealized;
_window.AppPaintable = true;
_window.DoubleBuffered = false;
_window.Realize();
_window.ButtonPressEvent += OnButtonPressEvent;
_window.ButtonReleaseEvent += OnButtonReleaseEvent;
_window.ScrollEvent += OnScrollEvent;
_window.Destroyed += OnDestroyed;
_window.KeyPressEvent += OnKeyPressEvent;
_window.KeyReleaseEvent += OnKeyReleaseEvent;
_window.ExposeEvent += OnExposeEvent;
_window.MotionNotifyEvent += OnMotionNotifyEvent;
_widget.Realized += OnRealized;
_widget.Realize();
_widget.ButtonPressEvent += OnButtonPressEvent;
_widget.ButtonReleaseEvent += OnButtonReleaseEvent;
_widget.ScrollEvent += OnScrollEvent;
_widget.Destroyed += OnDestroyed;
_widget.KeyPressEvent += OnKeyPressEvent;
_widget.KeyReleaseEvent += OnKeyReleaseEvent;
_widget.ExposeEvent += OnExposeEvent;
_widget.MotionNotifyEvent += OnMotionNotifyEvent;
}
public IPlatformHandle Handle { get; private set; }
public Gtk.Widget Widget => _window;
public Gtk.Widget Widget => _widget;
public Gdk.Drawable CurrentDrawable { get; private set; }
void OnRealized (object sender, EventArgs eventArgs)
{
_imContext.ClientWindow = _window.GdkWindow;
_imContext.ClientWindow = _widget.GdkWindow;
}
public abstract Size ClientSize { get; set; }
public abstract Size ClientSize { get; }
public Size MaxClientSize
{
@ -74,7 +71,7 @@ namespace Avalonia.Gtk
{
// TODO: This should take into account things such as taskbar and window border
// thickness etc.
return new Size(_window.Screen.Width, _window.Screen.Height);
return new Size(_widget.Screen.Width, _widget.Screen.Height);
}
}
@ -82,7 +79,7 @@ namespace Avalonia.Gtk
{
get
{
switch (_window.GdkWindow.State)
switch (_widget.GdkWindow.State)
{
case Gdk.WindowState.Iconified:
return Controls.WindowState.Minimized;
@ -98,14 +95,14 @@ namespace Avalonia.Gtk
switch (value)
{
case Controls.WindowState.Minimized:
_window.GdkWindow.Iconify();
_widget.GdkWindow.Iconify();
break;
case Controls.WindowState.Maximized:
_window.GdkWindow.Maximize();
_widget.GdkWindow.Maximize();
break;
case Controls.WindowState.Normal:
_window.GdkWindow.Deiconify();
_window.GdkWindow.Unmaximize();
_widget.GdkWindow.Deiconify();
_widget.GdkWindow.Unmaximize();
break;
}
}
@ -143,15 +140,15 @@ namespace Avalonia.Gtk
public void Invalidate(Rect rect)
{
if (_window?.GdkWindow != null)
_window.GdkWindow.InvalidateRect(
if (_widget?.GdkWindow != null)
_widget.GdkWindow.InvalidateRect(
new Rectangle((int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height), true);
}
public Point PointToClient(Point point)
{
int x, y;
_window.GdkWindow.GetDeskrelativeOrigin(out x, out y);
_widget.GdkWindow.GetDeskrelativeOrigin(out x, out y);
return new Point(point.X - x, point.Y - y);
}
@ -159,7 +156,7 @@ namespace Avalonia.Gtk
public Point PointToScreen(Point point)
{
int x, y;
_window.GdkWindow.GetDeskrelativeOrigin(out x, out y);
_widget.GdkWindow.GetDeskrelativeOrigin(out x, out y);
return new Point(point.X + x, point.Y + y);
}
@ -168,28 +165,15 @@ namespace Avalonia.Gtk
_inputRoot = inputRoot;
}
public abstract void SetTitle(string title);
public abstract IDisposable ShowDialog();
public abstract void SetSystemDecorations(bool enabled);
public abstract void SetIcon(IWindowIconImpl icon);
public void SetCursor(IPlatformHandle cursor)
{
_window.GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
_widget.GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
}
public void Show() => _window.Show();
public void Hide() => _window.Hide();
public abstract void BeginMoveDrag();
public abstract void BeginResizeDrag(WindowEdge edge);
public abstract Point Position { get; set; }
public void Show() => _widget.Show();
void ITopLevelImpl.Activate()
{
_window.Activate();
}
public void Hide() => _widget.Hide();
private static InputModifiers GetModifierKeys(ModifierType state)
{
@ -319,9 +303,9 @@ namespace Avalonia.Gtk
public void Dispose()
{
_framebuffer.Dispose();
_window.Hide();
_window.Dispose();
_window = null;
_widget.Hide();
_widget.Dispose();
_widget = null;
}
}
}

29
src/Gtk/Avalonia.Gtk/WindowImpl.cs

@ -6,7 +6,7 @@ using Gdk;
namespace Avalonia.Gtk
{
using Gtk = global::Gtk;
public class WindowImpl : WindowImplBase
public class WindowImpl : TopLevelImpl, IWindowImpl
{
private Gtk.Window _window;
private Gtk.Window Window => _window ?? (_window = (Gtk.Window) Widget);
@ -61,24 +61,29 @@ namespace Avalonia.Gtk
Window.GetSize(out width, out height);
return new Size(width, height);
}
}
set
{
Window.Resize((int)value.Width, (int)value.Height);
}
public void Resize(Size value)
{
Window.Resize((int)value.Width, (int)value.Height);
}
public override void SetTitle(string title)
public void SetTitle(string title)
{
Window.Title = title;
}
void IWindowBaseImpl.Activate()
{
_window.Activate();
}
void OnFocusActivated(object sender, EventArgs eventArgs)
{
Activated();
}
public override void BeginMoveDrag()
public void BeginMoveDrag()
{
int x, y;
ModifierType mod;
@ -86,7 +91,7 @@ namespace Avalonia.Gtk
Window.BeginMoveDrag(1, x, y, 0);
}
public override void BeginResizeDrag(Controls.WindowEdge edge)
public void BeginResizeDrag(Controls.WindowEdge edge)
{
int x, y;
ModifierType mod;
@ -94,7 +99,7 @@ namespace Avalonia.Gtk
Window.BeginResizeDrag((Gdk.WindowEdge)(int)edge, 1, x, y, 0);
}
public override Point Position
public Point Position
{
get
{
@ -108,7 +113,7 @@ namespace Avalonia.Gtk
}
}
public override IDisposable ShowDialog()
public IDisposable ShowDialog()
{
Window.Modal = true;
Window.Show();
@ -116,9 +121,9 @@ namespace Avalonia.Gtk
return Disposable.Empty;
}
public override void SetSystemDecorations(bool enabled) => Window.Decorated = enabled;
public void SetSystemDecorations(bool enabled) => Window.Decorated = enabled;
public override void SetIcon(IWindowIconImpl icon)
public void SetIcon(IWindowIconImpl icon)
{
Window.Icon = ((IconImpl)icon).Pixbuf;
}

2
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@ -66,7 +66,7 @@
<Compile Include="PopupImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SystemDialogs.cs" />
<Compile Include="TopLevelImpl.cs" />
<Compile Include="WindowBaseImpl.cs" />
<Compile Include="Interop\Utf8Buffer.cs" />
<Compile Include="WindowImpl.cs" />
</ItemGroup>

4
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@ -9,8 +9,8 @@ namespace Avalonia.Gtk3
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly TopLevelImpl _window;
public FramebufferManager(TopLevelImpl window)
private readonly WindowBaseImpl _window;
public FramebufferManager(WindowBaseImpl window)
{
_window = window;
}

2
src/Gtk/Avalonia.Gtk3/PopupImpl.cs

@ -9,7 +9,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class PopupImpl : TopLevelImpl, IPopupImpl
class PopupImpl : WindowBaseImpl, IPopupImpl
{
static GtkWindow CreateWindow()
{

4
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@ -77,14 +77,14 @@ namespace Avalonia.Gtk3
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
return ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
return ShowDialog(dialog.Title, ((WindowBaseImpl) parent)?.GtkWidget,
dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
(dialog as OpenFileDialog)?.AllowMultiple ?? false, dialog.InitialFileName);
}
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
var res = await ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
var res = await ShowDialog(dialog.Title, ((WindowBaseImpl) parent)?.GtkWidget,
GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory);
return res?.FirstOrDefault();
}

17
src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs → src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -12,7 +12,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle
abstract class WindowBaseImpl : IWindowBaseImpl, IPlatformHandle
{
public readonly GtkWindow GtkWidget;
private IInputRoot _inputRoot;
@ -25,7 +25,7 @@ namespace Avalonia.Gtk3
private uint _lastKbdEvent;
private uint _lastSmoothScrollEvent;
public TopLevelImpl(GtkWindow gtkWidget)
public WindowBaseImpl(GtkWindow gtkWidget)
{
GtkWidget = gtkWidget;
@ -318,12 +318,13 @@ namespace Avalonia.Gtk3
Native.GtkWindowGetSize(GtkWidget, out w, out h);
return new Size(w, h);
}
set
{
if (GtkWidget.IsClosed)
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
}
}
public void Resize(Size value)
{
if (GtkWidget.IsClosed)
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
}
public Point Position

2
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -5,7 +5,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class WindowImpl : TopLevelImpl, IWindowImpl
class WindowImpl : WindowBaseImpl, IWindowImpl
{
public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel))
{

2
src/Skia/Avalonia.Skia.iOS/RenderTarget.cs

@ -14,7 +14,7 @@ namespace Avalonia.Skia
public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return new DrawingContextImpl(Surface.Canvas);
return new DrawingContextImpl(Surface.Canvas, visualBrushRenderer);
}
public void Dispose()

2
src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs

@ -8,7 +8,7 @@ namespace Avalonia.Skia
WindowRenderTarget _target;
public WindowDrawingContextImpl(WindowRenderTarget target)
: base(target.Surface.Canvas)
: base(target.Surface.Canvas, null)
{
_target = target;
}

10
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@ -15,10 +15,12 @@ namespace Avalonia.Win32.Embedding
{
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot();
private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle;
public WinFormsAvaloniaControlHost()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
UnmanagedMethods.SetParent(_root.PlatformImpl.Handle.Handle, Handle);
UnmanagedMethods.SetParent(WindowHandle, Handle);
_root.Prepare();
if (_root.IsFocused)
FocusManager.Instance.Focus(null);
@ -59,20 +61,20 @@ namespace Avalonia.Win32.Embedding
private void RootGotFocus(object sender, Interactivity.RoutedEventArgs e)
{
UnmanagedMethods.SetFocus(_root.PlatformImpl.Handle.Handle);
UnmanagedMethods.SetFocus(WindowHandle);
}
protected override void OnGotFocus(EventArgs e)
{
if (_root != null)
UnmanagedMethods.SetFocus(_root.PlatformImpl.Handle.Handle);
UnmanagedMethods.SetFocus(WindowHandle);
}
void FixPosition()
{
if (_root != null && Width > 0 && Height > 0)
UnmanagedMethods.MoveWindow(_root.PlatformImpl.Handle.Handle, 0, 0, Width, Height, true);
UnmanagedMethods.MoveWindow(WindowHandle, 0, 0, Width, Height, true);
}

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

@ -187,7 +187,9 @@ namespace Avalonia.Win32
#if NETSTANDARD
throw new NotSupportedException();
#else
return new EmbeddedWindowImpl();
var embedded = new EmbeddedWindowImpl();
embedded.Show();
return embedded;
#endif
}

30
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -89,23 +89,23 @@ namespace Avalonia.Win32
UnmanagedMethods.GetClientRect(_hwnd, out rect);
return new Size(rect.right, rect.bottom) / Scaling;
}
}
set
public void Resize(Size value)
{
if (value != ClientSize)
{
if (value != ClientSize)
{
value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
0,
0,
(int)value.Width,
(int)value.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
0,
0,
(int)value.Width,
(int)value.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
}

66
src/iOS/Avalonia.iOS/TopLevelImpl.cs

@ -32,6 +32,7 @@ namespace Avalonia.iOS
{
_keyboardHelper = new KeyboardEventsHelper<TopLevelImpl>(this);
AutoresizingMask = UIViewAutoresizing.All;
_keyboardHelper.ActivateAutoShowKeybord();
}
[Export("hasText")]
@ -44,37 +45,21 @@ namespace Avalonia.iOS
public void DeleteBackward() => _keyboardHelper.DeleteBackward();
public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
public Action Activated { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<Point> PositionChanged { get; set; }
public IPlatformHandle Handle => null;
public double Scaling => UIScreen.MainScreen.Scale;
public WindowState WindowState
{
get { return WindowState.Normal; }
set { }
}
public override void LayoutSubviews() => Resized?.Invoke(ClientSize);
public Size ClientSize
{
get { return Bounds.Size.ToAvalonia(); }
set { InvokeOnMainThread(() => Resized?.Invoke(ClientSize)); }
}
public void Activate()
{
}
public Size ClientSize => Bounds.Size.ToAvalonia();
public override void Draw(CGRect rect)
{
@ -93,42 +78,9 @@ namespace Avalonia.iOS
{
//Not supported
}
public void Show()
{
_keyboardHelper.ActivateAutoShowKeybord();
}
public void BeginMoveDrag()
{
//Not supported
}
public void BeginResizeDrag(WindowEdge edge)
{
//Not supported
}
public Point Position
{
get { return _position; }
set
{
_position = value;
PositionChanged?.Invoke(_position);
}
}
public Size MaxClientSize => Bounds.Size.ToAvalonia();
public IEnumerable<object> Surfaces => new object[] { this };
public void Hide()
{
//Not supported
}
public override void TouchesEnded(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
@ -182,11 +134,7 @@ namespace Avalonia.iOS
_touchLastPoint = location;
}
}
public void SetIcon(IWindowIconImpl icon)
{
}
public ILockedFramebuffer Lock() => new EmulatedFramebuffer(this);
}
}

1
tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj

@ -101,6 +101,7 @@
<Compile Include="Presenters\ItemsPresenterTests_Virtualization_Simple.cs" />
<Compile Include="Presenters\ItemsPresenterTests_Virtualization.cs" />
<Compile Include="TextBoxTests_DataValidation.cs" />
<Compile Include="WindowBaseTests.cs" />
<Compile Include="UserControlTests.cs" />
<Compile Include="DockPanelTests.cs" />
<Compile Include="EnumerableExtensions.cs" />

98
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -86,7 +86,6 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
impl.SetupProperty(x => x.ClientSize);
impl.SetupProperty(x => x.Resized);
impl.SetupGet(x => x.Scaling).Returns(1);
@ -107,30 +106,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Impl_ClientSize_Should_Be_Set_After_Layout_Pass()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = Mock.Of<ITopLevelImpl>(x => x.Scaling == 1);
var target = new TestTopLevel(impl)
{
IsVisible = true,
Template = CreateTemplate(),
Content = new TextBlock
{
Width = 321,
Height = 432,
}
};
LayoutManager.Instance.ExecuteInitialLayoutPass(target);
Mock.Get(impl).VerifySet(x => x.ClientSize = new Size(321, 432));
}
}
[Fact]
public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass()
{
@ -165,61 +140,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Window_Resize_Notification_Should_Notify_Renderer_Dirty()
{
var renderer = new Mock<IRenderer>();
var services = TestServices.StyledWindow.With(renderer: (_, __) => renderer.Object);
using (UnitTestApplication.Start(services))
{
var impl = new Mock<ITopLevelImpl>();
impl.SetupAllProperties();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
impl.Setup(x => x.Scaling).Returns(1);
var target = new TestTopLevel(impl.Object);
target.Measure(new Size(123, 456));
Assert.True(target.IsMeasureValid);
impl.Object.Resized(new Size(100, 200));
renderer.Verify(x => x.AddDirty(target));
}
}
[Fact]
public void Activate_Should_Call_Impl_Activate()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var target = new TestTopLevel(impl.Object);
target.Activate();
impl.Verify(x => x.Activate());
}
}
[Fact]
public void Impl_Activate_Should_Call_Raise_Activated_Event()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
impl.SetupAllProperties();
bool raised = false;
var target = new TestTopLevel(impl.Object);
target.Activated += (s, e) => raised = true;
impl.Object.Activated();
Assert.True(raised);
}
}
[Fact]
public void Impl_Close_Should_Call_Raise_Closed_Event()
{
@ -238,24 +158,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Impl_Deactivate_Should_Call_Raise_Activated_Event()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
impl.SetupAllProperties();
bool raised = false;
var target = new TestTopLevel(impl.Object);
target.Deactivated += (s, e) => raised = true;
impl.Object.Deactivated();
Assert.True(raised);
}
}
[Fact]
public void Impl_Input_Should_Pass_Input_To_InputManager()
{

126
tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs

@ -0,0 +1,126 @@
// 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.Reactive;
using System.Reactive.Subjects;
using Moq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class WindowBaseTests
{
[Fact]
public void Impl_ClientSize_Should_Be_Set_After_Layout_Pass()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = Mock.Of<IWindowBaseImpl>(x => x.Scaling == 1);
var target = new TestWindowBase(impl)
{
Template = CreateTemplate(),
Content = new TextBlock
{
Width = 321,
Height = 432,
}
};
LayoutManager.Instance.ExecuteInitialLayoutPass(target);
Mock.Get(impl).Verify(x => x.Resize(new Size(321, 432)));
}
}
[Fact]
public void Activate_Should_Call_Impl_Activate()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
var target = new TestWindowBase(impl.Object);
target.Activate();
impl.Verify(x => x.Activate());
}
}
[Fact]
public void Impl_Activate_Should_Call_Raise_Activated_Event()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
impl.SetupAllProperties();
bool raised = false;
var target = new TestWindowBase(impl.Object);
target.Activated += (s, e) => raised = true;
impl.Object.Activated();
Assert.True(raised);
}
}
[Fact]
public void Impl_Deactivate_Should_Call_Raise_Deativated_Event()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
impl.SetupAllProperties();
bool raised = false;
var target = new TestWindowBase(impl.Object);
target.Deactivated += (s, e) => raised = true;
impl.Object.Deactivated();
Assert.True(raised);
}
}
private FuncControlTemplate<TestWindowBase> CreateTemplate()
{
return new FuncControlTemplate<TestWindowBase>(x =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
});
}
private class TestWindowBase : WindowBase
{
public bool IsClosed { get; private set; }
public TestWindowBase(IWindowBaseImpl impl)
: base(impl)
{
}
protected override void HandleApplicationExiting()
{
base.HandleApplicationExiting();
IsClosed = true;
}
}
}
}

5
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -141,7 +141,10 @@ namespace Avalonia.Layout.UnitTests
var renderInterface = fixture.Create<IPlatformRenderInterface>();
var windowImpl = new Mock<IWindowImpl>();
windowImpl.SetupProperty(x => x.ClientSize);
Size clientSize = default(Size);
windowImpl.SetupGet(x => x.ClientSize).Returns(() => clientSize);
windowImpl.Setup(x => x.Resize(It.IsAny<Size>())).Callback<Size>(s => clientSize = s);
windowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1024, 1024));
windowImpl.SetupGet(x => x.Scaling).Returns(1);

Loading…
Cancel
Save