Browse Source

Merge branch 'master' into feature/TextRunProperties

pull/4101/head
Benedikt Stebner 6 years ago
committed by GitHub
parent
commit
449ecb98a4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      native/Avalonia.Native/inc/avalonia-native.h
  2. 33
      native/Avalonia.Native/src/OSX/window.mm
  3. 9
      samples/interop/NativeEmbedSample/MainWindow.xaml
  4. 1
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  5. 9
      src/Avalonia.Controls/Utils/IEnumerableUtils.cs
  6. 10
      src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
  7. 29
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs
  8. 23
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  9. 14
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
  10. 78
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs
  11. 10
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  12. 37
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
  13. 22
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
  14. 12
      src/Avalonia.Native/PopupImpl.cs
  15. 2
      src/Avalonia.Native/WindowImplBase.cs
  16. 16
      src/Avalonia.Themes.Fluent/ToolTip.xaml

1
native/Avalonia.Native/inc/avalonia-native.h

@ -278,6 +278,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
virtual HRESULT TakeFocusFromChildren() = 0;
};
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown

33
native/Avalonia.Native/src/OSX/window.mm

@ -116,10 +116,15 @@ public:
{
SetPosition(lastPositionSet);
UpdateStyle();
[Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
if(ShouldTakeFocusOnShow())
{
[Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
}
else
{
[Window orderFront: Window];
}
[Window setTitle:_lastTitle];
_shown = true;
@ -128,6 +133,11 @@ public:
}
}
virtual bool ShouldTakeFocusOnShow()
{
return true;
}
virtual HRESULT Hide () override
{
@autoreleasepool
@ -774,6 +784,15 @@ private:
}
}
virtual HRESULT TakeFocusFromChildren () override
{
if(Window == nil)
return S_OK;
if([Window isKeyWindow])
[Window makeFirstResponder: View];
return S_OK;
}
void EnterFullScreenMode ()
{
_fullScreenActive = true;
@ -1858,7 +1877,6 @@ private:
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
}
protected:
virtual NSWindowStyleMask GetStyle() override
{
@ -1876,6 +1894,11 @@ protected:
return S_OK;
}
}
public:
virtual bool ShouldTakeFocusOnShow() override
{
return false;
}
};
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)

9
samples/interop/NativeEmbedSample/MainWindow.xaml

@ -20,7 +20,16 @@
<DockPanel DockPanel.Dock="Top">
<Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button>
<Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button>
<Border DockPanel.Dock="Right" Background="#c0c0c0">
<ToolTip.Tip>
<ToolTip>
<TextBlock>Text</TextBlock>
</ToolTip>
</ToolTip.Tip>
<TextBlock>Tooltip</TextBlock>
</Border>
<TextBox Text="Lorem ipsum dolor sit amet"/>
</DockPanel>
<Grid ColumnDefinitions="*,5,*">
<DockPanel>

1
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -140,6 +140,7 @@ namespace Avalonia.Collections
}
}
[Obsolete("Causes memory leaks. Use DynamicData or similar instead.")]
public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select)

9
src/Avalonia.Controls/Utils/IEnumerableUtils.cs

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Controls.Utils
@ -15,12 +16,14 @@ namespace Avalonia.Controls.Utils
{
if (items != null)
{
var collection = items as ICollection;
if (collection != null)
if (items is ICollection collection)
{
return collection.Count;
}
else if (items is IReadOnlyCollection<object> readOnly)
{
return readOnly.Count;
}
else
{
return Enumerable.Count(items.Cast<object>());

10
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@ -45,7 +45,15 @@ namespace Avalonia.Diagnostics
window.Closed += DevToolsClosed;
s_open.Add(root, window);
window.Show();
if (root is Window inspectedWindow)
{
window.Show(inspectedWindow);
}
else
{
window.Show();
}
}
return Disposable.Create(() => window?.Close());

29
src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.LogicalTree;
@ -9,7 +10,7 @@ namespace Avalonia.Diagnostics.ViewModels
public LogicalTreeNode(ILogical logical, TreeNode parent)
: base((Control)logical, parent)
{
Children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this));
Children = new LogicalTreeNodeCollection(this, logical);
}
public static LogicalTreeNode[] Create(object control)
@ -17,5 +18,31 @@ namespace Avalonia.Diagnostics.ViewModels
var logical = control as ILogical;
return logical != null ? new[] { new LogicalTreeNode(logical, null) } : null;
}
internal class LogicalTreeNodeCollection : TreeNodeCollection
{
private readonly ILogical _control;
private IDisposable _subscription;
public LogicalTreeNodeCollection(TreeNode owner, ILogical control)
: base(owner)
{
_control = control;
}
public override void Dispose()
{
base.Dispose();
_subscription?.Dispose();
}
protected override void Initialize(AvaloniaList<TreeNode> nodes)
{
_subscription = _control.LogicalChildren.ForEachItem(
(i, item) => nodes.Insert(i, new LogicalTreeNode(item, Owner)),
(i, item) => nodes.RemoveAt(i),
() => nodes.Clear());
}
}
}
}

