From 66bc512ae23febbac41be03b0f69f58056a87f8e Mon Sep 17 00:00:00 2001 From: rabbitism Date: Thu, 4 Jan 2024 20:33:42 +0800 Subject: [PATCH] WIP: layout with new panel. --- demo/Ursa.Demo/Pages/TimelineDemo.axaml | 9 +- .../ViewModels/TimelineDemoViewModel.cs | 3 - src/Ursa.Themes.Semi/Controls/Timeline.axaml | 132 +++++++++++++----- src/Ursa/Controls/Timeline/Timeline.cs | 26 ++++ .../Controls/Timeline/TimelineDisplayMode.cs | 7 + src/Ursa/Controls/Timeline/TimelineItem.cs | 83 +++++++++++ src/Ursa/Controls/Timeline/TimelinePanel.cs | 37 ++++- 7 files changed, 250 insertions(+), 47 deletions(-) diff --git a/demo/Ursa.Demo/Pages/TimelineDemo.axaml b/demo/Ursa.Demo/Pages/TimelineDemo.axaml index 04d12b4..70bccc0 100644 --- a/demo/Ursa.Demo/Pages/TimelineDemo.axaml +++ b/demo/Ursa.Demo/Pages/TimelineDemo.axaml @@ -47,23 +47,26 @@ - + ItemsSource="{Binding Items}" + TimeMemberBinding="{ReflectionBinding Time}" /> + diff --git a/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs index 73e1335..54cb092 100644 --- a/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs @@ -11,7 +11,6 @@ public class TimelineDemoViewModel: ViewModelBase new() { Time = DateTime.Now, - TimeFormat = "yyyy-MM-dd HH:mm:ss", Description = "Item 1", Header = "审核中", ItemType = TimelineItemType.Success, @@ -19,7 +18,6 @@ public class TimelineDemoViewModel: ViewModelBase new() { Time = DateTime.Now, - TimeFormat = "HH:mm:ss", Description = "Item 2", Header = "发布成功", ItemType = TimelineItemType.Ongoing, @@ -27,7 +25,6 @@ public class TimelineDemoViewModel: ViewModelBase new() { Time = DateTime.Now, - TimeFormat = "HH:mm:ss", Description = "Item 3", Header = "审核失败", ItemType = TimelineItemType.Error, diff --git a/src/Ursa.Themes.Semi/Controls/Timeline.axaml b/src/Ursa.Themes.Semi/Controls/Timeline.axaml index dd7c25f..87d5e48 100644 --- a/src/Ursa.Themes.Semi/Controls/Timeline.axaml +++ b/src/Ursa.Themes.Semi/Controls/Timeline.axaml @@ -18,7 +18,7 @@ - + @@ -32,51 +32,64 @@ WarningBrush="{DynamicResource WarningTimelineIconForeground}" /> + + - - + + - - + RowDefinitions="Auto, *"> + + + + ContentTemplate="{TemplateBinding HeaderTemplate}" /> + + + + + + + + @@ -86,12 +99,57 @@ - + + + + + + + + + + + + diff --git a/src/Ursa/Controls/Timeline/Timeline.cs b/src/Ursa/Controls/Timeline/Timeline.cs index 3577226..f6809ca 100644 --- a/src/Ursa/Controls/Timeline/Timeline.cs +++ b/src/Ursa/Controls/Timeline/Timeline.cs @@ -69,6 +69,27 @@ public class Timeline: ItemsControl set => SetValue(DescriptionTemplateProperty, value); } + public static readonly StyledProperty TimeMemberBindingProperty = AvaloniaProperty.Register( + nameof(TimeMemberBinding)); + + [AssignBinding] + [InheritDataTypeFromItems(nameof(ItemsSource))] + public IBinding? TimeMemberBinding + { + get => GetValue(TimeMemberBindingProperty); + set => SetValue(TimeMemberBindingProperty, value); + } + + public static readonly StyledProperty TimeFormatProperty = AvaloniaProperty.Register( + nameof(TimeFormat), defaultValue:"yyyy-MM-dd HH:mm:ss"); + + public string? TimeFormat + { + get => GetValue(TimeFormatProperty); + set => SetValue(TimeFormatProperty, value); + } + + public static readonly StyledProperty ModeProperty = AvaloniaProperty.Register( nameof(Mode)); @@ -124,6 +145,11 @@ public class Timeline: ItemsControl { t.Bind(ContentControl.ContentProperty, DescriptionMemberBinding); } + if (TimeMemberBinding != null) + { + t.Bind(TimelineItem.TimeProperty, TimeMemberBinding); + } + t.SetCurrentValue(TimelineItem.TimeFormatProperty, TimeFormat); t.SetCurrentValue(TimelineItem.IconTemplateProperty, IconTemplate); t.SetCurrentValue(HeaderedContentControl.HeaderTemplateProperty, ItemTemplate); t.SetCurrentValue(ContentControl.ContentTemplateProperty, DescriptionTemplate); diff --git a/src/Ursa/Controls/Timeline/TimelineDisplayMode.cs b/src/Ursa/Controls/Timeline/TimelineDisplayMode.cs index 1907672..9ecfcd9 100644 --- a/src/Ursa/Controls/Timeline/TimelineDisplayMode.cs +++ b/src/Ursa/Controls/Timeline/TimelineDisplayMode.cs @@ -6,4 +6,11 @@ public enum TimelineDisplayMode Center, Right, Alternate, +} + +public enum TimelineItemDisplayMode +{ + Left, + Right, + Separate, } \ No newline at end of file diff --git a/src/Ursa/Controls/Timeline/TimelineItem.cs b/src/Ursa/Controls/Timeline/TimelineItem.cs index 8f08983..4b63f70 100644 --- a/src/Ursa/Controls/Timeline/TimelineItem.cs +++ b/src/Ursa/Controls/Timeline/TimelineItem.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; @@ -9,11 +10,30 @@ using Avalonia.Media; namespace Ursa.Controls; [PseudoClasses(PC_First, PC_Last, PC_EmptyIcon)] +[TemplatePart(PART_Header, typeof(ContentPresenter))] +[TemplatePart(PART_Icon, typeof(ContentPresenter))] +[TemplatePart(PART_Content, typeof(ContentPresenter))] +[TemplatePart(PART_Time, typeof(TextBlock))] +[TemplatePart(PART_RootGrid, typeof(Grid))] public class TimelineItem: HeaderedContentControl { public const string PC_First = ":first"; public const string PC_Last = ":last"; public const string PC_EmptyIcon = ":empty-icon"; + public const string PC_AllLeft=":all-left"; + public const string PC_AllRight=":all-right"; + public const string PC_Separate = ":separate"; + public const string PART_Header = "PART_Header"; + public const string PART_Icon = "PART_Icon"; + public const string PART_Content = "PART_Content"; + public const string PART_Time = "PART_Time"; + public const string PART_RootGrid = "PART_RootGrid"; + + private ContentPresenter? _headerPresenter; + private ContentPresenter? _iconPresenter; + private ContentPresenter? _contentPresenter; + private TextBlock? _timePresenter; + private Grid? _rootGrid; public static readonly StyledProperty IconProperty = AvaloniaProperty.Register( nameof(Icon)); @@ -41,6 +61,15 @@ public class TimelineItem: HeaderedContentControl get => GetValue(TypeProperty); set => SetValue(TypeProperty, value); } + + public static readonly StyledProperty ModeProperty = AvaloniaProperty.Register( + nameof(Mode), defaultValue: TimelineItemDisplayMode.Right); + + public TimelineItemDisplayMode Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } public static readonly DirectProperty LeftWidthProperty = AvaloniaProperty.RegisterDirect( nameof(LeftWidth), o => o.LeftWidth, (o, v) => o.LeftWidth = v); @@ -69,9 +98,53 @@ public class TimelineItem: HeaderedContentControl set => SetAndRaise(RightWidthProperty, ref _rightWidth, value); } + public static readonly StyledProperty TimeProperty = AvaloniaProperty.Register( + nameof(Time)); + + public DateTime Time + { + get => GetValue(TimeProperty); + set => SetValue(TimeProperty, value); + } + + public static readonly StyledProperty TimeFormatProperty = AvaloniaProperty.Register( + nameof(TimeFormat)); + + public string? TimeFormat + { + get => GetValue(TimeFormatProperty); + set => SetValue(TimeFormatProperty, value); + } + static TimelineItem() { IconProperty.Changed.AddClassHandler((item, args) => { item.OnIconChanged(args); }); + ModeProperty.Changed.AddClassHandler((item, args) => { item.OnModeChanged(args); }); + AffectsMeasure(LeftWidthProperty, RightWidthProperty, IconWidthProperty); + } + + private void OnModeChanged(AvaloniaPropertyChangedEventArgs args) + { + SetMode(args.NewValue.Value); + } + + private void SetMode(TimelineItemDisplayMode mode) + { + PseudoClasses.Set(PC_AllLeft, mode == TimelineItemDisplayMode.Left); + PseudoClasses.Set(PC_AllRight, mode == TimelineItemDisplayMode.Right); + PseudoClasses.Set(PC_Separate, mode == TimelineItemDisplayMode.Separate); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + PseudoClasses.Set(PC_EmptyIcon, Icon is null); + _headerPresenter = e.NameScope.Find(PART_Header); + _iconPresenter = e.NameScope.Find(PART_Icon); + _contentPresenter = e.NameScope.Find(PART_Content); + _timePresenter = e.NameScope.Find(PART_Time); + _rootGrid = e.NameScope.Find(PART_RootGrid); + SetMode(Mode); } private void OnIconChanged(AvaloniaPropertyChangedEventArgs args) @@ -84,4 +157,14 @@ public class TimelineItem: HeaderedContentControl PseudoClasses.Set(PC_First, start); PseudoClasses.Set(PC_Last, end); } + + internal (double?, double?, double?, double?) GetWidth() + { + return (_headerPresenter?.Bounds.Width, _contentPresenter?.Bounds.Width, _iconPresenter?.Bounds.Width, _timePresenter?.Bounds.Width); + } + + internal void SetWidth(double? header, double? content, double? icon, double? time) + { + _rootGrid.ColumnDefinitions[0].Width = new GridLength(200); + } } \ No newline at end of file diff --git a/src/Ursa/Controls/Timeline/TimelinePanel.cs b/src/Ursa/Controls/Timeline/TimelinePanel.cs index ea6efba..37371f4 100644 --- a/src/Ursa/Controls/Timeline/TimelinePanel.cs +++ b/src/Ursa/Controls/Timeline/TimelinePanel.cs @@ -25,19 +25,48 @@ public class TimelinePanel: Panel double left = 0; double right = 0; double icon = 0; + double width = 0; + double height = 0; + foreach (var child in Children) + { + child.Measure(availableSize); + if (child is TimelineItem t) + { + var doubles = t.GetWidth(); + } + width = Math.Max(width, child.DesiredSize.Width); + height+=child.DesiredSize.Height; + } foreach (var child in Children) { if (child is TimelineItem t) { - + t.LeftWidth = left; + t.RightWidth = right; + t.IconWidth = icon; } } - return base.MeasureOverride(availableSize); + + return new Size(width, height); } protected override Size ArrangeOverride(Size finalSize) { - return base.ArrangeOverride(finalSize); - + Rect rect = new Rect(); + foreach (var child in Children) + { + rect = rect.WithWidth(Math.Max(rect.Width, child.DesiredSize.Width)); + rect = rect.WithHeight(rect.Height + child.DesiredSize.Height); + child.Arrange(rect); + rect = rect.WithY(rect.Y+child.DesiredSize.Height); + if (child is TimelineItem t) + { + var doubles = t.GetWidth(); + t.SetWidth(0, 0, 0, 0); + } + + } + //return base.ArrangeOverride(finalSize); + return rect.Size; } } \ No newline at end of file