SettingsLayout.axaml.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using Avalonia;
  2. using Avalonia.Animation;
  3. using Avalonia.Animation.Easings;
  4. using Avalonia.Controls;
  5. using Avalonia.Controls.Primitives;
  6. using Avalonia.Controls.Templates;
  7. using Avalonia.Data;
  8. using Avalonia.LogicalTree;
  9. using Avalonia.Markup.Xaml;
  10. using Avalonia.Styling;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Linq;
  15. using System.Threading;
  16. using System.Threading.Tasks;
  17. namespace SukiUI.Controls;
  18. public class SettingsLayoutItem
  19. {
  20. public string Header { get; set; }
  21. public Control Content { get; set; }
  22. }
  23. public partial class SettingsLayout : UserControl
  24. {
  25. public SettingsLayout()
  26. {
  27. InitializeComponent();
  28. }
  29. protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
  30. {
  31. base.OnApplyTemplate(e);
  32. UpdateItems();
  33. }
  34. protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
  35. {
  36. }
  37. private void InitializeComponent()
  38. {
  39. AvaloniaXamlLoader.Load(this);
  40. }
  41. private ObservableCollection<SettingsLayoutItem> _items;
  42. public static readonly DirectProperty<SettingsLayout, ObservableCollection<SettingsLayoutItem>> StepsProperty =
  43. AvaloniaProperty.RegisterDirect<SettingsLayout, ObservableCollection<SettingsLayoutItem>>(nameof(Items),
  44. l => l.Items,
  45. (numpicker, v) => { numpicker.Items = v; }, defaultBindingMode: BindingMode.TwoWay,
  46. enableDataValidation: true);
  47. public ObservableCollection<SettingsLayoutItem> Items
  48. {
  49. get { return _items; }
  50. set { SetAndRaise(StepsProperty, ref _items, value); }
  51. }
  52. private void UpdateItems()
  53. {
  54. if (Items is null)
  55. {
  56. return;
  57. }
  58. var stackSummary = (StackPanel)this.GetTemplateChildren().First(n => n.Name == "StackSummary");
  59. var myScroll = (ScrollViewer)this.GetTemplateChildren().First(n => n.Name == "MyScroll");
  60. if (myScroll.Content is not StackPanel stackItems)
  61. return;
  62. stackItems.Children.Clear();
  63. stackSummary.Children.Clear();
  64. var radios = new List<RadioButton>();
  65. var borders = new List<Border>();
  66. stackItems.Children.Add(new Border() { Height = 8 });
  67. foreach (var settingsLayoutItem in Items)
  68. {
  69. var border = new Border
  70. {
  71. Child = new GroupBox()
  72. {
  73. Margin = new Thickness(10, 20),
  74. Header = new TextBlock() { Text = settingsLayoutItem.Header },
  75. Content = new Border()
  76. {
  77. Margin = new Thickness(35, 12),
  78. Child = settingsLayoutItem.Content
  79. }
  80. }
  81. };
  82. borders.Add(border);
  83. stackItems.Children.Add(border);
  84. var summaryButton = new RadioButton()
  85. {
  86. Content = new TextBlock() { Text = settingsLayoutItem.Header, FontSize = 17 },
  87. Classes = { "MenuChip" }
  88. };
  89. summaryButton.Click += async (sender, args) =>
  90. {
  91. if (isAnimatingScroll)
  92. return;
  93. var x = border.TranslatePoint(new Point(), stackItems);
  94. if (x.HasValue)
  95. await AnimateScroll(x.Value.Y); // myScroll.Offset = new Vector(0, x.Value.Y);
  96. };
  97. radios.Add(summaryButton);
  98. stackSummary.Children.Add(summaryButton);
  99. }
  100. stackSummary.Children.Add(new Border() { Height = 300 });
  101. myScroll.ScrollChanged += (sender, args) =>
  102. {
  103. if (isAnimatingScroll)
  104. return;
  105. var OffsetY = myScroll.Offset.Y;
  106. var l = borders.Select(b => Math.Abs(b.TranslatePoint(new Point(), stackItems).Value.Y - OffsetY)).ToList();
  107. radios[l.IndexOf(l.Min())].IsChecked = true;
  108. };
  109. }
  110. private Mutex mut = new Mutex();
  111. private double LastDesiredSize = -1;
  112. private async void DockPanel_SizeChanged(object sender, SizeChangedEventArgs e)
  113. {
  114. var stack = this.GetTemplateChildren().First(n => n.Name == "StackSummary");
  115. var desiredSize = e.NewSize.Width > 1100 ? 400 : 0;
  116. if (LastDesiredSize == desiredSize)
  117. return;
  118. LastDesiredSize = desiredSize;
  119. if (stack.Width != desiredSize && (stack.Width == 0 || stack.Width == 400))
  120. stack.Animate<double>(WidthProperty, stack.Width, desiredSize, TimeSpan.FromMilliseconds(800));
  121. }
  122. private bool isAnimatingWidth = false;
  123. private bool isAnimatingMargin = false;
  124. private bool isAnimatingScroll = false;
  125. private async Task AnimateScroll(double desiredScroll)
  126. {
  127. isAnimatingScroll = true;
  128. var myscroll = (ScrollViewer)this.GetTemplateChildren().First(n => n.Name == "MyScroll");
  129. var animationTask = new Animation
  130. {
  131. Duration = TimeSpan.FromMilliseconds(800),
  132. FillMode = FillMode.Forward,
  133. Easing = new CubicEaseInOut(),
  134. IterationCount = new IterationCount(1),
  135. PlaybackDirection = PlaybackDirection.Normal,
  136. Children =
  137. {
  138. new KeyFrame()
  139. {
  140. Setters = { new Setter { Property = ScrollViewer.OffsetProperty, Value = myscroll.Offset } },
  141. KeyTime = TimeSpan.FromSeconds(0)
  142. },
  143. new KeyFrame()
  144. {
  145. Setters =
  146. {
  147. new Setter
  148. {
  149. Property = ScrollViewer.OffsetProperty,
  150. Value = new Vector(myscroll.Offset.X, desiredScroll - 30)
  151. }
  152. },
  153. KeyTime = TimeSpan.FromMilliseconds(800)
  154. }
  155. }
  156. }.RunAsync(myscroll);
  157. var abortTask = Task.Run(async () =>
  158. {
  159. await Task.Delay(1000);
  160. isAnimatingScroll = false;
  161. });
  162. await Task.WhenAll(animationTask, abortTask);
  163. }
  164. }