mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-22 00:46:48 +08:00
Add DataGridMenuFilterFlyout
This commit is contained in:
@@ -114,16 +114,14 @@ internal class AbstractMotion : IMotion
|
||||
}
|
||||
else
|
||||
{
|
||||
observables.Zip()
|
||||
.LastAsync()
|
||||
.ObserveOn(AvaloniaScheduler.Instance)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
FinishedCallback();
|
||||
});
|
||||
});
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await observables.Zip()
|
||||
.LastAsync()
|
||||
.ObserveOn(AvaloniaScheduler.Instance)
|
||||
.ToTask();
|
||||
FinishedCallback();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,5 +16,7 @@
|
||||
<ResourceInclude Source="Themes/DataGridSortIndicatorTheme.axaml"/>
|
||||
<ResourceInclude Source="Themes/DataGridTheme.axaml"/>
|
||||
<ResourceInclude Source="Themes/DataGridTopLeftColumnHeaderTheme.axaml"/>
|
||||
<ResourceInclude Source="Themes/Filters/DataGridFilterMenuItemTheme.axaml"/>
|
||||
<ResourceInclude Source="Themes/Filters/DataGridMenuFilterFlyoutPresenterTheme.axaml"/>
|
||||
</atom:ControlThemesProvider.ControlThemes>
|
||||
</atom:ControlThemesProvider>
|
||||
@@ -249,7 +249,7 @@ public abstract partial class DataGridColumn : AvaloniaObject
|
||||
public bool CanUserFilter
|
||||
{
|
||||
get => CanUserResizeInternal ??
|
||||
OwningGrid?.CanUserResizeColumns ??
|
||||
OwningGrid?.CanUserFilterColumns ??
|
||||
DataGrid.DefaultCanUserResizeColumns;
|
||||
set
|
||||
{
|
||||
@@ -424,6 +424,11 @@ public abstract partial class DataGridColumn : AvaloniaObject
|
||||
/// </summary>
|
||||
public DataGridFilterMode FilterMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether multiple filters can be selected
|
||||
/// </summary>
|
||||
public bool FilterMultiple { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to trigger filter when the filter menu closes
|
||||
/// </summary>
|
||||
|
||||
@@ -174,9 +174,21 @@ internal class DataGridColumnHeader : ContentControl
|
||||
get => _canUserFilter;
|
||||
set => SetAndRaise(CanUserFilterProperty, ref _canUserFilter, value);
|
||||
}
|
||||
|
||||
internal DataGridColumn? OwningColumn { get; set; }
|
||||
|
||||
|
||||
private DataGridColumn? _owningColumn;
|
||||
internal DataGridColumn? OwningColumn
|
||||
{
|
||||
get => _owningColumn;
|
||||
set
|
||||
{
|
||||
_owningColumn = value;
|
||||
if (_filterIndicator != null)
|
||||
{
|
||||
_filterIndicator.OwningColumn = OwningColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGrid? OwningGrid => OwningColumn?.OwningGrid;
|
||||
|
||||
internal int ColumnIndex
|
||||
@@ -212,6 +224,7 @@ internal class DataGridColumnHeader : ContentControl
|
||||
private bool _desiredSeparatorVisibility = true;
|
||||
private StackPanel? _indicatorsLayout;
|
||||
private static Lazy<Cursor> _resizeCursor = new Lazy<Cursor>(() => new Cursor(StandardCursorType.SizeWestEast));
|
||||
private DataGridFilterIndicator? _filterIndicator;
|
||||
|
||||
static DataGridColumnHeader()
|
||||
{
|
||||
@@ -971,6 +984,11 @@ internal class DataGridColumnHeader : ContentControl
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
_indicatorsLayout = e.NameScope.Find<StackPanel>(DataGridColumnHeaderThemeConstants.IndicatorsLayoutPart);
|
||||
_filterIndicator = e.NameScope.Find<DataGridFilterIndicator>(DataGridColumnHeaderThemeConstants.FilterIndicatorPart);
|
||||
if (_filterIndicator != null && OwningColumn != null)
|
||||
{
|
||||
_filterIndicator.OwningColumn = OwningColumn;
|
||||
}
|
||||
if (_indicatorsLayout != null)
|
||||
{
|
||||
_indicatorsLayout.Children.CollectionChanged += HandleIndicatorLayoutChildrenChanged;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.IconPkg.AntDesign;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
|
||||
@@ -12,13 +15,83 @@ internal class DataGridFilterIndicator : IconButton
|
||||
public static readonly StyledProperty<bool> IsFilterActivatedProperty
|
||||
= AvaloniaProperty.Register<DataGridFilterIndicator, bool>(nameof(IsFilterActivated));
|
||||
|
||||
public static readonly DirectProperty<DataGridFilterIndicator, DataGridFilterMode> FilterModeProperty =
|
||||
AvaloniaProperty.RegisterDirect<DataGridFilterIndicator, DataGridFilterMode>(
|
||||
nameof(FilterMode),
|
||||
o => o.FilterMode,
|
||||
(o, v) => o.FilterMode = v);
|
||||
|
||||
public static readonly StyledProperty<MenuItemToggleType> ToggleTypeProperty =
|
||||
MenuItem.ToggleTypeProperty.AddOwner<DataGridFilterIndicator>();
|
||||
|
||||
public bool IsFilterActivated
|
||||
{
|
||||
get => GetValue(IsFilterActivatedProperty);
|
||||
set => SetValue(IsFilterActivatedProperty, value);
|
||||
}
|
||||
|
||||
private DataGridFilterMode _filterMode;
|
||||
public DataGridFilterMode FilterMode
|
||||
{
|
||||
get => _filterMode;
|
||||
set => SetAndRaise(FilterModeProperty, ref _filterMode, value);
|
||||
}
|
||||
|
||||
public MenuItemToggleType ToggleType
|
||||
{
|
||||
get => GetValue(ToggleTypeProperty);
|
||||
set => SetValue(ToggleTypeProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private DataGridColumn? _owningColumn;
|
||||
private static int _indicatorSeed = 0;
|
||||
private string _radioCheckGroupName;
|
||||
|
||||
internal DataGridColumn? OwningColumn
|
||||
{
|
||||
get => _owningColumn;
|
||||
set
|
||||
{
|
||||
_owningColumn = value;
|
||||
if (_owningColumn != null)
|
||||
{
|
||||
FilterMode = _owningColumn.FilterMode;
|
||||
ToggleType = _owningColumn.FilterMultiple ? MenuItemToggleType.CheckBox : MenuItemToggleType.Radio;
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly FlyoutStateHelper _flyoutStateHelper;
|
||||
|
||||
public DataGridFilterIndicator()
|
||||
{
|
||||
_flyoutStateHelper = new FlyoutStateHelper
|
||||
{
|
||||
AnchorTarget = this,
|
||||
TriggerType = FlyoutTriggerType.Click,
|
||||
ClickHideFlyoutPredicate = (provider, args) =>
|
||||
{
|
||||
if (provider.PopupHost != args.Root)
|
||||
{
|
||||
if (args.Root is Control control)
|
||||
{
|
||||
if (control.Parent is Visual visualParent)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(visualParent);
|
||||
if (provider.PopupHost == topLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
_radioCheckGroupName = $"{nameof(DataGridFilterIndicator)}-{_indicatorSeed++}";
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
@@ -26,6 +99,83 @@ internal class DataGridFilterIndicator : IconButton
|
||||
{
|
||||
SetValue(IconProperty, AntDesignIconPackage.FilterFilled(), BindingPriority.Template);
|
||||
}
|
||||
|
||||
CreateFlyout();
|
||||
base.OnApplyTemplate(e);
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs changed)
|
||||
{
|
||||
base.OnPropertyChanged(changed);
|
||||
if (changed.Property == FilterModeProperty)
|
||||
{
|
||||
CreateFlyout();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateFlyout()
|
||||
{
|
||||
Debug.Assert(OwningColumn is not null);
|
||||
Debug.Assert(OwningColumn.OwningGrid is not null);
|
||||
var owningGrid = OwningColumn.OwningGrid;
|
||||
if (FilterMode == DataGridFilterMode.Menu && Flyout is not DataGridMenuFilterFlyout)
|
||||
{
|
||||
var menuFlyout = new DataGridMenuFilterFlyout()
|
||||
{
|
||||
IsShowArrow = false,
|
||||
Placement = PlacementMode.BottomEdgeAlignedRight,
|
||||
IsDetectMouseClickEnabled = false
|
||||
};
|
||||
BindUtils.RelayBind(owningGrid, MotionAwareControlProperty.IsMotionEnabledProperty, menuFlyout, MotionAwareControlProperty.IsMotionEnabledProperty);
|
||||
var menuItems = BuildMenuItems(OwningColumn.Filters.ToList());
|
||||
foreach (var menuItem in menuItems)
|
||||
{
|
||||
menuFlyout.Items.Add(menuItem);
|
||||
}
|
||||
Flyout = menuFlyout;
|
||||
_flyoutStateHelper.Flyout = menuFlyout;
|
||||
}
|
||||
else if (FilterMode == DataGridFilterMode.Tree && Flyout is not DataGridTreeFilterFlyout)
|
||||
{
|
||||
Flyout = new DataGridTreeFilterFlyout();
|
||||
}
|
||||
}
|
||||
|
||||
private List<DataGridFilterMenuItem> BuildMenuItems(List<DataGridFilterItem> filterItems)
|
||||
{
|
||||
var menuItems = new List<DataGridFilterMenuItem>();
|
||||
foreach (var item in filterItems)
|
||||
{
|
||||
var menuItem = new DataGridFilterMenuItem()
|
||||
{
|
||||
Header = item.Text,
|
||||
FilterValue = item.Value,
|
||||
StaysOpenOnClick = true,
|
||||
GroupName = _radioCheckGroupName
|
||||
};
|
||||
BindUtils.RelayBind(this, ToggleTypeProperty, menuItem, ToggleTypeProperty);
|
||||
menuItems.Add(menuItem);
|
||||
if (item.Children.Count > 0)
|
||||
{
|
||||
var childItems = BuildMenuItems(item.Children);
|
||||
foreach (var childItem in childItems)
|
||||
{
|
||||
menuItem.Items.Add(childItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
_flyoutStateHelper.NotifyDetachedFromVisualTree();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_flyoutStateHelper.NotifyAttachedToVisualTree();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,33 @@
|
||||
using AtomUI.Data;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public class DataGridMenuFilterFlyout
|
||||
internal class DataGridMenuFilterFlyout : MenuFlyout
|
||||
{
|
||||
|
||||
protected override Control CreatePresenter()
|
||||
{
|
||||
var presenter = new DataGridMenuFilterFlyoutPresenter
|
||||
{
|
||||
ItemsSource = Items,
|
||||
[!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
|
||||
[!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
|
||||
MenuFlyout = this
|
||||
};
|
||||
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, MenuFlyoutPresenter.IsShowArrowProperty);
|
||||
BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, MenuFlyoutPresenter.IsMotionEnabledProperty);
|
||||
SetupArrowPosition(Popup, presenter);
|
||||
CalculateShowArrowEffective();
|
||||
return presenter;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DataGridFilterMenuItem : MenuItem
|
||||
{
|
||||
public string? FilterValue { get; set; }
|
||||
|
||||
static DataGridFilterMenuItem()
|
||||
{
|
||||
StaysOpenOnClickProperty.OverrideDefaultValue<DataGridFilterMenuItem>(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
internal class DataGridMenuFilterFlyoutPresenter : MenuFlyoutPresenter
|
||||
{
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public class DataGridTreeFilterFlyout
|
||||
public class DataGridTreeFilterFlyout : Flyout
|
||||
{
|
||||
|
||||
}
|
||||
@@ -8,7 +8,6 @@ using System.Collections.Specialized;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
|
||||
@@ -217,7 +217,14 @@ internal class DataGridToken : AbstractControlDesignToken
|
||||
public Color TableFixedHeaderSortActiveBg { get; set; }
|
||||
public Color TableHeaderFilterActiveBg { get; set; }
|
||||
public Color TableFilterDropdownBg { get; set; }
|
||||
public double TableFilterButtonSpacing { get; set; }
|
||||
public Thickness TableFilterButtonContainerMargin { get; set; }
|
||||
public Thickness TableFilterButtonLayoutSeparatorMargin { get; set; }
|
||||
|
||||
public double TableFilterDropdownHeight { get; set; }
|
||||
public double TableFilterDropdownWidth { get; set; }
|
||||
public double TableFilterDropdownSearchWidth { get; set; }
|
||||
|
||||
public Color TableRowHoverBg { get; set; }
|
||||
public Color TableSelectedRowBg { get; set; }
|
||||
public Color TableSelectedRowHoverBg { get; set; }
|
||||
@@ -227,8 +234,7 @@ internal class DataGridToken : AbstractControlDesignToken
|
||||
public Color TableExpandIconBg { get; set; }
|
||||
public double TableExpandColumnWidth { get; set; }
|
||||
public Color TableExpandedRowBg { get; set; }
|
||||
public double TableFilterDropdownWidth { get; set; }
|
||||
public double TableFilterDropdownSearchWidth { get; set; }
|
||||
|
||||
public int ZIndexTableFixed { get; set; }
|
||||
public int ZIndexTableSticky { get; set; }
|
||||
|
||||
@@ -338,10 +344,13 @@ internal class DataGridToken : AbstractControlDesignToken
|
||||
TableExpandedRowBg = RowExpandedBg;
|
||||
|
||||
// Dropdown
|
||||
TableFilterDropdownWidth = 120;
|
||||
TableFilterDropdownHeight = 264;
|
||||
TableFilterDropdownSearchWidth = 140;
|
||||
|
||||
// TODO 暂未使用
|
||||
TableFilterDropdownWidth = 120;
|
||||
TableFilterDropdownHeight = 264;
|
||||
TableFilterDropdownSearchWidth = 140;
|
||||
TableFilterButtonSpacing = SharedToken.MarginXS;
|
||||
TableFilterButtonContainerMargin = new Thickness(0, SharedToken.MarginXS, 0, SharedToken.MarginXXS);
|
||||
TableFilterButtonLayoutSeparatorMargin = new Thickness(0, SharedToken.MarginXXS, 0, 0);
|
||||
// Virtual Scroll Bar
|
||||
TableScrollThumbSize = 8;
|
||||
TableScrollThumbBg = StickyScrollBarBg;
|
||||
|
||||
+3
@@ -47,6 +47,9 @@ namespace AtomUI.Theme.Styling
|
||||
public static readonly TokenResourceKey TableExpandColumnWidth = new TokenResourceKey("DataGrid.TableExpandColumnWidth");
|
||||
public static readonly TokenResourceKey TableExpandedRowBg = new TokenResourceKey("DataGrid.TableExpandedRowBg");
|
||||
public static readonly TokenResourceKey TableExpandIconBg = new TokenResourceKey("DataGrid.TableExpandIconBg");
|
||||
public static readonly TokenResourceKey TableFilterButtonContainerMargin = new TokenResourceKey("DataGrid.TableFilterButtonContainerMargin");
|
||||
public static readonly TokenResourceKey TableFilterButtonLayoutSeparatorMargin = new TokenResourceKey("DataGrid.TableFilterButtonLayoutSeparatorMargin");
|
||||
public static readonly TokenResourceKey TableFilterButtonSpacing = new TokenResourceKey("DataGrid.TableFilterButtonSpacing");
|
||||
public static readonly TokenResourceKey TableFilterDropdownBg = new TokenResourceKey("DataGrid.TableFilterDropdownBg");
|
||||
public static readonly TokenResourceKey TableFilterDropdownHeight = new TokenResourceKey("DataGrid.TableFilterDropdownHeight");
|
||||
public static readonly TokenResourceKey TableFilterDropdownSearchWidth = new TokenResourceKey("DataGrid.TableFilterDropdownSearchWidth");
|
||||
|
||||
@@ -36,8 +36,15 @@
|
||||
IsVisible="{TemplateBinding CanUserSort}" />
|
||||
<atom:DataGridFilterIndicator
|
||||
Name="{x:Static atom:DataGridColumnHeaderThemeConstants.FilterIndicatorPart}"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{TemplateBinding AreSeparatorsVisible}"/>
|
||||
VerticalAlignment="Center">
|
||||
<atom:DataGridFilterIndicator.IsVisible>
|
||||
<MultiBinding Converter="{x:Static converters:DataGridFilterIndicatorVisibleConvert.Default}">
|
||||
<Binding Path="AreSeparatorsVisible" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding Path="CanUserFilter" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding Path="OwningColumn?.Filters.Count" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
</MultiBinding>
|
||||
</atom:DataGridFilterIndicator.IsVisible>
|
||||
</atom:DataGridFilterIndicator>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Decorator>
|
||||
|
||||
+8
@@ -114,4 +114,12 @@ internal static class DataGridThemeConstants
|
||||
internal static class DataGridTopLeftColumnHeaderThemeConstants
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal static class DataGridMenuFilterFlyoutPresenterThemeConstants
|
||||
{
|
||||
public const string ButtonLayoutPart = "PART_ButtonLayout";
|
||||
public const string OkButtonPart = "PART_OkButton";
|
||||
public const string ResetButtonPart = "PART_ResetButton";
|
||||
public const string ButtonLayoutSeparatorPart = "PART_ButtonLayoutSeparator";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net"
|
||||
xmlns:themes="using:AtomUI.Controls.Themes">
|
||||
<ControlTheme x:Key="{x:Type atom:DataGridFilterMenuItem}" TargetType="atom:DataGridFilterMenuItem">
|
||||
<ControlTheme.BasedOn>
|
||||
<themes:MenuItemTheme TargetType="atom:DataGridFilterMenuItem"/>
|
||||
</ControlTheme.BasedOn>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net"
|
||||
xmlns:themes="using:AtomUI.Controls.Themes"
|
||||
xmlns:localization="clr-namespace:AtomUI.Controls.Localization;assembly=AtomUI.Controls">
|
||||
<ControlTheme x:Key="{x:Type atom:DataGridMenuFilterFlyoutPresenter}"
|
||||
TargetType="atom:DataGridMenuFilterFlyoutPresenter">
|
||||
<ControlTheme.BasedOn>
|
||||
<themes:MenuFlyoutPresenterTheme TargetType="atom:DataGridMenuFilterFlyoutPresenter" />
|
||||
</ControlTheme.BasedOn>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<atom:ArrowDecoratedBox Name="{x:Static atom:MenuFlyoutThemeConstants.ArrowDecoratorPart}"
|
||||
IsShowArrow="{TemplateBinding IsShowArrow}"
|
||||
ArrowPosition="{TemplateBinding ArrowPosition}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<StackPanel
|
||||
Name="{x:Static atom:DataGridMenuFilterFlyoutPresenterThemeConstants.ButtonLayoutPart}"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
DockPanel.Dock="Bottom">
|
||||
<atom:Button
|
||||
Name="{x:Static atom:DataGridMenuFilterFlyoutPresenterThemeConstants.ResetButtonPart}"
|
||||
SizeType="Small"
|
||||
Margin="0"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Reset}}" />
|
||||
|
||||
<atom:Button
|
||||
Name="{x:Static atom:DataGridMenuFilterFlyoutPresenterThemeConstants.OkButtonPart}"
|
||||
SizeType="Small"
|
||||
ButtonType="Primary"
|
||||
Margin="0"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Ok}}" />
|
||||
</StackPanel>
|
||||
|
||||
<Rectangle Name="{x:Static atom:DataGridMenuFilterFlyoutPresenterThemeConstants.ButtonLayoutSeparatorPart}"
|
||||
Height="1.0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
DockPanel.Dock="Bottom"/>
|
||||
|
||||
<atom:MenuScrollViewer HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<ItemsPresenter Name="{x:Static atom:MenuFlyoutThemeConstants.ItemsPresenterPart}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
KeyboardNavigation.TabNavigation="Continue"
|
||||
Grid.IsSharedSizeScope="True" />
|
||||
</atom:MenuScrollViewer>
|
||||
</DockPanel>
|
||||
</atom:ArrowDecoratedBox>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^ /template/ StackPanel#PART_ButtonLayout">
|
||||
<Setter Property="Spacing" Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableFilterButtonSpacing}}" />
|
||||
<Setter Property="Margin"
|
||||
Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableFilterButtonContainerMargin}}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="^ /template/ Rectangle#PART_ButtonLayoutSeparator">
|
||||
<Setter Property="Fill" Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableBorderColor}}" />
|
||||
<Setter Property="Margin"
|
||||
Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableFilterButtonLayoutSeparatorMargin}}" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
|
||||
</ResourceDictionary>
|
||||
+8
-1
@@ -9,6 +9,13 @@ internal class DataGridFilterIndicatorVisibleConvert : IMultiValueConverter
|
||||
|
||||
public object Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return values.All(v => v is bool && (bool)v);
|
||||
if (values.Count != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool areSeparatorsVisible = values[0] is bool b0 && b0;
|
||||
bool canUserFilter = values[1] is bool b1 && b1;
|
||||
int filterCount = values[2] is int count ? count : 0;
|
||||
return areSeparatorsVisible && canUserFilter && filterCount > 0;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="AtomUI" />
|
||||
<InternalsVisibleTo Include="AtomUI.Controls.DataGrid" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ResourceInclude Source="Drawer/Themes/DrawerThemes.axaml" />
|
||||
<ResourceInclude Source="EmptyIndicator/Themes/EmptyIndicatorTheme.axaml" />
|
||||
<ResourceInclude Source="Expander/Themes/ExpanderTheme.axaml" />
|
||||
<ResourceInclude Source="Flyouts/Themes/MenuFlyoutPresenterTheme.axaml" />
|
||||
<ResourceInclude Source="Flyouts/Themes/FlyoutThemes.axaml" />
|
||||
<ResourceInclude Source="GroupBox/Themes/GroupBoxTheme.axaml" />
|
||||
<ResourceInclude Source="ListBox/Themes/ListBoxThemes.axaml" />
|
||||
<ResourceInclude Source="Loading/Themes/LoadingIndicatorThemes.axaml" />
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using AtomUI.Animations;
|
||||
using AtomUI.Controls.Themes;
|
||||
using AtomUI.Controls.Utils;
|
||||
using AtomUI.IconPkg;
|
||||
using AtomUI.Reflection;
|
||||
using AtomUI.Theme;
|
||||
using AtomUI.Theme.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Rendering;
|
||||
using Avalonia.VisualTree;
|
||||
@@ -139,6 +144,13 @@ public class IconButton : AvaloniaButton,
|
||||
|
||||
#endregion
|
||||
|
||||
#region 反射信息定义
|
||||
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicFields, typeof(AvaloniaButton))]
|
||||
private static readonly Lazy<FieldInfo> IsFlyoutOpenFieldInfo = new Lazy<FieldInfo>(() =>
|
||||
typeof(AvaloniaButton).GetFieldInfoOrThrow("_isFlyoutOpen",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic));
|
||||
#endregion
|
||||
|
||||
private Border? _frame;
|
||||
|
||||
static IconButton()
|
||||
@@ -198,4 +210,9 @@ public class IconButton : AvaloniaButton,
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool IsFlyoutOpen()
|
||||
{
|
||||
return IsFlyoutOpenFieldInfo.Value.GetValue(this) as bool? ?? false;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
ButtonType="Primary"
|
||||
HorizontalAlignment="Right"
|
||||
SizeType="Small"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.OkText}}" />
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Ok}}" />
|
||||
</Panel>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
ButtonType="Primary"
|
||||
HorizontalAlignment="Right"
|
||||
SizeType="Small"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.OkText}}" />
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Ok}}" />
|
||||
</Panel>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
ButtonType="Primary"
|
||||
HorizontalAlignment="Right"
|
||||
SizeType="Small"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.OkText}}" />
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Ok}}" />
|
||||
</Panel>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
@@ -210,6 +210,7 @@ public class Flyout : PopupFlyoutBase
|
||||
|
||||
protected override void OnClosed()
|
||||
{
|
||||
base.OnClosed();
|
||||
CompositeDisposable?.Dispose();
|
||||
}
|
||||
|
||||
@@ -340,10 +341,13 @@ public class Flyout : PopupFlyoutBase
|
||||
return false;
|
||||
}
|
||||
|
||||
IsOpen = true;
|
||||
IsOpen = true;
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Popup.MotionAwareOpen(() => { HandlePopupOpened(placementTarget); });
|
||||
Popup.MotionAwareOpen(() =>
|
||||
{
|
||||
HandlePopupOpened(placementTarget);
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -368,8 +372,14 @@ public class Flyout : PopupFlyoutBase
|
||||
return base.HideCore(false);
|
||||
}
|
||||
|
||||
IsOpen = false;
|
||||
Dispatcher.UIThread.InvokeAsync(() => { Popup.MotionAwareClose(HandlePopupClosed); });
|
||||
IsOpen = false;
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Popup.MotionAwareClose(() =>
|
||||
{
|
||||
HandlePopupClosed();
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ internal class FlyoutStateHelper : AvaloniaObject
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
FlyoutAboutToClose?.Invoke(this, EventArgs.Empty);
|
||||
Flyout.Hide();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class MenuFlyout : Flyout
|
||||
ItemsSource = Items,
|
||||
[!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
|
||||
[!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
|
||||
MenuFlyout = new WeakReference<MenuFlyout>(this)
|
||||
MenuFlyout = this
|
||||
};
|
||||
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, MenuFlyoutPresenter.IsShowArrowProperty);
|
||||
BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, MenuFlyoutPresenter.IsMotionEnabledProperty);
|
||||
@@ -73,7 +73,7 @@ public class MenuFlyout : Flyout
|
||||
return presenter;
|
||||
}
|
||||
|
||||
private void SetupArrowPosition(Popup popup, MenuFlyoutPresenter? flyoutPresenter = null)
|
||||
protected void SetupArrowPosition(Popup popup, MenuFlyoutPresenter? flyoutPresenter = null)
|
||||
{
|
||||
if (flyoutPresenter is null)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ public class MenuFlyoutPresenter : MenuBase,
|
||||
remove => RemoveHandler(MenuItemClickedEvent, value);
|
||||
}
|
||||
|
||||
public WeakReference<MenuFlyout>? MenuFlyout { get; set; }
|
||||
public MenuFlyout? MenuFlyout { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -98,11 +98,8 @@ public class MenuFlyoutPresenter : MenuBase,
|
||||
// DefaultMenuInteractionHandler calls this
|
||||
if (MenuFlyout is not null)
|
||||
{
|
||||
if (MenuFlyout.TryGetTarget(out var menuFlyout))
|
||||
{
|
||||
SelectedIndex = -1;
|
||||
menuFlyout.Hide();
|
||||
}
|
||||
SelectedIndex = -1;
|
||||
MenuFlyout.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +166,7 @@ public class MenuFlyoutPresenter : MenuBase,
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
_arrowDecoratedBox = e.NameScope.Find<ArrowDecoratedBox>(MenuFlyoutThemeConstants.RootContainerPart);
|
||||
_arrowDecoratedBox = e.NameScope.Find<ArrowDecoratedBox>(MenuFlyoutThemeConstants.ArrowDecoratorPart);
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
|
||||
@@ -88,8 +88,10 @@ public abstract class PopupFlyoutBase : FlyoutBase,
|
||||
public static readonly StyledProperty<PopupPositionerConstraintAdjustment> PlacementConstraintAdjustmentProperty =
|
||||
Avalonia.Controls.Primitives.Popup.PlacementConstraintAdjustmentProperty.AddOwner<PopupFlyoutBase>();
|
||||
|
||||
public static readonly StyledProperty<bool> IsMotionEnabledProperty
|
||||
= MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<PopupFlyoutBase>();
|
||||
public static readonly StyledProperty<bool> IsMotionEnabledProperty = MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<PopupFlyoutBase>();
|
||||
|
||||
public static readonly StyledProperty<bool> IsDetectMouseClickEnabledProperty =
|
||||
AvaloniaProperty.Register<PopupFlyoutBase, bool>(nameof(IsDetectMouseClickEnabled), true);
|
||||
|
||||
public double MarginToAnchor
|
||||
{
|
||||
@@ -164,19 +166,12 @@ public abstract class PopupFlyoutBase : FlyoutBase,
|
||||
set => SetValue(IsMotionEnabledProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly StyledProperty<bool> IsDetectMouseClickEnabledProperty =
|
||||
AvaloniaProperty.Register<PopupFlyoutBase, bool>(nameof(IsDetectMouseClickEnabled), true);
|
||||
|
||||
internal bool IsDetectMouseClickEnabled
|
||||
public bool IsDetectMouseClickEnabled
|
||||
{
|
||||
get => GetValue(IsDetectMouseClickEnabledProperty);
|
||||
set => SetValue(IsDetectMouseClickEnabledProperty, value);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共事件定义
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:themes="using:AtomUI.Controls.Themes"
|
||||
xmlns:atom="https://atomui.net">
|
||||
<themes:MenuFlyoutPresenterTheme x:Key="{x:Type atom:MenuFlyoutPresenter}" TargetType="atom:MenuFlyoutPresenter"/>
|
||||
</ResourceDictionary>
|
||||
@@ -3,5 +3,5 @@ namespace AtomUI.Controls.Themes;
|
||||
internal static class MenuFlyoutThemeConstants
|
||||
{
|
||||
public const string ItemsPresenterPart = "PART_ItemsPresenter";
|
||||
public const string RootContainerPart = "PART_RootContainer";
|
||||
public const string ArrowDecoratorPart = "PART_ArrowDecorator";
|
||||
}
|
||||
@@ -1,28 +1,29 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net">
|
||||
<ControlTheme x:Key="{x:Type atom:MenuFlyoutPresenter}" TargetType="atom:MenuFlyoutPresenter">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<atom:ArrowDecoratedBox Name="{x:Static atom:MenuFlyoutThemeConstants.RootContainerPart}"
|
||||
IsShowArrow="{TemplateBinding IsShowArrow}"
|
||||
ArrowPosition="{TemplateBinding ArrowPosition}">
|
||||
<atom:MenuScrollViewer HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<ItemsPresenter Name="{x:Static atom:MenuFlyoutThemeConstants.ItemsPresenterPart}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
KeyboardNavigation.TabNavigation="Continue"
|
||||
Grid.IsSharedSizeScope="True"/>
|
||||
</atom:MenuScrollViewer>
|
||||
</atom:ArrowDecoratedBox>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<ControlTheme xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net"
|
||||
x:Class="AtomUI.Controls.Themes.MenuFlyoutPresenterTheme"
|
||||
TargetType="atom:MenuFlyoutPresenter">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="atom:MenuFlyoutPresenter">
|
||||
<atom:ArrowDecoratedBox Name="{x:Static atom:MenuFlyoutThemeConstants.ArrowDecoratorPart}"
|
||||
IsShowArrow="{TemplateBinding IsShowArrow}"
|
||||
ArrowPosition="{TemplateBinding ArrowPosition}">
|
||||
<atom:MenuScrollViewer HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<ItemsPresenter Name="{x:Static atom:MenuFlyoutThemeConstants.ItemsPresenterPart}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
KeyboardNavigation.TabNavigation="Continue"
|
||||
Grid.IsSharedSizeScope="True" />
|
||||
</atom:MenuScrollViewer>
|
||||
</atom:ArrowDecoratedBox>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^:is(atom|MenuFlyoutPresenter)">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupBorderRadius}}" />
|
||||
|
||||
<Style Selector="^ /template/ atom|ArrowDecoratedBox#PART_RootContainer">
|
||||
|
||||
<Style Selector="^ /template/ atom|ArrowDecoratedBox#PART_ArrowDecorator">
|
||||
<Setter Property="Background" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuBgColor}}" />
|
||||
<Setter Property="MinWidth" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMinWidth}}" />
|
||||
<Setter Property="MaxWidth" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMaxWidth}}" />
|
||||
@@ -30,6 +31,6 @@
|
||||
<Setter Property="MaxHeight" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMaxHeight}}" />
|
||||
<Setter Property="Padding" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupContentPadding}}" />
|
||||
</Style>
|
||||
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
</Style>
|
||||
|
||||
</ControlTheme>
|
||||
@@ -0,0 +1,7 @@
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace AtomUI.Controls.Themes;
|
||||
|
||||
public class MenuFlyoutPresenterTheme : ControlTheme
|
||||
{
|
||||
}
|
||||
+5
-4
@@ -13,8 +13,9 @@ namespace AtomUI.Controls.Localization
|
||||
{
|
||||
public static class CommonLangResourceKey
|
||||
{
|
||||
public static readonly LanguageResourceKey CancelText = new LanguageResourceKey("Common.CancelText", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey OkText = new LanguageResourceKey("Common.OkText", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey Cancel = new LanguageResourceKey("Common.Cancel", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey Ok = new LanguageResourceKey("Common.Ok", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey Reset = new LanguageResourceKey("Common.Reset", "AtomUI.Lang");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +33,8 @@ namespace AtomUI.Controls.PopupConfirmLang
|
||||
{
|
||||
public static class PopupConfirmLangResourceKey
|
||||
{
|
||||
public static readonly LanguageResourceKey CancelText = new LanguageResourceKey("PopupConfirm.CancelText", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey OkText = new LanguageResourceKey("PopupConfirm.OkText", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey Cancel = new LanguageResourceKey("PopupConfirm.Cancel", "AtomUI.Lang");
|
||||
public static readonly LanguageResourceKey Ok = new LanguageResourceKey("PopupConfirm.Ok", "AtomUI.Lang");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace AtomUI.Controls.Localization;
|
||||
[LanguageProvider(LanguageCode.en_US, Constants.CommonLangID)]
|
||||
internal class en_US : AbstractLanguageProvider
|
||||
{
|
||||
public const string OkText = "Ok";
|
||||
public const string CancelText = "Cancel";
|
||||
public const string Ok = "Ok";
|
||||
public const string Cancel = "Cancel";
|
||||
public const string Reset = "Reset";
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace AtomUI.Controls.Localization;
|
||||
[LanguageProvider(LanguageCode.zh_CN, Constants.CommonLangID)]
|
||||
internal class zh_CN : AbstractLanguageProvider
|
||||
{
|
||||
public const string OkText = "确定";
|
||||
public const string CancelText = "取消";
|
||||
public const string Ok = "确定";
|
||||
public const string Cancel = "取消";
|
||||
public const string Reset = "重置";
|
||||
}
|
||||
@@ -124,7 +124,7 @@ public class MenuItem : AvaloniaMenuItem
|
||||
{
|
||||
if (_headerPresenterPart != null)
|
||||
{
|
||||
_headerPresenterPart.Transitions = new Transitions()
|
||||
_headerPresenterPart.Transitions = new Transitions
|
||||
{
|
||||
TransitionUtils.CreateTransition<SolidColorBrushTransition>(ContentPresenter.BackgroundProperty),
|
||||
TransitionUtils.CreateTransition<SolidColorBrushTransition>(ContentPresenter.ForegroundProperty)
|
||||
|
||||
@@ -1,91 +1,90 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net"
|
||||
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls">
|
||||
<atom:ToggleItemsLayoutVisibleConverter x:Key="ToggleItemsLayoutVisibleConverter" />
|
||||
<atom:StringToTextBlockConverter x:Key="StringToTextBlockConverter" VerticalAlignment="Center" />
|
||||
<converters:PlatformKeyGestureConverter x:Key="KeyGestureConverter" />
|
||||
<ControlTheme x:Key="{x:Type atom:MenuItem}" TargetType="atom:MenuItem">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Panel>
|
||||
<Border Name="{x:Static atom:MenuItemThemeConstants.ItemDecoratorPart}">
|
||||
<Grid Name="{x:Static atom:MenuItemThemeConstants.MainContainerPart}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="ToggleItemsLayout" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="IconPresenter" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="InputGestureText" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuIndicatorIcon" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ControlTheme xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atom="https://atomui.net"
|
||||
x:Class="AtomUI.Controls.Themes.MenuItemTheme"
|
||||
TargetType="atom:MenuItem">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="atom:MenuItem">
|
||||
<Panel>
|
||||
<Border Name="{x:Static atom:MenuItemThemeConstants.ItemDecoratorPart}">
|
||||
<Grid Name="{x:Static atom:MenuItemThemeConstants.MainContainerPart}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="ToggleItemsLayout" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="IconPresenter" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="InputGestureText" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuIndicatorIcon" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Panel Name="{x:Static atom:MenuItemThemeConstants.ToggleItemsLayoutPart}"
|
||||
Grid.Column="0"
|
||||
IsEnabled="{TemplateBinding IsEnabled}">
|
||||
<Panel.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource ToggleItemsLayoutVisibleConverter}">
|
||||
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="IsTopLevel" />
|
||||
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ToggleType" />
|
||||
</MultiBinding>
|
||||
</Panel.IsVisible>
|
||||
<atom:CheckBox Name="{x:Static atom:MenuItemThemeConstants.ToggleCheckboxPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{TemplateBinding IsChecked}"
|
||||
IsVisible="False" />
|
||||
<atom:RadioButton Name="{x:Static atom:MenuItemThemeConstants.ToggleRadioPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
GroupName="{TemplateBinding GroupName}"
|
||||
IsVisible="False" />
|
||||
</Panel>
|
||||
|
||||
<atom:IconPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemIconPresenterPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Icon="{TemplateBinding Icon}"
|
||||
IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Grid.Column="1" />
|
||||
<ContentPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemTextPresenterPart}"
|
||||
HorizontalAlignment="Stretch"
|
||||
<Panel Name="{x:Static atom:MenuItemThemeConstants.ToggleItemsLayoutPart}"
|
||||
Grid.Column="0"
|
||||
IsEnabled="{TemplateBinding IsEnabled}">
|
||||
<Panel.IsVisible>
|
||||
<MultiBinding
|
||||
Converter="{x:Static atom:MenuItemTheme.ToggleItemsLayoutVisibleConverter}">
|
||||
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="IsTopLevel" />
|
||||
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ToggleType" />
|
||||
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ItemCount" />
|
||||
</MultiBinding>
|
||||
</Panel.IsVisible>
|
||||
<atom:CheckBox Name="{x:Static atom:MenuItemThemeConstants.ToggleCheckboxPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{TemplateBinding IsChecked}"
|
||||
IsVisible="False" />
|
||||
<atom:RadioButton Name="{x:Static atom:MenuItemThemeConstants.ToggleRadioPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
IsHitTestVisible="False"
|
||||
Content="{TemplateBinding Header, Converter={StaticResource StringToTextBlockConverter}}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Grid.Column="2" />
|
||||
<atom:TextBlock Name="{x:Static atom:MenuItemThemeConstants.InputGestureTextPart}"
|
||||
HorizontalAlignment="Right"
|
||||
GroupName="{TemplateBinding GroupName}"
|
||||
IsVisible="False" />
|
||||
</Panel>
|
||||
|
||||
<atom:IconPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemIconPresenterPart}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Right"
|
||||
Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}"
|
||||
Grid.Column="3" />
|
||||
Icon="{TemplateBinding Icon}"
|
||||
IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Grid.Column="1" />
|
||||
<ContentPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemTextPresenterPart}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
IsHitTestVisible="False"
|
||||
Content="{TemplateBinding Header, Converter={x:Static atom:MenuItemTheme.StringToTextBlockConverter}}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Grid.Column="2" />
|
||||
<atom:TextBlock Name="{x:Static atom:MenuItemThemeConstants.InputGestureTextPart}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Right"
|
||||
Text="{TemplateBinding InputGesture, Converter={x:Static atom:MenuItemTheme.KeyGestureConverter}}"
|
||||
Grid.Column="3" />
|
||||
|
||||
<atom:Icon Name="{x:Static atom:MenuItemThemeConstants.MenuIndicatorIconPart}"
|
||||
IconInfo="{atom:IconInfoProvider RightOutlined}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="4" />
|
||||
</Grid>
|
||||
<atom:Icon Name="{x:Static atom:MenuItemThemeConstants.MenuIndicatorIconPart}"
|
||||
IconInfo="{atom:IconInfoProvider RightOutlined}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="4" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<atom:Popup Name="{x:Static atom:MenuItemThemeConstants.PopupPart}"
|
||||
WindowManagerAddShadowHint="False"
|
||||
IsLightDismissEnabled="False"
|
||||
Placement="RightEdgeAlignedTop"
|
||||
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
|
||||
<Border Name="{x:Static atom:MenuItemThemeConstants.PopupFramePart}">
|
||||
<atom:MenuScrollViewer IsScrollChainingEnabled="False">
|
||||
<ItemsPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemsPresenterPart}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
Grid.IsSharedSizeScope="True" />
|
||||
</atom:MenuScrollViewer>
|
||||
</Border>
|
||||
<atom:Popup Name="{x:Static atom:MenuItemThemeConstants.PopupPart}"
|
||||
WindowManagerAddShadowHint="False"
|
||||
IsLightDismissEnabled="False"
|
||||
Placement="RightEdgeAlignedTop"
|
||||
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
|
||||
<Border Name="{x:Static atom:MenuItemThemeConstants.PopupFramePart}">
|
||||
<atom:MenuScrollViewer IsScrollChainingEnabled="False">
|
||||
<ItemsPresenter Name="{x:Static atom:MenuItemThemeConstants.ItemsPresenterPart}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
Grid.IsSharedSizeScope="True" />
|
||||
</atom:MenuScrollViewer>
|
||||
</Border>
|
||||
</atom:Popup>
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</atom:Popup>
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^:is(atom|MenuItem)">
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemColor}}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
@@ -140,7 +139,7 @@
|
||||
<Setter Property="Background" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemHoverBg}}" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^ /template/ atom|Icon#PART_MenuIndicatorIcon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Width" Value="{DynamicResource {x:Static atom:SharedTokenKey.IconSizeXS}}" />
|
||||
@@ -152,31 +151,32 @@
|
||||
<Setter Property="IsVisible" Value="False" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^ /template/ atom|IconPresenter#PART_ItemIconPresenter">
|
||||
<Setter Property="NormalFilledBrush" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemColor}}" />
|
||||
<Setter Property="IconWidth" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemIconSize}}" />
|
||||
<Setter Property="IconHeight" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemIconSize}}" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^ /template/ Border#PART_PopupFrame">
|
||||
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgContainer}}" />
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupBorderRadius}}" />
|
||||
<Setter Property="CornerRadius"
|
||||
Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupBorderRadius}}" />
|
||||
<Setter Property="MinWidth" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMinWidth}}" />
|
||||
<Setter Property="MaxWidth" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMaxWidth}}" />
|
||||
<Setter Property="MinHeight" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMinHeight}}" />
|
||||
<Setter Property="MaxHeight" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupMaxHeight}}" />
|
||||
<Setter Property="Padding" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupContentPadding}}" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^ /template/ atom|Popup#PART_Popup">
|
||||
<Setter Property="MarginToAnchor" Value="{DynamicResource {x:Static atom:MenuTokenKey.TopLevelItemPopupMarginToAnchor}}" />
|
||||
<Setter Property="MarginToAnchor"
|
||||
Value="{DynamicResource {x:Static atom:MenuTokenKey.TopLevelItemPopupMarginToAnchor}}" />
|
||||
<Setter Property="MaskShadows" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupBoxShadows}}" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^:disabled">
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static atom:MenuTokenKey.ItemDisabledColor}}" />
|
||||
</Style>
|
||||
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
@@ -0,0 +1,16 @@
|
||||
using AtomUI.Controls.Utils;
|
||||
using Avalonia.Controls.Converters;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace AtomUI.Controls.Themes;
|
||||
|
||||
public class MenuItemTheme : ControlTheme
|
||||
{
|
||||
public static readonly StringToTextBlockConverter StringToTextBlockConverter = new ()
|
||||
{
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
public static readonly PlatformKeyGestureConverter KeyGestureConverter = new();
|
||||
public static readonly ToggleItemsLayoutVisibleConverter ToggleItemsLayoutVisibleConverter = new();
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:themes="using:AtomUI.Controls.Themes"
|
||||
xmlns:atom="https://atomui.net">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="ContextMenuTheme.axaml"/>
|
||||
<ResourceInclude Source="MenuItemTheme.axaml"/>
|
||||
<ResourceInclude Source="MenuScrollViewerTheme.axaml"/>
|
||||
<ResourceInclude Source="MenuSeparatorTheme.axaml"/>
|
||||
<ResourceInclude Source="TopLevelMenuItemTheme.axaml"/>
|
||||
<ResourceInclude Source="MenuTheme.axaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<themes:MenuItemTheme x:Key="{x:Type atom:MenuItem}" TargetType="atom:MenuItem"/>
|
||||
</ResourceDictionary>
|
||||
@@ -4,14 +4,17 @@ using Avalonia.Data.Converters;
|
||||
|
||||
namespace AtomUI.Controls.Utils;
|
||||
|
||||
internal class ToggleItemsLayoutVisibleConverter : IMultiValueConverter
|
||||
public class ToggleItemsLayoutVisibleConverter : IMultiValueConverter
|
||||
{
|
||||
public object? Convert(IList<object?> objects, Type targetType, object? parameter, CultureInfo culture)
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
var items = objects.ToList();
|
||||
if (items[0] is bool isTopLevel && items[1] is MenuItemToggleType toggleType)
|
||||
if (values.Count != 3)
|
||||
{
|
||||
return !isTopLevel && toggleType != MenuItemToggleType.None;
|
||||
return false;
|
||||
}
|
||||
if (values[0] is bool isTopLevel && values[1] is MenuItemToggleType toggleType && values[2] is int itemCount)
|
||||
{
|
||||
return !isTopLevel && toggleType != MenuItemToggleType.None && itemCount == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -133,8 +133,8 @@ public class Popup : AvaloniaPopup,
|
||||
public Popup()
|
||||
{
|
||||
this.BindWaveSpiritProperties();
|
||||
Closed += HandleClosed;
|
||||
Opened += HandleOpened;
|
||||
Closed += HandleClosed;
|
||||
Opened += HandleOpened;
|
||||
}
|
||||
|
||||
// Popup 好像不加入视觉树,所以我们放在逻辑树
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace AtomUI.Controls.PopupConfirmLang;
|
||||
[LanguageProvider(LanguageCode.en_US, PopupConfirmToken.ID)]
|
||||
internal class en_US : AbstractLanguageProvider
|
||||
{
|
||||
public const string OkText = "Ok";
|
||||
public const string CancelText = "Cancel";
|
||||
public const string Ok = "Ok";
|
||||
public const string Cancel = "Cancel";
|
||||
}
|
||||
@@ -6,6 +6,6 @@ namespace AtomUI.Controls.PopupConfirmLang;
|
||||
[LanguageProvider(LanguageCode.zh_CN, PopupConfirmToken.ID)]
|
||||
internal class zh_CN : AbstractLanguageProvider
|
||||
{
|
||||
public const string OkText = "确定";
|
||||
public const string CancelText = "取消";
|
||||
public const string Ok = "确定";
|
||||
public const string Cancel = "取消";
|
||||
}
|
||||
@@ -149,8 +149,8 @@ public class PopupConfirm : FlyoutHost,
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
Flyout ??= new PopupConfirmFlyout(this);
|
||||
this.AddResourceBindingDisposable(LanguageResourceBinder.CreateBinding(this, OkTextProperty, PopupConfirmLangResourceKey.OkText));
|
||||
this.AddResourceBindingDisposable(LanguageResourceBinder.CreateBinding(this, CancelTextProperty, PopupConfirmLangResourceKey.CancelText));
|
||||
this.AddResourceBindingDisposable(LanguageResourceBinder.CreateBinding(this, OkTextProperty, PopupConfirmLangResourceKey.Ok));
|
||||
this.AddResourceBindingDisposable(LanguageResourceBinder.CreateBinding(this, CancelTextProperty, PopupConfirmLangResourceKey.Cancel));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
ButtonType="Primary"
|
||||
HorizontalAlignment="Right"
|
||||
SizeType="Small"
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.OkText}}" />
|
||||
Content="{DynamicResource {x:Static localization:CommonLangResourceKey.Ok}}" />
|
||||
</Panel>
|
||||
</Border>
|
||||
<atom:TimeView Name="{x:Static atom:TimePickerPresenterThemeConstants.TimeViewPart}"
|
||||
|
||||
@@ -5,7 +5,7 @@ using Avalonia.Media;
|
||||
|
||||
namespace AtomUI.Controls.Utils;
|
||||
|
||||
internal class StringToTextBlockConverter : IValueConverter
|
||||
public class StringToTextBlockConverter : IValueConverter
|
||||
{
|
||||
public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Stretch;
|
||||
public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Stretch;
|
||||
|
||||
Reference in New Issue
Block a user