Feature(FloatButton): Add FloatButtonSeparatorLayer

This commit is contained in:
chinboy
2026-03-25 15:01:50 +08:00
parent a0d3ca5cdc
commit e642d1841d
12 changed files with 674 additions and 269 deletions
@@ -9,121 +9,135 @@
Title="Basic"
Description="The most basic usage.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonHost />
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost />
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Type"
Description="Change the type of the FloatButton with the type property.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonHost ButtonType="Default"
Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}"
FloatOffsetX="80" />
<atom:FloatButtonHost ButtonType="Primary"
Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost ButtonType="Default"
Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}"
FloatOffsetX="80" />
<atom:FloatButtonHost ButtonType="Primary"
Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Shape"
Description="Change the shape of the FloatButton with the shape property.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonHost ButtonType="Primary"
Shape="Circle"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
FloatOffsetX="80" />
<atom:FloatButtonHost ButtonType="Primary"
Shape="Square"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}" />
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost ButtonType="Primary"
Shape="Circle"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
FloatOffsetX="80" />
<atom:FloatButtonHost ButtonType="Primary"
Shape="Square"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}" />
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="FloatButton with tooltip"
Description="Setting the tooltip property shows the FloatButton with a tooltip.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonHost FloatOffsetX="80"
Tooltip="Since 5.25.0+" TooltipColor="blue" />
<atom:FloatButtonHost Tooltip="Documents" />
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost FloatOffsetX="80"
Tooltip="Since 5.25.0+" TooltipColor="blue" />
<atom:FloatButtonHost Tooltip="Documents" />
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Description"
Description="Setting the description property allows you to show a FloatButton with a description.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonHost Shape="Square"
Icon="{antdicons:AntDesignIconProvider FileTextOutlined}" Margin="0"
FloatOffsetX="145"
Description="HELP INFO" />
<atom:FloatButtonHost Shape="Square"
FloatOffsetX="80"
Description="HELP INFO" />
<atom:FloatButtonHost Shape="Square"
Icon="{antdicons:AntDesignIconProvider FileTextOutlined}"
Description="HELP INFO" />
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost Shape="Square"
Icon="{antdicons:AntDesignIconProvider FileTextOutlined}" Margin="0"
FloatOffsetX="145"
Description="HELP INFO" />
<atom:FloatButtonHost Shape="Square"
FloatOffsetX="80"
Description="HELP INFO" />
<atom:FloatButtonHost Shape="Square"
Icon="{antdicons:AntDesignIconProvider FileTextOutlined}"
Description="HELP INFO" />
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="FloatButton Group"
Description="When multiple buttons are used together, FloatButtonGroup is recommended. By setting the shape property of FloatButton.Group, you can change the shape of group. The shape of the FloatButton.Group will override the shape of FloatButtons inside.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Square" FloatOffsetX="80">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider SyncOutlined}" />
<atom:BackTopFloatButton />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider SyncOutlined}" />
<atom:BackTopFloatButton />
</atom:FloatButtonGroupHost>
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Square" FloatOffsetX="80">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider SyncOutlined}" />
<atom:BackTopFloatButton />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QuestionCircleOutlined}" />
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider SyncOutlined}" />
<atom:BackTopFloatButton />
</atom:FloatButtonGroupHost>
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Menu mode"
Description="Open menu mode with trigger, which could be hover or click.">
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Hover"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary"
FloatOffsetX="80">
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary">
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Hover"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary"
FloatOffsetX="80">
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary">
<atom:FloatButton />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Controlled mode"
Description="Set the component to controlled mode through open, which need to be used together with trigger.">
@@ -132,74 +146,110 @@
VerticalAlignment="Top"
IsChecked="{Binding IsOpened, Mode=TwoWay}" />
<atom:ScrollViewer Height="300">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Circle"
FloatOffsetX="80"
Trigger="Hover"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
IsOpen="{Binding IsOpened}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider TwitterOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
IsOpen="{Binding IsOpened}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider TwitterOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonGroupHost Shape="Square"
FloatOffsetX="80"
Trigger="Hover"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
IsOpen="{Binding IsOpened}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider TwitterOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
IsOpen="{Binding IsOpened}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider TwitterOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
</Border>
</atom:ScrollViewer>
</StackPanel>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Controlled mode"
Title="placement"
IsOccupyEntireRow="True"
Description="Set the component to controlled mode through open, which need to be used together with trigger.">
Description="Customize animation placement, providing four preset placement: top, right, bottom, left, the top position by default.">
<atom:ScrollViewer Height="400">
<Panel Height="600">
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Placement="Center"
MenuPlacement="Top"
FloatOffsetY="-60"
Icon="{antdicons:AntDesignIconProvider UpOutlined}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Placement="Center"
MenuPlacement="Left"
FloatOffsetX="-60"
Icon="{antdicons:AntDesignIconProvider LeftOutlined}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider DownOutlined}"
Placement="Center"
MenuPlacement="Bottom"
FloatOffsetY="60">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider RightOutlined}"
Placement="Center"
FloatOffsetX="60"
MenuPlacement="Right">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
<Border Background="White">
<Panel Height="600">
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Placement="Center"
MenuPlacement="Top"
FloatOffsetY="-60"
Icon="{antdicons:AntDesignIconProvider UpOutlined}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Placement="Center"
MenuPlacement="Left"
FloatOffsetX="-60"
Icon="{antdicons:AntDesignIconProvider LeftOutlined}">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider DownOutlined}"
Placement="Center"
MenuPlacement="Bottom"
FloatOffsetY="60">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Trigger="Click"
Icon="{antdicons:AntDesignIconProvider RightOutlined}"
Placement="Center"
FloatOffsetX="60"
MenuPlacement="Right">
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider WechatOutlined}" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider QqOutlined}" />
</atom:FloatButtonGroupHost>
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="badge"
Description="FloatButton with Badge.">
<atom:ScrollViewer Height="300">
<Border Background="White">
<Panel Height="500">
<atom:FloatButtonHost FloatOffsetX="145" IsBadgeEnabled="True" IsDotBadge="True" />
<atom:FloatButtonGroupHost Shape="Circle"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary"
FloatOffsetX="80">
<atom:FloatButton IsBadgeEnabled="True" IsDotBadge="False" BadgeCount="5" BadgeColor="blue" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}"
IsBadgeEnabled="True" IsDotBadge="False" BadgeCount="5" />
</atom:FloatButtonGroupHost>
<atom:FloatButtonGroupHost Shape="Circle"
Icon="{antdicons:AntDesignIconProvider CustomerServiceOutlined}"
ButtonType="Primary">
<atom:FloatButton IsBadgeEnabled="True" IsDotBadge="False" BadgeCount="12" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider CommentOutlined}"
IsBadgeEnabled="True" IsDotBadge="False" BadgeCount="123" />
<atom:FloatButton Icon="{antdicons:AntDesignIconProvider ToTopOutlined}"
IsBadgeEnabled="True" IsDotBadge="False" BadgeCount="1230"
BadgeOverflowCount="" />
</atom:FloatButtonGroupHost>
</Panel>
</Border>
</atom:ScrollViewer>
</gallery:ShowCaseItem>
</gallery:ShowCasePanel>
@@ -0,0 +1,30 @@
using AtomUI.Theme.Palette;
using Avalonia.Media;
namespace AtomUI.Desktop.Controls;
internal static class BadgeColorUtils
{
internal static IBrush? CalculateColor(string? colorStr)
{
if (string.IsNullOrEmpty(colorStr))
{
return null;
}
colorStr = colorStr.Trim().ToLower();
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
{
if (presetColor.Type.ToString().ToLower() == colorStr)
{
return new SolidColorBrush(presetColor.Color());
}
}
if (Color.TryParse(colorStr, out var color))
{
return new SolidColorBrush(color);
}
return null;
}
}
@@ -2,12 +2,10 @@
using AtomUI.Controls;
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Palette;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.VisualTree;
@@ -147,7 +145,7 @@ public class CountBadge : Control,
HandleDecoratedTargetChanged();
if (BadgeColor is not null)
{
SetupBadgeColor(BadgeColor);
ConfigureDotColor(BadgeColor);
}
}
@@ -275,7 +273,7 @@ public class CountBadge : Control,
if (change.Property == BadgeColorProperty)
{
SetupBadgeColor(change.GetNewValue<string>());
ConfigureDotColor(change.GetNewValue<string>());
}
}
@@ -297,23 +295,9 @@ public class CountBadge : Control,
BadgeIsVisible = true;
}
}
private void SetupBadgeColor(string colorStr)
private void ConfigureDotColor(string colorStr)
{
colorStr = colorStr.Trim().ToLower();
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
{
if (presetColor.Type.ToString().ToLower() == colorStr)
{
_badgeAdorner!.BadgeColor = new SolidColorBrush(presetColor.Color());
return;
}
}
if (Color.TryParse(colorStr, out var color))
{
_badgeAdorner!.BadgeColor = new SolidColorBrush(color);
}
_badgeAdorner!.BadgeColor = BadgeColorUtils.CalculateColor(colorStr);
}
}
+11 -27
View File
@@ -2,13 +2,11 @@
using AtomUI.Controls;
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Palette;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.VisualTree;
@@ -29,17 +27,16 @@ public class DotBadge : Control,
{
#region 公共属性定义
public static readonly StyledProperty<string?> DotColorProperty
= AvaloniaProperty.Register<DotBadge, string?>(
public static readonly StyledProperty<string?> DotColorProperty =
AvaloniaProperty.Register<DotBadge, string?>(
nameof(DotColor));
public static readonly StyledProperty<DotBadgeStatus?> StatusProperty
= AvaloniaProperty.Register<DotBadge, DotBadgeStatus?>(
public static readonly StyledProperty<DotBadgeStatus?> StatusProperty =
AvaloniaProperty.Register<DotBadge, DotBadgeStatus?>(
nameof(Status));
public static readonly StyledProperty<string?> TextProperty
= AvaloniaProperty.Register<DotBadge, string?>(
nameof(Text));
public static readonly StyledProperty<string?> TextProperty =
AvaloniaProperty.Register<DotBadge, string?>(nameof(Text));
public static readonly StyledProperty<Control?> DecoratedTargetProperty =
AvaloniaProperty.Register<DotBadge, Control?>(nameof(DecoratedTarget));
@@ -132,7 +129,7 @@ public class DotBadge : Control,
HandleDecoratedTargetChanged();
if (DotColor is not null)
{
SetupDotColor(DotColor);
ConfigureDotColor(DotColor);
}
}
@@ -256,27 +253,14 @@ public class DotBadge : Control,
if (change.Property == DotColorProperty)
{
SetupDotColor(change.GetNewValue<string>());
ConfigureDotColor(change.GetNewValue<string>());
}
}
}
private void SetupDotColor(string colorStr)
private void ConfigureDotColor(string colorStr)
{
colorStr = colorStr.Trim().ToLower();
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
{
if (presetColor.Type.ToString().ToLower() == colorStr)
{
_dotBadgeAdorner!.BadgeDotColor = new SolidColorBrush(presetColor.Color());
return;
}
}
if (Color.TryParse(colorStr, out var color))
{
_dotBadgeAdorner!.BadgeDotColor = new SolidColorBrush(color);
}
_dotBadgeAdorner!.BadgeDotColor = BadgeColorUtils.CalculateColor(colorStr);
}
}
@@ -7,6 +7,7 @@ using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Media;
@@ -66,6 +67,26 @@ public class FloatButton : AvaloniaButton,
public static readonly StyledProperty<bool> IsMotionEnabledProperty =
MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<FloatButton>();
public static readonly StyledProperty<bool> IsBadgeEnabledProperty =
AvaloniaProperty.Register<FloatButton, bool>(nameof(IsBadgeEnabled));
public static readonly StyledProperty<bool> IsDotBadgeProperty =
AvaloniaProperty.Register<FloatButton, bool>(nameof(IsDotBadge));
public static readonly StyledProperty<int> BadgeCountProperty =
AvaloniaProperty.Register<FloatButton, int>(nameof(BadgeCount));
public static readonly StyledProperty<string?> BadgeColorProperty =
AvaloniaProperty.Register<FloatButton, string?>(
nameof(BadgeColor));
public static readonly StyledProperty<Point> BadgeOffsetProperty =
AvaloniaProperty.Register<FloatButton, Point>(nameof(BadgeOffset));
public static readonly StyledProperty<int> BadgeOverflowCountProperty =
AvaloniaProperty.Register<FloatButton, int>(nameof(BadgeOverflowCount), 99,
coerce: (o, v) => Math.Max(0, v));
public FloatButtonPlacement Placement
{
get => GetValue(PlacementProperty);
@@ -131,7 +152,42 @@ public class FloatButton : AvaloniaButton,
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
public bool IsBadgeEnabled
{
get => GetValue(IsBadgeEnabledProperty);
set => SetValue(IsBadgeEnabledProperty, value);
}
public bool IsDotBadge
{
get => GetValue(IsDotBadgeProperty);
set => SetValue(IsDotBadgeProperty, value);
}
public int BadgeCount
{
get => GetValue(BadgeCountProperty);
set => SetValue(BadgeCountProperty, value);
}
public string? BadgeColor
{
get => GetValue(BadgeColorProperty);
set => SetValue(BadgeColorProperty, value);
}
public Point BadgeOffset
{
get => GetValue(BadgeOffsetProperty);
set => SetValue(BadgeOffsetProperty, value);
}
public int BadgeOverflowCount
{
get => GetValue(BadgeOverflowCountProperty);
set => SetValue(BadgeOverflowCountProperty, value);
}
#endregion
#region 内部属性定义
@@ -140,6 +196,10 @@ public class FloatButton : AvaloniaButton,
nameof(IsEmbedMode),
o => o.IsEmbedMode,
(o, v) => o.IsEmbedMode = v);
internal static readonly StyledProperty<IBrush?> BadgeEffectiveColorProperty =
AvaloniaProperty.Register<FloatButton, IBrush?>(
nameof(BadgeEffectiveColor));
private bool _isEmbedMode;
@@ -149,12 +209,20 @@ public class FloatButton : AvaloniaButton,
set => SetAndRaise(IsEmbedModeProperty, ref _isEmbedMode, value);
}
internal IBrush? BadgeEffectiveColor
{
get => GetValue(BadgeEffectiveColorProperty);
set => SetValue(BadgeEffectiveColorProperty, value);
}
Control IControlSharedTokenResourcesHost.HostControl => this;
string IControlSharedTokenResourcesHost.TokenId => FloatButtonToken.ID;
#endregion
ScopeAwareOverlayLayer? _overlayLayer;
private ScopeAwareOverlayLayer? _overlayLayer;
private Canvas? _badgeLayout;
private Control? _badge;
public FloatButton()
{
@@ -174,6 +242,14 @@ public class FloatButton : AvaloniaButton,
{
CalculatePosition(this, _overlayLayer.Bounds.Size, Placement, FloatOffsetX, FloatOffsetY);
}
CalculateBadgePosition();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_badgeLayout = e.NameScope.Find<Canvas>("BadgeLayout");
ConfigureBadge();
}
protected override Size MeasureOverride(Size availableSize)
@@ -227,6 +303,20 @@ public class FloatButton : AvaloniaButton,
CalculatePosition(this, _overlayLayer.Bounds.Size, Placement, FloatOffsetX, FloatOffsetY);
}
}
else if (change.Property == IsBadgeEnabledProperty ||
change.Property == IsDotBadgeProperty)
{
ConfigureBadge();
}
else if (change.Property == BadgeOffsetProperty)
{
CalculateBadgePosition();
}
else if (change.Property == BadgeColorProperty)
{
SetCurrentValue(BadgeEffectiveColorProperty, BadgeColorUtils.CalculateColor(BadgeColor));
}
}
protected override void OnInitialized()
@@ -309,6 +399,56 @@ public class FloatButton : AvaloniaButton,
CalculatePosition(this, e.NewSize, Placement, FloatOffsetX, FloatOffsetY);
}
private void ConfigureBadge()
{
if (_badgeLayout != null)
{
if (IsBadgeEnabled)
{
if (IsDotBadge)
{
var dotBadge = new DotBadgeAdorner();
dotBadge[!DotBadgeAdorner.BadgeDotColorProperty] = this[!BadgeEffectiveColorProperty];
dotBadge[!DotBadgeAdorner.IsMotionEnabledProperty] = this[!IsMotionEnabledProperty];
_badge = dotBadge;
}
else
{
var countBadge = new CountBadgeAdorner();
countBadge[!CountBadgeAdorner.BadgeColorProperty] = this[!BadgeEffectiveColorProperty];
countBadge[!CountBadgeAdorner.IsMotionEnabledProperty] = this[!IsMotionEnabledProperty];
countBadge[!CountBadgeAdorner.CountProperty] = this[!BadgeCountProperty];
countBadge[!CountBadgeAdorner.OverflowCountProperty] = this[!BadgeOverflowCountProperty];
_badge = countBadge;
}
_badgeLayout.Children.Add(_badge);
CalculateBadgePosition();
}
else
{
if (_badge != null)
{
_badgeLayout.Children.Remove(_badge);
}
_badge = null;
}
}
}
private void CalculateBadgePosition()
{
if (IsBadgeEnabled && _badge != null)
{
var targetOffsetX = DesiredSize.Width;
var targetOffsetY = 0.0d;
targetOffsetX += BadgeOffset.X;
targetOffsetY += BadgeOffset.Y;
Canvas.SetLeft(_badge, targetOffsetX);
Canvas.SetTop(_badge, targetOffsetY);
}
}
internal static void CalculatePosition(Control floatControl,
Size layerSize,
FloatButtonPlacement placement,
@@ -278,6 +278,11 @@ public class FloatButtonGroup : TemplatedControl,
_triggerButton.PointerExited += HandlePointerExited;
}
if (_itemsControl != null)
{
_itemsControl.IsTriggerMode = Trigger != FloatButtonGroupTrigger.Default;
}
_itemsControl?.Children.AddRange(Children);
ConfigureTriggerType();
foreach (var item in Children)
@@ -404,6 +409,11 @@ public class FloatButtonGroup : TemplatedControl,
var inputManager = AvaloniaLocator.Current.GetService<IInputManager>()!;
_clickTriggerDisposable = inputManager.Process.Subscribe(HandleMouseClick);
}
if (_itemsControl != null)
{
_itemsControl.IsTriggerMode = Trigger != FloatButtonGroupTrigger.Default;
}
}
private void HandleMouseClick(RawInputEventArgs args)
@@ -54,6 +54,24 @@ public class FloatButtonHost : TemplatedControl,
public static readonly StyledProperty<bool> IsMotionEnabledProperty =
FloatButton.IsMotionEnabledProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<bool> IsBadgeEnabledProperty =
FloatButton.IsBadgeEnabledProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<bool> IsDotBadgeProperty =
FloatButton.IsDotBadgeProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<int> BadgeCountProperty =
FloatButton.BadgeCountProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<string?> BadgeColorProperty =
FloatButton.BadgeColorProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<Point> BadgeOffsetProperty =
FloatButton.BadgeOffsetProperty.AddOwner<FloatButtonHost>();
public static readonly StyledProperty<int> BadgeOverflowCountProperty =
FloatButton.BadgeOverflowCountProperty.AddOwner<FloatButtonHost>();
public FloatButtonPlacement Placement
{
get => GetValue(PlacementProperty);
@@ -126,6 +144,42 @@ public class FloatButtonHost : TemplatedControl,
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
public bool IsBadgeEnabled
{
get => GetValue(IsBadgeEnabledProperty);
set => SetValue(IsBadgeEnabledProperty, value);
}
public bool IsDotBadge
{
get => GetValue(IsDotBadgeProperty);
set => SetValue(IsDotBadgeProperty, value);
}
public int BadgeCount
{
get => GetValue(BadgeCountProperty);
set => SetValue(BadgeCountProperty, value);
}
public string? BadgeColor
{
get => GetValue(BadgeColorProperty);
set => SetValue(BadgeColorProperty, value);
}
public Point BadgeOffset
{
get => GetValue(BadgeOffsetProperty);
set => SetValue(BadgeOffsetProperty, value);
}
public int BadgeOverflowCount
{
get => GetValue(BadgeOverflowCountProperty);
set => SetValue(BadgeOverflowCountProperty, value);
}
#endregion
#region 内部属性定义
@@ -189,7 +243,16 @@ public class FloatButtonHost : TemplatedControl,
disposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, floatButton, IsMotionEnabledProperty));
disposables.Add(BindUtils.RelayBind(this, PlacementProperty, floatButton, PlacementProperty));
disposables.Add(BindUtils.RelayBind(this, FloatOffsetXProperty, floatButton, FloatOffsetXProperty));
disposables.Add(BindUtils.RelayBind(this, FloatOffsetXProperty, floatButton, FloatOffsetXProperty));
disposables.Add(BindUtils.RelayBind(this, FloatOffsetYProperty, floatButton, FloatOffsetYProperty));
disposables.Add(BindUtils.RelayBind(this, DescriptionProperty, floatButton, FloatButton.ContentProperty));
disposables.Add(BindUtils.RelayBind(this, DescriptionTemplateProperty, floatButton, FloatButton.ContentTemplateProperty));
disposables.Add(BindUtils.RelayBind(this, IsBadgeEnabledProperty, floatButton, IsBadgeEnabledProperty));
disposables.Add(BindUtils.RelayBind(this, IsDotBadgeProperty, floatButton, IsDotBadgeProperty));
disposables.Add(BindUtils.RelayBind(this, BadgeCountProperty, floatButton, BadgeCountProperty));
disposables.Add(BindUtils.RelayBind(this, BadgeColorProperty, floatButton, BadgeColorProperty));
disposables.Add(BindUtils.RelayBind(this, BadgeOffsetProperty, floatButton, BadgeOffsetProperty));
disposables.Add(BindUtils.RelayBind(this, BadgeOverflowCountProperty, floatButton, BadgeOverflowCountProperty));
return floatButton;
}
}
@@ -23,11 +23,11 @@ internal class FloatButtonItemsControl : TemplatedControl
public static readonly StyledProperty<Orientation> OrientationProperty =
StackPanel.OrientationProperty.AddOwner<FloatButtonItemsControl>();
public static readonly StyledProperty<IBrush?> SeparatorBrushProperty =
AvaloniaProperty.Register<FloatButtonItemsControl, IBrush?>(nameof(SeparatorBrush));
public static readonly StyledProperty<FloatButtonGroupMenuPlacement> MenuPlacementProperty =
FloatButtonGroup.MenuPlacementProperty.AddOwner<FloatButtonItemsControl>();
public static readonly StyledProperty<bool> IsTriggerModeProperty =
AvaloniaProperty.Register<FloatButtonGroup, bool>(nameof(IsTriggerMode));
public FloatButtonShape Shape
{
@@ -47,28 +47,40 @@ internal class FloatButtonItemsControl : TemplatedControl
set => SetValue(OrientationProperty, value);
}
public IBrush? SeparatorBrush
{
get => GetValue(SeparatorBrushProperty);
set => SetValue(SeparatorBrushProperty, value);
}
public FloatButtonGroupMenuPlacement MenuPlacement
{
get => GetValue(MenuPlacementProperty);
set => SetValue(MenuPlacementProperty, value);
}
public bool IsTriggerMode
{
get => GetValue(IsTriggerModeProperty);
set => SetValue(IsTriggerModeProperty, value);
}
[Content]
public ControlList Children { get; } = new ();
#endregion
#region 内部属性定义
internal static readonly StyledProperty<IList<(Point, Point)>?> LinesProperty =
AvaloniaProperty.Register<FloatButtonItemsControl, IList<(Point, Point)>?>(nameof(Lines));
internal IList<(Point, Point)>? Lines
{
get => GetValue(LinesProperty);
set => SetValue(LinesProperty, value);
}
#endregion
private StackPanel? _itemsLayout;
static FloatButtonItemsControl()
{
AffectsRender<FloatButtonItemsControl>(SeparatorBrushProperty);
AffectsMeasure<FloatButtonItemsControl>(ShapeProperty, OrientationProperty);
}
@@ -117,22 +129,22 @@ internal class FloatButtonItemsControl : TemplatedControl
}
}
public override void Render(DrawingContext context)
protected override Size ArrangeOverride(Size finalSize)
{
if (Shape == FloatButtonShape.Square && SeparatorBrush != null)
var size = base.ArrangeOverride(finalSize);
if (Shape == FloatButtonShape.Square)
{
var lines = new List<(Point, Point)>();
var children = Children.Where(c => c.IsVisible).ToList();
var count = children.Count;
// TODO 没有处理中间隐藏的
Point startPoint = default;
Point endPoint = default;
var pen = new Pen(SeparatorBrush);
for (var i = 0; i < count; ++i)
{
if (i != count - 1)
{
var child = children[i];
var childPos = child.TranslatePoint(new Point(0, 0), this);
var child = children[i];
var childPos = child.TranslatePoint(new Point(0, 0), this);
if (childPos != null)
{
var childBounds = child.Bounds;
@@ -146,10 +158,13 @@ internal class FloatButtonItemsControl : TemplatedControl
startPoint = new Point(0, childPos.Value.Y + childBounds.Height);
endPoint = new Point(DesiredSize.Width, childPos.Value.Y + childBounds.Height);
}
context.DrawLine(pen, startPoint, endPoint);
lines.Add((startPoint, endPoint));
}
}
}
Lines = lines;
}
return size;
}
}
@@ -0,0 +1,56 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Desktop.Controls;
internal class FloatButtonSeparatorLayer : Control
{
#region 公共属性定义
public static readonly StyledProperty<IList<(Point, Point)>?> LinesProperty =
AvaloniaProperty.Register<FloatButtonSeparatorLayer, IList<(Point, Point)>?>(nameof(Lines));
public static readonly StyledProperty<Orientation> OrientationProperty =
StackPanel.OrientationProperty.AddOwner<FloatButtonSeparatorLayer>();
public static readonly StyledProperty<IBrush?> SeparatorBrushProperty =
AvaloniaProperty.Register<FloatButtonSeparatorLayer, IBrush?>(nameof(SeparatorBrush));
public IList<(Point, Point)>? Lines
{
get => GetValue(LinesProperty);
set => SetValue(LinesProperty, value);
}
public Orientation Orientation
{
get => GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public IBrush? SeparatorBrush
{
get => GetValue(SeparatorBrushProperty);
set => SetValue(SeparatorBrushProperty, value);
}
#endregion
static FloatButtonSeparatorLayer()
{
AffectsRender<FloatButtonSeparatorLayer>(LinesProperty, OrientationProperty, SeparatorBrushProperty);
}
public override void Render(DrawingContext context)
{
if (SeparatorBrush != null && Lines != null)
{
var pen = new Pen(SeparatorBrush);
foreach (var line in Lines)
{
context.DrawLine(pen, line.Item1, line.Item2);
}
}
}
}
@@ -8,5 +8,18 @@
<Setter Property="Placement" Value="BottomRight" />
<Setter Property="FloatOffsetX" Value="{DynamicResource {x:Static atom:FloatButtonTokenKey.FloatOffsetX}}" />
<Setter Property="FloatOffsetY" Value="{DynamicResource {x:Static atom:FloatButtonTokenKey.FloatOffsetY}}" />
<Style Selector="^[Shape=Circle][IsDotBadge=True]">
<Setter Property="BadgeOffset" Value="-10, 4" />
</Style>
<Style Selector="^[Shape=Circle][IsDotBadge=False]">
<Setter Property="BadgeOffset" Value="-10, -2" />
</Style>
<Style Selector="^[Shape=Square][IsDotBadge=True]">
<Setter Property="BadgeOffset" Value="-5, -1" />
</Style>
<Style Selector="^[Shape=Square][IsDotBadge=False]">
<Setter Property="BadgeOffset" Value="-10, -5" />
</Style>
</ControlTheme>
</ResourceDictionary>
@@ -4,59 +4,89 @@
xmlns:atom="https://atomui.net"
x:ClassModifier="internal">
<ControlTheme x:Key="{x:Type atom:FloatButtonItemsControl}" TargetType="atom:FloatButtonItemsControl">
<Setter Property="Template">
<ControlTemplate>
<Border Name="Frame" Background="Transparent">
<DockPanel Name="RootLayout" LastChildFill="True">
<Control Name="EmptyControl" />
<Border Name="ContentFrame"
CornerRadius="{TemplateBinding CornerRadius}"
BoxShadow="{TemplateBinding BoxShadow}"
Background="{TemplateBinding Background}">
<Style Selector="^[IsTriggerMode=True]">
<Setter Property="Template">
<ControlTemplate>
<Border Name="Frame" Background="Transparent">
<DockPanel Name="RootLayout" LastChildFill="True">
<Control Name="EmptyControl" />
<Border Name="ContentFrame"
CornerRadius="{TemplateBinding CornerRadius}"
BoxShadow="{TemplateBinding BoxShadow}"
Background="{TemplateBinding Background}">
<Panel>
<atom:FloatButtonSeparatorLayer Orientation="{TemplateBinding Orientation}"
Lines="{TemplateBinding Lines}" />
<StackPanel Name="ItemsLayout" Orientation="{TemplateBinding Orientation}" />
</Panel>
</Border>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="^[IsTriggerMode=False]">
<Setter Property="Template">
<ControlTemplate>
<Border Name="ContentFrame"
CornerRadius="{TemplateBinding CornerRadius}"
BoxShadow="{TemplateBinding BoxShadow}"
Background="{TemplateBinding Background}">
<Panel>
<atom:FloatButtonSeparatorLayer Orientation="{TemplateBinding Orientation}"
Lines="{TemplateBinding Lines}" />
<StackPanel Name="ItemsLayout" Orientation="{TemplateBinding Orientation}" />
</Border>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Setter Property="ClipToBounds" Value="False" />
<Style Selector="^[Shape=Square]">
<Setter Property="Background" Value="Transparent" />
<Setter Property="SeparatorBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorSplit}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgElevated}}" />
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:SharedTokenKey.BoxShadowsSecondary}}" />
<Setter Property="CornerRadius" Value="{DynamicResource {x:Static atom:SharedTokenKey.BorderRadiusLG}}" />
</Style>
<Style Selector="^ /template/ atom|FloatButtonSeparatorLayer">
<Setter Property="SeparatorBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorSplit}}" />
</Style>
<Style Selector="^[Shape=Circle]">
<Style Selector="^ /template/ StackPanel#ItemsLayout">
<Setter Property="Spacing" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
</Style>
</Style>
<Style Selector="^[MenuPlacement=Top]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Bottom" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
<Style Selector="^ /template/ atom|FloatButtonSeparatorLayer">
<Setter Property="IsVisible" Value="False" />
</Style>
</Style>
<Style Selector="^[MenuPlacement=Bottom]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Top" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
<Style Selector="^[IsTriggerMode=True]">
<Style Selector="^[MenuPlacement=Top]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Bottom" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
</Style>
</Style>
</Style>
<Style Selector="^[MenuPlacement=Left]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Right" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
<Style Selector="^[MenuPlacement=Bottom]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Top" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
</Style>
</Style>
</Style>
<Style Selector="^[MenuPlacement=Right]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Left" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
<Style Selector="^[MenuPlacement=Left]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Right" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
</Style>
</Style>
<Style Selector="^[MenuPlacement=Right]">
<Style Selector="^ /template/ Control#EmptyControl">
<Setter Property="DockPanel.Dock" Value="Left" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:SharedTokenKey.Spacing}}" />
</Style>
</Style>
</Style>
@@ -7,22 +7,27 @@
<Style Selector="^[Shape=Square]">
<Setter Property="Template">
<ControlTemplate TargetType="atom:FloatButton">
<Border Name="Frame"
Padding="{TemplateBinding Padding}"
BoxShadow="{TemplateBinding BoxShadow}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Cursor="{TemplateBinding Cursor}">
<StackPanel Name="RootLayout">
<atom:IconPresenter Name="IconPresenter" Icon="{TemplateBinding Icon}"
IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}" />
<ContentPresenter Content="{TemplateBinding Content}"
TextWrapping="Wrap"
HorizontalContentAlignment="Center"
ContentTemplate="{TemplateBinding ContentTemplate}"
IsVisible="{TemplateBinding Content, Converter={x:Static ObjectConverters.IsNotNull}}" />
</StackPanel>
</Border>
<Panel>
<Border Name="BackgroundFrame"
BoxShadow="{TemplateBinding BoxShadow}"
CornerRadius="{TemplateBinding CornerRadius}"/>
<Border Name="Frame"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Cursor="{TemplateBinding Cursor}">
<StackPanel Name="RootLayout">
<atom:IconPresenter Name="IconPresenter" Icon="{TemplateBinding Icon}"
IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}" />
<ContentPresenter Content="{TemplateBinding Content}"
TextWrapping="Wrap"
HorizontalContentAlignment="Center"
ContentTemplate="{TemplateBinding ContentTemplate}"
IsVisible="{TemplateBinding Content, Converter={x:Static ObjectConverters.IsNotNull}}" />
</StackPanel>
</Border>
<Canvas Name="BadgeLayout"/>
</Panel>
</ControlTemplate>
</Setter>
</Style>
@@ -30,28 +35,53 @@
<Style Selector="^[Shape=Circle]">
<Setter Property="Template">
<ControlTemplate TargetType="atom:FloatButton">
<Border Name="Frame"
Padding="{TemplateBinding Padding}"
BoxShadow="{TemplateBinding BoxShadow}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Cursor="{TemplateBinding Cursor}">
<atom:IconPresenter Name="IconPresenter" Icon="{TemplateBinding Icon}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<Panel>
<Border Name="BackgroundFrame"
BoxShadow="{TemplateBinding BoxShadow}"
CornerRadius="{TemplateBinding CornerRadius}"/>
<Border Name="Frame"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Cursor="{TemplateBinding Cursor}">
<atom:IconPresenter Name="IconPresenter"
Icon="{TemplateBinding Icon}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<Canvas Name="BadgeLayout"/>
</Panel>
</ControlTemplate>
</Setter>
</Style>
<Setter Property="ClipToBounds" Value="False" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="BadgeEffectiveColor" Value="{DynamicResource {x:Static atom:BadgeTokenKey.BadgeColor}}" />
<Setter Property="IsMotionEnabled" Value="{DynamicResource {x:Static atom:SharedTokenKey.EnableMotion}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgContainer}}" />
<Setter Property="Background" Value="Transparent" />
<Style Selector="^[IsEmbedMode=False], ^[IsEmbedMode=True][Shape=Circle]">
<Style Selector="^ /template/ Border#BackgroundFrame">
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgElevated}}" />
</Style>
</Style>
<Setter Property="FontSize" Value="{DynamicResource {x:Static atom:SharedTokenKey.FontSizeSM}}" />
<Setter Property="ZIndex" Value="{DynamicResource {x:Static atom:SharedTokenKey.ZIndexPopupBase}}" />
<Setter Property="Padding" Value="{DynamicResource {x:Static atom:SharedTokenKey.PaddingXXS}}" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:FloatButtonTokenKey.FloatButtonSize}}" />
<Style Selector="^[Shape=Circle][IsDotBadge=True]">
<Setter Property="BadgeOffset" Value="-10, 4" />
</Style>
<Style Selector="^[Shape=Circle][IsDotBadge=False]">
<Setter Property="BadgeOffset" Value="-10, -2" />
</Style>
<Style Selector="^[Shape=Square][IsDotBadge=True]">
<Setter Property="BadgeOffset" Value="-5, -1" />
</Style>
<Style Selector="^[Shape=Square][IsDotBadge=False]">
<Setter Property="BadgeOffset" Value="-10, -5" />
</Style>
<Style Selector="^[IsEmbedMode=False]">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:SharedTokenKey.BoxShadowsSecondary}}" />
@@ -88,7 +118,7 @@
</Style>
<Style Selector="^ /template/ atom|IconPresenter#IconPresenter">
<Setter Property="IconBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorIconHover}}" />
<Setter Property="IconBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorText}}" />
<Setter Property="Width" Value="{DynamicResource {x:Static atom:FloatButtonTokenKey.FloatButtonIconSize}}" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:FloatButtonTokenKey.FloatButtonIconSize}}" />
</Style>