23
src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs

@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Diagnostics.Models;
using Avalonia.Input;
@ -12,6 +13,7 @@ namespace Avalonia.Diagnostics.ViewModels
private readonly TreePageViewModel _logicalTree;
private readonly TreePageViewModel _visualTree;
private readonly EventsPageViewModel _events;
private readonly IDisposable _pointerOverSubscription;
private ViewModelBase _content;
private int _selectedTab;
private string _focusedControl;
@ -25,16 +27,9 @@ namespace Avalonia.Diagnostics.ViewModels
_events = new EventsPageViewModel(root);
UpdateFocusedControl();
KeyboardDevice.Instance.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
{
UpdateFocusedControl();
}
};
KeyboardDevice.Instance.PropertyChanged += KeyboardPropertyChanged;
SelectedTab = 0;
root.GetObservable(TopLevel.PointerOverElementProperty)
_pointerOverSubscription = root.GetObservable(TopLevel.PointerOverElementProperty)
.Subscribe(x => PointerOverElement = x?.GetType().Name);
Console = new ConsoleViewModel(UpdateConsoleContext);
}
@ -129,6 +124,8 @@ namespace Avalonia.Diagnostics.ViewModels
public void Dispose()
{
KeyboardDevice.Instance.PropertyChanged -= KeyboardPropertyChanged;
_pointerOverSubscription.Dispose();
_logicalTree.Dispose();
_visualTree.Dispose();
}
@ -137,5 +134,13 @@ namespace Avalonia.Diagnostics.ViewModels
{
FocusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name;
}
private void KeyboardPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
{
UpdateFocusedControl();
}
}
}
}

14
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs

@ -3,15 +3,15 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
internal class TreeNode : ViewModelBase
internal class TreeNode : ViewModelBase, IDisposable
{
private IDisposable _classesSubscription;
private string _classes;
private bool _isExpanded;
@ -33,7 +33,7 @@ namespace Avalonia.Diagnostics.ViewModels
x => control.Classes.CollectionChanged -= x)
.TakeUntil(removed);
classesChanged.Select(_ => Unit.Default)
_classesSubscription = classesChanged.Select(_ => Unit.Default)
.StartWith(Unit.Default)
.Subscribe(_ =>
{
@ -49,7 +49,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public IAvaloniaReadOnlyList<TreeNode> Children
public TreeNodeCollection Children
{
get;
protected set;
@ -104,6 +104,12 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public void Dispose()
{
_classesSubscription.Dispose();
Children.Dispose();
}
private static int IndexOf(IReadOnlyList<TreeNode> collection, TreeNode item)
{
var count = collection.Count;

78
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs

@ -0,0 +1,78 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using Avalonia.Collections;
namespace Avalonia.Diagnostics.ViewModels
{
internal abstract class TreeNodeCollection : IAvaloniaReadOnlyList<TreeNode>, IDisposable
{
private AvaloniaList<TreeNode> _inner;
public TreeNodeCollection(TreeNode owner) => Owner = owner;
public TreeNode this[int index]
{
get
{
EnsureInitialized();
return _inner[index];
}
}
public int Count
{
get
{
EnsureInitialized();
return _inner.Count;
}
}
protected TreeNode Owner { get; }
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add => _inner.CollectionChanged += value;
remove => _inner.CollectionChanged -= value;
}
public event PropertyChangedEventHandler PropertyChanged
{
add => _inner.PropertyChanged += value;
remove => _inner.PropertyChanged -= value;
}
public virtual void Dispose()
{
if (_inner is object)
{
foreach (var node in _inner)
{
node.Dispose();
}
}
}
public IEnumerator<TreeNode> GetEnumerator()
{
EnsureInitialized();
return _inner.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
protected abstract void Initialize(AvaloniaList<TreeNode> nodes);
private void EnsureInitialized()
{
if (_inner is null)
{
_inner = new AvaloniaList<TreeNode>();
Initialize(_inner);
}
}
}
}

10
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -62,7 +62,15 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public void Dispose() => _details?.Dispose();
public void Dispose()
{
foreach (var node in Nodes)
{
node.Dispose();
}
_details?.Dispose();
}
public TreeNode FindNode(IControl control)
{

37
src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Collections;
using Avalonia.Styling;
using Avalonia.VisualTree;
@ -9,16 +10,7 @@ namespace Avalonia.Diagnostics.ViewModels
public VisualTreeNode(IVisual visual, TreeNode parent)
: base(visual, parent)
{
var host = visual as IVisualTreeHost;
if (host?.Root == null)
{
Children = visual.VisualChildren.CreateDerivedList(x => new VisualTreeNode(x, this));
}
else
{
Children = new AvaloniaList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) });
}
Children = new VisualTreeNodeCollection(this, visual);
if ((Visual is IStyleable styleable))
{
@ -33,5 +25,30 @@ namespace Avalonia.Diagnostics.ViewModels
var visual = control as IVisual;
return visual != null ? new[] { new VisualTreeNode(visual, null) } : null;
}
internal class VisualTreeNodeCollection : TreeNodeCollection
{
private readonly IVisual _control;
private IDisposable _subscription;
public VisualTreeNodeCollection(TreeNode owner, IVisual control)
: base(owner)
{
_control = control;
}
public override void Dispose()
{
_subscription?.Dispose();
}
protected override void Initialize(AvaloniaList<TreeNode> nodes)
{
_subscription = _control.VisualChildren.ForEachItem(
(i, item) => nodes.Insert(i, new VisualTreeNode(item, Owner)),
(i, item) => nodes.RemoveAt(i),
() => nodes.Clear());
}
}
}
}

22
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs

@ -14,8 +14,8 @@ namespace Avalonia.Diagnostics.Views
{
internal class MainWindow : Window, IStyleHost
{
private readonly IDisposable _keySubscription;
private TopLevel _root;
private IDisposable _keySubscription;
public MainWindow()
{
@ -33,8 +33,22 @@ namespace Avalonia.Diagnostics.Views
{
if (_root != value)
{
if (_root != null)
{
_root.Closed -= RootClosed;
}
_root = value;
DataContext = new MainViewModel(value);
if (_root != null)
{
_root.Closed += RootClosed;
DataContext = new MainViewModel(value);
}
else
{
DataContext = null;
}
}
}
}
@ -45,6 +59,8 @@ namespace Avalonia.Diagnostics.Views
{
base.OnClosed(e);
_keySubscription.Dispose();
_root.Closed -= RootClosed;
_root = null;
((MainViewModel)DataContext)?.Dispose();
}
@ -70,5 +86,7 @@ namespace Avalonia.Diagnostics.Views
}
}
}
private void RootClosed(object sender, EventArgs e) => Close();
}
}

