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/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.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..4acf2a217f
--- /dev/null
+++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs
@@ -0,0 +1,15 @@
+#nullable enable
+
+namespace Avalonia.Controls.Diagnostics
+{
+ ///
+ /// Helper class to provide diagnostics information for .
+ ///
+ public static class ToolTipDiagnostics
+ {
+ ///
+ /// Provides access to the internal for use in DevTools.
+ ///
+ public static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty;
+ }
+}
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index 8448dde21e..230b4954fe 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -1,19 +1,18 @@
using System;
using System.ComponentModel;
+using Avalonia.Controls.Diagnostics;
using System.Linq;
-
using Avalonia.Input;
using Avalonia.Input.Platform;
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()
{
@@ -59,6 +58,7 @@ namespace Avalonia.Controls.Primitives
private Rect? _enlargedPopupRect;
private PixelRect? _enlargePopupRectScreenPixelRect;
private IDisposable? _transientDisposable;
+ private Action? _popupHostChangedHandler;
public FlyoutBase()
{
@@ -103,6 +103,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;
@@ -363,6 +371,8 @@ namespace Avalonia.Controls.Primitives
private void OnPopupOpened(object sender, EventArgs e)
{
IsOpen = true;
+
+ _popupHostChangedHandler?.Invoke(Popup!.Host);
}
private void OnPopupClosing(object sender, CancelEventArgs e)
@@ -376,6 +386,8 @@ namespace Avalonia.Controls.Primitives
private void OnPopupClosed(object sender, EventArgs e)
{
HideCore(false);
+
+ _popupHostChangedHandler?.Invoke(null);
}
// This method is handling both popup logical tree and target logical tree.
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/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);
}
}
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));
- }
}
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index f23a27e67a..d5fb69a672 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -2,6 +2,7 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
+using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input;
@@ -18,7 +19,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);
@@ -134,6 +135,7 @@ namespace Avalonia.Controls.Primitives
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
private IInputElement _overlayInputPassThroughElement;
+ private Action? _popupHostChangedHandler;
///
/// Initializes static members of the class.
@@ -351,6 +353,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.
///
@@ -482,6 +492,8 @@ namespace Avalonia.Controls.Primitives
}
Opened?.Invoke(this, EventArgs.Empty);
+
+ _popupHostChangedHandler?.Invoke(Host);
}
///
@@ -591,6 +603,8 @@ namespace Avalonia.Controls.Primitives
_openState.Dispose();
_openState = null;
+ _popupHostChangedHandler?.Invoke(null);
+
using (BeginIgnoringIsOpen())
{
IsOpen = false;
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()
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.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 91411cbd3b..0eade8d6df 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 =
@@ -157,6 +157,10 @@ namespace Avalonia.Controls
}
public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text);
+
+ public override bool Equals(object obj) => obj is UndoRedoState other && Equals(other);
+
+ public override int GetHashCode() => Text.GetHashCode();
}
private string _text;
@@ -174,6 +178,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 +210,8 @@ namespace Avalonia.Controls
horizontalScrollBarVisibility,
BindingPriority.Style);
_undoRedoHelper = new UndoRedoHelper(this);
-
+ _selectedTextChangesMadeSinceLastUndoSnapshot = 0;
+ _hasDoneSnapshotOnce = false;
UpdatePseudoclasses();
}
@@ -331,6 +340,7 @@ namespace Avalonia.Controls
if (SetAndRaise(TextProperty, ref _text, value) && IsUndoEnabled && !_isUndoingRedoing)
{
_undoRedoHelper.Clear();
+ SnapshotUndoRedo(); // so we always have an initial state
}
}
}
@@ -341,16 +351,16 @@ namespace Avalonia.Controls
get { return GetSelection(); }
set
{
- SnapshotUndoRedo();
if (string.IsNullOrEmpty(value))
{
+ _selectedTextChangesMadeSinceLastUndoSnapshot++;
+ SnapshotUndoRedo(ignoreChangeCount: false);
DeleteSelection();
}
else
{
HandleTextInput(value);
}
- SnapshotUndoRedo();
}
}
@@ -422,7 +432,7 @@ namespace Avalonia.Controls
get { return _newLine; }
set { SetAndRaise(NewLineProperty, ref _newLine, value); }
}
-
+
///
/// Clears the current selection, maintaining the
///
@@ -480,11 +490,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 +527,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 +591,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();
@@ -627,7 +643,6 @@ namespace Avalonia.Controls
SnapshotUndoRedo();
Copy();
DeleteSelection();
- SnapshotUndoRedo();
}
public async void Copy()
@@ -647,7 +662,6 @@ namespace Avalonia.Controls
SnapshotUndoRedo();
HandleTextInput(text);
- SnapshotUndoRedo();
}
protected override void OnKeyDown(KeyEventArgs e)
@@ -696,6 +710,7 @@ namespace Avalonia.Controls
{
try
{
+ SnapshotUndoRedo();
_isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
@@ -830,7 +845,6 @@ namespace Avalonia.Controls
CaretIndex -= removedCharacters;
ClearSelection();
}
- SnapshotUndoRedo();
handled = true;
break;
@@ -858,7 +872,6 @@ namespace Avalonia.Controls
SetTextInternal(text.Substring(0, caretIndex) +
text.Substring(caretIndex + removedCharacters));
}
- SnapshotUndoRedo();
handled = true;
break;
@@ -868,7 +881,6 @@ namespace Avalonia.Controls
{
SnapshotUndoRedo();
HandleTextInput(NewLine);
- SnapshotUndoRedo();
handled = true;
}
@@ -879,7 +891,6 @@ namespace Avalonia.Controls
{
SnapshotUndoRedo();
HandleTextInput("\t");
- SnapshotUndoRedo();
handled = true;
}
else
@@ -889,6 +900,10 @@ namespace Avalonia.Controls
break;
+ case Key.Space:
+ SnapshotUndoRedo(); // always snapshot in between words
+ break;
+
default:
handled = false;
break;
@@ -1319,11 +1334,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/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.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;
- }
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
index 3f367165ac..d0a4ad38c5 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;
@@ -22,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.
@@ -41,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/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
index 4cb470eeac..94707ac189 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
@@ -1,26 +1,28 @@
using System;
-using System.Collections.Generic;
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, string? customName = null)
{
+ _classes = string.Empty;
Parent = parent;
- Type = visual.GetType().Name;
+ Type = customName ?? visual.GetType().Name;
Visual = visual;
- _classes = string.Empty;
+ FontWeight = IsRoot ? FontWeight.Bold : FontWeight.Normal;
if (visual is IControl control)
{
@@ -52,6 +54,12 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
+ private bool IsRoot => Visual is TopLevel ||
+ Visual is ContextMenu ||
+ Visual is IPopupHost;
+
+ public FontWeight FontWeight { get; }
+
public abstract TreeNodeCollection Children
{
get;
@@ -95,20 +103,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/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
index 48fa636664..6a430897ba 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 Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.Diagnostics;
+using Avalonia.Controls.Primitives;
using Avalonia.Styling;
using Avalonia.VisualTree;
@@ -7,31 +12,30 @@ 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))
- {
+ if (Visual is IStyleable styleable)
IsInTemplate = styleable.TemplatedParent != null;
- }
}
- public bool IsInTemplate { get; private set; }
+ public bool IsInTemplate { get; }
public override TreeNodeCollection Children { get; }
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
{
private readonly IVisual _control;
- private IDisposable? _subscription;
+ private readonly CompositeDisposable _subscriptions = new CompositeDisposable(2);
public VisualTreeNodeCollection(TreeNode owner, IVisual control)
: base(owner)
@@ -41,15 +45,106 @@ namespace Avalonia.Diagnostics.ViewModels
public override void Dispose()
{
- _subscription?.Dispose();
+ _subscriptions.Dispose();
+ }
+
+ private static IObservable? GetHostedPopupRootObservable(IVisual visual)
+ {
+ static IObservable GetPopupHostObservable(
+ IPopupHostProvider popupHostProvider,
+ string? providerName = null)
+ {
+ return Observable.FromEvent(
+ x => popupHostProvider.PopupHostChanged += x,
+ x => popupHostProvider.PopupHostChanged -= x)
+ .StartWith(popupHostProvider.PopupHost)
+ .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 => GetPopupHostObservable(p),
+ 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(new PopupRoot(ContextMenu));
+
+ 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);
+ })
+ .Switch(),
+ _ => null
+ };
}
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());
+ _subscriptions.Clear();
+
+ if (GetHostedPopupRootObservable(_control) is { } popupRootObservable)
+ {
+ VisualTreeNode? childNode = null;
+
+ _subscriptions.Add(
+ popupRootObservable
+ .Subscribe(popupRoot =>
+ {
+ if (popupRoot != null)
+ {
+ childNode = new VisualTreeNode(
+ popupRoot.Value.Root,
+ Owner,
+ popupRoot.Value.CustomName);
+
+ 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()));
+ }
+
+ private struct PopupRoot
+ {
+ public PopupRoot(IControl root, string? customName = null)
+ {
+ Root = root;
+ CustomName = customName;
+ }
+
+ public IControl Root { get; }
+ public string? CustomName { get; }
}
}
}
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 @@
-
+
-
-
-
+
+
+
+ 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:
+
+
+
+
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index d1232b749a..ea06c33e4d 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -1,8 +1,11 @@
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.Data;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.Input.Raw;
@@ -15,6 +18,7 @@ namespace Avalonia.Diagnostics.Views
internal class MainWindow : Window, IStyleHost
{
private readonly IDisposable _keySubscription;
+ private readonly Dictionary _frozenPopupStates;
private TopLevel? _root;
public MainWindow()
@@ -23,23 +27,26 @@ namespace Avalonia.Diagnostics.Views
_keySubscription = InputManager.Instance.Process
.OfType()
+ .Where(x => x.Type == RawKeyEventType.KeyDown)
.Subscribe(RawKeyDown);
+ _frozenPopupStates = new Dictionary();
+
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;
}
@@ -77,6 +84,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;
@@ -91,6 +105,53 @@ 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 static List GetPopupRoots(IVisual root)
+ {
+ var popupRoots = new List();
+
+ void ProcessProperty(IControl control, AvaloniaProperty property)
+ {
+ if (control.GetValue(property) is IPopupHostProvider popupProvider
+ && popupProvider.PopupHost is PopupRoot popupRoot)
+ {
+ popupRoots.Add(popupRoot);
+ }
+ }
+
+ foreach (var control in root.GetVisualDescendants().OfType())
+ {
+ if (control is Popup p && p.Host is PopupRoot popupRoot)
+ {
+ popupRoots.Add(popupRoot);
+ }
+
+ ProcessProperty(control, ContextFlyoutProperty);
+ ProcessProperty(control, ContextMenuProperty);
+ ProcessProperty(control, FlyoutBase.AttachedFlyoutProperty);
+ ProcessProperty(control, ToolTipDiagnostics.ToolTipProperty);
+ }
+
+ return popupRoots;
+ }
+
private void RawKeyDown(RawKeyEventArgs e)
{
var vm = (MainViewModel?)DataContext;
@@ -99,34 +160,72 @@ 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;
+
+ foreach (var popupRoot in GetPopupRoots(Root))
+ {
+ control = GetHoveredControl(popupRoot);
+
+ if (control != null)
+ {
+ break;
+ }
+ }
+
+ control ??= GetHoveredControl(Root);
- var control = Root.GetVisualsAt(point, x =>
+ if (control != null)
{
- if (x is AdornerLayer || !x.IsVisible) return false;
- if (!(x is IInputElement ie)) return true;
- return ie.IsHitTestVisible;
- })
- .FirstOrDefault();
+ vm.SelectControl(control);
+ }
+
+ break;
+ }
- if (control != null)
+ case RawInputModifiers.Control | RawInputModifiers.Alt when e.Key == Key.F:
{
- vm.SelectControl((IControl)control);
+ vm.FreezePopups = !vm.FreezePopups;
+
+ foreach (var popupRoot in GetPopupRoots(Root))
+ {
+ if (popupRoot.Parent is Popup popup)
+ {
+ if (vm.FreezePopups)
+ {
+ var lightDismissEnabledState = popup.SetValue(
+ Popup.IsLightDismissEnabledProperty,
+ !vm.FreezePopups,
+ BindingPriority.Animation);
+
+ if (lightDismissEnabledState != null)
+ {
+ _frozenPopupStates[popup] = lightDismissEnabledState;
+ }
+ }
+ else
+ {
+ //TODO Use Dictionary.Remove(Key, out Value) in netstandard 2.1
+ if (_frozenPopupStates.ContainsKey(popup))
+ {
+ _frozenPopupStates[popup].Dispose();
+ _frozenPopupStates.Remove(popup);
+ }
+ }
+ }
+ }
+
+ 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;
}
}
}
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 @@
-
+
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index 60c0b36891..a7d05e416f 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -109,11 +109,17 @@ 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))
.Bind().ToConstant(applicationPlatform);
+ 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
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}">
/// Interface for controls that host their own separate visual tree, such as popups.
///
+ [Obsolete]
public interface IVisualTreeHost
{
///