diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
index bbfbd4b4cd..486cc55d44 100644
--- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
@@ -6,7 +6,7 @@
A drop-down list.
-
+
Inline Items
Inline Item 2
Inline Item 3
diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs
index 9e9b84537b..067d9f462f 100644
--- a/src/Avalonia.Animation/Animatable.cs
+++ b/src/Avalonia.Animation/Animatable.cs
@@ -93,17 +93,17 @@ namespace Avalonia.Animation
var oldTransitions = change.OldValue.GetValueOrDefault();
var newTransitions = change.NewValue.GetValueOrDefault();
- if (oldTransitions is object)
- {
- oldTransitions.CollectionChanged -= TransitionsCollectionChanged;
- RemoveTransitions(oldTransitions);
- }
-
if (newTransitions is object)
{
newTransitions.CollectionChanged += TransitionsCollectionChanged;
AddTransitions(newTransitions);
}
+
+ if (oldTransitions is object)
+ {
+ oldTransitions.CollectionChanged -= TransitionsCollectionChanged;
+ RemoveTransitions(oldTransitions);
+ }
}
else if (_transitionsEnabled &&
Transitions is object &&
@@ -111,8 +111,10 @@ namespace Avalonia.Animation
!change.Property.IsDirect &&
change.Priority > BindingPriority.Animation)
{
- foreach (var transition in Transitions)
+ for (var i = Transitions.Count -1; i >= 0; --i)
{
+ var transition = Transitions[i];
+
if (transition.Property == change.Property)
{
var state = _transitionState[transition];
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index e71bcc24a7..6557346b1f 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -51,6 +51,18 @@ namespace Avalonia.Controls
public static readonly StyledProperty VirtualizationModeProperty =
ItemsPresenter.VirtualizationModeProperty.AddOwner();
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PlaceholderTextProperty =
+ AvaloniaProperty.Register(nameof(PlaceholderText));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PlaceholderForegroundProperty =
+ AvaloniaProperty.Register(nameof(PlaceholderForeground));
+
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@@ -94,6 +106,24 @@ namespace Avalonia.Controls
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
}
+ ///
+ /// Gets or sets the PlaceHolder text.
+ ///
+ public string PlaceholderText
+ {
+ get { return GetValue(PlaceholderTextProperty); }
+ set { SetValue(PlaceholderTextProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the Brush that renders the placeholder text.
+ ///
+ public IBrush PlaceholderForeground
+ {
+ get { return GetValue(PlaceholderForegroundProperty); }
+ set { SetValue(PlaceholderForegroundProperty, value); }
+ }
+
///
/// Gets or sets the virtualization mode for the items.
///
diff --git a/src/Avalonia.Controls/ListBoxItem.cs b/src/Avalonia.Controls/ListBoxItem.cs
index 199c9e0ff8..e04c79987f 100644
--- a/src/Avalonia.Controls/ListBoxItem.cs
+++ b/src/Avalonia.Controls/ListBoxItem.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls.Mixins;
+using Avalonia.Input;
namespace Avalonia.Controls
{
@@ -19,6 +20,7 @@ namespace Avalonia.Controls
static ListBoxItem()
{
SelectableMixin.Attach(IsSelectedProperty);
+ PressedMixin.Attach();
FocusableProperty.OverrideDefaultValue(true);
}
diff --git a/src/Avalonia.Controls/Mixins/PressedMixin.cs b/src/Avalonia.Controls/Mixins/PressedMixin.cs
new file mode 100644
index 0000000000..4c5792b4a0
--- /dev/null
+++ b/src/Avalonia.Controls/Mixins/PressedMixin.cs
@@ -0,0 +1,37 @@
+using Avalonia.Interactivity;
+using Avalonia.Input;
+
+namespace Avalonia.Controls.Mixins
+{
+ ///
+ /// Adds pressed functionality to control classes.
+ ///
+ /// Adds the ':pressed' class when the item is pressed.
+ ///
+ public static class PressedMixin
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The control type.
+ public static void Attach() where TControl : Control
+ {
+ InputElement.PointerPressedEvent.AddClassHandler((x, e) => HandlePointerPressed(x, e), RoutingStrategies.Tunnel);
+ InputElement.PointerReleasedEvent.AddClassHandler((x, e) => HandlePointerReleased(x), RoutingStrategies.Tunnel);
+ InputElement.PointerCaptureLostEvent.AddClassHandler((x, e) => HandlePointerReleased(x), RoutingStrategies.Tunnel);
+ }
+
+ private static void HandlePointerPressed(TControl sender, PointerPressedEventArgs e) where TControl : Control
+ {
+ if (e.GetCurrentPoint(sender).Properties.IsLeftButtonPressed)
+ {
+ ((IPseudoClasses)sender.Classes).Set(":pressed", true);
+ }
+ }
+
+ private static void HandlePointerReleased(TControl sender) where TControl : Control
+ {
+ ((IPseudoClasses)sender.Classes).Set(":pressed", false);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Platform/IPopupImpl.cs b/src/Avalonia.Controls/Platform/IPopupImpl.cs
index 3944695f04..477d5fab43 100644
--- a/src/Avalonia.Controls/Platform/IPopupImpl.cs
+++ b/src/Avalonia.Controls/Platform/IPopupImpl.cs
@@ -8,5 +8,7 @@ namespace Avalonia.Platform
public interface IPopupImpl : IWindowBaseImpl
{
IPopupPositioner PopupPositioner { get; }
+
+ void SetWindowManagerAddShadowHint(bool enabled);
}
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 66f2153b6c..49315e1b25 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -19,6 +19,9 @@ namespace Avalonia.Controls.Primitives
///
public class Popup : Control, IVisualTreeHost
{
+ public static readonly StyledProperty WindowManagerAddShadowHintProperty =
+ AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), true);
+
///
/// Defines the property.
///
@@ -89,7 +92,7 @@ namespace Avalonia.Controls.Primitives
{
IsHitTestVisibleProperty.OverrideDefaultValue(false);
ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e));
- IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs)e));
+ IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs)e));
}
///
@@ -104,6 +107,12 @@ namespace Avalonia.Controls.Primitives
public IPopupHost? Host => _openState?.PopupHost;
+ public bool WindowManagerAddShadowHint
+ {
+ get { return GetValue(WindowManagerAddShadowHintProperty); }
+ set { SetValue(WindowManagerAddShadowHintProperty, value); }
+ }
+
///
/// Gets or sets the control to display in the popup.
///
@@ -293,6 +302,8 @@ namespace Avalonia.Controls.Primitives
_openState = new PopupOpenState(topLevel, popupHost, cleanupPopup);
+ WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint);
+
popupHost.Show();
using (BeginIgnoringIsOpen())
@@ -332,6 +343,14 @@ namespace Avalonia.Controls.Primitives
return Disposable.Create((unsubscribe, target, handler), state => state.unsubscribe(state.target, state.handler));
}
+ private void WindowManagerAddShadowHintChanged(IPopupHost host, bool hint)
+ {
+ if(host is PopupRoot pr)
+ {
+ pr.PlatformImpl.SetWindowManagerAddShadowHint(hint);
+ }
+ }
+
///
/// Called when the property changes.
///
diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index 788fe03162..aab7a68795 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -17,14 +17,14 @@ namespace Avalonia.Controls.Primitives
public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
{
private readonly TopLevel _parent;
- private PopupPositionerParameters _positionerParameters;
+ private PopupPositionerParameters _positionerParameters;
///
/// Initializes static members of the class.
///
static PopupRoot()
{
- BackgroundProperty.OverrideDefaultValue(typeof(PopupRoot), Brushes.White);
+ BackgroundProperty.OverrideDefaultValue(typeof(PopupRoot), Brushes.White);
}
///
@@ -53,7 +53,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the platform-specific window implementation.
///
[CanBeNull]
- public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
+ public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
///
/// Gets the parent control in the event route.
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index e9867e4503..13bc4ed124 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -70,6 +70,14 @@ namespace Avalonia.Controls
Brushes.Black,
inherits: true);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty MaxLinesProperty =
+ AvaloniaProperty.Register(
+ nameof(MaxLines),
+ validate: IsValidMaxLines);
+
///
/// Defines the property.
///
@@ -222,6 +230,15 @@ namespace Avalonia.Controls
set { SetValue(ForegroundProperty, value); }
}
+ ///
+ /// Gets or sets the maximum number of text lines.
+ ///
+ public int MaxLines
+ {
+ get => GetValue(MaxLinesProperty);
+ set => SetValue(MaxLinesProperty, value);
+ }
+
///
/// Gets or sets the control's text wrapping mode.
///
@@ -404,7 +421,8 @@ namespace Avalonia.Controls
TextTrimming,
TextDecorations,
constraint.Width,
- constraint.Height);
+ constraint.Height,
+ MaxLines);
}
///
@@ -451,5 +469,7 @@ namespace Avalonia.Controls
InvalidateMeasure();
}
+
+ private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;
}
}
diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
index 586fe4d341..51eb6edbea 100644
--- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
+++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
@@ -118,12 +118,11 @@ namespace Avalonia.Controls.Utils
pen = new Pen(borderBrush, borderThickness);
}
- var rrect = new RoundedRect(new Rect(_size), _cornerRadius.TopLeft, _cornerRadius.TopRight,
- _cornerRadius.BottomRight, _cornerRadius.BottomLeft);
+ var rect = new Rect(_size);
if (Math.Abs(borderThickness) > double.Epsilon)
- {
- rrect = rrect.Deflate(borderThickness * 0.5, borderThickness * 0.5);
- }
+ rect = rect.Deflate(borderThickness * 0.5);
+ var rrect = new RoundedRect(rect, _cornerRadius.TopLeft, _cornerRadius.TopRight,
+ _cornerRadius.BottomRight, _cornerRadius.BottomLeft);
context.PlatformImpl.DrawRectangle(background, pen, rrect, boxShadows);
}
diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
index a9665b1519..c2565cc59c 100644
--- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs
+++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
@@ -146,6 +146,10 @@ namespace Avalonia.DesignerSupport.Remote
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { }
+ public void SetWindowManagerAddShadowHint(bool enabled)
+ {
+ }
+
public WindowTransparencyLevel TransparencyLevel { get; private set; }
}
diff --git a/src/Avalonia.Input/NavigationDirection.cs b/src/Avalonia.Input/NavigationDirection.cs
index 34bedac141..9b9af0b0a6 100644
--- a/src/Avalonia.Input/NavigationDirection.cs
+++ b/src/Avalonia.Input/NavigationDirection.cs
@@ -102,7 +102,7 @@ namespace Avalonia.Input
switch (key)
{
case Key.Tab:
- return (modifiers & KeyModifiers.Shift) != 0 ?
+ return (modifiers & KeyModifiers.Shift) == 0 ?
NavigationDirection.Next : NavigationDirection.Previous;
case Key.Up:
return NavigationDirection.Up;
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index e4ee293757..c41be1723b 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -59,6 +59,11 @@ namespace Avalonia.Native
}
public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this);
+
+ public void SetWindowManagerAddShadowHint(bool enabled)
+ {
+ }
+
public IPopupPositioner PopupPositioner { get; }
}
}
diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml
index 7ce6c5a19f..44318ffa8f 100644
--- a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml
@@ -138,7 +138,9 @@
+
+
False
@@ -159,8 +161,8 @@
24
40
3
- 374
- 0
+ 374
+ 0
2
0
21
@@ -258,10 +260,10 @@
0,0,0,0
0,0,0,0
3,0,3,0
- 1
- 0,2,0,2
- -1,0,-1,0
- 12,11,0,13
+ 1
+ 0,2,0,2
+ -1,0,-1,0
+ 12,11,0,13
2
2
2
diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml
index 026edca4f9..e43a7ab4e7 100644
--- a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml
@@ -138,7 +138,9 @@
+
+
False
@@ -158,8 +160,8 @@
24
40
3
- 374
- 0
+ 374
+ 0
2
0
21
@@ -257,10 +259,10 @@
0,0,0,0
0,0,0,0
3,0,3,0
- 1
- 0,2,0,2
- -1,0,-1,0
- 12,11,0,13
+ 1
+ 0,2,0,2
+ -1,0,-1,0
+ 12,11,0,13
2
2
2
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
index ed6f56b211..7a715bbde7 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
@@ -1,6 +1,4 @@
-
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
index ac7b792a9a..5c6286a0bc 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
@@ -1,6 +1,4 @@
-
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
index a0f3ef0d62..7364c339f1 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
@@ -39,6 +39,136 @@
+
+ 21
+ 64
+ 80
+ 240
+ 1
+ 1
+ 0,0,0,4
+ 2
+ 11,5,11,7
+ 11,11,11,13
+ 11,11,11,13
+ Normal
+ SemiLight
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0,4,0,4
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -64,6 +194,12 @@
+
+
+
+
+ 12
+
1
0
@@ -153,5 +289,59 @@
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
index ec4f35664c..15e157f573 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
@@ -39,6 +39,136 @@
+
+ 21
+ 64
+ 80
+ 240
+ 1
+ 1
+ 0,0,0,4
+ 2
+ 11,5,11,7
+ 11,11,11,13
+ 11,11,11,13
+ Normal
+ SemiLight
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0,4,0,4
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -64,6 +194,12 @@
+
+
+
+
+ 12
+
1
0
@@ -153,5 +289,59 @@
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml b/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml
index 788b60892b..44e7962e17 100644
--- a/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml
+++ b/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml
@@ -1,37 +1,70 @@
-
+
+
+
+
+
+ Alabama
+ Alaska
+ Arizona
+ Arkansas
+ California
+ Colorado
+ Connecticut
+ Delaware
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/ComboBox.xaml b/src/Avalonia.Themes.Fluent/ComboBox.xaml
index 95bd9550a5..1fc657c1a3 100644
--- a/src/Avalonia.Themes.Fluent/ComboBox.xaml
+++ b/src/Avalonia.Themes.Fluent/ComboBox.xaml
@@ -1,64 +1,192 @@
-
+
+
+
+
+
+ Item 1
+ Item 2
+
+
+
+ Item 1
+ Item 2
+
+
+
+
+
+ 0,0,0,4
+ 15
+ 7
+
+ 12,5,0,7
+ 11,5,32,6
+ 32
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/ComboBoxItem.xaml
index 23456ec744..c26dec3d34 100644
--- a/src/Avalonia.Themes.Fluent/ComboBoxItem.xaml
+++ b/src/Avalonia.Themes.Fluent/ComboBoxItem.xaml
@@ -1,9 +1,27 @@
+
+
+
+
+ Item 1
+ Item 2
+
+
+
+ Item 1
+ Item 2
+
+
+
+
+
-
+
-
+
-
+
-
+
diff --git a/src/Avalonia.Themes.Fluent/ListBox.xaml b/src/Avalonia.Themes.Fluent/ListBox.xaml
index e91d8a6772..7a87387de8 100644
--- a/src/Avalonia.Themes.Fluent/ListBox.xaml
+++ b/src/Avalonia.Themes.Fluent/ListBox.xaml
@@ -1,31 +1,43 @@
-
-
+
+
+
+ Test
+ Test
+ Test
+ Test
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/ListBoxItem.xaml b/src/Avalonia.Themes.Fluent/ListBoxItem.xaml
index 19a6e3d4ec..430d3c5468 100644
--- a/src/Avalonia.Themes.Fluent/ListBoxItem.xaml
+++ b/src/Avalonia.Themes.Fluent/ListBoxItem.xaml
@@ -1,12 +1,27 @@
-
+
+
+
+
+ Disabled
+ Test
+ Test
+
+
+
+
+ 12,9,12,12
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
+
diff --git a/src/Avalonia.Themes.Fluent/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/PopupRoot.xaml
index 9af4f5a910..25c71e3493 100644
--- a/src/Avalonia.Themes.Fluent/PopupRoot.xaml
+++ b/src/Avalonia.Themes.Fluent/PopupRoot.xaml
@@ -1,17 +1,21 @@
-
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/RadioButton.xaml b/src/Avalonia.Themes.Fluent/RadioButton.xaml
index 4cdb116cdd..23bcfc616a 100644
--- a/src/Avalonia.Themes.Fluent/RadioButton.xaml
+++ b/src/Avalonia.Themes.Fluent/RadioButton.xaml
@@ -1,60 +1,173 @@
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index 8d4475d1c3..720185a3ad 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
@@ -32,6 +32,7 @@ namespace Avalonia.Media.TextFormatting
/// The text decorations.
/// The maximum width.
/// The maximum height.
+ /// The maximum number of text lines.
/// The text style overrides.
public TextLayout(
string text,
@@ -44,6 +45,7 @@ namespace Avalonia.Media.TextFormatting
TextDecorationCollection textDecorations = null,
double maxWidth = double.PositiveInfinity,
double maxHeight = double.PositiveInfinity,
+ int maxLines = 0,
IReadOnlyList textStyleOverrides = null)
{
_text = string.IsNullOrEmpty(text) ?
@@ -59,6 +61,8 @@ namespace Avalonia.Media.TextFormatting
MaxHeight = maxHeight;
+ MaxLines = maxLines;
+
UpdateLayout();
}
@@ -73,6 +77,12 @@ namespace Avalonia.Media.TextFormatting
///
public double MaxHeight { get; }
+
+ ///
+ /// Gets the maximum number of text lines.
+ ///
+ public double MaxLines { get; }
+
///
/// Gets the text lines.
///
@@ -192,7 +202,7 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = 0;
- while (currentPosition < _text.Length)
+ while (currentPosition < _text.Length && (MaxLines == 0 || textLines.Count < MaxLines))
{
int length;
@@ -222,7 +232,7 @@ namespace Avalonia.Media.TextFormatting
var remainingLength = length;
- while (remainingLength > 0)
+ while (remainingLength > 0 && (MaxLines == 0 || textLines.Count < MaxLines))
{
var textSlice = _text.AsSlice(currentPosition, remainingLength);
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 4899f0efc0..1d41fe4bdd 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -1098,6 +1098,10 @@ namespace Avalonia.X11
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) =>
_transparencyHelper.SetTransparencyRequest(transparencyLevel);
+ public void SetWindowManagerAddShadowHint(bool enabled)
+ {
+ }
+
public WindowTransparencyLevel TransparencyLevel => _transparencyHelper.CurrentLevel;
}
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
index e266c5ee54..787a2e4cb8 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
@@ -176,7 +176,13 @@ namespace Avalonia.LinuxFramebuffer.Output
[DllImport(libdrm, SetLastError = true)]
public static extern int drmModeAddFB(int fd, uint width, uint height, byte depth,
byte bpp, uint pitch, uint bo_handle,
- out uint buf_id);
+ out uint buf_id);
+
+ [DllImport(libdrm, SetLastError = true)]
+ public static extern int drmModeAddFB2(int fd, uint width, uint height,
+ uint pixel_format, uint[] bo_handles, uint[] pitches,
+ uint[] offsets, out uint buf_id, uint flags);
+
[DllImport(libdrm, SetLastError = true)]
public static extern int drmModeSetCrtc(int fd, uint crtcId, uint bufferId,
uint x, uint y, uint *connectors, int count,
@@ -261,6 +267,8 @@ namespace Avalonia.LinuxFramebuffer.Output
[DllImport(libgbm, SetLastError = true)]
public static extern uint gbm_bo_get_stride(IntPtr bo);
+ [DllImport(libgbm, SetLastError = true)]
+ public static extern uint gbm_bo_get_format(IntPtr bo);
[StructLayout(LayoutKind.Explicit)]
public struct GbmBoHandle
@@ -278,7 +286,7 @@ namespace Avalonia.LinuxFramebuffer.Output
}
[DllImport(libgbm, SetLastError = true)]
- public static extern ulong gbm_bo_get_handle(IntPtr bo);
+ public static extern GbmBoHandle gbm_bo_get_handle(IntPtr bo);
public static class GbmColorFormats
{
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
index 0f5b66befb..7a5d20fc83 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
@@ -7,6 +7,8 @@ using Avalonia.OpenGL;
using Avalonia.Platform.Interop;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
using static Avalonia.LinuxFramebuffer.Output.LibDrm;
+using static Avalonia.LinuxFramebuffer.Output.LibDrm.GbmColorFormats;
+
namespace Avalonia.LinuxFramebuffer.Output
{
public unsafe class DrmOutput : IOutputBackend, IGlPlatformSurface, IWindowingPlatformGlFeature
@@ -71,11 +73,26 @@ namespace Avalonia.LinuxFramebuffer.Output
var w = gbm_bo_get_width(bo);
var h = gbm_bo_get_height(bo);
var stride = gbm_bo_get_stride(bo);
- var handle = gbm_bo_get_handle(bo);
+ var handle = gbm_bo_get_handle(bo).u32;
+ var format = gbm_bo_get_format(bo);
+
+ // prepare for the new ioctl call
+ var handles = new uint[] {handle, 0, 0, 0};
+ var pitches = new uint[] {stride, 0, 0, 0};
+ var offsets = new uint[] {};
+
+ var ret = drmModeAddFB2(_card.Fd, w, h, format, handles, pitches,
+ offsets, out var fbHandle, 0);
- var ret = drmModeAddFB(_card.Fd, w, h, 24, 32, stride, (uint)handle, out var fbHandle);
if (ret != 0)
- throw new Win32Exception(ret, "drmModeAddFb failed");
+ {
+ // legacy fallback
+ ret = drmModeAddFB(_card.Fd, w, h, 24, 32, stride, (uint)handle,
+ out fbHandle);
+
+ if (ret != 0)
+ throw new Win32Exception(ret, $"drmModeAddFb failed {ret}");
+ }
gbm_bo_set_user_data(bo, new IntPtr((int)fbHandle), FbDestroyDelegate);
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 67db56dd1b..49700710e9 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -6,6 +6,7 @@
+
-
+
diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs
index 504cefb9b3..efcf1ea674 100644
--- a/src/Windows/Avalonia.Win32/PopupImpl.cs
+++ b/src/Windows/Avalonia.Win32/PopupImpl.cs
@@ -7,6 +7,8 @@ namespace Avalonia.Win32
{
class PopupImpl : WindowImpl, IPopupImpl
{
+ private bool _dropShadowHint = true;
+
public override void Show()
{
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);
@@ -36,11 +38,7 @@ namespace Avalonia.Win32
IntPtr.Zero,
IntPtr.Zero);
- var classes = (int)UnmanagedMethods.GetClassLongPtr(result, (int)UnmanagedMethods.ClassLongIndex.GCL_STYLE);
-
- classes |= (int)UnmanagedMethods.ClassStyles.CS_DROPSHADOW;
-
- UnmanagedMethods.SetClassLong(result, UnmanagedMethods.ClassLongIndex.GCL_STYLE, new IntPtr(classes));
+ EnableBoxShadow(result, _dropShadowHint);
return result;
}
@@ -68,6 +66,32 @@ namespace Avalonia.Win32
//TODO: We ignore the scaling override for now
}
+ private void EnableBoxShadow (IntPtr hwnd, bool enabled)
+ {
+ var classes = (int)UnmanagedMethods.GetClassLongPtr(hwnd, (int)UnmanagedMethods.ClassLongIndex.GCL_STYLE);
+
+ if (enabled)
+ {
+ classes |= (int)UnmanagedMethods.ClassStyles.CS_DROPSHADOW;
+ }
+ else
+ {
+ classes &= ~(int)UnmanagedMethods.ClassStyles.CS_DROPSHADOW;
+ }
+
+ UnmanagedMethods.SetClassLong(hwnd, UnmanagedMethods.ClassLongIndex.GCL_STYLE, new IntPtr(classes));
+ }
+
+ public void SetWindowManagerAddShadowHint(bool enabled)
+ {
+ _dropShadowHint = enabled;
+
+ if (Handle != null)
+ {
+ EnableBoxShadow(Handle.Handle, enabled);
+ }
+ }
+
public IPopupPositioner PopupPositioner { get; }
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index e25c5e4733..e324febfd5 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -36,7 +36,7 @@ namespace Avalonia
public class Win32PlatformOptions
{
public bool UseDeferredRendering { get; set; } = true;
- public bool AllowEglInitialization { get; set; }
+ public bool AllowEglInitialization { get; set; } = true;
public bool? EnableMultitouch { get; set; }
public bool OverlayPopups { get; set; }
}
diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
index b5c61883e7..784f40fe1f 100644
--- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
+++ b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
@@ -2,6 +2,7 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
+using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
@@ -265,6 +266,70 @@ namespace Avalonia.Animation.UnitTests
}
}
+ [Fact]
+ public void Replacing_Transitions_During_Animation_Does_Not_Throw_KeyNotFound()
+ {
+ // Issue #4059
+ using (UnitTestApplication.Start(TestServices.RealStyler))
+ {
+ Border target;
+ var clock = new TestClock();
+ var root = new TestRoot
+ {
+ Clock = clock,
+ Styles =
+ {
+ new Style(x => x.OfType())
+ {
+ Setters =
+ {
+ new Setter(Border.TransitionsProperty,
+ new Transitions
+ {
+ new DoubleTransition
+ {
+ Property = Border.OpacityProperty,
+ Duration = TimeSpan.FromSeconds(1),
+ },
+ }),
+ },
+ },
+ new Style(x => x.OfType().Class("foo"))
+ {
+ Setters =
+ {
+ new Setter(Border.TransitionsProperty,
+ new Transitions
+ {
+ new DoubleTransition
+ {
+ Property = Border.OpacityProperty,
+ Duration = TimeSpan.FromSeconds(1),
+ },
+ }),
+ new Setter(Border.OpacityProperty, 0.0),
+ },
+ },
+ },
+ Child = target = new Border
+ {
+ Background = Brushes.Red,
+ }
+ };
+
+ root.Measure(Size.Infinity);
+ root.Arrange(new Rect(root.DesiredSize));
+
+ target.Classes.Add("foo");
+ clock.Step(TimeSpan.FromSeconds(0));
+ clock.Step(TimeSpan.FromSeconds(0.5));
+
+ Assert.Equal(0.5, target.Opacity);
+
+ target.Classes.Remove("foo");
+ }
+ }
+
private static Mock CreateTarget()
{
return CreateTransition(Visual.OpacityProperty);
diff --git a/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs
new file mode 100644
index 0000000000..0ff1f40121
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs
@@ -0,0 +1,37 @@
+using Avalonia.Controls.Mixins;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests.Mixins
+{
+ public class PressedMixinTests
+ {
+ private MouseTestHelper _mouse = new MouseTestHelper();
+
+ [Fact]
+ public void Selected_Class_Should_Not_Initially_Be_Added()
+ {
+ var target = new TestControl();
+
+ Assert.Empty(target.Classes);
+ }
+
+ [Fact]
+ public void Setting_IsSelected_Should_Add_Selected_Class()
+ {
+ var target = new TestControl();
+
+ _mouse.Down(target);
+
+ Assert.Equal(new[] { ":pressed" }, target.Classes);
+ }
+
+ private class TestControl : Control
+ {
+ static TestControl()
+ {
+ PressedMixin.Attach();
+ }
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 04d0ac0a7a..fe9c7b1261 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -994,7 +994,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Presenter.ApplyTemplate();
_helper.Down((Interactive)target.Presenter.Panel.Children[3]);
- Assert.Equal(new[] { ":selected" }, target.Presenter.Panel.Children[3].Classes);
+ Assert.Equal(new[] { ":pressed", ":selected" }, target.Presenter.Panel.Children[3].Classes);
}
[Fact]
diff --git a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
index 0d9fd31e52..a2c9f8b8cd 100644
--- a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
@@ -506,6 +506,26 @@ namespace Avalonia.Skia.UnitTests
}
}
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 0, 3)]
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 1, 1)]
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 4, 3)]
+ [Theory]
+ public void Should_Not_Exceed_MaxLines(string text, int maxLines, int expectedLines)
+ {
+ using (Start())
+ {
+ var layout = new TextLayout(
+ text,
+ Typeface.Default,
+ 12,
+ Brushes.Black,
+ maxWidth: 50,
+ maxLines: maxLines);
+
+ Assert.Equal(expectedLines, layout.TextLines.Count);
+ }
+ }
+
private const string Text = "日本でTest一番読まれている英字新聞・ジャパンタイムズが発信する国内外ニュースと、様々なジャンルの特集記事。";
[Fact(Skip= "Only used for profiling.")]