12
src/Avalonia.Native/PopupImpl.cs

@ -10,6 +10,7 @@ namespace Avalonia.Native
private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature;
private readonly IWindowBaseImpl _parent;
public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts,
@ -19,6 +20,7 @@ namespace Avalonia.Native
_factory = factory;
_opts = opts;
_glFeature = glFeature;
_parent = parent;
using (var e = new PopupEvents(this))
{
var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
@ -58,6 +60,16 @@ namespace Avalonia.Native
}
}
public override void Show()
{
var parent = _parent;
while (parent is PopupImpl p)
parent = p._parent;
if (parent is WindowImpl w)
w.Native.TakeFocusFromChildren();
base.Show();
}
public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this);
public void SetWindowManagerAddShadowHint(bool enabled)

2
src/Avalonia.Native/WindowImplBase.cs

@ -319,7 +319,7 @@ namespace Avalonia.Native
}
public void Show()
public virtual void Show()
{
_native.Show();
}

16
src/Avalonia.Themes.Fluent/ToolTip.xaml

@ -1,11 +1,14 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Design.PreviewWith>
<Grid RowDefinitions="Auto,Auto"
ColumnDefinitions="Auto,Auto"
HorizontalAlignment="Center">
<Border Grid.Column="0"
Grid.Row="1"
Background="{DynamicResource ThemeAccentBrush}"
Background="{DynamicResource SystemControlBackgroundAccentBrush}"
Margin="5"
Padding="50"
ToolTip.Tip="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.">
@ -19,7 +22,7 @@
<Border Name="Border"
Grid.Column="1"
Grid.Row="1"
Background="{DynamicResource ThemeAccentBrush}"
Background="{DynamicResource SystemControlBackgroundAccentBrush}"
Margin="5"
Padding="50"
ToolTip.Placement="Bottom">
@ -34,6 +37,10 @@
</Grid>
</Design.PreviewWith>
<Styles.Resources>
<sys:Double x:Key="ToolTipContentMaxWidth">320</sys:Double>
</Styles.Resources>
<Style Selector="ToolTip">
<Setter Property="Foreground" Value="{DynamicResource ToolTipForeground}" />
<Setter Property="Background" Value="{DynamicResource ToolTipBackground}" />
@ -42,6 +49,7 @@
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ToolTipContentThemeFontSize}" />
<Setter Property="Padding" Value="{DynamicResource ToolTipBorderThemePadding}" />
<Setter Property="MaxWidth" Value="{DynamicResource ToolTipContentMaxWidth}" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.15" />
@ -56,7 +64,7 @@
Padding="{TemplateBinding Padding}"
CornerRadius="{DynamicResource OverlayCornerRadius}">
<ContentPresenter Name="PART_ContentPresenter"
MaxWidth="320"
MaxWidth="{TemplateBinding MaxWidth}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Border>

Loading…
Cancel
Save