diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml
index 14c371efef..b583503327 100644
--- a/samples/BindingDemo/MainWindow.xaml
+++ b/samples/BindingDemo/MainWindow.xaml
@@ -116,6 +116,7 @@
+
diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
index abb3245c90..f0241cad48 100644
--- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using System.Threading;
using ReactiveUI;
using Avalonia.Controls;
+using Avalonia.Metadata;
using Avalonia.Controls.Selection;
namespace BindingDemo.ViewModels
@@ -103,5 +104,16 @@ namespace BindingDemo.ViewModels
get { return _nested; }
private set { this.RaiseAndSetIfChanged(ref _nested, value); }
}
+
+ public void Do(object parameter)
+ {
+
+ }
+
+ [DependsOn(nameof(BooleanFlag))]
+ bool CanDo(object parameter)
+ {
+ return BooleanFlag;
+ }
}
}
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index bab57f3544..9bac320c79 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -1,8 +1,7 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index d69052ad3d..f616a42cac 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -88,6 +88,23 @@ namespace Avalonia.Controls
};
}
+ ///
+ /// Begin configuring an .
+ ///
+ /// Factory function for .
+ /// The subclass of to configure.
+ /// is useful for passing of dependencies to .
+ /// An instance.
+ public static TAppBuilder Configure(Func appFactory)
+ where TApp : Application
+ {
+ return new TAppBuilder()
+ {
+ ApplicationType = typeof(TApp),
+ _appFactory = appFactory
+ };
+ }
+
protected TAppBuilder Self => (TAppBuilder)this;
public TAppBuilder AfterSetup(Action callback)
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index 327cfb7736..5fcb14c858 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -257,15 +257,7 @@ namespace Avalonia.Controls.Presenters
return base.ArrangeOverride(finalSize);
}
- try
- {
- _arranging = true;
- return ArrangeWithAnchoring(finalSize);
- }
- finally
- {
- _arranging = false;
- }
+ return ArrangeWithAnchoring(finalSize);
}
private Size ArrangeWithAnchoring(Size finalSize)
@@ -316,7 +308,17 @@ namespace Avalonia.Controls.Presenters
}
Extent = newExtent;
- Offset = newOffset;
+
+ try
+ {
+ _arranging = true;
+ Offset = newOffset;
+ }
+ finally
+ {
+ _arranging = false;
+ }
+
ArrangeOverrideImpl(size, -Offset);
}
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 4e742b3b7b..73a1ae3335 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -18,6 +18,15 @@ namespace Avalonia.Controls
{
public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost
{
+ public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current
+ .GetService()?.Cut.FirstOrDefault();
+
+ public static KeyGesture CopyGesture { get; } = AvaloniaLocator.Current
+ .GetService()?.Copy.FirstOrDefault();
+
+ public static KeyGesture PasteGesture { get; } = AvaloniaLocator.Current
+ .GetService()?.Paste.FirstOrDefault();
+
public static readonly StyledProperty AcceptsReturnProperty =
AvaloniaProperty.Register(nameof(AcceptsReturn));
@@ -103,6 +112,21 @@ namespace Avalonia.Controls
public static readonly StyledProperty RevealPasswordProperty =
AvaloniaProperty.Register(nameof(RevealPassword));
+
+ public static readonly DirectProperty CanCutProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanCut),
+ o => o.CanCut);
+
+ public static readonly DirectProperty CanCopyProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanCopy),
+ o => o.CanCopy);
+
+ public static readonly DirectProperty CanPasteProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanPaste),
+ o => o.CanPaste);
struct UndoRedoState : IEquatable
{
@@ -126,6 +150,9 @@ namespace Avalonia.Controls
private UndoRedoHelper _undoRedoHelper;
private bool _isUndoingRedoing;
private bool _ignoreTextChanges;
+ private bool _canCut;
+ private bool _canCopy;
+ private bool _canPaste;
private string _newLine = Environment.NewLine;
private static readonly string[] invalidCharacters = new String[1] { "\u007f" };
@@ -369,6 +396,41 @@ namespace Avalonia.Controls
get { return _newLine; }
set { SetAndRaise(NewLineProperty, ref _newLine, value); }
}
+
+ ///
+ /// Clears the current selection, maintaining the
+ ///
+ public void ClearSelection()
+ {
+ SelectionStart = SelectionEnd = CaretIndex;
+ }
+
+ ///
+ /// Property for determining if the Cut command can be executed.
+ ///
+ public bool CanCut
+ {
+ get { return _canCut; }
+ private set { SetAndRaise(CanCutProperty, ref _canCut, value); }
+ }
+
+ ///
+ /// Property for determining if the Copy command can be executed.
+ ///
+ public bool CanCopy
+ {
+ get { return _canCopy; }
+ private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); }
+ }
+
+ ///
+ /// Property for determining if the Paste command can be executed.
+ ///
+ public bool CanPaste
+ {
+ get { return _canPaste; }
+ private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); }
+ }
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
@@ -387,9 +449,19 @@ namespace Avalonia.Controls
if (change.Property == TextProperty)
{
UpdatePseudoclasses();
+ UpdateCommandStates();
}
}
+ private void UpdateCommandStates()
+ {
+ var text = GetSelection();
+ var isSelectionNullOrEmpty = string.IsNullOrEmpty(text);
+ CanCopy = !IsPasswordBox && !isSelectionNullOrEmpty;
+ CanCut = !IsPasswordBox && !isSelectionNullOrEmpty && !IsReadOnly;
+ CanPaste = !IsReadOnly;
+ }
+
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
@@ -404,6 +476,8 @@ namespace Avalonia.Controls
SelectAll();
}
+ UpdateCommandStates();
+
_presenter?.ShowCaret();
}
@@ -413,11 +487,12 @@ namespace Avalonia.Controls
if (ContextMenu == null || !ContextMenu.IsOpen)
{
- SelectionStart = 0;
- SelectionEnd = 0;
+ ClearSelection();
RevealPassword = false;
}
-
+
+ UpdateCommandStates();
+
_presenter?.HideCaret();
}
@@ -444,7 +519,7 @@ namespace Avalonia.Controls
text = Text ?? string.Empty;
SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex));
CaretIndex += input.Length;
- SelectionStart = SelectionEnd = CaretIndex;
+ ClearSelection();
_undoRedoHelper.DiscardRedo();
}
}
@@ -460,19 +535,31 @@ namespace Avalonia.Controls
return text;
}
- private async void Copy()
+ public async void Cut()
{
+ var text = GetSelection();
+ if (text is null) return;
+
+ _undoRedoHelper.Snapshot();
+ Copy();
+ DeleteSelection();
+ _undoRedoHelper.Snapshot();
+ }
+
+ public async void Copy()
+ {
+ var text = GetSelection();
+ if (text is null) return;
+
await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))
- .SetTextAsync(GetSelection());
+ .SetTextAsync(text);
}
- private async void Paste()
+ public async void Paste()
{
var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync();
- if (text == null)
- {
- return;
- }
+
+ if (text is null) return;
_undoRedoHelper.Snapshot();
HandleTextInput(text);
@@ -511,23 +598,18 @@ namespace Avalonia.Controls
{
if (!IsPasswordBox)
{
- _undoRedoHelper.Snapshot();
- Copy();
- DeleteSelection();
- _undoRedoHelper.Snapshot();
+ Cut();
}
handled = true;
}
else if (Match(keymap.Paste))
{
-
Paste();
handled = true;
}
else if (Match(keymap.Undo))
{
-
try
{
_isUndoingRedoing = true;
@@ -662,7 +744,7 @@ namespace Avalonia.Controls
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) +
text.Substring(caretIndex));
CaretIndex -= removedCharacters;
- SelectionStart = SelectionEnd = CaretIndex;
+ ClearSelection();
}
_undoRedoHelper.Snapshot();
@@ -735,7 +817,7 @@ namespace Avalonia.Controls
}
else if (movement)
{
- SelectionStart = SelectionEnd = CaretIndex;
+ ClearSelection();
}
if (handled || movement)
@@ -1042,7 +1124,8 @@ namespace Avalonia.Controls
var end = Math.Max(selectionStart, selectionEnd);
var text = Text;
SetTextInternal(text.Substring(0, start) + text.Substring(end));
- SelectionStart = SelectionEnd = CaretIndex = start;
+ CaretIndex = start;
+ ClearSelection();
return true;
}
else
@@ -1131,7 +1214,8 @@ namespace Avalonia.Controls
set
{
Text = value.Text;
- SelectionStart = SelectionEnd = CaretIndex = value.CaretPosition;
+ CaretIndex = value.CaretPosition;
+ ClearSelection();
}
}
}
diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
index 438cbc8b27..3128753781 100644
--- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
+++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
@@ -141,7 +141,7 @@ namespace Avalonia.Controls.Utils
var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y;
if (radiusX != 0 || radiusY != 0)
{
- context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise);
+ context.ArcTo(keypoints.RightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
// Right
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
index acc3ef16c2..1c49b24f52 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
@@ -18,12 +18,13 @@ namespace Avalonia.Diagnostics.ViewModels
private int _selectedTab;
private string _focusedControl;
private string _pointerOverElement;
+ private bool _shouldVisualizeMarginPadding = true;
public MainViewModel(IControl root)
{
_root = root;
- _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
- _visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
+ _logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root));
+ _visualTree = new TreePageViewModel(this, VisualTreeNode.Create(root));
_events = new EventsPageViewModel(root);
UpdateFocusedControl();
@@ -34,6 +35,17 @@ namespace Avalonia.Diagnostics.ViewModels
Console = new ConsoleViewModel(UpdateConsoleContext);
}
+ public bool ShouldVisualizeMarginPadding
+ {
+ get => _shouldVisualizeMarginPadding;
+ set => RaiseAndSetIfChanged(ref _shouldVisualizeMarginPadding, value);
+ }
+
+ public void ToggleVisualizeMarginPadding()
+ {
+ ShouldVisualizeMarginPadding = !ShouldVisualizeMarginPadding;
+ }
+
public ConsoleViewModel Console { get; }
public ViewModelBase Content
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
index 19d2de442c..d02c8994eb 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Controls;
+using Avalonia.Controls.Selection;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
@@ -10,10 +11,13 @@ namespace Avalonia.Diagnostics.ViewModels
private ControlDetailsViewModel _details;
private string _propertyFilter;
- public TreePageViewModel(TreeNode[] nodes)
+ public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes)
{
+ MainView = mainView;
Nodes = nodes;
- }
+ }
+
+ public MainViewModel MainView { get; }
public TreeNode[] Nodes { get; protected set; }
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
index 663722acba..0165398718 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
@@ -16,6 +16,15 @@
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
index 633d18ddd8..1b61986ce6 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
@@ -1,7 +1,7 @@
+using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Shapes;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
@@ -11,45 +11,78 @@ namespace Avalonia.Diagnostics.Views
{
internal class TreePageView : UserControl
{
- private Control _adorner;
+ private readonly Panel _adorner;
+ private AdornerLayer _currentLayer;
private TreeView _tree;
public TreePageView()
{
- this.InitializeComponent();
+ InitializeComponent();
_tree.ItemContainerGenerator.Index.Materialized += TreeViewItemMaterialized;
+
+ _adorner = new Panel
+ {
+ ClipToBounds = false,
+ Children =
+ {
+ //Padding frame
+ new Border { BorderBrush = new SolidColorBrush(Colors.Green, 0.5) },
+ //Content frame
+ new Border { Background = new SolidColorBrush(Color.FromRgb(160, 197, 232), 0.5) },
+ //Margin frame
+ new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) }
+ },
+ };
}
protected void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
- var layer = AdornerLayer.GetAdornerLayer(node.Visual);
+ var visual = (Visual)node.Visual;
+
+ _currentLayer = AdornerLayer.GetAdornerLayer(visual);
- if (layer != null)
+ if (_currentLayer == null ||
+ _currentLayer.Children.Contains(_adorner))
{
- if (_adorner != null)
- {
- ((Panel)_adorner.Parent).Children.Remove(_adorner);
- _adorner = null;
- }
+ return;
+ }
- _adorner = new Rectangle
- {
- Fill = new SolidColorBrush(0x80a0c5e8),
- [AdornerLayer.AdornedElementProperty] = node.Visual,
- };
+ _currentLayer.Children.Add(_adorner);
+ AdornerLayer.SetAdornedElement(_adorner, visual);
+
+ var vm = (TreePageViewModel) DataContext;
- layer.Children.Add(_adorner);
+ if (vm.MainView.ShouldVisualizeMarginPadding)
+ {
+ var paddingBorder = (Border)_adorner.Children[0];
+ paddingBorder.BorderThickness = visual.GetValue(PaddingProperty);
+
+ var contentBorder = (Border)_adorner.Children[1];
+ contentBorder.Margin = visual.GetValue(PaddingProperty);
+
+ var marginBorder = (Border)_adorner.Children[2];
+ marginBorder.BorderThickness = visual.GetValue(MarginProperty);
+ marginBorder.Margin = InvertThickness(visual.GetValue(MarginProperty));
}
}
+ private static Thickness InvertThickness(Thickness input)
+ {
+ return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom);
+ }
+
protected void RemoveAdorner(object sender, PointerEventArgs e)
{
- if (_adorner != null)
+ foreach (var border in _adorner.Children.OfType())
{
- ((Panel)_adorner.Parent).Children.Remove(_adorner);
- _adorner = null;
+ border.Margin = default;
+ border.Padding = default;
+ border.BorderThickness = default;
}
+
+ _currentLayer?.Children.Remove(_adorner);
+ _currentLayer = null;
}
private void InitializeComponent()
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index cfd47d48de..804cf7f8ac 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -110,11 +110,20 @@ namespace Avalonia.Native
.Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
.Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta))
.Bind().ToConstant(new MacOSMountedVolumeInfoProvider())
- .Bind().ToConstant(new AvaloniaNativeDragSource(_factory))
- ;
+ .Bind().ToConstant(new AvaloniaNativeDragSource(_factory));
+
if (_options.UseGpu)
- AvaloniaLocator.CurrentMutable.Bind()
- .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay()));
+ {
+ try
+ {
+ AvaloniaLocator.CurrentMutable.Bind()
+ .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay()));
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
}
public IWindowImpl CreateWindow()
diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs
index 0d593bc2f6..f28be52dd5 100644
--- a/src/Avalonia.Native/ScreenImpl.cs
+++ b/src/Avalonia.Native/ScreenImpl.cs
@@ -20,21 +20,26 @@ namespace Avalonia.Native
{
get
{
- var count = ScreenCount;
- var result = new Screen[count];
-
- for(int i = 0; i < count; i++)
+ if (_native != null)
{
- var screen = _native.GetScreen(i);
+ var count = ScreenCount;
+ var result = new Screen[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ var screen = _native.GetScreen(i);
+
+ result[i] = new Screen(
+ screen.PixelDensity,
+ screen.Bounds.ToAvaloniaPixelRect(),
+ screen.WorkingArea.ToAvaloniaPixelRect(),
+ screen.Primary);
+ }
- result[i] = new Screen(
- screen.PixelDensity,
- screen.Bounds.ToAvaloniaPixelRect(),
- screen.WorkingArea.ToAvaloniaPixelRect(),
- screen.Primary);
+ return result;
}
- return result;
+ return Array.Empty();
}
}
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 4b13666edd..56cf544d9d 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -94,8 +94,13 @@ namespace Avalonia.Native
{
get
{
- var s = _native.GetClientSize();
- return new Size(s.Width, s.Height);
+ if (_native != null)
+ {
+ var s = _native.GetClientSize();
+ return new Size(s.Width, s.Height);
+ }
+
+ return default;
}
}
@@ -144,7 +149,6 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.Closed()
{
var n = _parent._native;
- _parent._native = null;
try
{
_parent?.Closed?.Invoke();
@@ -153,6 +157,7 @@ namespace Avalonia.Native
{
n?.Dispose();
}
+
_parent._mouse.Dispose();
}
@@ -351,12 +356,12 @@ namespace Avalonia.Native
public Point PointToClient(PixelPoint point)
{
- return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
+ return _native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default;
}
public PixelPoint PointToScreen(Point point)
{
- return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint();
+ return _native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default;
}
public void Hide()
diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml
index d63f785e77..08d8b4c995 100644
--- a/src/Avalonia.Themes.Default/Expander.xaml
+++ b/src/Avalonia.Themes.Default/Expander.xaml
@@ -86,7 +86,7 @@