From ca4dee94fd2ab4884031072da4a92c9f00a5fcd2 Mon Sep 17 00:00:00 2001 From: Deadpikle Date: Tue, 8 Jun 2021 18:00:04 -0400 Subject: [PATCH 01/27] Remove timer from undo helper, improve u/r for textbox --- src/Avalonia.Controls/TextBox.cs | 57 ++++++++++++------- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 11 +--- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 1bee15bccd..31ec12c22f 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls public static KeyGesture PasteGesture { get; } = AvaloniaLocator.Current .GetService()?.Paste.FirstOrDefault(); - + public static readonly StyledProperty AcceptsReturnProperty = AvaloniaProperty.Register(nameof(AcceptsReturn)); @@ -117,7 +117,7 @@ namespace Avalonia.Controls public static readonly StyledProperty RevealPasswordProperty = AvaloniaProperty.Register(nameof(RevealPassword)); - + public static readonly DirectProperty CanCutProperty = AvaloniaProperty.RegisterDirect( nameof(CanCut), @@ -135,7 +135,7 @@ namespace Avalonia.Controls public static readonly StyledProperty IsUndoEnabledProperty = AvaloniaProperty.Register( - nameof(IsUndoEnabled), + nameof(IsUndoEnabled), defaultValue: true); public static readonly DirectProperty UndoLimitProperty = @@ -174,6 +174,10 @@ namespace Avalonia.Controls private string _newLine = Environment.NewLine; private static readonly string[] invalidCharacters = new String[1] { "\u007f" }; + private int _selectedTextChangesMadeSinceLastUndoSnapshot; + private bool _hasDoneSnapshotOnce; + private const int _maxCharsBeforeUndoSnapshot = 7; + static TextBox() { FocusableProperty.OverrideDefaultValue(typeof(TextBox), true); @@ -202,7 +206,8 @@ namespace Avalonia.Controls horizontalScrollBarVisibility, BindingPriority.Style); _undoRedoHelper = new UndoRedoHelper(this); - + _selectedTextChangesMadeSinceLastUndoSnapshot = 0; + _hasDoneSnapshotOnce = false; UpdatePseudoclasses(); } @@ -331,6 +336,7 @@ namespace Avalonia.Controls if (SetAndRaise(TextProperty, ref _text, value) && IsUndoEnabled && !_isUndoingRedoing) { _undoRedoHelper.Clear(); + SnapshotUndoRedo(); // so we always have an initial state } } } @@ -341,7 +347,6 @@ namespace Avalonia.Controls get { return GetSelection(); } set { - SnapshotUndoRedo(); if (string.IsNullOrEmpty(value)) { DeleteSelection(); @@ -350,7 +355,6 @@ namespace Avalonia.Controls { HandleTextInput(value); } - SnapshotUndoRedo(); } } @@ -422,7 +426,7 @@ namespace Avalonia.Controls get { return _newLine; } set { SetAndRaise(NewLineProperty, ref _newLine, value); } } - + /// /// Clears the current selection, maintaining the /// @@ -480,11 +484,13 @@ namespace Avalonia.Controls var oldValue = _undoRedoHelper.Limit; _undoRedoHelper.Limit = value; RaisePropertyChanged(UndoLimitProperty, oldValue, value); - } + } // from docs at // https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.textboxbase.isundoenabled: // "Setting UndoLimit clears the undo queue." _undoRedoHelper.Clear(); + _selectedTextChangesMadeSinceLastUndoSnapshot = 0; + _hasDoneSnapshotOnce = false; } } @@ -515,6 +521,8 @@ namespace Avalonia.Controls // Therefore, if you disable undo and then re-enable it, undo commands still do not work // because the undo stack was emptied when you disabled undo." _undoRedoHelper.Clear(); + _selectedTextChangesMadeSinceLastUndoSnapshot = 0; + _hasDoneSnapshotOnce = false; } } @@ -577,23 +585,25 @@ namespace Avalonia.Controls { return; } - + input = RemoveInvalidCharacters(input); - + if (string.IsNullOrEmpty(input)) { return; } - + _selectedTextChangesMadeSinceLastUndoSnapshot++; + SnapshotUndoRedo(ignoreChangeCount: false); + string text = Text ?? string.Empty; int caretIndex = CaretIndex; int newLength = input.Length + text.Length - Math.Abs(SelectionStart - SelectionEnd); - + if (MaxLength > 0 && newLength > MaxLength) { input = input.Remove(Math.Max(0, input.Length - (newLength - MaxLength))); } - + if (!string.IsNullOrEmpty(input)) { DeleteSelection(); @@ -696,6 +706,7 @@ namespace Avalonia.Controls { try { + SnapshotUndoRedo(); _isUndoingRedoing = true; _undoRedoHelper.Undo(); } @@ -830,7 +841,6 @@ namespace Avalonia.Controls CaretIndex -= removedCharacters; ClearSelection(); } - SnapshotUndoRedo(); handled = true; break; @@ -858,7 +868,6 @@ namespace Avalonia.Controls SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters)); } - SnapshotUndoRedo(); handled = true; break; @@ -868,7 +877,6 @@ namespace Avalonia.Controls { SnapshotUndoRedo(); HandleTextInput(NewLine); - SnapshotUndoRedo(); handled = true; } @@ -879,7 +887,6 @@ namespace Avalonia.Controls { SnapshotUndoRedo(); HandleTextInput("\t"); - SnapshotUndoRedo(); handled = true; } else @@ -889,6 +896,10 @@ namespace Avalonia.Controls break; + case Key.Space: + SnapshotUndoRedo(); // always snapshot in between words + break; + default: handled = false; break; @@ -1306,11 +1317,19 @@ namespace Avalonia.Controls } } - private void SnapshotUndoRedo() + private void SnapshotUndoRedo(bool ignoreChangeCount = true) { if (IsUndoEnabled) { - _undoRedoHelper.Snapshot(); + if (ignoreChangeCount || + !_hasDoneSnapshotOnce || + (!ignoreChangeCount && + _selectedTextChangesMadeSinceLastUndoSnapshot >= _maxCharsBeforeUndoSnapshot)) + { + _undoRedoHelper.Snapshot(); + _selectedTextChangesMadeSinceLastUndoSnapshot = 0; + _hasDoneSnapshotOnce = true; + } } } } diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 7374f20a0c..fd1ca54b57 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -7,7 +7,7 @@ using Avalonia.Utilities; namespace Avalonia.Controls.Utils { - class UndoRedoHelper : WeakTimer.IWeakTimerSubscriber where TState : struct, IEquatable + class UndoRedoHelper { private readonly IUndoRedoHost _host; @@ -31,7 +31,6 @@ namespace Avalonia.Controls.Utils public UndoRedoHelper(IUndoRedoHost host) { _host = host; - WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1)); } public void Undo() @@ -61,7 +60,7 @@ namespace Avalonia.Controls.Utils if (_states.Last != null) { _states.Last.Value = state; - } + } } public void UpdateLastState() @@ -103,11 +102,5 @@ namespace Avalonia.Controls.Utils _states.Clear(); _currentNode = null; } - - bool WeakTimer.IWeakTimerSubscriber.Tick() - { - Snapshot(); - return true; - } } } From 2c215d1b3917af0c31e55cb22e2ba07db203b535 Mon Sep 17 00:00:00 2001 From: Deadpikle Date: Tue, 8 Jun 2021 18:02:29 -0400 Subject: [PATCH 02/27] Make sure to snapshot undo before delete selected --- src/Avalonia.Controls/TextBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 31ec12c22f..23c0d70f20 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -349,6 +349,8 @@ namespace Avalonia.Controls { if (string.IsNullOrEmpty(value)) { + _selectedTextChangesMadeSinceLastUndoSnapshot++; + SnapshotUndoRedo(ignoreChangeCount: false); DeleteSelection(); } else From 11138b1fc5937bb268460b36dd259a64c6791caf Mon Sep 17 00:00:00 2001 From: Deadpikle Date: Tue, 8 Jun 2021 18:05:02 -0400 Subject: [PATCH 03/27] Remove extra snapshots after cut, paste --- src/Avalonia.Controls/TextBox.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 23c0d70f20..dad2401921 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -639,7 +639,6 @@ namespace Avalonia.Controls SnapshotUndoRedo(); Copy(); DeleteSelection(); - SnapshotUndoRedo(); } public async void Copy() @@ -659,7 +658,6 @@ namespace Avalonia.Controls SnapshotUndoRedo(); HandleTextInput(text); - SnapshotUndoRedo(); } protected override void OnKeyDown(KeyEventArgs e) From 2fe25f8acf3567ad1869942eb20c6bc068b44387 Mon Sep 17 00:00:00 2001 From: Deadpikle Date: Tue, 8 Jun 2021 18:48:12 -0400 Subject: [PATCH 04/27] Fix equality for UndoRedoState In UndoRedoHelper, the .Equals call in Snapshot() did not call Equals(UndoRedoState other). It called the default Equals(object obj) implementation. This could throw of the undo redo snapshotting. Fix that by overriding Equals(object obj). --- src/Avalonia.Controls/TextBox.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index dad2401921..edd268a86a 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -157,6 +157,13 @@ namespace Avalonia.Controls } public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != GetType()) return false; + return Equals((UndoRedoState)obj); + } } private string _text; From f92804ff05b6361a0e355ad894e59c453927c6c7 Mon Sep 17 00:00:00 2001 From: Deadpikle Date: Tue, 8 Jun 2021 18:57:18 -0400 Subject: [PATCH 05/27] Improve UndoRedoState.Equals(object obj) impl --- src/Avalonia.Controls/TextBox.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index edd268a86a..b89bd22038 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -158,12 +158,9 @@ namespace Avalonia.Controls public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text); - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (obj.GetType() != GetType()) return false; - return Equals((UndoRedoState)obj); - } + public override bool Equals(object obj) => obj is UndoRedoState other && Equals(other); + + public override int GetHashCode() => Text.GetHashCode(); } private string _text; From 05361cdb748a29d67d480246702544165fa1c0bd Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Mon, 5 Jul 2021 15:56:34 +0200 Subject: [PATCH 06/27] Adding option to inspect Popup visual tree --- .../Diagnostics/ViewModels/MainViewModel.cs | 1 - .../Diagnostics/ViewModels/VisualTreeNode.cs | 30 ++++-- .../Diagnostics/Views/MainWindow.xaml.cs | 91 ++++++++++++------- 3 files changed, 81 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 3f367165ac..07ae222a9c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; - using Avalonia.Controls; using Avalonia.Diagnostics.Models; using Avalonia.Input; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index 48fa636664..b9981babf7 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Collections; +using Avalonia.Controls.Primitives; using Avalonia.Styling; using Avalonia.VisualTree; @@ -24,8 +25,7 @@ namespace Avalonia.Diagnostics.ViewModels public static VisualTreeNode[] Create(object control) { - var visual = control as IVisual; - return visual != null ? new[] { new VisualTreeNode(visual, null) } : Array.Empty(); + return control is IVisual visual ? new[] { new VisualTreeNode(visual, null) } : Array.Empty(); } internal class VisualTreeNodeCollection : TreeNodeCollection @@ -46,10 +46,28 @@ namespace Avalonia.Diagnostics.ViewModels protected override void Initialize(AvaloniaList nodes) { - _subscription = _control.VisualChildren.ForEachItem( - (i, item) => nodes.Insert(i, new VisualTreeNode(item, Owner)), - (i, item) => nodes.RemoveAt(i), - () => nodes.Clear()); + if (_control is Popup p) + { + _subscription = p.GetObservable(Popup.ChildProperty).Subscribe(child => + { + if (child != null) + { + nodes.Add(new VisualTreeNode(child, Owner)); + } + else + { + nodes.Clear(); + } + + }); + } + else + { + _subscription = _control.VisualChildren.ForEachItem( + (i, item) => nodes.Insert(i, new VisualTreeNode(item, Owner)), + (i, item) => nodes.RemoveAt(i), + () => nodes.Clear()); + } } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index d1232b749a..9d37b5acdc 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -27,19 +27,19 @@ namespace Avalonia.Diagnostics.Views EventHandler? lh = default; lh = (s, e) => - { - this.Opened -= lh; - if ((DataContext as MainViewModel)?.StartupScreenIndex is int index) - { - var screens = this.Screens; - if (index > -1 && index < screens.ScreenCount) - { - var screen = screens.All[index]; - this.Position = screen.Bounds.TopLeft; - this.WindowState = WindowState.Maximized; - } - } - }; + { + this.Opened -= lh; + if ((DataContext as MainViewModel)?.StartupScreenIndex is { } index) + { + var screens = this.Screens; + if (index > -1 && index < screens.ScreenCount) + { + var screen = screens.All[index]; + this.Position = screen.Bounds.TopLeft; + this.WindowState = WindowState.Maximized; + } + } + }; this.Opened += lh; } @@ -91,6 +91,24 @@ namespace Avalonia.Diagnostics.Views AvaloniaXamlLoader.Load(this); } + private IControl? GetHoveredControl(TopLevel topLevel) + { +#pragma warning disable CS0618 // Type or member is obsolete + var point = (topLevel as IInputRoot)?.MouseDevice?.GetPosition(topLevel) ?? default; +#pragma warning restore CS0618 // Type or member is obsolete + + return (IControl?)topLevel.GetVisualsAt(point, x => + { + if (x is AdornerLayer || !x.IsVisible) + { + return false; + } + + return !(x is IInputElement ie) || ie.IsHitTestVisible; + }) + .FirstOrDefault(); + } + private void RawKeyDown(RawKeyEventArgs e) { var vm = (MainViewModel?)DataContext; @@ -99,34 +117,39 @@ namespace Avalonia.Diagnostics.Views return; } - const RawInputModifiers modifiers = RawInputModifiers.Control | RawInputModifiers.Shift; - - if (e.Modifiers == modifiers) + switch (e.Modifiers) { -#pragma warning disable CS0618 // Type or member is obsolete - var point = (Root as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default; -#pragma warning restore CS0618 // Type or member is obsolete + case RawInputModifiers.Control | RawInputModifiers.Shift: + { + IControl? control = null; - var control = Root.GetVisualsAt(point, x => + foreach (var popup in Root.GetVisualDescendants().OfType()) { - if (x is AdornerLayer || !x.IsVisible) return false; - if (!(x is IInputElement ie)) return true; - return ie.IsHitTestVisible; - }) - .FirstOrDefault(); + if (popup.Host?.HostedVisualTreeRoot is PopupRoot popupRoot) + { + control = GetHoveredControl(popupRoot); + + if (control != null) + { + break; + } + } + } - if (control != null) - { - vm.SelectControl((IControl)control); + control ??= GetHoveredControl(Root); + + if (control != null) + { + vm.SelectControl(control); + } + + break; } - } - else if (e.Modifiers == RawInputModifiers.Alt) - { - if (e.Key == Key.S || e.Key == Key.D) + case RawInputModifiers.Alt when e.Key == Key.S || e.Key == Key.D: { - var enable = e.Key == Key.S; + vm.EnableSnapshotStyles(e.Key == Key.S); - vm.EnableSnapshotStyles(enable); + break; } } } From 31d80a9fb67165417d327e008190bb0344563aae Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Tue, 6 Jul 2021 10:30:24 +0200 Subject: [PATCH 07/27] Make tree roots bold --- .../Diagnostics/ViewModels/TreeNode.cs | 25 ++++++------------- .../Diagnostics/Views/TreePageView.xaml | 2 +- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 4cb470eeac..14d704db93 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -4,24 +4,28 @@ using System.Collections.Specialized; using System.Reactive; using System.Reactive.Linq; using Avalonia.Controls; +using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; +using Avalonia.Media; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { internal abstract class TreeNode : ViewModelBase, IDisposable { - private IDisposable? _classesSubscription; + private readonly IDisposable? _classesSubscription; private string _classes; private bool _isExpanded; - public TreeNode(IVisual visual, TreeNode? parent) + protected TreeNode(IVisual visual, TreeNode? parent) { Parent = parent; Type = visual.GetType().Name; Visual = visual; _classes = string.Empty; + FontWeight = Visual is TopLevel or Popup ? FontWeight.Bold : FontWeight.Normal; + if (visual is IControl control) { ElementName = control.Name; @@ -52,6 +56,8 @@ namespace Avalonia.Diagnostics.ViewModels } } + public FontWeight FontWeight { get; } + public abstract TreeNodeCollection Children { get; @@ -95,20 +101,5 @@ namespace Avalonia.Diagnostics.ViewModels _classesSubscription?.Dispose(); Children.Dispose(); } - - private static int IndexOf(IReadOnlyList collection, TreeNode item) - { - var count = collection.Count; - - for (var i = 0; i < count; ++i) - { - if (collection[i] == item) - { - return i; - } - } - - throw new AvaloniaInternalException("TreeNode was not present in parent Children collection."); - } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml index a5328716fc..bb661f7f4c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml @@ -11,7 +11,7 @@ - + From cd56e1a9fc6e914571b2b38ab042fcc0275feaeb Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Tue, 6 Jul 2021 10:40:07 +0200 Subject: [PATCH 08/27] Remove "or" --- src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 14d704db93..f4c04dbca6 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -24,7 +24,7 @@ namespace Avalonia.Diagnostics.ViewModels Visual = visual; _classes = string.Empty; - FontWeight = Visual is TopLevel or Popup ? FontWeight.Bold : FontWeight.Normal; + FontWeight = Visual is TopLevel || Visual is Popup ? FontWeight.Bold : FontWeight.Normal; if (visual is IControl control) { From a9affd64bf67d2ff39e3853b7f0adc168be71f3f Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 15 Jul 2021 15:46:47 +0200 Subject: [PATCH 09/27] Add support for Flyouts, ToolTips & ContextMenus --- .../Diagnostics/IPopupHostProvider.cs | 23 +++++ .../Diagnostics/ToolTipDiagnostics.cs | 12 +++ src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 25 ++++- src/Avalonia.Controls/ToolTip.cs | 40 +++++--- .../Diagnostics/ViewModels/TreeNode.cs | 11 ++- .../Diagnostics/ViewModels/VisualTreeNode.cs | 97 ++++++++++++++----- 6 files changed, 163 insertions(+), 45 deletions(-) create mode 100644 src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs create mode 100644 src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs new file mode 100644 index 0000000000..11d3b1792a --- /dev/null +++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs @@ -0,0 +1,23 @@ +using System; +using Avalonia.Controls.Primitives; + +#nullable enable + +namespace Avalonia.Controls.Diagnostics +{ + /// + /// Diagnostics interface to retrieve an associated . + /// + public interface IPopupHostProvider + { + /// + /// The popup host. + /// + IPopupHost? PopupHost { get; } + + /// + /// Raised when the popup host changes. + /// + event Action? PopupHostChanged; + } +} diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs new file mode 100644 index 0000000000..58174b1039 --- /dev/null +++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs @@ -0,0 +1,12 @@ +#nullable enable + +namespace Avalonia.Controls.Diagnostics +{ + /// + /// Helper class to provide some diagnostics insides into . + /// + public static class ToolTipDiagnostics + { + public static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; + } +} diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index e4b68c62fd..6b72b8c887 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -1,16 +1,16 @@ using System; using System.ComponentModel; +using Avalonia.Controls.Diagnostics; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.Logging; -using Avalonia.Rendering; #nullable enable namespace Avalonia.Controls.Primitives { - public abstract class FlyoutBase : AvaloniaObject + public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider { static FlyoutBase() { @@ -55,6 +55,7 @@ namespace Avalonia.Controls.Primitives private Rect? _enlargedPopupRect; private PixelRect? _enlargePopupRectScreenPixelRect; private IDisposable? _transientDisposable; + private Action? _popupHostChangedHandler; protected Popup? Popup { get; private set; } @@ -94,6 +95,14 @@ namespace Avalonia.Controls.Primitives private set => SetAndRaise(TargetProperty, ref _target, value); } + IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host; + + event Action? IPopupHostProvider.PopupHostChanged + { + add => _popupHostChangedHandler += value; + remove => _popupHostChangedHandler -= value; + } + public event EventHandler? Closed; public event EventHandler? Closing; public event EventHandler? Opened; @@ -322,9 +331,11 @@ namespace Avalonia.Controls.Primitives private void InitPopup() { - Popup = new Popup(); - Popup.WindowManagerAddShadowHint = false; - Popup.IsLightDismissEnabled = true; + Popup = new Popup + { + WindowManagerAddShadowHint = false, + IsLightDismissEnabled = true + }; Popup.Opened += OnPopupOpened; Popup.Closed += OnPopupClosed; @@ -333,11 +344,15 @@ namespace Avalonia.Controls.Primitives private void OnPopupOpened(object sender, EventArgs e) { IsOpen = true; + + _popupHostChangedHandler?.Invoke(Popup!.Host); } private void OnPopupClosed(object sender, EventArgs e) { HideCore(); + + _popupHostChangedHandler?.Invoke(null); } private void PositionPopup(bool showAtPointer) diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index ab507d07a2..ab310d60ef 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -1,9 +1,8 @@ #nullable enable using System; -using System.Reactive.Linq; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; -using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -17,7 +16,7 @@ namespace Avalonia.Controls /// assigning the content that you want displayed. /// [PseudoClasses(":open")] - public class ToolTip : ContentControl + public class ToolTip : ContentControl, IPopupHostProvider { /// /// Defines the ToolTip.Tip attached property. @@ -61,7 +60,8 @@ namespace Avalonia.Controls internal static readonly AttachedProperty ToolTipProperty = AvaloniaProperty.RegisterAttached("ToolTip"); - private IPopupHost? _popup; + private IPopupHost? _popupHost; + private Action? _popupHostChangedHandler; /// /// Initializes static members of the class. @@ -251,35 +251,45 @@ namespace Avalonia.Controls tooltip.RecalculatePosition(control); } + + IPopupHost? IPopupHostProvider.PopupHost => _popupHost; + + event Action? IPopupHostProvider.PopupHostChanged + { + add => _popupHostChangedHandler += value; + remove => _popupHostChangedHandler -= value; + } internal void RecalculatePosition(Control control) { - _popup?.ConfigurePosition(control, GetPlacement(control), new Point(GetHorizontalOffset(control), GetVerticalOffset(control))); + _popupHost?.ConfigurePosition(control, GetPlacement(control), new Point(GetHorizontalOffset(control), GetVerticalOffset(control))); } private void Open(Control control) { Close(); - _popup = OverlayPopupHost.CreatePopupHost(control, null); - _popup.SetChild(this); - ((ISetLogicalParent)_popup).SetParent(control); + _popupHost = OverlayPopupHost.CreatePopupHost(control, null); + _popupHost.SetChild(this); + ((ISetLogicalParent)_popupHost).SetParent(control); - _popup.ConfigurePosition(control, GetPlacement(control), + _popupHost.ConfigurePosition(control, GetPlacement(control), new Point(GetHorizontalOffset(control), GetVerticalOffset(control))); - WindowManagerAddShadowHintChanged(_popup, false); + WindowManagerAddShadowHintChanged(_popupHost, false); - _popup.Show(); + _popupHost.Show(); + _popupHostChangedHandler?.Invoke(_popupHost); } private void Close() { - if (_popup != null) + if (_popupHost != null) { - _popup.SetChild(null); - _popup.Dispose(); - _popup = null; + _popupHost.SetChild(null); + _popupHost.Dispose(); + _popupHost = null; + _popupHostChangedHandler?.Invoke(null); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index f4c04dbca6..9667751e54 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using System.Reactive; using System.Reactive.Linq; using Avalonia.Controls; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; using Avalonia.Media; @@ -19,12 +20,11 @@ namespace Avalonia.Diagnostics.ViewModels protected TreeNode(IVisual visual, TreeNode? parent) { + _classes = string.Empty; Parent = parent; Type = visual.GetType().Name; Visual = visual; - _classes = string.Empty; - - FontWeight = Visual is TopLevel || Visual is Popup ? FontWeight.Bold : FontWeight.Normal; + FontWeight = IsRoot ? FontWeight.Bold : FontWeight.Normal; if (visual is IControl control) { @@ -56,6 +56,11 @@ namespace Avalonia.Diagnostics.ViewModels } } + private bool IsRoot => Visual is TopLevel || + Visual is Popup || + Visual is ContextMenu || + Visual is IPopupHost; + public FontWeight FontWeight { get; } public abstract TreeNodeCollection Children diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index b9981babf7..6d803a1a7b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,5 +1,10 @@ using System; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Security.Cryptography.X509Certificates; using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.Styling; using Avalonia.VisualTree; @@ -13,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels { Children = new VisualTreeNodeCollection(this, visual); - if ((Visual is IStyleable styleable)) + if (Visual is IStyleable styleable) { IsInTemplate = styleable.TemplatedParent != null; } @@ -25,13 +30,15 @@ namespace Avalonia.Diagnostics.ViewModels public static VisualTreeNode[] Create(object control) { - return control is IVisual visual ? new[] { new VisualTreeNode(visual, null) } : Array.Empty(); + return control is IVisual visual ? + new[] { new VisualTreeNode(visual, null) } : + Array.Empty(); } internal class VisualTreeNodeCollection : TreeNodeCollection { private readonly IVisual _control; - private IDisposable? _subscription; + private readonly CompositeDisposable _subscriptions = new CompositeDisposable(2); public VisualTreeNodeCollection(TreeNode owner, IVisual control) : base(owner) @@ -41,33 +48,79 @@ namespace Avalonia.Diagnostics.ViewModels public override void Dispose() { - _subscription?.Dispose(); + _subscriptions.Dispose(); } - protected override void Initialize(AvaloniaList nodes) + private static IObservable? GetHostedPopupRootObservable(IVisual visual) { - if (_control is Popup p) + static IObservable GetPopupHostObservable(IPopupHostProvider popupHostProvider) { - _subscription = p.GetObservable(Popup.ChildProperty).Subscribe(child => - { - if (child != null) - { - nodes.Add(new VisualTreeNode(child, Owner)); - } - else - { - nodes.Clear(); - } - - }); + return Observable.FromEvent( + x => popupHostProvider.PopupHostChanged += x, + x => popupHostProvider.PopupHostChanged -= x) + .StartWith(popupHostProvider.PopupHost) + .Select(x => x is IControl c ? c : null); } - else + + return visual switch { - _subscription = _control.VisualChildren.ForEachItem( + Popup p => p.GetObservable(Popup.ChildProperty), + Control c => Observable.CombineLatest( + c.GetObservable(Control.ContextFlyoutProperty), + c.GetObservable(Control.ContextMenuProperty), + c.GetObservable(FlyoutBase.AttachedFlyoutProperty), + c.GetObservable(ToolTipDiagnostics.ToolTipProperty), + (ContextFlyout, ContextMenu, AttachedFlyout, ToolTip) => + { + if (ContextMenu != null) + { + //Note: ContextMenus are special since all the items are added as visual children. + //So we don't need to go via Popup + return Observable.Return(ContextMenu); + } + + if ((ContextFlyout ?? (IPopupHostProvider?) AttachedFlyout ?? ToolTip) is { } popupHostProvider) + { + return GetPopupHostObservable(popupHostProvider); + } + + return Observable.Return(null); + }) + .Switch(), + _ => null + }; + } + + protected override void Initialize(AvaloniaList nodes) + { + _subscriptions.Clear(); + + if (GetHostedPopupRootObservable(_control) is { } popupRootObservable) + { + VisualTreeNode? childNode = null; + + _subscriptions.Add( + popupRootObservable + .Subscribe(root => + { + if (root != null) + { + childNode = new VisualTreeNode(root, Owner); + + nodes.Add(childNode); + } + else if (childNode != null) + { + nodes.Remove(childNode); + } + })); + } + + _subscriptions.Add( + _control.VisualChildren.ForEachItem( (i, item) => nodes.Insert(i, new VisualTreeNode(item, Owner)), (i, item) => nodes.RemoveAt(i), - () => nodes.Clear()); - } + () => nodes.Clear())); } } } From 4d9131091e04db53b53622238ade11f635a751e1 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 15 Jul 2021 16:19:56 +0200 Subject: [PATCH 10/27] Add custom names to PopupRoot --- src/Avalonia.Controls/Primitives/Popup.cs | 17 ++++- .../Diagnostics/ViewModels/TreeNode.cs | 6 +- .../Diagnostics/ViewModels/VisualTreeNode.cs | 72 ++++++++++++------- 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index b445de0472..bdc71f9a62 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -1,6 +1,6 @@ using System; -using System.Linq; using System.Reactive.Disposables; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; @@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives /// /// Displays a popup window. /// - public class Popup : Control, IVisualTreeHost + public class Popup : Control, IVisualTreeHost, IPopupHostProvider { public static readonly StyledProperty WindowManagerAddShadowHintProperty = AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), true); @@ -133,6 +133,7 @@ namespace Avalonia.Controls.Primitives private bool _ignoreIsOpenChanged; private PopupOpenState? _openState; private IInputElement _overlayInputPassThroughElement; + private Action? _popupHostChangedHandler; /// /// Initializes static members of the class. @@ -348,6 +349,14 @@ namespace Avalonia.Controls.Primitives /// IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot; + IPopupHost? IPopupHostProvider.PopupHost => Host; + + event Action? IPopupHostProvider.PopupHostChanged + { + add => _popupHostChangedHandler += value; + remove => _popupHostChangedHandler -= value; + } + /// /// Opens the popup. /// @@ -479,6 +488,8 @@ namespace Avalonia.Controls.Primitives } Opened?.Invoke(this, EventArgs.Empty); + + _popupHostChangedHandler?.Invoke(Host); } /// @@ -581,6 +592,8 @@ namespace Avalonia.Controls.Primitives _openState.Dispose(); _openState = null; + _popupHostChangedHandler?.Invoke(null); + using (BeginIgnoringIsOpen()) { IsOpen = false; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 9667751e54..4b957c2382 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.Reactive; using System.Reactive.Linq; using Avalonia.Controls; -using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; using Avalonia.Media; @@ -18,11 +16,11 @@ namespace Avalonia.Diagnostics.ViewModels private string _classes; private bool _isExpanded; - protected TreeNode(IVisual visual, TreeNode? parent) + protected TreeNode(IVisual visual, TreeNode? parent, string? customName = null) { _classes = string.Empty; Parent = parent; - Type = visual.GetType().Name; + Type = customName ?? visual.GetType().Name; Visual = visual; FontWeight = IsRoot ? FontWeight.Bold : FontWeight.Normal; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index 6d803a1a7b..f03f16d18f 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,7 +1,7 @@ using System; +using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Security.Cryptography.X509Certificates; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Diagnostics; @@ -13,18 +13,15 @@ namespace Avalonia.Diagnostics.ViewModels { internal class VisualTreeNode : TreeNode { - public VisualTreeNode(IVisual visual, TreeNode? parent) - : base(visual, parent) + public VisualTreeNode(IVisual visual, TreeNode? parent, string? customName = null) + : base(visual, parent, customName) { Children = new VisualTreeNodeCollection(this, visual); - if (Visual is IStyleable styleable) - { - IsInTemplate = styleable.TemplatedParent != null; - } + if (Visual is IStyleable styleable) IsInTemplate = styleable.TemplatedParent != null; } - public bool IsInTemplate { get; private set; } + public bool IsInTemplate { get; } public override TreeNodeCollection Children { get; } @@ -51,20 +48,30 @@ namespace Avalonia.Diagnostics.ViewModels _subscriptions.Dispose(); } - private static IObservable? GetHostedPopupRootObservable(IVisual visual) + private static IObservable? GetHostedPopupRootObservable(IVisual visual) { - static IObservable GetPopupHostObservable(IPopupHostProvider popupHostProvider) + static IObservable GetPopupHostObservable( + IPopupHostProvider popupHostProvider, + string? providerName = null) { return Observable.FromEvent( x => popupHostProvider.PopupHostChanged += x, x => popupHostProvider.PopupHostChanged -= x) .StartWith(popupHostProvider.PopupHost) - .Select(x => x is IControl c ? c : null); + .Select(popupHost => + { + if (popupHost is IControl control) + return new PopupRoot( + control, + providerName != null ? $"{providerName} ({control.GetType().Name})" : null); + + return (PopupRoot?)null; + }); } return visual switch { - Popup p => p.GetObservable(Popup.ChildProperty), + Popup p => GetPopupHostObservable(p), Control c => Observable.CombineLatest( c.GetObservable(Control.ContextFlyoutProperty), c.GetObservable(Control.ContextMenuProperty), @@ -73,18 +80,20 @@ namespace Avalonia.Diagnostics.ViewModels (ContextFlyout, ContextMenu, AttachedFlyout, ToolTip) => { if (ContextMenu != null) - { //Note: ContextMenus are special since all the items are added as visual children. //So we don't need to go via Popup - return Observable.Return(ContextMenu); - } + return Observable.Return(new PopupRoot(ContextMenu)); - if ((ContextFlyout ?? (IPopupHostProvider?) AttachedFlyout ?? ToolTip) is { } popupHostProvider) - { - return GetPopupHostObservable(popupHostProvider); - } + if (ContextFlyout != null) + return GetPopupHostObservable(ContextFlyout, "ContextFlyout"); + + if (AttachedFlyout != null) + return GetPopupHostObservable(AttachedFlyout, "AttachedFlyout"); + + if (ToolTip != null) + return GetPopupHostObservable(ToolTip, "ToolTip"); - return Observable.Return(null); + return Observable.Return(null); }) .Switch(), _ => null @@ -101,18 +110,21 @@ namespace Avalonia.Diagnostics.ViewModels _subscriptions.Add( popupRootObservable - .Subscribe(root => + .Subscribe(popupRoot => { - if (root != null) + if (popupRoot != null) { - childNode = new VisualTreeNode(root, Owner); + childNode = new VisualTreeNode( + popupRoot.Value.Root, + Owner, + popupRoot.Value.CustomName); nodes.Add(childNode); } else if (childNode != null) { nodes.Remove(childNode); - } + }s })); } @@ -122,6 +134,18 @@ namespace Avalonia.Diagnostics.ViewModels (i, item) => nodes.RemoveAt(i), () => nodes.Clear())); } + + private struct PopupRoot + { + public PopupRoot(IControl root, string? customName = null) + { + Root = root; + CustomName = customName; + } + + public IControl Root { get; } + public string? CustomName { get; } + } } } } From dfc5669507d6910c510a4a7619f377e0b02a5100 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 15 Jul 2021 16:20:22 +0200 Subject: [PATCH 11/27] Typo --- .../Diagnostics/ViewModels/VisualTreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index f03f16d18f..5524d06f1b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -124,7 +124,7 @@ namespace Avalonia.Diagnostics.ViewModels else if (childNode != null) { nodes.Remove(childNode); - }s + } })); } From 650ae63bd641868decb2bce426a21aa88ad94496 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 15 Jul 2021 17:46:00 +0200 Subject: [PATCH 12/27] make "ctrl+shift+click" work --- src/Avalonia.Controls/ContextMenu.cs | 17 ++++++- .../Diagnostics/ViewModels/VisualTreeNode.cs | 4 +- .../Diagnostics/Views/MainWindow.xaml.cs | 46 +++++++++++++++---- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index ead5b0d9f3..0c8fc31df1 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; - +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Generators; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; @@ -21,7 +21,7 @@ namespace Avalonia.Controls /// /// A control context menu. /// - public class ContextMenu : MenuBase, ISetterValue + public class ContextMenu : MenuBase, ISetterValue, IPopupHostProvider { /// /// Defines the property. @@ -82,6 +82,7 @@ namespace Avalonia.Controls private Popup? _popup; private List? _attachedControls; private IInputElement? _previousFocus; + private Action? _popupHostChangedHandler; /// /// Initializes a new instance of the class. @@ -304,6 +305,14 @@ namespace Avalonia.Controls } } + IPopupHost? IPopupHostProvider.PopupHost => _popup?.Host; + + event Action? IPopupHostProvider.PopupHostChanged + { + add => _popupHostChangedHandler += value; + remove => _popupHostChangedHandler -= value; + } + protected override IItemContainerGenerator CreateItemContainerGenerator() { return new MenuItemContainerGenerator(this); @@ -364,6 +373,8 @@ namespace Avalonia.Controls { _previousFocus = FocusManager.Instance?.Current; Focus(); + + _popupHostChangedHandler?.Invoke(_popup!.Host); } private void PopupClosing(object sender, CancelEventArgs e) @@ -397,6 +408,8 @@ namespace Avalonia.Controls RoutedEvent = MenuClosedEvent, Source = this, }); + + _popupHostChangedHandler?.Invoke(null); } private void PopupKeyUp(object sender, KeyEventArgs e) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index 5524d06f1b..6a430897ba 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Collections; @@ -18,7 +17,8 @@ namespace Avalonia.Diagnostics.ViewModels { Children = new VisualTreeNodeCollection(this, visual); - if (Visual is IStyleable styleable) IsInTemplate = styleable.TemplatedParent != null; + if (Visual is IStyleable styleable) + IsInTemplate = styleable.TemplatedParent != null; } public bool IsInTemplate { get; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 9d37b5acdc..26e197d790 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using Avalonia.Controls; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.Diagnostics.ViewModels; using Avalonia.Input; @@ -109,6 +111,37 @@ namespace Avalonia.Diagnostics.Views .FirstOrDefault(); } + private static IEnumerable GetPopupRoots(IVisual root) + { + foreach (var control in root.GetVisualDescendants().OfType()) + { + if (control is Popup { Host: PopupRoot r0 }) + { + yield return r0; + } + + if (control.GetValue(ContextFlyoutProperty) is IPopupHostProvider { PopupHost: PopupRoot r1 }) + { + yield return r1; + } + + if (control.GetValue(FlyoutBase.AttachedFlyoutProperty) is IPopupHostProvider { PopupHost: PopupRoot r2 }) + { + yield return r2; + } + + if (control.GetValue(ToolTipDiagnostics.ToolTipProperty) is IPopupHostProvider { PopupHost: PopupRoot r3 }) + { + yield return r3; + } + + if (control.GetValue(ContextMenuProperty) is IPopupHostProvider { PopupHost: PopupRoot r4 }) + { + yield return r4; + } + } + } + private void RawKeyDown(RawKeyEventArgs e) { var vm = (MainViewModel?)DataContext; @@ -123,16 +156,13 @@ namespace Avalonia.Diagnostics.Views { IControl? control = null; - foreach (var popup in Root.GetVisualDescendants().OfType()) + foreach (var popupRoot in GetPopupRoots(Root)) { - if (popup.Host?.HostedVisualTreeRoot is PopupRoot popupRoot) - { - control = GetHoveredControl(popupRoot); + control = GetHoveredControl(popupRoot); - if (control != null) - { - break; - } + if (control != null) + { + break; } } From 838626d763a9e446844244636c3cc51d55cbd946 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 16 Jul 2021 10:33:51 +0200 Subject: [PATCH 13/27] remove c#9 features, clean up --- .../Diagnostics/Views/MainWindow.xaml.cs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 26e197d790..cfb9e2ead9 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -111,35 +111,33 @@ namespace Avalonia.Diagnostics.Views .FirstOrDefault(); } - private static IEnumerable GetPopupRoots(IVisual root) + private static List GetPopupRoots(IVisual root) { - foreach (var control in root.GetVisualDescendants().OfType()) - { - if (control is Popup { Host: PopupRoot r0 }) - { - yield return r0; - } - - if (control.GetValue(ContextFlyoutProperty) is IPopupHostProvider { PopupHost: PopupRoot r1 }) - { - yield return r1; - } + var popupRoots = new List(); - if (control.GetValue(FlyoutBase.AttachedFlyoutProperty) is IPopupHostProvider { PopupHost: PopupRoot r2 }) + void ProcessProperty(IControl control, AvaloniaProperty property) + { + if (control.GetValue(property) is IPopupHostProvider popupProvider + && popupProvider.PopupHost is PopupRoot popupRoot) { - yield return r2; + popupRoots.Add(popupRoot); } + } - if (control.GetValue(ToolTipDiagnostics.ToolTipProperty) is IPopupHostProvider { PopupHost: PopupRoot r3 }) + foreach (var control in root.GetVisualDescendants().OfType()) + { + if (control is Popup p && p.Host is PopupRoot popupRoot) { - yield return r3; + popupRoots.Add(popupRoot); } - if (control.GetValue(ContextMenuProperty) is IPopupHostProvider { PopupHost: PopupRoot r4 }) - { - yield return r4; - } + ProcessProperty(control, ContextFlyoutProperty); + ProcessProperty(control, ContextMenuProperty); + ProcessProperty(control, FlyoutBase.AttachedFlyoutProperty); + ProcessProperty(control, ToolTipDiagnostics.ToolTipProperty); } + + return popupRoots; } private void RawKeyDown(RawKeyEventArgs e) From 9b0287bfd6e397f4150820defa0edf598214167a Mon Sep 17 00:00:00 2001 From: mat1jaczyyy Date: Sat, 17 Jul 2021 04:19:31 +0200 Subject: [PATCH 14/27] Fix existing and add missing osx-specific key shortcuts --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 82a845ffc1..6ff5294d4a 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -109,10 +109,16 @@ namespace Avalonia.Native .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new DefaultRenderTimer(60)) .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) - .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta)) + .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta, wholeWordTextActionModifiers: KeyModifiers.Alt)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)); + var hotkeys = AvaloniaLocator.Current.GetService(); + hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); + hotkeys.MoveCursorToTheStartOfLineWithSelection.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); + hotkeys.MoveCursorToTheEndOfLine.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers)); + hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); + if (_options.UseGpu) { try From 2387e0bb5af5fa7448847bf11fd5157a0f111fcd Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 21 Jul 2021 18:34:14 +0200 Subject: [PATCH 15/27] Add option to freez popups (Alt + Ctrl + F) --- .../Diagnostics/ViewModels/MainViewModel.cs | 7 +++++++ .../Diagnostics/Views/MainWindow.xaml.cs | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 07ae222a9c..d0a4ad38c5 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -21,6 +21,7 @@ namespace Avalonia.Diagnostics.ViewModels private bool _shouldVisualizeMarginPadding = true; private bool _shouldVisualizeDirtyRects; private bool _showFpsOverlay; + private bool _freezePopups; #nullable disable // Remove "nullable disable" after MemberNotNull will work on our CI. @@ -40,6 +41,12 @@ namespace Avalonia.Diagnostics.ViewModels Console = new ConsoleViewModel(UpdateConsoleContext); } + public bool FreezePopups + { + get => _freezePopups; + set => RaiseAndSetIfChanged(ref _freezePopups, value); + } + public bool ShouldVisualizeMarginPadding { get => _shouldVisualizeMarginPadding; diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index cfb9e2ead9..810a630f46 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; @@ -173,6 +174,22 @@ namespace Avalonia.Diagnostics.Views break; } + + case RawInputModifiers.Control | RawInputModifiers.Alt when e.Key == Key.F: + { + vm.FreezePopups = !vm.FreezePopups; + + foreach (var popupRoot in GetPopupRoots(Root)) + { + if (popupRoot.Parent is Popup popup) + { + popup.IsLightDismissEnabled = !vm.FreezePopups; + } + } + + break; + } + case RawInputModifiers.Alt when e.Key == Key.S || e.Key == Key.D: { vm.EnableSnapshotStyles(e.Key == Key.S); From df2079454079e43a59ca3b8032ac5837a8cf8fb6 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 22 Jul 2021 12:01:20 +0200 Subject: [PATCH 16/27] Set LightDimissEnabled via SetValue() --- .../Diagnostics/Views/MainWindow.xaml.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 810a630f46..1791672940 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -1,15 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using Avalonia.Controls; using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; +using Avalonia.Data; using Avalonia.Diagnostics.ViewModels; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Markup.Xaml; +using Avalonia.Media; using Avalonia.Styling; using Avalonia.VisualTree; @@ -18,6 +21,7 @@ namespace Avalonia.Diagnostics.Views internal class MainWindow : Window, IStyleHost { private readonly IDisposable _keySubscription; + private readonly Dictionary _frozenPopupStates; private TopLevel? _root; public MainWindow() @@ -28,6 +32,8 @@ namespace Avalonia.Diagnostics.Views .OfType() .Subscribe(RawKeyDown); + _frozenPopupStates = new Dictionary(); + EventHandler? lh = default; lh = (s, e) => { @@ -183,7 +189,25 @@ namespace Avalonia.Diagnostics.Views { if (popupRoot.Parent is Popup popup) { - popup.IsLightDismissEnabled = !vm.FreezePopups; + if (vm.FreezePopups) + { + var lightDismissEnabledState = popup.SetValue( + Popup.IsLightDismissEnabledProperty, + !vm.FreezePopups, + BindingPriority.Animation); + + if (lightDismissEnabledState != null) + { + _frozenPopupStates[popup] = lightDismissEnabledState; + } + } + else + { + if (_frozenPopupStates.TryGetValue(popup, out var state)) + { + state.Dispose(); + } + } } } From cabe6daa3f6f13d0991655465f6d286029eb1e27 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 23 Jul 2021 10:57:19 +0200 Subject: [PATCH 17/27] Review changes --- .../Diagnostics/Views/MainWindow.xaml.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 1791672940..a85fe6c0c4 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -86,6 +86,13 @@ namespace Avalonia.Diagnostics.Views base.OnClosed(e); _keySubscription.Dispose(); + foreach (var state in _frozenPopupStates) + { + state.Value.Dispose(); + } + + _frozenPopupStates.Clear(); + if (_root != null) { _root.Closed -= RootClosed; @@ -203,9 +210,11 @@ namespace Avalonia.Diagnostics.Views } else { - if (_frozenPopupStates.TryGetValue(popup, out var state)) + //TODO Use Dictionary.Remove(Key, out Value) in netstandard 2.1 + if (_frozenPopupStates.ContainsKey(popup)) { - state.Dispose(); + _frozenPopupStates[popup].Dispose(); + _frozenPopupStates.Remove(popup); } } } From 64922a34a937757380700199fcb555f8f39f6e90 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 1 Aug 2021 21:45:07 -0400 Subject: [PATCH 18/27] Add CornerRadius to TemplatedControl and use it where possible --- samples/ControlCatalog/SideBar.xaml | 1 - samples/RenderDemo/SideBar.xaml | 1 - .../Flyouts/FlyoutPresenter.cs | 9 ----- .../Primitives/TemplatedControl.cs | 40 ++++++++++++++----- .../AutoCompleteBox.xaml | 1 + src/Avalonia.Themes.Default/Button.xaml | 3 +- .../ButtonSpinner.xaml | 2 + src/Avalonia.Themes.Default/Calendar.xaml | 3 +- .../CalendarDatePicker.xaml | 3 +- src/Avalonia.Themes.Default/CalendarItem.xaml | 5 ++- src/Avalonia.Themes.Default/Carousel.xaml | 3 +- src/Avalonia.Themes.Default/CheckBox.xaml | 1 + src/Avalonia.Themes.Default/ComboBox.xaml | 3 +- src/Avalonia.Themes.Default/ComboBoxItem.xaml | 1 + .../ContentControl.xaml | 3 +- src/Avalonia.Themes.Default/ContextMenu.xaml | 1 + .../DataValidationErrors.xaml | 1 + src/Avalonia.Themes.Default/DatePicker.xaml | 3 ++ src/Avalonia.Themes.Default/Expander.xaml | 5 ++- src/Avalonia.Themes.Default/GridSplitter.xaml | 1 + src/Avalonia.Themes.Default/ItemsControl.xaml | 1 + src/Avalonia.Themes.Default/Label.xaml | 1 + src/Avalonia.Themes.Default/ListBox.xaml | 3 +- src/Avalonia.Themes.Default/ListBoxItem.xaml | 1 + src/Avalonia.Themes.Default/Menu.xaml | 3 +- src/Avalonia.Themes.Default/MenuItem.xaml | 6 ++- .../NotificationCard.xaml | 1 + .../NumericUpDown.xaml | 2 +- src/Avalonia.Themes.Default/ProgressBar.xaml | 2 +- src/Avalonia.Themes.Default/RepeatButton.xaml | 1 + src/Avalonia.Themes.Default/Separator.xaml | 1 + src/Avalonia.Themes.Default/TabControl.xaml | 2 +- src/Avalonia.Themes.Default/TabItem.xaml | 2 +- src/Avalonia.Themes.Default/TabStripItem.xaml | 3 +- src/Avalonia.Themes.Default/TextBox.xaml | 3 +- src/Avalonia.Themes.Default/TimePicker.xaml | 3 ++ src/Avalonia.Themes.Default/ToggleButton.xaml | 3 +- src/Avalonia.Themes.Default/ToolTip.xaml | 3 +- src/Avalonia.Themes.Default/TreeView.xaml | 3 +- src/Avalonia.Themes.Default/TreeViewItem.xaml | 1 + src/Avalonia.Themes.Default/UserControl.xaml | 1 + .../Controls/AutoCompleteBox.xaml | 4 +- .../Controls/Button.xaml | 6 +-- .../Controls/ButtonSpinner.xaml | 3 +- .../Controls/Calendar.xaml | 10 ++++- .../Controls/CalendarDatePicker.xaml | 2 + .../Controls/CalendarItem.xaml | 8 +++- .../Controls/Carousel.xaml | 3 +- .../Controls/CheckBox.xaml | 13 ++---- .../Controls/ComboBox.xaml | 13 ++---- .../Controls/ComboBoxItem.xaml | 1 + .../Controls/ContentControl.xaml | 3 +- .../Controls/ContextMenu.xaml | 3 +- .../Controls/DataValidationErrors.xaml | 2 + .../Controls/DatePicker.xaml | 16 ++++++-- .../Controls/Expander.xaml | 29 +++++++------- .../Controls/GridSplitter.xaml | 1 + .../Controls/ItemsControl.xaml | 1 + .../Controls/Label.xaml | 3 +- .../Controls/ListBox.xaml | 8 ++-- .../Controls/ListBoxItem.xaml | 1 + src/Avalonia.Themes.Fluent/Controls/Menu.xaml | 1 + .../Controls/MenuItem.xaml | 6 ++- .../Controls/NotificationCard.xaml | 6 ++- .../Controls/NumericUpDown.xaml | 2 + .../Controls/ProgressBar.xaml | 3 +- .../Controls/RadioButton.xaml | 11 ++--- .../Controls/RepeatButton.xaml | 2 + .../Controls/Separator.xaml | 1 + .../Controls/Slider.xaml | 4 +- .../Controls/TabControl.xaml | 4 +- .../Controls/TabItem.xaml | 1 + .../Controls/TabStrip.xaml | 14 +++++-- .../Controls/TabStripItem.xaml | 1 + .../Controls/TextBox.xaml | 6 +-- .../Controls/TimePicker.xaml | 9 +++-- .../Controls/ToggleButton.xaml | 2 + .../Controls/ToolTip.xaml | 3 +- .../Controls/TreeView.xaml | 3 +- .../Controls/TreeViewItem.xaml | 1 + .../Controls/UserControl.xaml | 1 + 81 files changed, 223 insertions(+), 124 deletions(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 7c911e91e9..2b5215a3fe 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -16,7 +16,6 @@ diff --git a/samples/RenderDemo/SideBar.xaml b/samples/RenderDemo/SideBar.xaml index fd23067f61..b82a7b0514 100644 --- a/samples/RenderDemo/SideBar.xaml +++ b/samples/RenderDemo/SideBar.xaml @@ -7,7 +7,6 @@ diff --git a/src/Avalonia.Controls/Flyouts/FlyoutPresenter.cs b/src/Avalonia.Controls/Flyouts/FlyoutPresenter.cs index 10f97794d7..0f257224dd 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutPresenter.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutPresenter.cs @@ -6,15 +6,6 @@ namespace Avalonia.Controls { public class FlyoutPresenter : ContentControl { - public static readonly StyledProperty CornerRadiusProperty = - Border.CornerRadiusProperty.AddOwner(); - - public CornerRadius CornerRadius - { - get => GetValue(CornerRadiusProperty); - set => SetValue(CornerRadiusProperty, value); - } - protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.Escape) diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 9c73ff2411..59975b072d 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -5,7 +5,8 @@ using Avalonia.Logging; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Styling; -using Avalonia.VisualTree; + +#nullable enable namespace Avalonia.Controls.Primitives { @@ -17,13 +18,13 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty BackgroundProperty = + public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); /// /// Defines the property. /// - public static readonly StyledProperty BorderBrushProperty = + public static readonly StyledProperty BorderBrushProperty = Border.BorderBrushProperty.AddOwner(); /// @@ -32,6 +33,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty CornerRadiusProperty = + Border.CornerRadiusProperty.AddOwner(); + /// /// Defines the property. /// @@ -59,7 +66,7 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty ForegroundProperty = + public static readonly StyledProperty ForegroundProperty = TextBlock.ForegroundProperty.AddOwner(); /// @@ -71,8 +78,8 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty TemplateProperty = - AvaloniaProperty.Register(nameof(Template)); + public static readonly StyledProperty TemplateProperty = + AvaloniaProperty.Register(nameof(Template)); /// /// Defines the IsTemplateFocusTarget attached property. @@ -88,7 +95,7 @@ namespace Avalonia.Controls.Primitives "TemplateApplied", RoutingStrategies.Direct); - private IControlTemplate _appliedTemplate; + private IControlTemplate? _appliedTemplate; /// /// Initializes static members of the class. @@ -111,7 +118,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the brush used to draw the control's background. /// - public IBrush Background + public IBrush? Background { get { return GetValue(BackgroundProperty); } set { SetValue(BackgroundProperty, value); } @@ -120,7 +127,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the brush used to draw the control's border. /// - public IBrush BorderBrush + public IBrush? BorderBrush { get { return GetValue(BorderBrushProperty); } set { SetValue(BorderBrushProperty, value); } @@ -135,6 +142,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(BorderThicknessProperty, value); } } + /// + /// Gets or sets the radius of the border rounded corners. + /// + public CornerRadius CornerRadius + { + get { return GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + /// /// Gets or sets the font family used to draw the control's text. /// @@ -174,7 +190,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the brush used to draw the control's text and other foreground elements. /// - public IBrush Foreground + public IBrush? Foreground { get { return GetValue(ForegroundProperty); } set { SetValue(ForegroundProperty, value); } @@ -192,7 +208,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the template that defines the control's appearance. /// - public IControlTemplate Template + public IControlTemplate? Template { get { return GetValue(TemplateProperty); } set { SetValue(TemplateProperty, value); } @@ -265,7 +281,9 @@ namespace Avalonia.Controls.Primitives var e = new TemplateAppliedEventArgs(nameScope); OnApplyTemplate(e); +#pragma warning disable CS0618 // Type or member is obsolete OnTemplateApplied(e); +#pragma warning restore CS0618 // Type or member is obsolete RaiseEvent(e); } diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml index 66d0f17ede..fe4cd48e72 100644 --- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml +++ b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml @@ -11,6 +11,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Padding="{TemplateBinding Padding}" Watermark="{TemplateBinding Watermark}" DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" /> diff --git a/src/Avalonia.Themes.Default/Button.xaml b/src/Avalonia.Themes.Default/Button.xaml index 698ddec2a8..81d96aaa14 100644 --- a/src/Avalonia.Themes.Default/Button.xaml +++ b/src/Avalonia.Themes.Default/Button.xaml @@ -13,6 +13,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" @@ -31,4 +32,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index 89fbb9d64d..ce2b85d2b5 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -47,6 +47,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"> @@ -73,6 +74,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"> diff --git a/src/Avalonia.Themes.Default/Calendar.xaml b/src/Avalonia.Themes.Default/Calendar.xaml index 6bbee4ef17..4b67aa232b 100644 --- a/src/Avalonia.Themes.Default/Calendar.xaml +++ b/src/Avalonia.Themes.Default/Calendar.xaml @@ -22,10 +22,11 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" HeaderBackground="{TemplateBinding HeaderBackground}"/> - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml index aab7d06c46..57b77f70ea 100644 --- a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml @@ -88,7 +88,8 @@ \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/ContextMenu.xaml index 9b84253c8a..0df4866184 100644 --- a/src/Avalonia.Themes.Default/ContextMenu.xaml +++ b/src/Avalonia.Themes.Default/ContextMenu.xaml @@ -9,6 +9,7 @@ diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/DatePicker.xaml index da878c88e2..c6c117138d 100644 --- a/src/Avalonia.Themes.Default/DatePicker.xaml +++ b/src/Avalonia.Themes.Default/DatePicker.xaml @@ -134,6 +134,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" IsEnabled="{TemplateBinding IsEnabled}" MinWidth="{DynamicResource DatePickerThemeMinWidth}" MaxWidth="{DynamicResource DatePickerThemeMaxWidth}" @@ -148,6 +149,7 @@ BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" @@ -242,6 +244,7 @@ diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml index 08d8b4c995..5e0958c54c 100644 --- a/src/Avalonia.Themes.Default/Expander.xaml +++ b/src/Avalonia.Themes.Default/Expander.xaml @@ -10,7 +10,10 @@ \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index 4bfae4c223..18bf79ce6c 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -14,7 +14,8 @@ + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> @@ -96,7 +97,8 @@ + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> diff --git a/src/Avalonia.Themes.Default/NumericUpDown.xaml b/src/Avalonia.Themes.Default/NumericUpDown.xaml index 025e822404..6740be69bb 100644 --- a/src/Avalonia.Themes.Default/NumericUpDown.xaml +++ b/src/Avalonia.Themes.Default/NumericUpDown.xaml @@ -9,6 +9,7 @@ - + diff --git a/src/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/RepeatButton.xaml index 702e4e6ebd..a9a03c8ed5 100644 --- a/src/Avalonia.Themes.Default/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/RepeatButton.xaml @@ -20,6 +20,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" diff --git a/src/Avalonia.Themes.Default/Separator.xaml b/src/Avalonia.Themes.Default/Separator.xaml index cf0db16ee6..6a318d2e85 100644 --- a/src/Avalonia.Themes.Default/Separator.xaml +++ b/src/Avalonia.Themes.Default/Separator.xaml @@ -6,6 +6,7 @@ diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/TabControl.xaml index ed2e67df28..afb5010baa 100644 --- a/src/Avalonia.Themes.Default/TabControl.xaml +++ b/src/Avalonia.Themes.Default/TabControl.xaml @@ -3,9 +3,9 @@ diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml index 6e344ce58e..c7748299a0 100644 --- a/src/Avalonia.Themes.Default/TabItem.xaml +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -12,11 +12,11 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" - Margin="{TemplateBinding Margin}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Avalonia.Themes.Default/TabStripItem.xaml b/src/Avalonia.Themes.Default/TabStripItem.xaml index 28c4c68a3d..61eecc0395 100644 --- a/src/Avalonia.Themes.Default/TabStripItem.xaml +++ b/src/Avalonia.Themes.Default/TabStripItem.xaml @@ -9,6 +9,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" @@ -20,4 +21,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml index 12df4b6213..9471beaaeb 100644 --- a/src/Avalonia.Themes.Default/TextBox.xaml +++ b/src/Avalonia.Themes.Default/TextBox.xaml @@ -30,7 +30,8 @@ + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> diff --git a/src/Avalonia.Themes.Default/TimePicker.xaml b/src/Avalonia.Themes.Default/TimePicker.xaml index c76f900cfe..a58fd62a99 100644 --- a/src/Avalonia.Themes.Default/TimePicker.xaml +++ b/src/Avalonia.Themes.Default/TimePicker.xaml @@ -58,6 +58,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" IsEnabled="{TemplateBinding IsEnabled}" MinWidth="{DynamicResource TimePickerThemeMinWidth}" MaxWidth="{DynamicResource TimePickerThemeMaxWidth}" @@ -71,6 +72,7 @@ BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" @@ -178,6 +180,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Padding="{DynamicResource DateTimeFlyoutBorderPadding}" MaxHeight="398"> diff --git a/src/Avalonia.Themes.Default/ToggleButton.xaml b/src/Avalonia.Themes.Default/ToggleButton.xaml index 9e05c38eef..ffebd4f63d 100644 --- a/src/Avalonia.Themes.Default/ToggleButton.xaml +++ b/src/Avalonia.Themes.Default/ToggleButton.xaml @@ -13,6 +13,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ToolTip.xaml b/src/Avalonia.Themes.Default/ToolTip.xaml index 1fc0202dd3..35c1dceb8d 100644 --- a/src/Avalonia.Themes.Default/ToolTip.xaml +++ b/src/Avalonia.Themes.Default/ToolTip.xaml @@ -9,9 +9,10 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/TreeView.xaml b/src/Avalonia.Themes.Default/TreeView.xaml index 026bed5899..990d5d0823 100644 --- a/src/Avalonia.Themes.Default/TreeView.xaml +++ b/src/Avalonia.Themes.Default/TreeView.xaml @@ -8,7 +8,8 @@ + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> - + + @@ -36,6 +37,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" FontWeight="{TemplateBinding FontWeight}" diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 597f5d00ec..53d53ef127 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -16,6 +16,7 @@ + @@ -29,6 +30,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Padding="{TemplateBinding Padding}" @@ -93,8 +95,4 @@ - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index 12b4845522..d228c37912 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -58,6 +58,7 @@ + @@ -69,7 +70,7 @@ - + + + + + + - - - - - - - - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml index 5110d70a80..df800b4a06 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml @@ -39,6 +39,7 @@ + @@ -55,7 +56,7 @@ MaxWidth="{TemplateBinding MaxWidth}" MinHeight="{TemplateBinding MinHeight}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" - CornerRadius="{DynamicResource OverlayCornerRadius}"> + CornerRadius="{TemplateBinding CornerRadius}"> @@ -88,6 +89,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" /> diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index 032fdd9ae4..3e4471cada 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -8,6 +8,12 @@ + + + + + + 0,0,0,4 40 @@ -50,6 +56,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 3f70939953..d5d44e1270 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -1,23 +1,23 @@ - - + + Expanded content - + Expanded content - + Expanded content - + Expanded content @@ -51,6 +51,7 @@ + @@ -140,31 +141,31 @@ - - - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml index 1c52c6272c..3320fc9a41 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml @@ -37,6 +37,7 @@ + @@ -59,6 +60,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" IsEnabled="{TemplateBinding IsEnabled}" MinWidth="{DynamicResource TimePickerThemeMinWidth}" MaxWidth="{DynamicResource TimePickerThemeMaxWidth}" @@ -72,11 +74,11 @@ BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" - VerticalContentAlignment="Stretch" - CornerRadius="{DynamicResource ControlCornerRadius}" /> + VerticalContentAlignment="Stretch" /> @@ -176,13 +178,14 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml index dd8e51e4e5..b1d07059b8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml @@ -18,6 +18,7 @@ + @@ -29,6 +30,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Padding="{TemplateBinding Padding}" diff --git a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml index f7a1ebbc6b..debdfb2772 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml @@ -49,6 +49,7 @@ + @@ -61,7 +62,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Padding="{TemplateBinding Padding}" - CornerRadius="{DynamicResource OverlayCornerRadius}"> + CornerRadius="{TemplateBinding CornerRadius}"> + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> Date: Wed, 4 Aug 2021 15:01:14 +0200 Subject: [PATCH 19/27] Make event actually keydown --- src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index a85fe6c0c4..ea06c33e4d 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Reactive.Subjects; using Avalonia.Controls; using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; @@ -12,7 +10,6 @@ using Avalonia.Diagnostics.ViewModels; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Markup.Xaml; -using Avalonia.Media; using Avalonia.Styling; using Avalonia.VisualTree; @@ -30,6 +27,7 @@ namespace Avalonia.Diagnostics.Views _keySubscription = InputManager.Instance.Process .OfType() + .Where(x => x.Type == RawKeyEventType.KeyDown) .Subscribe(RawKeyDown); _frozenPopupStates = new Dictionary(); From 5fb8126d2dfbb55ed62772a718f1610276794961 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 Aug 2021 15:41:31 +0200 Subject: [PATCH 20/27] Add label for frozen popups to status bar --- .../Diagnostics/Views/MainView.xaml | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml index 8c4db33f91..6f2ac96a66 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml @@ -5,14 +5,14 @@ - + + IsEnabled="False" /> @@ -21,58 +21,68 @@ + IsEnabled="False" /> + IsEnabled="False" /> - + + IsEnabled="False" /> - + - - - + + + + Content="{Binding Content}" /> - + IsVisible="False" /> + + IsVisible="{Binding IsVisible}" /> + - - Hold Ctrl+Shift over a control to inspect. - - Focused: - - - Pointer Over: - - + + + Hold Ctrl+Shift over a control to inspect. + + Focused: + + + Pointer Over: + + + + + + From 7e6d59d6702e1c679c5cd77193bda8f8f3bd3812 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 5 Aug 2021 11:04:52 +0200 Subject: [PATCH 21/27] Respect CancellationToken in RunLoop. --- .../Platform/InternalPlatformThreadingInterface.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs index cb1291410a..a5495fdfc9 100644 --- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs +++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs @@ -21,10 +21,12 @@ namespace Avalonia.Controls.Platform public void RunLoop(CancellationToken cancellationToken) { - while (true) + var handles = new[] { _signaled, cancellationToken.WaitHandle }; + + while (!cancellationToken.IsCancellationRequested) { Signaled?.Invoke(null); - _signaled.WaitOne(); + WaitHandle.WaitAny(handles); } } From d8dec842a320b16cb11a001de3d8f7dc7942bf9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Sat, 7 Aug 2021 13:12:09 +0200 Subject: [PATCH 22/27] Allow for controling delay of scrollbar hide/show. --- src/Avalonia.Controls/Primitives/ScrollBar.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index d264ed76cc..d5d6af8bfa 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -57,6 +57,18 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty AllowAutoHideProperty = AvaloniaProperty.Register(nameof(AllowAutoHide), true); + /// + /// Defines the property. + /// + public static readonly StyledProperty HideDelayProperty = + AvaloniaProperty.Register(nameof(HideDelay), TimeSpan.FromSeconds(2)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowDelayProperty = + AvaloniaProperty.Register(nameof(ShowDelay), TimeSpan.FromSeconds(0.5)); + private Button _lineUpButton; private Button _lineDownButton; private Button _pageUpButton; @@ -126,6 +138,24 @@ namespace Avalonia.Controls.Primitives get => GetValue(AllowAutoHideProperty); set => SetValue(AllowAutoHideProperty, value); } + + /// + /// Gets a value that determines how long will be the hide delay after user stops interacting with the scrollbar. + /// + public TimeSpan HideDelay + { + get => GetValue(HideDelayProperty); + set => SetValue(HideDelayProperty, value); + } + + /// + /// Gets a value that determines how long will be the show delay when user starts interacting with the scrollbar. + /// + public TimeSpan ShowDelay + { + get => GetValue(ShowDelayProperty); + set => SetValue(ShowDelayProperty, value); + } public event EventHandler Scroll; @@ -296,12 +326,12 @@ namespace Avalonia.Controls.Primitives private void CollapseAfterDelay() { - InvokeAfterDelay(Collapse, TimeSpan.FromSeconds(2)); + InvokeAfterDelay(Collapse, HideDelay); } private void ExpandAfterDelay() { - InvokeAfterDelay(Expand, TimeSpan.FromMilliseconds(400)); + InvokeAfterDelay(Expand, ShowDelay); } private void Collapse() From da06a15d16d9fb0fbe12e970aceebedd0a7611ad Mon Sep 17 00:00:00 2001 From: Sergey Mikolaytis Date: Sun, 8 Aug 2021 02:15:21 +0300 Subject: [PATCH 23/27] [OverlayPopupHost] remove render white rect override --- src/Avalonia.Controls/Primitives/OverlayPopupHost.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 762d8d37a6..403902f676 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -140,10 +140,5 @@ namespace Avalonia.Controls.Primitives return new OverlayPopupHost(overlayLayer); } - - public override void Render(DrawingContext context) - { - context.FillRectangle(Brushes.White, new Rect(default, Bounds.Size)); - } } } From ae222e25e6b27b0035421d2583c2b37a4036fff1 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Tue, 10 Aug 2021 11:30:19 +0200 Subject: [PATCH 24/27] Mark IVisualTreeHost [Obsolete] --- src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs b/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs index b63c49bd5a..01212b238d 100644 --- a/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs +++ b/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs @@ -1,8 +1,11 @@ +using System; + namespace Avalonia.VisualTree { /// /// Interface for controls that host their own separate visual tree, such as popups. /// + [Obsolete] public interface IVisualTreeHost { /// From d666a4263c580b21c7221a972ee34aab65c86c6a Mon Sep 17 00:00:00 2001 From: "Luis v.d.Eltz" Date: Tue, 10 Aug 2021 11:30:42 +0200 Subject: [PATCH 25/27] Update comment Co-authored-by: Steven Kirk --- src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs index 58174b1039..4dfe5eb174 100644 --- a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs +++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs @@ -3,7 +3,7 @@ namespace Avalonia.Controls.Diagnostics { /// - /// Helper class to provide some diagnostics insides into . + /// Helper class to provide diagnostics information for . /// public static class ToolTipDiagnostics { From 08849539a72ccb7163bee028fd70fea20aa13b67 Mon Sep 17 00:00:00 2001 From: "Luis v.d.Eltz" Date: Tue, 10 Aug 2021 11:31:39 +0200 Subject: [PATCH 26/27] Update comment Co-authored-by: Steven Kirk --- src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs index 4dfe5eb174..4acf2a217f 100644 --- a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs +++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs @@ -7,6 +7,9 @@ namespace Avalonia.Controls.Diagnostics /// public static class ToolTipDiagnostics { + /// + /// Provides access to the internal for use in DevTools. + /// public static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; } } From 71ac5e3db8990c3b5a10bdf809dad8d68ceca4e9 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Tue, 10 Aug 2021 11:41:05 +0200 Subject: [PATCH 27/27] Only IPopupHost should be root visual --- src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 4b957c2382..94707ac189 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -54,8 +54,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private bool IsRoot => Visual is TopLevel || - Visual is Popup || + private bool IsRoot => Visual is TopLevel || Visual is ContextMenu || Visual is IPopupHost;