commit 05429d4689a571d76567864b20a47f8a22f2065b Author: polarboy Date: Wed Jun 19 18:46:29 2024 +0800 完成 UI 项目从 ATOMDB 项目剥离 完成 UI 项目从 ATOMDB 项目剥离 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba4ef4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,199 @@ +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +.vs/ + +# Build results + +*.sln.ide/ +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Installshield output folder +[Ee]xpress/ + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# NCrunch +.NCrunch_*/ +_NCrunch_*/ +*.ncrunchsolution.user +nCrunchTemp_* + +# CodeRush +.cr/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + +################# +## Monodevelop +################# +*.userprefs +*.nugetreferenceswitcher + +################# +## Rider +################# +.idea + +################# +## VS Code +################# +.vscode/ + +################# +## Cake +################# +tools/* +!tools/packages.config +.nuget +artifacts/ +nuget +project.lock.json +.idea/* + +################## +## BenchmarkDotNet +################## +BenchmarkDotNet.Artifacts/ +dirs.sln + +################## +# Xcode +################## +Index/ +Logs/ +ModuleCache.noindex/ +Build/Intermediates.noindex/ +build-intermediate +obj-Direct2D1/ +obj-Skia/ + +################## +# Vim +################## +.vim +coc-settings.json +.ccls-cache +.ccls +*.map +node_modules +api/diff diff --git a/AtomUI.sln b/AtomUI.sln new file mode 100644 index 0000000..7db4669 --- /dev/null +++ b/AtomUI.sln @@ -0,0 +1,70 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI", "src\AtomUI\AtomUI.csproj", "{87085491-3C99-4C8F-8FA6-F179B9569CE8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Base", "src\AtomUI.Base\AtomUI.Base.csproj", "{8FFEB15F-7E48-4AF4-B708-8E96A68CF2D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Icon", "src\AtomUI.Icon\AtomUI.Icon.csproj", "{B2A7349B-4B38-45CB-8D22-3E06D1E3650F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Icon.Generator", "src\AtomUI.Icon.Generator\AtomUI.Icon.Generator.csproj", "{954FB7F2-7706-4E2B-86D1-624F66C1EFF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Icon.AntDesign", "src\AtomUI.Icon.AntDesign\AtomUI.Icon.AntDesign.csproj", "{021AE0B0-B148-4925-9C22-EBD5A7B43E95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Generator", "src\AtomUI.Generator\AtomUI.Generator.csproj", "{30910056-F8F6-4429-B25E-722C57440210}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Demo.Desktop", "Samples\AtomUI.Demo.Desktop\AtomUI.Demo.Desktop.csproj", "{59931F42-8DD8-4DFC-9060-563841F39669}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.TestBase", "tests\AtomUI.TestBase\AtomUI.TestBase.csproj", "{EADEF2AC-B7E5-436A-8B39-8BBECF8706AB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Base.Tests", "tests\AtomUI.Base.Tests\AtomUI.Base.Tests.csproj", "{E41D737A-5CF0-4B33-9F51-2C1B1541659C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomUI.Controls", "src\AtomUI.Controls\AtomUI.Controls.csproj", "{A07CB66E-7A5F-4C44-BB16-02A0A99D6C2D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {87085491-3C99-4C8F-8FA6-F179B9569CE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87085491-3C99-4C8F-8FA6-F179B9569CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87085491-3C99-4C8F-8FA6-F179B9569CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87085491-3C99-4C8F-8FA6-F179B9569CE8}.Release|Any CPU.Build.0 = Release|Any CPU + {8FFEB15F-7E48-4AF4-B708-8E96A68CF2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FFEB15F-7E48-4AF4-B708-8E96A68CF2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FFEB15F-7E48-4AF4-B708-8E96A68CF2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FFEB15F-7E48-4AF4-B708-8E96A68CF2D9}.Release|Any CPU.Build.0 = Release|Any CPU + {B2A7349B-4B38-45CB-8D22-3E06D1E3650F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2A7349B-4B38-45CB-8D22-3E06D1E3650F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2A7349B-4B38-45CB-8D22-3E06D1E3650F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2A7349B-4B38-45CB-8D22-3E06D1E3650F}.Release|Any CPU.Build.0 = Release|Any CPU + {954FB7F2-7706-4E2B-86D1-624F66C1EFF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {954FB7F2-7706-4E2B-86D1-624F66C1EFF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {954FB7F2-7706-4E2B-86D1-624F66C1EFF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {954FB7F2-7706-4E2B-86D1-624F66C1EFF0}.Release|Any CPU.Build.0 = Release|Any CPU + {021AE0B0-B148-4925-9C22-EBD5A7B43E95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {021AE0B0-B148-4925-9C22-EBD5A7B43E95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {021AE0B0-B148-4925-9C22-EBD5A7B43E95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {021AE0B0-B148-4925-9C22-EBD5A7B43E95}.Release|Any CPU.Build.0 = Release|Any CPU + {30910056-F8F6-4429-B25E-722C57440210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30910056-F8F6-4429-B25E-722C57440210}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30910056-F8F6-4429-B25E-722C57440210}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30910056-F8F6-4429-B25E-722C57440210}.Release|Any CPU.Build.0 = Release|Any CPU + {59931F42-8DD8-4DFC-9060-563841F39669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59931F42-8DD8-4DFC-9060-563841F39669}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59931F42-8DD8-4DFC-9060-563841F39669}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59931F42-8DD8-4DFC-9060-563841F39669}.Release|Any CPU.Build.0 = Release|Any CPU + {EADEF2AC-B7E5-436A-8B39-8BBECF8706AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EADEF2AC-B7E5-436A-8B39-8BBECF8706AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EADEF2AC-B7E5-436A-8B39-8BBECF8706AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EADEF2AC-B7E5-436A-8B39-8BBECF8706AB}.Release|Any CPU.Build.0 = Release|Any CPU + {E41D737A-5CF0-4B33-9F51-2C1B1541659C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E41D737A-5CF0-4B33-9F51-2C1B1541659C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E41D737A-5CF0-4B33-9F51-2C1B1541659C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E41D737A-5CF0-4B33-9F51-2C1B1541659C}.Release|Any CPU.Build.0 = Release|Any CPU + {A07CB66E-7A5F-4C44-BB16-02A0A99D6C2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A07CB66E-7A5F-4C44-BB16-02A0A99D6C2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A07CB66E-7A5F-4C44-BB16-02A0A99D6C2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A07CB66E-7A5F-4C44-BB16-02A0A99D6C2D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..ddbe3ed --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,20 @@ + + + true + true + $(NoWarn);CS1591;CS0436 + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + false + false + false + Nullable + + + + latest + 0.0.1-preview + Chinware Technologies Ltd. + https://atomui.net/ + 11.1.0-rc1 + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..fe52073 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,30 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/App.axaml b/Samples/AtomUI.Demo.Desktop/App.axaml new file mode 100644 index 0000000..4448fb6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/App.axaml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/App.axaml.cs b/Samples/AtomUI.Demo.Desktop/App.axaml.cs new file mode 100644 index 0000000..271ba4d --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/App.axaml.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using AtomUI.Demo.Desktop.Views; + +namespace AtomUI.Demo.Desktop; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + switch (ApplicationLifetime) + { + case IClassicDesktopStyleApplicationLifetime desktop: + desktop.MainWindow = new MainWindow(); + break; + case ISingleViewApplicationLifetime singleView: + singleView.MainView = new MainView(); + break; + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Assets/WORLD.png b/Samples/AtomUI.Demo.Desktop/Assets/WORLD.png new file mode 100644 index 0000000..7c5b71f Binary files /dev/null and b/Samples/AtomUI.Demo.Desktop/Assets/WORLD.png differ diff --git a/Samples/AtomUI.Demo.Desktop/Assets/avalonia-logo.ico b/Samples/AtomUI.Demo.Desktop/Assets/avalonia-logo.ico new file mode 100644 index 0000000..da8d49f Binary files /dev/null and b/Samples/AtomUI.Demo.Desktop/Assets/avalonia-logo.ico differ diff --git a/Samples/AtomUI.Demo.Desktop/AtomUI.Demo.Desktop.csproj b/Samples/AtomUI.Demo.Desktop/AtomUI.Demo.Desktop.csproj new file mode 100644 index 0000000..e847db2 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/AtomUI.Demo.Desktop.csproj @@ -0,0 +1,36 @@ + + + + WinExe + net8.0 + enable + enable + AtomUI.Demo.Desktop + + + + + + + + + true + app.manifest + + + + + + + + + + + + + + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/Base/ShowCaseItem.cs b/Samples/AtomUI.Demo.Desktop/Base/ShowCaseItem.cs new file mode 100644 index 0000000..b1af313 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Base/ShowCaseItem.cs @@ -0,0 +1,60 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.LogicalTree; +using Avalonia.Media; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public class ShowCaseItem : ContentControl +{ + private bool _initialized = false; + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + + private void SetupUi() + { + var mainLayout = new StackPanel(); + var showCaseTitle = new StackPanel + { + Orientation = Orientation.Horizontal, + Margin = new Thickness(0, 30, 0, 0) + }; + showCaseTitle.Children.Add(new Label() + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Left, + Content = Title, + FontWeight = FontWeight.Bold + }); + showCaseTitle.Children.Add(new Separator() + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Stretch, + Margin = new Thickness(10, 0), + Width = 200, + BorderBrush = new SolidColorBrush(Colors.Gray), + }); + + if (Content is Control contentControl) { + LogicalChildren.Remove(contentControl); + mainLayout.Children.Add(contentControl); + } + + mainLayout.Children.Add(showCaseTitle); + mainLayout.Children.Add(new TextBlock() + { + Text = Description, + TextWrapping = TextWrapping.Wrap + }); + Content = mainLayout; + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (!_initialized) { + SetupUi(); + _initialized = true; + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Base/ShowCasePanel.cs b/Samples/AtomUI.Demo.Desktop/Base/ShowCasePanel.cs new file mode 100644 index 0000000..b95c129 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Base/ShowCasePanel.cs @@ -0,0 +1,92 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.LogicalTree; +using Avalonia.Metadata; +using AvaloniaControlList = Avalonia.Controls.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public class ShowCasePanel : Control +{ + public string? Id { get; protected set; } + private bool _initialized = false; + private StackPanel _leftContainer = default!; + private StackPanel _rightContainer = default!; + + [Content] + public AvaloniaControlList Children { get; } = new AvaloniaControlList(); + + public ShowCasePanel() + { + } + + protected void SetupUi() + { + var mainLayout = new UniformGrid() + { + Rows = 1, + Columns = 2, + Margin = new Thickness(0) + }; + _leftContainer = new StackPanel() + { + Orientation = Orientation.Vertical, + Spacing = 20, + Margin = new Thickness(0, 0, 10, 0), + }; + _rightContainer = new StackPanel() + { + Orientation = Orientation.Vertical, + Spacing = 20, + }; + mainLayout.Children.Add(_leftContainer); + mainLayout.Children.Add(_rightContainer); + + for (int i = 0; i < Children.Count; ++i) { + var control = Children[i]; + if (i % 2 == 0) { + _leftContainer.Children.Add(control); + } else { + _rightContainer.Children.Add(control); + } + } + var scrollView = new ScrollViewer() + { + Content = mainLayout, + }; + LogicalChildren.Add(scrollView); + VisualChildren.Add(scrollView); + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (!_initialized) { + SetupUi(); + NotifyShowCaseLayoutReady(); + _initialized = true; + } + base.OnAttachedToLogicalTree(e); + } + + internal virtual void NotifyAboutToActive() + { + } + + internal virtual void NotifyActivated() + { + } + + internal virtual void NotifyAboutToDeactivated() + { + } + + internal virtual void NotifyDeactivated() + { + } + + protected virtual void NotifyShowCaseLayoutReady() + { + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.axaml b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.axaml new file mode 100644 index 0000000..1df7e2d --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.axaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.cs b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.cs new file mode 100644 index 0000000..c609c78 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorItemControl.cs @@ -0,0 +1,38 @@ +using Avalonia; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using CommunityToolkit.Mvvm.Messaging; +using AtomUI.Demo.Desktop.ViewModels; + +namespace AtomUI.Demo.Desktop.Controls; + +public class ColorItemControl : TemplatedControl +{ + public static readonly StyledProperty ColorNameProperty = AvaloniaProperty.Register( + nameof(ColorName)); + + public string? ColorName + { + get => GetValue(ColorNameProperty); + set => SetValue(ColorNameProperty, value); + } + + public static readonly StyledProperty HexProperty = AvaloniaProperty.Register( + nameof(Hex)); + + public string? Hex + { + get => GetValue(HexProperty); + set => SetValue(HexProperty, value); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + if (this.DataContext is ColorItemViewModel v) + { + WeakReferenceMessenger.Default.Send(v); + } + + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.axaml b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.axaml new file mode 100644 index 0000000..d45f10d --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.axaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.cs b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.cs new file mode 100644 index 0000000..b07a34e --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListControl.cs @@ -0,0 +1,17 @@ +using AtomUI.Demo.Desktop.ViewModels; +using Avalonia; +using Avalonia.Controls.Primitives; + +namespace AtomUI.Demo.Desktop.Controls; + +public class ColorListControl : TemplatedControl +{ + public static readonly StyledProperty ListDataProperty = AvaloniaProperty.Register( + nameof(ListData), new ColorListViewModel()); + + public ColorListViewModel ListData + { + get => GetValue(ListDataProperty); + set => SetValue(ListDataProperty, value); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.axaml b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.axaml new file mode 100644 index 0000000..a0f9d7b --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.axaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.cs b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.cs new file mode 100644 index 0000000..7c3aae5 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/Colors/ColorListGroupControl.cs @@ -0,0 +1,17 @@ +using AtomUI.Demo.Desktop.ViewModels; +using Avalonia; +using Avalonia.Controls.Primitives; + +namespace AtomUI.Demo.Desktop.Controls; + +public class ColorListGroupControl : TemplatedControl +{ + public static readonly StyledProperty GroupDataProperty = AvaloniaProperty.Register( + nameof(GroupData), new ColorGroupViewModel()); + + public ColorGroupViewModel GroupData + { + get => GetValue(GroupDataProperty); + set => SetValue(GroupDataProperty, value); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml new file mode 100644 index 0000000..35bc613 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml.cs b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml.cs new file mode 100644 index 0000000..eab4b92 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconGallery.axaml.cs @@ -0,0 +1,50 @@ +using Avalonia; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using CommunityToolkit.Mvvm.Messaging; +using AtomUI.Demo.Desktop.ViewModels; +using AtomUI.Icon; +using Avalonia.LogicalTree; + +namespace AtomUI.Demo.Desktop.Controls; + +public class IconGallery : TemplatedControl +{ + private bool _initialized = false; + private IconGalleryModel _galleryModel; + + public static readonly StyledProperty IconThemeTypeProperty = AvaloniaProperty.Register( + nameof(IconThemeType)); + + public IconThemeType? IconThemeType + { + get => GetValue(IconThemeTypeProperty); + set => SetValue(IconThemeTypeProperty, value); + } + + public IconGallery() + { + _galleryModel = new IconGalleryModel(); + DataContext = _galleryModel; + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + if (this.DataContext is ColorItemViewModel v) { + WeakReferenceMessenger.Default.Send(v); + } + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + if (!_initialized) { + if (IconThemeType.HasValue) { + _galleryModel.LoadThemeIcons(IconThemeType.Value); + } + _initialized = true; + } + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml new file mode 100644 index 0000000..bbd2dc4 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml.cs b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml.cs new file mode 100644 index 0000000..11cfbfb --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Controls/PathIcon/IconInfoItem.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls.Primitives; + +namespace AtomUI.Demo.Desktop.Controls; + +public class IconInfoItem : TemplatedControl +{ + public static readonly StyledProperty IconNameProperty = AvaloniaProperty.Register( + nameof(IconName)); + + public string IconName + { + get => GetValue(IconNameProperty); + set => SetValue(IconNameProperty, value); + } + + public static readonly StyledProperty IconKindProperty = AvaloniaProperty.Register( + nameof(IconKind)); + + public string IconKind + { + get => GetValue(IconKindProperty); + set => SetValue(IconKindProperty, value); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Program.cs b/Samples/AtomUI.Demo.Desktop/Program.cs new file mode 100644 index 0000000..2b518b7 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Program.cs @@ -0,0 +1,36 @@ +using AtomUI.Icon; +using AtomUI.Icon.AntDesign; +using Avalonia; +using Avalonia.Dialogs; +using Avalonia.Media; + +namespace AtomUI.Demo.Desktop; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .With(new FontManagerOptions + { + FontFallbacks = new[] + { + new FontFallback + { + FontFamily = new FontFamily("Microsoft YaHei") + } + } + }) + .StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UseManagedSystemDialogs() + .UsePlatformDetect() + .UseAtomUI() + .UseIconPackage(true) + .With(new Win32PlatformOptions()) + .LogToTrace(); +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Roots.xml b/Samples/AtomUI.Demo.Desktop/Roots.xml new file mode 100644 index 0000000..6d683a6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Roots.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml new file mode 100644 index 0000000..3304ca4 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml @@ -0,0 +1,157 @@ + + + + Success Text + + + + + Success Text + Info Text + Warning Text + Error Text + + + + + + + Warning Text Warning Text Warning Text Warning Text Warning Text Warning TextWarning Text + + + Error Text + + + Error Text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UNDO + + Success Tips + + + + + Detail + + Error Text + + + + + Done + + Warning Text + + + + + + Accept + Decline + + + Info Text + + + + + + + I can be a React component, multiple React components, or just some text, Info Description Info Description Info Description Info Description + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml.cs new file mode 100644 index 0000000..bfbea50 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/AlertShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class AlertShowCase : UserControl +{ + public AlertShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml new file mode 100644 index 0000000..10155d4 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml.cs new file mode 100644 index 0000000..2e47d7d --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/AvatarShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class AvatarShowCase : UserControl +{ + public AvatarShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml new file mode 100644 index 0000000..ab4adc6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml.cs new file mode 100644 index 0000000..42e250f --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/BadgeShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class BadgeShowCase : UserControl +{ + public BadgeShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml new file mode 100644 index 0000000..4c0b5bc --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml @@ -0,0 +1,233 @@ + + + + + + + + Primary + Default + Text + Link + + + + + + + Primary + Default + Text + Link + + + Primary + Default + Text + Link + + + AA + AA + AA + AA + + + + + + + + + + + + + + + + + Search + + + + + + + + + + + + Search + + + + + + + Search + + + + + + + Search + + + + + + + + Primary + + + Default + + + Text + + + Link + + + + + + + + Primary + + + Default + + + Text + + + Link + + + + + + + + + Primary + + + Default + + + Text + + + Link + + + Danger + + + + + + + + + + Primary + + + Primary(disabled) + + + + + Default + + + Default(disabled) + + + + + Text + + + Text(disabled) + + + + + Link + + + Link(disabled) + + + + + + Danger Primary + + + Danger Primary(disabled) + + + + + Danger Default + + + Danger Default(disabled) + + + + + Danger Text + + + Danger Text(disabled) + + + + + Danger Link + + + Danger Link(disabled) + + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml.cs new file mode 100644 index 0000000..7b2f1f0 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ButtonShowCase.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ButtonShowCase : UserControl +{ + public ButtonShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml new file mode 100644 index 0000000..765bd07 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml.cs new file mode 100644 index 0000000..0375dfe --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/CardShowCase.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class CardShowCase : UserControl +{ + public CardShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml new file mode 100644 index 0000000..5748199 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml @@ -0,0 +1,126 @@ + + + + + + + + Checkbox + + + + + + UnChecked + Indeterminate + Checked + + + + + + + + + + + + + + + + + Apple + Pear + Orange + + + Apple + Pear + Orange + + + Apple + Pear + Orange + + + + + + + + + + Check all + + + + + + Apple + + + Pear + + + Orange + + + + + + + + A + B + C + D + D + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml.cs new file mode 100644 index 0000000..ce54b25 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/CheckBoxShowCase.axaml.cs @@ -0,0 +1,13 @@ +using AtomUI.Demo.Desktop.ViewModels; +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class CheckBoxShowCase : UserControl +{ + public CheckBoxShowCase() + { + DataContext = new CheckBoxShowCaseModel(); + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml new file mode 100644 index 0000000..bf1de74 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml.cs new file mode 100644 index 0000000..1429f22 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ComboBoxShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ComboBoxShowCase : UserControl +{ + public ComboBoxShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml new file mode 100644 index 0000000..e483dc6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml @@ -0,0 +1,22 @@ + + + + + Primary Button + Default Button + Text Button + Link Button + + + + diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml.cs new file mode 100644 index 0000000..81f2454 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/EmptyShowCase.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class EmptyShowCase : UserControl +{ + public EmptyShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml new file mode 100644 index 0000000..4fa1d68 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml.cs new file mode 100644 index 0000000..b0658cb --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ExpanderShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ExpanderShowCase : UserControl +{ + public ExpanderShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml new file mode 100644 index 0000000..05bf8a0 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml.cs new file mode 100644 index 0000000..8d6bc44 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/IconShowCase.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Threading; +using AtomUI.Demo.Desktop.ViewModels; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class IconShowCase : UserControl +{ + public IconShowCase() + { + InitializeComponent(); + } + + protected override async void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + PaletteDemoViewModel vm = new PaletteDemoViewModel(); + await Dispatcher.UIThread.InvokeAsync(() => + { + vm.InitializeResources(); + }); + DataContext = vm; + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml new file mode 100644 index 0000000..df79ecc --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml.cs new file mode 100644 index 0000000..56f85d1 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/InputNumberShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class InputNumberShowCase : UserControl +{ + public InputNumberShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml new file mode 100644 index 0000000..c802720 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml.cs new file mode 100644 index 0000000..9667a72 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/LineEditShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class LineEditShowCase : UserControl +{ + public LineEditShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml new file mode 100644 index 0000000..83d4f39 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml.cs new file mode 100644 index 0000000..023215f --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ListBoxShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ListBoxShowCase : UserControl +{ + public ListBoxShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml new file mode 100644 index 0000000..4bdc6f5 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml.cs new file mode 100644 index 0000000..cddefe9 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class MenuShowCase : UserControl +{ + public MenuShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml new file mode 100644 index 0000000..8db7760 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml.cs new file mode 100644 index 0000000..af7f217 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/Overview.axaml.cs @@ -0,0 +1,47 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class Overview : UserControl +{ + public Overview() + { + InitializeComponent(); + } + + public string MainInstall { get; set; } = "dotnet add package AtomUI --version 11.0.7"; + + public string MainStyle { get; set; } = """ + + + +"""; + + public string ColorPickerInstall { get; set; } = "dotnet add package AtomUI.ColorPicker --version 11.0.7"; + + public string ColorPickerStyle { get; set; } = """ + + + +"""; + + public string DataGridInstall { get; set; } = "dotnet add package AtomUI.DataGrid --version 11.0.7"; + + public string DataGridStyle { get; set; } = """ + + + +"""; + + public string TreeDataGridInstall { get; set; } = "dotnet add package AtomUI.TreeDataGrid --version 11.0.7"; + + public string TreeDataGridStyle { get; set; } = """ + + + +"""; +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml new file mode 100644 index 0000000..c126bcc --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml.cs new file mode 100644 index 0000000..a37dea7 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PaginationShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class PaginationShowCase : UserControl +{ + public PaginationShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml new file mode 100644 index 0000000..758e809 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml.cs new file mode 100644 index 0000000..595d318 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PaletteShowCase.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Threading; +using AtomUI.Demo.Desktop.ViewModels; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class PaletteShowCase : UserControl +{ + public PaletteShowCase() + { + InitializeComponent(); + } + + protected override async void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + PaletteDemoViewModel vm = new PaletteDemoViewModel(); + await Dispatcher.UIThread.InvokeAsync(() => + { + vm.InitializeResources(); + }); + DataContext = vm; + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml new file mode 100644 index 0000000..e20093d --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml.cs new file mode 100644 index 0000000..d3b28da --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/PopoverShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class PopoverShowCase : UserControl +{ + public PopoverShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml new file mode 100644 index 0000000..e4bb00b --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs new file mode 100644 index 0000000..ca52044 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ProgressBarShowCase : UserControl +{ + public ProgressBarShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml new file mode 100644 index 0000000..0de9735 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml.cs new file mode 100644 index 0000000..11b2ebd --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressIndicatorShowCase.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ProgressIndicatorShowCase : UserControl +{ + public ProgressIndicatorShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml new file mode 100644 index 0000000..de83004 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml @@ -0,0 +1,183 @@ + + + + + + + + Radio + + + + + + + Radio1 + Radio2 + + + toggle disabled + + + + + + + A + B + C + D + + + + + + Option A + Option B + Option C + Option D + + + + + + + Apple + Pear + Orange + + + Apple + Pear + Orange + + + + + Apple + Pear + Orange + + + + Apple + Pear + Orange + + + + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + Hangzhou + Shanghai + Beijing + Chengdu + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml.cs new file mode 100644 index 0000000..cc773b1 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/RadioButtonShowCase.axaml.cs @@ -0,0 +1,33 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class RadioButtonShowCase : UserControl +{ + protected List CheckRadios { get; set; } + + public RadioButtonShowCase() + { + CheckRadios = new List() + { + "ToggleDisabledRadioUnChecked", + "ToggleDisabledRadioChecked" + }; + InitializeComponent(); + } + + public static void ToggleDisabledStatus(object arg) + { + var btn = (arg as Button)!; + var stackPanel = btn.Parent as StackPanel; + var radioBtn1 = stackPanel?.FindControl("ToggleDisabledRadioUnChecked"); + var radioBtn2 = stackPanel?.FindControl("ToggleDisabledRadioChecked"); + if (radioBtn1 != null) { + radioBtn1.IsEnabled = !radioBtn1.IsEnabled; + } + + if (radioBtn2 != null) { + radioBtn2.IsEnabled = !radioBtn2.IsEnabled; + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml new file mode 100644 index 0000000..af6bc11 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml @@ -0,0 +1,146 @@ + + + + + + Daily + Weekly + Monthly + Quarterly + Yearly + + + + + + + + 123 + 456 + longtext-longtext-longtext-longtext + + + + + + + + Map + Transit + Satellite + + + Daily + Weekly + Monthly + Quarterly + Yearly + + + + + + + + Daily + Weekly + Monthly + Quarterly + Yearly + + + + Daily + Weekly + Monthly + Quarterly + Yearly + + + + Daily + Weekly + Monthly + Quarterly + Yearly + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List + + + + + + + + + + + + + + Ava 牛逼 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml.cs new file mode 100644 index 0000000..0aab06f --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SegmentedShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class SegmentedShowCase : UserControl +{ + public SegmentedShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml new file mode 100644 index 0000000..e410b56 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml.cs new file mode 100644 index 0000000..de2c678 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SeparatorShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class SeparatorShowCase : UserControl +{ + public SeparatorShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml new file mode 100644 index 0000000..83d9738 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml.cs new file mode 100644 index 0000000..fc28682 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SliderShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class SliderShowCase : UserControl +{ + public SliderShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml new file mode 100644 index 0000000..3add9bd --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml.cs new file mode 100644 index 0000000..da2dbcc --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SpinIndicatorShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class SpinIndicatorShowCase : UserControl +{ + public SpinIndicatorShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml new file mode 100644 index 0000000..8fdd536 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + toggle disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + toggle loading + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml.cs new file mode 100644 index 0000000..f790f07 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/SwitchShowCase.axaml.cs @@ -0,0 +1,36 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class SwitchShowCase : UserControl +{ + public SwitchShowCase() + { + InitializeComponent(); + } + + public static void ToggleDisabledStatus(object arg) + { + var switchBtn = (arg as ToggleSwitch)!; + switchBtn.IsEnabled = !switchBtn.IsEnabled; + } + + public static void ToggleLoadingStatus(object arg) + { + var btn = (arg as Button)!; + var stackPanel = btn.Parent as StackPanel; + var toggleSwitchDefault = stackPanel?.Children[0] as ToggleSwitch; + var toggleSwitchSmall = stackPanel?.Children[1] as ToggleSwitch; + if (toggleSwitchDefault is not null) { + toggleSwitchDefault.IsLoading = !toggleSwitchDefault.IsLoading; + } + + if (toggleSwitchSmall is not null) { + toggleSwitchSmall.IsLoading = !toggleSwitchSmall.IsLoading; + } + } + + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml new file mode 100644 index 0000000..fc09126 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml.cs new file mode 100644 index 0000000..82a3960 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TabControlShowCase : UserControl +{ + public TabControlShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml new file mode 100644 index 0000000..0599af1 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml @@ -0,0 +1,185 @@ + + + + + + + + Tag 1 + Link + Prevent Default + + + + + Tag 2 + + + + + + + Presets + + magenta + red + volcano + orange + gold + lime + green + cyan + blue + geekblue + purple + + + Custom + + #f50 + #2db7f5 + #87d068 + #108ee9 + + + + + + + + Without icon + + success + processing + error + warning + default + + + Custom + + + + + + success + + + + + + processing + + + + + + error + + + + + + warning + + + + + + default + + + + + + default + + + + + + + + + + + + + Twitter + + + + + + Youtube + + + + + + Facebook + + + + + + Linkedin + + + + + + + + Tag1 + Tag2 + Tag3 + Tag4 + + + + + magenta + red + volcano + orange + gold + lime + green + cyan + blue + geekblue + purple + + success + processing + error + warning + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml.cs new file mode 100644 index 0000000..d810e9b --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TagShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TagShowCase : UserControl +{ + public TagShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml new file mode 100644 index 0000000..03ba4cc --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml.cs new file mode 100644 index 0000000..3408990 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TimelineShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TimelineShowCase : UserControl +{ + public TimelineShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml new file mode 100644 index 0000000..9465263 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml.cs new file mode 100644 index 0000000..3af90d6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TitleBarShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TitleBarShowCase : UserControl +{ + public TitleBarShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml new file mode 100644 index 0000000..66cbfe7 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml.cs new file mode 100644 index 0000000..e7a24b3 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ToolBarShowCase.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class ToolBarShowCase : UserControl +{ + public ToolBarShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml new file mode 100644 index 0000000..3d69e80 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml.cs new file mode 100644 index 0000000..c65dd28 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TooltipShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TooltipShowCase : UserControl +{ + public TooltipShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml new file mode 100644 index 0000000..86ad3ae --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml.cs new file mode 100644 index 0000000..b975cd0 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/TreeViewShowCase.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Button = AtomUI.Controls.Button; +using ToggleSwitch = AtomUI.Controls.ToggleSwitch; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class TreeViewShowCase : UserControl +{ + public TreeViewShowCase() + { + InitializeComponent(); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Themes/TabMenu.axaml b/Samples/AtomUI.Demo.Desktop/Themes/TabMenu.axaml new file mode 100644 index 0000000..ccdbbf4 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Themes/TabMenu.axaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/Themes/ToggleButton.axaml b/Samples/AtomUI.Demo.Desktop/Themes/ToggleButton.axaml new file mode 100644 index 0000000..acbfbff --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Themes/ToggleButton.axaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/Samples/AtomUI.Demo.Desktop/ViewModels/CheckBoxShowCaseModel.cs b/Samples/AtomUI.Demo.Desktop/ViewModels/CheckBoxShowCaseModel.cs new file mode 100644 index 0000000..b904bff --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ViewModels/CheckBoxShowCaseModel.cs @@ -0,0 +1,165 @@ +using AtomUI.Controls; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace AtomUI.Demo.Desktop.ViewModels; + +public class CheckBoxShowCaseModel : ObservableObject +{ + public bool? _controlledCheckBoxCheckedStatus; + public bool? ControlledCheckBoxCheckedStatus + { + get => _controlledCheckBoxCheckedStatus; + set => SetProperty(ref _controlledCheckBoxCheckedStatus, value); + } + + public bool _controlledCheckBoxEnabledStatus; + public bool ControlledCheckBoxEnabledStatus + { + get => _controlledCheckBoxEnabledStatus; + set => SetProperty(ref _controlledCheckBoxEnabledStatus, value); + } + + private string? _checkStatusBtnText; + public string? CheckStatusBtnText + { + get => _checkStatusBtnText; + set => SetProperty(ref _checkStatusBtnText, value); + } + + private string? _enableStatusBtnText; + public string? EnableStatusBtnText + { + get => _enableStatusBtnText; + set => SetProperty(ref _enableStatusBtnText, value); + } + + private string? _controlledCheckBoxText; + public string? ControlledCheckBoxText + { + get => _controlledCheckBoxText; + set => SetProperty(ref _controlledCheckBoxText, value); + } + + // CheckAll 例子 + private bool? _checkedAllStatus; + public bool? CheckedAllStatus + { + get => _checkedAllStatus; + set => SetProperty(ref _checkedAllStatus, value); + } + + private bool _appleCheckedStatus; + public bool AppleCheckedStatus + { + get => _appleCheckedStatus; + set => SetProperty(ref _appleCheckedStatus, value); + } + + private bool _pearCheckedStatus; + public bool PearCheckedStatus + { + get => _pearCheckedStatus; + set => SetProperty(ref _pearCheckedStatus, value); + } + + private bool _orangeCheckedStatus; + public bool OrangeCheckedStatus + { + get => _orangeCheckedStatus; + set => SetProperty(ref _orangeCheckedStatus, value); + } + + public CheckBoxShowCaseModel() + { + CheckStatusBtnText = "UnCheck"; + EnableStatusBtnText = "Disable"; + ControlledCheckBoxCheckedStatus = true; + ControlledCheckBoxEnabledStatus = true; + SetupControlledCheckBoxText(); + + AppleCheckedStatus = false; + PearCheckedStatus = true; + OrangeCheckedStatus = true; + CheckedAllStatus = null; + } + + public void CheckStatusHandler(object arg) + { + ControlledCheckBoxCheckedStatus = !ControlledCheckBoxCheckedStatus; + SetupCheckBtnText(); + SetupControlledCheckBoxText(); + } + + public void EnableStatusHandler(object arg) + { + ControlledCheckBoxEnabledStatus = !ControlledCheckBoxEnabledStatus; + SetupEnabledBtnText(); + SetupControlledCheckBoxText(); + } + + public void CheckBoxHandler(object arg) + { + SetupCheckBtnText(); + SetupControlledCheckBoxText(); + } + + private void SetupCheckBtnText() + { + if (ControlledCheckBoxCheckedStatus.HasValue) { + if (ControlledCheckBoxCheckedStatus.Value) { + CheckStatusBtnText = "UnCheck"; + } else { + CheckStatusBtnText = "Check"; + } + } else { + CheckStatusBtnText = "Check"; + } + } + + private void SetupEnabledBtnText() + { + if (ControlledCheckBoxEnabledStatus) { + EnableStatusBtnText = "Disable"; + } else { + EnableStatusBtnText = "Enable"; + } + } + + private void SetupControlledCheckBoxText() + { + var checkedText = "UnChecked"; + if (ControlledCheckBoxCheckedStatus.HasValue && ControlledCheckBoxCheckedStatus.Value) { + checkedText = "Checked"; + } + + var enabledText = "Disabled"; + if (ControlledCheckBoxEnabledStatus) { + enabledText = "Enabled"; + } + ControlledCheckBoxText = $"{checkedText}-{enabledText}"; + } + + public void CheckedAllStatusHandler() + { + if (!CheckedAllStatus.HasValue || !CheckedAllStatus.Value) { + AppleCheckedStatus = false; + PearCheckedStatus = false; + OrangeCheckedStatus = false; + } else { + AppleCheckedStatus = true; + PearCheckedStatus = true; + OrangeCheckedStatus = true; + } + } + + public void CheckedItemStatusHandler(object arg) + { + if (OrangeCheckedStatus && PearCheckedStatus && AppleCheckedStatus) { + CheckedAllStatus = true; + } else if (!OrangeCheckedStatus && !PearCheckedStatus && !AppleCheckedStatus) { + CheckedAllStatus = false; + } else { + CheckedAllStatus = null; + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ViewModels/DataGridDemoViewModel.cs b/Samples/AtomUI.Demo.Desktop/ViewModels/DataGridDemoViewModel.cs new file mode 100644 index 0000000..6a5d9a4 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ViewModels/DataGridDemoViewModel.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace AtomUI.Demo.Desktop.ViewModels; + +public class DataGridDemoViewModel: ObservableObject +{ + public ObservableCollection GridData1 { get; set; } + + public DataGridCollectionView GridData2 { get; set; } + + public ObservableCollection GridData3 { get; set; } + + public RelayCommand AddCommand { get; set; } + + public DataGridDemoViewModel() + { + GridData1 = new ObservableCollection(Song.Songs); + GridData2 = new DataGridCollectionView(Song.Songs); + GridData2.GroupDescriptions.Add(new DataGridPathGroupDescription("Album")); + GridData3 = new ObservableCollection(Song.Songs.Take(10).Select(a=>new SongViewModel() + { + Title = a.Title, + Artist = a.Artist, + Album = a.Album, + CountOfComment = a.CountOfComment, + IsSelected = false + })); + AddCommand = new RelayCommand(Add); + } + + private void Add() + { + GridData3.Add(new SongViewModel()); + } +} + +public class Song +{ + public string? Title { get; set; } + public string? Artist { get; set; } + public TimeSpan? Duration { get; set; } + public string? Album { get; set; } + public int CountOfComment { get; set; } + public string Url { get; set; } + + public Song(string title, string artist, int m, int s, string album, int countOfComment, int netEaseId) + { + Title = title; + Artist = artist; + Duration = new TimeSpan(0, m, s); + Album = album; + CountOfComment = countOfComment; + Url = $"https://music.163.com/song?id={netEaseId}"; + + } + + public static List Songs { get; set; } = new List() + { + new("好肚有肚(feat.李玲玉)", "熊猫堂ProducePandas", 2, 50, "A.S.I.A", 730, 1487039339), + new("荒诞秀", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 639, 1487037601), + new("长大", "熊猫堂ProducePandas", 4, 6, "A.S.I.A", 1114, 1487037690), + new("招财猫(feat.纪粹希(G-Tracy))", "熊猫堂ProducePandas", 3, 37, "A.S.I.A", 361, 1487039632), + new("千转", "熊猫堂ProducePandas", 4, 0, "A.S.I.A", 1115, 1477312398), + new("辣辣辣", "熊猫堂ProducePandas", 3, 24, "A.S.I.A", 1873, 1465043716), + new("碎碎念", "熊猫堂ProducePandas", 3, 25, "A.S.I.A", 676, 1474142064), + new("盘他", "熊猫堂ProducePandas", 2, 16, "A.S.I.A", 365, 1481652786), + new("Na Na Na", "熊猫堂ProducePandas", 3, 26, "A.S.I.A", 312, 1469022662), + new("Indigo", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 137, 1487039517), + new("饕餮人间", "熊猫堂ProducePandas", 3, 20, "饕餮人间", 1295, 1499584605), + new("七步咙咚呛", "熊猫堂ProducePandas", 3, 10, "七步咙咚呛", 175, 1809095152), + new("大惊小怪", "熊猫堂ProducePandas", 3, 32, "大惊小怪", 10420, 1847477425), + new("工具人", "熊猫堂ProducePandas", 2, 46, "大惊小怪", 1135, 1847476499), + new("以梦为马", "熊猫堂ProducePandas", 4, 19, "大惊小怪", 18361, 1836034373), + new("以梦为马(Piano Version)", "熊猫堂ProducePandas", 3, 4, "大惊小怪", 570, 1847477423), + new("The ONE", "熊猫堂ProducePandas", 2, 58, "The ONE", 1508, 1864329424), + new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "The ONE", 385, 1864329429), + new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 19, "以梦为马 (壮志骄阳版)", 161, 1865138896), + new("New Horse", "熊猫堂ProducePandas", 2, 30, "emo了", 643, 1887021307), + new("不例外", "熊猫堂ProducePandas", 3, 31, "emo了", 1818, 1887022665), + new("满意", "熊猫堂ProducePandas", 4, 32, "emo了", 1081, 1882433472), + new("就算与全世界为敌也要跟你在一起", "熊猫堂ProducePandas", 3, 32, "emo了", 2119, 1881759960), + new("The ONE", "熊猫堂ProducePandas", 2, 58, "emo了", 67, 1887022648), + new("口香糖", "熊猫堂ProducePandas", 3, 10, "emo了", 2181, 1885502254), + new("Suuuuuuper Mario", "熊猫堂ProducePandas", 3, 32, "emo了", 1010, 1887021318), + new("饕餮人间", "熊猫堂ProducePandas", 3, 22, "emo了", 109, 1887021320), + new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 21, "emo了", 34, 1887022666), + new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "emo了", 27, 1887022646), + new("满意(DJheap九天版)", "熊猫堂ProducePandas", 4, 31, "emo了", 31, 1901605941), + new("一眼万年", "熊猫堂ProducePandas", 3, 54, "一眼万年", 20, 1922599361), + new("冲刺", "熊猫堂ProducePandas", 3, 49, "冲刺吧", 1006, 1932878194), + new("滴答滴", "熊猫堂ProducePandas", 2, 30, "爱的赏味期限", 86, 1957515790), + new("热带季风", "熊猫堂ProducePandas", 2, 45, "爱的赏味期限", 212, 1957514964), + new("渣", "熊猫堂ProducePandas", 3, 28, "爱的赏味期限", 22, 1957514965), + new("独特", "熊猫堂ProducePandas", 3, 33, "爱的赏味期限", 62, 1957514966), + new("雨后", "熊猫堂ProducePandas", 4, 15, "爱的赏味期限", 23, 1957514967), + new("然后然后", "熊猫堂ProducePandas", 3, 50, "爱的赏味期限", 108, 1957514968), + new("丢", "熊猫堂ProducePandas", 3, 26, "爱的赏味期限", 30, 1957515792), + new("热带疾风(FACEVOID桃心连哥 Remix)", "熊猫堂ProducePandas", 3, 23, "爱的赏味期限", 55, 1957515793), + new("COSMIC ANTHEM -Japanese Ver.-", "熊猫堂ProducePandas", 3, 11, "COSMIC ANTHEM / 手紙", 0, 1977171493), + new("手紙 (「長大-You Raise Me Up-」-Japanese Ver.-)", "熊猫堂ProducePandas", 4, 11, "COSMIC ANTHEM / 手紙", 0, + 1977171494), + new("COSMIC ANTHEM -Chinese Ver.-", "熊猫堂ProducePandas", 3, 31, "COSMIC ANTHEM / 手紙", 0, 1977172202), + new("世界晚安", "熊猫堂ProducePandas", 2, 59, "世界晚安", 652, 1985063377), + new("世界晚安(泰文版)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 134, 1987842504), + new("世界晚安(钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 76, 1990475933), + new("世界晚安(泰文钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 29, 1990475934), + new("世界晚安(DJ沈念版)", "熊猫堂ProducePandas", 3, 9, "世界晚安", 34, 2014263184), + new("世界晚安(钢琴配乐)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 11, 2014263185), + new("明年也要好好长大", "熊猫堂ProducePandas", 3, 12, "明年也要好好长大", 0, 2010515162), + new("320万年前(DJ沈念版)", "熊猫堂ProducePandas", 3, 21, "320万年前", 8, 2055888636), + new("320万年前", "熊猫堂ProducePandas", 3, 7, "W.O.R.L.D.", 329, 2049770469), + new("隐德来希", "熊猫堂ProducePandas", 3, 3, "W.O.R.L.D.", 594, 2061317924), + new("孔明", "熊猫堂ProducePandas", 3, 59, "W.O.R.L.D.", 91, 2063175274), + new("锦鲤卟噜噜", "熊猫堂ProducePandas", 3, 5, "W.O.R.L.D.", 67, 2059208262), + new("指鹿为马", "熊猫堂ProducePandas", 3, 12, "W.O.R.L.D.", 74, 2063175272), + new("热带季风Remix", "熊猫堂ProducePandas", 3, 22, "W.O.R.L.D.", 23, 2063173319), + new("加州梦境", "熊猫堂ProducePandas", 2, 56, "W.O.R.L.D.", 1662, 2063173324), + new("渐近自由", "熊猫堂ProducePandas", 4, 19, "W.O.R.L.D.", 124, 2063173321), + new("世界所有的烂漫", "熊猫堂ProducePandas", 3, 30, "W.O.R.L.D.", 335, 2053388775), + }; +} + +public class SongViewModel: ObservableObject +{ + private string? _title; + private string? _artist; + private string? _album; + private int _countOfComment; + private bool? _isSelected; + public string? Title + { + get => _title; + set => SetProperty(ref _title, value); + } + public string? Artist + { + get => _artist; + set => SetProperty(ref _artist, value); + } + public string? Album + { + get => _album; + set => SetProperty(ref _album, value); + } + public int CountOfComment + { + get => _countOfComment; + set => SetProperty(ref _countOfComment, value); + } + public bool? IsSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ViewModels/PaletteDemoViewModel.cs b/Samples/AtomUI.Demo.Desktop/ViewModels/PaletteDemoViewModel.cs new file mode 100644 index 0000000..5062803 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ViewModels/PaletteDemoViewModel.cs @@ -0,0 +1,234 @@ +using System.Collections.ObjectModel; +using AtomUI.ColorSystem; +using Avalonia.Media; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using SolidColorBrush = Avalonia.Media.SolidColorBrush; + +namespace AtomUI.Demo.Desktop.ViewModels; + +public class PaletteMetaItem +{ + public string Title; + public string Desc; + public PresetPrimaryColor PresetPrimaryColor; + + public PaletteMetaItem(string title, string desc, PresetPrimaryColor presetPrimaryColor) + { + Title = title; + Desc = desc; + PresetPrimaryColor = presetPrimaryColor; + } +} + +public class PaletteDemoViewModel : ObservableObject +{ + private readonly PaletteMetaItem[] _presetPaletteInfos = + { + new PaletteMetaItem("Dust Red / 薄暮", "斗志、奔放", PresetPrimaryColor.Red), + new PaletteMetaItem("Volcano / 火山", "醒目、澎湃", PresetPrimaryColor.Volcano), + new PaletteMetaItem("Sunset Orange / 日暮", "温暖、欢快", PresetPrimaryColor.Orange), + new PaletteMetaItem("Calendula Gold / 金盏花", "活力、积极", PresetPrimaryColor.Gold), + new PaletteMetaItem("Sunrise Yellow / 日出", "出生、阳光", PresetPrimaryColor.Yellow), + new PaletteMetaItem("Lime / 青柠", "自然、生机", PresetPrimaryColor.Lime), + new PaletteMetaItem("Polar Green / 极光绿", "健康、创新", PresetPrimaryColor.Green), + new PaletteMetaItem("Cyan / 明青", "希望、坚强", PresetPrimaryColor.Cyan), + new PaletteMetaItem("Daybreak Blue / 拂晓蓝", "包容、科技、普惠", PresetPrimaryColor.Blue), + new PaletteMetaItem("Geek Blue / 极客蓝", "探索、钻研", PresetPrimaryColor.GeekBlue), + new PaletteMetaItem("Golden Purple / 酱紫", "优雅、浪漫", PresetPrimaryColor.Purple), + new PaletteMetaItem("Magenta / 法式洋红", "明快、感性", PresetPrimaryColor.Magenta), + }; + + private ColorItemViewModel _selectedColor = null!; + + public ColorItemViewModel SelectedColor + { + get => _selectedColor; + set => SetProperty(ref _selectedColor, value); + } + + private ObservableCollection? _lightLists; + + public ObservableCollection? LightLists + { + get => _lightLists; + set => SetProperty(ref _lightLists, value); + } + + private ObservableCollection? _darkLists; + + public ObservableCollection? DarkLists + { + get => _darkLists; + set => SetProperty(ref _darkLists, value); + } + + public PaletteDemoViewModel() + { + WeakReferenceMessenger.Default.Register(this, OnClickColorItem); + } + + public void InitializeResources() + { + InitializePalette(); + } + + private void InitializePalette() + { + LightLists = new ObservableCollection(); + var cycleColorList = new ObservableCollection(); + int cycleCount = 0; + for (int i = 0; i < _presetPaletteInfos.Length; ++i) { + var metaInfo = _presetPaletteInfos[i]; + ColorListViewModel colorListViewModel = new ColorListViewModel(); + colorListViewModel.Title = metaInfo.Title; + colorListViewModel.Desc = metaInfo.Desc; + var paletteInfo = PresetPalettes.GetPresetPalette(metaInfo.PresetPrimaryColor); + var colorItemViewModels = new ObservableCollection(); + var presetColorName = metaInfo.PresetPrimaryColor.Name(); + + for (int j = 0; j < paletteInfo.ColorSequence.Count; j++) { + var color = paletteInfo.ColorSequence[j]; + var colorItem = new ColorItemViewModel($"{presetColorName}-{j + 1}", + new SolidColorBrush(color), + true, + j); + colorItemViewModels.Add(colorItem); + } + + colorListViewModel.Colors = colorItemViewModels; + cycleColorList.Add(colorListViewModel); + ++cycleCount; + + if (cycleCount == 3) { + var colorGroupModel = new ColorGroupViewModel(); + colorGroupModel.ColorList = cycleColorList; + LightLists.Add(colorGroupModel); + cycleColorList = new ObservableCollection(); + cycleCount = 0; + } + } + + DarkLists = new ObservableCollection(); + + for (int i = 0; i < _presetPaletteInfos.Length; ++i) { + var metaInfo = _presetPaletteInfos[i]; + ColorListViewModel colorListViewModel = new ColorListViewModel(); + colorListViewModel.Title = metaInfo.Title; + colorListViewModel.Desc = metaInfo.Desc; + var paletteInfo = PresetPalettes.GetPresetPalette(metaInfo.PresetPrimaryColor, true); + var colorItemViewModels = new ObservableCollection(); + var presetColorName = metaInfo.PresetPrimaryColor.Name(); + + for (int j = 0; j < paletteInfo.ColorSequence.Count; j++) { + var color = paletteInfo.ColorSequence[j]; + var colorItem = new ColorItemViewModel($"{presetColorName}-{j + 1}", + new SolidColorBrush(color), + false, + j); + colorItemViewModels.Add(colorItem); + } + + colorListViewModel.Colors = colorItemViewModels; + cycleColorList.Add(colorListViewModel); + ++cycleCount; + + if (cycleCount == 3) { + var colorGroupModel = new ColorGroupViewModel(); + colorGroupModel.ColorList = cycleColorList; + DarkLists.Add(colorGroupModel); + cycleColorList = new ObservableCollection(); + cycleCount = 0; + } + } + } + + private void OnClickColorItem(PaletteDemoViewModel vm, ColorItemViewModel item) + { + SelectedColor = item; + } +} + +public class ColorGroupViewModel : ObservableObject +{ + private ObservableCollection? _colorList; + + public ObservableCollection? ColorList + { + get => _colorList; + set => SetProperty(ref _colorList, value); + } +} + +public class ColorListViewModel : ObservableObject +{ + private ObservableCollection? _colors; + + public ObservableCollection? Colors + { + get => _colors; + set => SetProperty(ref _colors, value); + } + + private string? _title; + + public string? Title + { + get => _title; + set => SetProperty(ref _title, value); + } + + private string? _desc; + public string? Desc + { + get => _desc; + set => SetProperty(ref _desc, value); + } +} + +public class ColorItemViewModel : ObservableObject +{ + private IBrush _brush = null!; + + public IBrush Brush + { + get => _brush; + set => SetProperty(ref _brush, value); + } + + private IBrush _textBrush = null!; + + public IBrush TextBrush + { + get => _textBrush; + set => SetProperty(ref _textBrush, value); + } + + private string _colorDisplayName = null!; + + public string ColorDisplayName + { + get => _colorDisplayName; + set => SetProperty(ref _colorDisplayName, value); + } + + private string _hex = null!; + + public string Hex + { + get => _hex; + set => SetProperty(ref _hex, value); + } + + public ColorItemViewModel(string colorDisplayName, ISolidColorBrush brush, bool light, int index) + { + ColorDisplayName = colorDisplayName; + Brush = brush; + Hex = brush.ToString()!.ToUpperInvariant(); + if ((light && index < 5) || (!light && index >= 5)) { + TextBrush = Brushes.Black; + } else { + TextBrush = Brushes.White; + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ViewModels/PathIconModel.cs b/Samples/AtomUI.Demo.Desktop/ViewModels/PathIconModel.cs new file mode 100644 index 0000000..38b7be6 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ViewModels/PathIconModel.cs @@ -0,0 +1,66 @@ +using System.Collections.ObjectModel; +using AtomUI.Icon; +using AtomUI.Icon.AntDesign; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace AtomUI.Demo.Desktop.ViewModels; + +public class IconInfoItemModel : ObservableObject +{ + private string _iconName = null!; + + public string IconName + { + get => _iconName; + set => SetProperty(ref _iconName, value); + } + + private string _iconKind = null!; + + public string IconKind + { + get => _iconKind; + set => SetProperty(ref _iconKind, value); + } + + public IconInfoItemModel(string iconName, string iconKind) + { + IconName = iconName; + IconKind = iconKind; + } +} + +public class IconGalleryModel : ObservableObject +{ + private IconThemeType? _iconThemeType; + + private ObservableCollection? _iconInfos; + public ObservableCollection? IconInfos + { + get => _iconInfos; + set => SetProperty(ref _iconInfos, value); + } + + public IconGalleryModel(IconThemeType? iconThemeType = null) + { + _iconThemeType = iconThemeType; + if (_iconThemeType.HasValue) { + LoadThemeIcons(_iconThemeType.Value); + } + } + + public void LoadThemeIcons(IconThemeType iconThemeType) + { + var iconPackage = IconManager.Current.GetIconProvider(); + if (iconPackage is null) { + return; + } + + IconInfos = new ObservableCollection(); + var iconInfos = iconPackage.GetIconInfos(iconThemeType); + foreach (var iconInfo in iconInfos) { + var iconInfoModel = new IconInfoItemModel(iconInfo.Name, iconInfo.Name); + IconInfos.Add(iconInfoModel); + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ViewModels/TabControlDemoViewModel.cs b/Samples/AtomUI.Demo.Desktop/ViewModels/TabControlDemoViewModel.cs new file mode 100644 index 0000000..d402708 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/ViewModels/TabControlDemoViewModel.cs @@ -0,0 +1,15 @@ +using System.Collections.ObjectModel; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace AtomUI.Demo.Desktop.ViewModels; + +public class TabControlDemoViewModel: ObservableObject +{ + public ObservableCollection Items { get; set; } + + public TabControlDemoViewModel() + { + Items = new ObservableCollection(Enumerable.Range(1, 200).Select(a => "Tab " + a)); + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml b/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml new file mode 100644 index 0000000..8400a79 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml.cs b/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml.cs new file mode 100644 index 0000000..4762fd8 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Views/MainView.axaml.cs @@ -0,0 +1,24 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Styling; + +namespace AtomUI.Demo.Desktop.Views; + +public partial class MainView : UserControl +{ + public MainView() + { + InitializeComponent(); + } + + private void ToggleButton_OnIsCheckedChanged(object sender, RoutedEventArgs e) + { + var app = Application.Current; + if (app is not null) + { + var theme = app.ActualThemeVariant; + app.RequestedThemeVariant = theme == ThemeVariant.Dark ? ThemeVariant.Light : ThemeVariant.Dark; + } + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml b/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml new file mode 100644 index 0000000..9c7cfef --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml @@ -0,0 +1,16 @@ + + + diff --git a/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml.cs b/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml.cs new file mode 100644 index 0000000..e3869dd --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/Views/MainWindow.axaml.cs @@ -0,0 +1,15 @@ +using Avalonia; +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.Views; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } +} \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/app.manifest b/Samples/AtomUI.Demo.Desktop/app.manifest new file mode 100644 index 0000000..065e2f5 --- /dev/null +++ b/Samples/AtomUI.Demo.Desktop/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/global.json b/global.json new file mode 100644 index 0000000..4e08f95 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.300", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/src/AtomUI.Base/AtomUI.Base.csproj b/src/AtomUI.Base/AtomUI.Base.csproj new file mode 100644 index 0000000..952f422 --- /dev/null +++ b/src/AtomUI.Base/AtomUI.Base.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + AtomUI + + + + + + + diff --git a/src/AtomUI.Base/ColorSystem/ColorExtensions.cs b/src/AtomUI.Base/ColorSystem/ColorExtensions.cs new file mode 100644 index 0000000..b0669f6 --- /dev/null +++ b/src/AtomUI.Base/ColorSystem/ColorExtensions.cs @@ -0,0 +1,107 @@ +using System.Globalization; +using AtomUI.Utils; +using Avalonia.Media; + +namespace AtomUI.ColorSystem; + + +public static class ColorExtensions +{ + public static double GetRedF(this Color color) + { + return color.R / 255d; + } + + public static double GetGreenF(this Color color) + { + return color.G / 255d; + } + + public static double GetBlueF(this Color color) + { + return color.B / 255d; + } + + public static double GetAlphaF(this Color color) + { + return color.A / 255d; + } + + public static string HexName(this Color color, ColorNameFormat format = ColorNameFormat.HexRgb) + { + uint rgb = color.ToUInt32(); + string formatStr = "x8"; + if (format == ColorNameFormat.HexRgb) { + formatStr = "x6"; + rgb &= 0xFFFFFF; + } + return $"#{rgb.ToString(formatStr, CultureInfo.InvariantCulture)}"; + } + + public static Color Desaturate(this Color color, int amount = 10) + { + amount = NumberUtils.Clamp(amount, 0, 100); + HslColor hslColor = color.ToHsl(); + double s = hslColor.S; + s -= amount / 100d; + s = NumberUtils.Clamp(s, 0d, 1d); + return HslColor.FromHsl(hslColor.H, s, hslColor.L).ToRgb(); + } + + public static Color Saturate(this Color color, int amount = 10) + { + amount = NumberUtils.Clamp(amount, 0, 100); + HslColor hslColor = color.ToHsl(); + double s = hslColor.S; + s += amount / 100d; + s = NumberUtils.Clamp(s, 0d, 1d); + return HslColor.FromHsl(hslColor.H, s, hslColor.L).ToRgb(); + } + + public static Color Greyscale(this Color color) + { + return color.Desaturate(100); + } + + public static Color Lighten(this Color color, int amount = 10) + { + amount = NumberUtils.Clamp(amount, 0, 100); + HslColor hslColor = color.ToHsl(); + double l = hslColor.L; + l += amount / 100d; + l = NumberUtils.Clamp(l, 0d, 1d); + return HslColor.FromHsl(hslColor.H, hslColor.S, l).ToRgb(); + } + + public static Color Brighten(this Color color, int amount = 10) + { + amount = NumberUtils.Clamp(amount, 0, 100); + int r = color.R; + int g = color.G; + int b = color.B; + int delta = (int)Math.Round(255d * -(amount / 100d)); + r = Math.Max(0, Math.Min(255, r - delta)); + g = Math.Max(0, Math.Min(255, g - delta)); + b = Math.Max(0, Math.Min(255, b - delta)); + return Color.FromRgb((byte)r, (byte)g, (byte)b); + } + + public static Color Darken(this Color color, int amount = 10) + { + amount = NumberUtils.Clamp(amount, 0, 100); + HslColor hslColor = color.ToHsl(); + double l = hslColor.L; + l -= amount / 100d; + l = NumberUtils.Clamp(l, 0d, 1d); + return HslColor.FromHsl(hslColor.H, hslColor.S, l).ToRgb(); + } + + public static Color Spin(this Color color, int amount = 10) + { + HslColor hslColor = color.ToHsl(); + double h = hslColor.H; + h = (h + amount) % 360; + h = h < 0 ? 360 + h : h; + return HslColor.FromHsl(h, hslColor.S, hslColor.L).ToRgb(); + } +} \ No newline at end of file diff --git a/src/AtomUI.Base/ColorSystem/ColorUtils.cs b/src/AtomUI.Base/ColorSystem/ColorUtils.cs new file mode 100644 index 0000000..6ba791e --- /dev/null +++ b/src/AtomUI.Base/ColorSystem/ColorUtils.cs @@ -0,0 +1,177 @@ +using Avalonia.Media; + +namespace AtomUI.ColorSystem; + +public static class ColorUtils +{ + public static Color FromRgbF(double alpha, double red, double green, double blue) + { + return Color.FromArgb((byte)Math.Round(alpha * 255d), + (byte)Math.Round(red * 255d), + (byte)Math.Round(green * 255d), + (byte)Math.Round(blue * 255d)); + } + + public static Color FromRgbF(double red, double green, double blue) + { + return FromRgbF(1d, red, green, blue); + } + + public static Color TransparentColor() + { + return Color.FromArgb(0, 255, 255, 255); + } + + public static Color Desaturate(string color, int amount = 10) + { + return Color.Parse(color).Desaturate(amount); + } + + public static Color Saturate(string color, int amount = 10) + { + return Color.Parse(color).Saturate(amount); + } + + public static Color Lighten(string color, int amount = 10) + { + return Color.Parse(color).Lighten(amount); + } + + public static Color Brighten(string color, int amount = 10) + { + return Color.Parse(color).Brighten(amount); + } + + public static Color Darken(string color, int amount = 10) + { + return Color.Parse(color).Darken(amount); + } + + public static Color Spin(string color, int amount = 10) + { + return Color.Parse(color).Spin(amount); + } + + public static Color OnBackground(in Color frontColor, in Color backgroundColor) + { + double fr = frontColor.GetRedF(); + double fg = frontColor.GetGreenF(); + double fb = frontColor.GetBlueF(); + double fa = frontColor.GetAlphaF(); + + double br = backgroundColor.GetRedF(); + double bg = backgroundColor.GetGreenF(); + double bb = backgroundColor.GetBlueF(); + double ba = backgroundColor.GetAlphaF(); + + double alpha = fa + ba * (1 - fa); + + double nr = (fr * fa + br * ba * (1 - fa)) / alpha; + double ng = (fg * fa + bg * ba * (1 - fa)) / alpha; + double nb = (fb * fa + bb * ba * (1 - fa)) / alpha; + double na = alpha; + + return ColorUtils.FromRgbF(na, nr, ng, nb); + } + + public static bool IsStableColor(int color) + { + return color >= 0 && color <= 255; + } + + public static bool IsStableColor(float color) + { + return color >= 0.0f && color <= 1.0f; + } + + public static bool IsStableColor(double color) + { + return color >= 0.0d && color <= 1.0d; + } + + public static Color AlphaColor(in Color frontColor, in Color backgroundColor) + { + double fR = frontColor.GetRedF(); + double fG = frontColor.GetGreenF(); + double fB = frontColor.GetBlueF(); + double originAlpha = frontColor.GetAlphaF(); + if (originAlpha < 1d) { + return frontColor; + } + + double bR = backgroundColor.GetRedF(); + double bG = backgroundColor.GetGreenF(); + double bB = backgroundColor.GetBlueF(); + + for (var fA = 0.01d; fA <= 1.0d; fA += 0.01d) { + double r = Math.Round((fR - bR * (1d - fA)) / fA); + double g = Math.Round((fG - bG * (1d - fA)) / fA); + double b = Math.Round((fB - bB * (1d - fA)) / fA); + if (IsStableColor(r) && IsStableColor(g) && IsStableColor(b)) { + return ColorUtils.FromRgbF(Math.Round(fA * 100d) / 100d, r, g, b); + } + } + // fallback + /* istanbul ignore next */ + return ColorUtils.FromRgbF(1.0d, fR, fG, fB); + } + + public static Color ParseCssRgbColor(string colorExpr) + { + if (TryParseCssRgbColor(colorExpr, out Color color)) { + return color; + } + throw new FormatException($"Invalid color string: '{colorExpr.ToString()}'."); + } + + public static bool TryParseCssRgbColor(string? colorExpr, out Color color) + { + color = default; + if (string.IsNullOrEmpty(colorExpr)) { + return false; + } + + if (colorExpr[0] == '#') { + return Color.TryParse(colorExpr, out color); + } + + bool isRgba = colorExpr.StartsWith("rgba", StringComparison.InvariantCultureIgnoreCase); + bool isRgb = false; + if (!isRgba) { + isRgb = colorExpr.StartsWith("rgb", StringComparison.InvariantCultureIgnoreCase); + } + + if (isRgb || isRgba) { + int leftParen = colorExpr.IndexOf('('); + int rightParen = colorExpr.IndexOf(')'); + if (leftParen == -1 || rightParen == -1) { + return false; + } + + var parts = new List(colorExpr.Substring(leftParen + 1, rightParen - leftParen - 1).Split(',', StringSplitOptions.RemoveEmptyEntries)); + if (isRgb) { + if (parts.Count != 3) { + return false; + } + parts.Add("255"); + } else { + if (parts.Count != 4) { + return false; + } + } + List rgbaValues = new List(); + foreach (var part in parts) { + if (int.TryParse(part, out int partValue)) { + rgbaValues.Add(partValue); + } else { + return false; + } + } + + color = Color.FromArgb((byte)rgbaValues[0], (byte)rgbaValues[1], (byte)rgbaValues[2], (byte)rgbaValues[3]); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/AtomUI.Base/ColorSystem/PaletteGenerator.cs b/src/AtomUI.Base/ColorSystem/PaletteGenerator.cs new file mode 100644 index 0000000..9dcb18a --- /dev/null +++ b/src/AtomUI.Base/ColorSystem/PaletteGenerator.cs @@ -0,0 +1,193 @@ +using AtomUI.Utils; +using Avalonia.Media; +using Avalonia.Styling; + +namespace AtomUI.ColorSystem; + +public static class PaletteGenerator +{ + /// + /// 色相阶梯 + /// + public const int HUE_STEP = 2; + + /// + /// 饱和度阶梯,浅色部分 + /// + public const float SATURATION_STEP1 = 0.16f; + + /// + /// 饱和度阶梯,深色部分 + /// + public const float SATURATION_STEP2 = 0.05f; + + /// + /// 亮度阶梯,浅色部分 + /// + public const float BRIGHTNESS_STEP1 = 0.05f; + + /// + /// 亮度阶梯,深色部分 + /// + public const float BRIGHTNESS_STEP2 = 0.15f; + + /// + /// 浅色数量,主色上 + /// + public const int LIGHT_COLOR_COUNT = 5; + + /// + /// 深色数量,主色下 + /// + public const int DARK_COLOR_COUNT = 4; + + private static IReadOnlyList sm_darkColorMap; + + static PaletteGenerator() + { + sm_darkColorMap = new List + { + new DarkColorMapItem { Index = 7, Opacity = 0.15f }, + new DarkColorMapItem { Index = 6, Opacity = 0.25f }, + new DarkColorMapItem { Index = 5, Opacity = 0.3f }, + new DarkColorMapItem { Index = 5, Opacity = 0.45f }, + new DarkColorMapItem { Index = 5, Opacity = 0.65f }, + new DarkColorMapItem { Index = 5, Opacity = 0.85f }, + new DarkColorMapItem { Index = 4, Opacity = 0.9f }, + new DarkColorMapItem { Index = 3, Opacity = 0.95f }, + new DarkColorMapItem { Index = 2, Opacity = 0.97f }, + new DarkColorMapItem { Index = 1, Opacity = 0.98f } + }; + } + + public static IReadOnlyList GeneratePalette(Color color, PaletteGenerateOption? option = null) + { + if (option is null) { + option = new PaletteGenerateOption(); + } + var patterns = new List(); + HsvColor hsvColor = color.ToHsv(); + for (int i = LIGHT_COLOR_COUNT; i > 0; --i) { + Color rgbColor = HsvColor.ToRgb(GetHsvHue(hsvColor, i, true), + GetHsvSaturation(hsvColor, i, true), + GetHsvValue(hsvColor, i, true)); + patterns.Add(rgbColor); + } + + patterns.Add(color); + for (int i = 1; i <= DARK_COLOR_COUNT; ++i) { + Color rgbColor = HsvColor.ToRgb(GetHsvHue(hsvColor, i, false), + GetHsvSaturation(hsvColor, i, false), + GetHsvValue(hsvColor, i, false)); + patterns.Add(rgbColor); + } + + // dark theme patterns + if (option.ThemeVariant == ThemeVariant.Dark) { + var darkPatterns = new List(); + foreach (var entry in sm_darkColorMap) { + Color color1 = option.BackgroundColor ?? Color.Parse("#141414"); + Color color2 = patterns[entry.Index]; + RgbF darkColorRgb = MixRgbF(new RgbF(color1.GetRedF(), color1.GetGreenF(), color1.GetBlueF()), + new RgbF(color2.GetRedF(), color2.GetGreenF(), color2.GetBlueF()), + entry.Opacity * 100); + darkPatterns.Add(ColorUtils.FromRgbF(1.0, darkColorRgb.R, darkColorRgb.G, darkColorRgb.B)); + } + return darkPatterns; + } + + return patterns; + } + + private static RgbF MixRgbF(RgbF rgb1, RgbF rgb2, double amount) + { + double p = amount / 100; + double r = (rgb2.R - rgb1.R) * p + rgb1.R; + double g = (rgb2.G - rgb1.G) * p + rgb1.G; + double b = (rgb2.B - rgb1.B) * p + rgb1.B; + return new RgbF(r, g, b); + } + + private static double GetHsvHue(HsvColor hsvColor, int index, bool isLight) + { + double hue; + // 根据色相不同,色相转向不同 + if (Math.Round(hsvColor.H) >= 60d && Math.Round(hsvColor.H) <= 240d) { + hue = isLight ? Math.Round(hsvColor.H) - HUE_STEP * index : Math.Round(hsvColor.H) + HUE_STEP * index; + } else { + hue = isLight ? Math.Round(hsvColor.H) + HUE_STEP * index : Math.Round(hsvColor.H) - HUE_STEP * index; + } + + if (hue < 0) { + hue += 360d; + } else if (hue > 360d || NumberUtils.FuzzyCompare(hue, 360d)) { + hue -= 360d; + } + return hue; + } + + private static double GetHsvSaturation(HsvColor hsvColor, int index, bool isLight) + { + // grey color don't change saturation + if (NumberUtils.FuzzyCompare(hsvColor.H, 0d) && NumberUtils.FuzzyCompare(hsvColor.S, 0d)) { + return hsvColor.S; + } + + double saturation = 0; + if (isLight) { + saturation = hsvColor.S - SATURATION_STEP1 * index; + } else if (index == DARK_COLOR_COUNT) { + saturation = hsvColor.S + SATURATION_STEP1; + } else { + saturation = hsvColor.S + SATURATION_STEP2 * index; + } + + // 边界值修正 + saturation = Math.Min(saturation, 1d); + // 第一格的 s 限制在 0.06-0.1 之间 + if (isLight && index == LIGHT_COLOR_COUNT && saturation > 0.1d) { + saturation = 0.1d; + } + + saturation = Math.Max(saturation, 0.06d); + return NumberUtils.RoundToFixedPoint(saturation, 2); + } + + private static double GetHsvValue(HsvColor hsvColor, int index, bool isLight) + { + double value; + if (isLight) { + value = hsvColor.V + BRIGHTNESS_STEP1 * index; + } else { + value = hsvColor.V - BRIGHTNESS_STEP2 * index; + } + value = Math.Min(value, 1d); + return NumberUtils.RoundToFixedPoint(value, 2);; + } + + internal struct DarkColorMapItem + { + public int Index { get; set; } + public double Opacity { get; set; } + } +} + +internal struct RgbF +{ + public RgbF(double r, double g, double b) + { + R = r; + G = g; + B = b; + } + + public double R { get; set; } + public double G { get; set; } + public double B { get; set; } +} + +public class PaletteGenerateOption +{ + public ThemeVariant ThemeVariant { get; set; } = ThemeVariant.Light; + public Color? BackgroundColor { get; set; } +} \ No newline at end of file diff --git a/src/AtomUI.Base/ColorSystem/PresetPalettes.cs b/src/AtomUI.Base/ColorSystem/PresetPalettes.cs new file mode 100644 index 0000000..509dbf5 --- /dev/null +++ b/src/AtomUI.Base/ColorSystem/PresetPalettes.cs @@ -0,0 +1,68 @@ +using Avalonia.Media; +using Avalonia.Styling; + +namespace AtomUI.ColorSystem; + +public class PaletteInfo +{ + public Color Primary { get; set; } + public IReadOnlyList ColorSequence { get; set; } = default!; +} + +public static class PresetPalettes +{ + private static Dictionary sm_presetPalettes; + private static Dictionary sm_presetDarkPalettes; + + static PresetPalettes() + { + sm_presetPalettes = new Dictionary(); + sm_presetDarkPalettes = new Dictionary(); + InitPalettes(false); + InitPalettes(true); + } + + public static PaletteInfo GetPresetPalette(PresetPrimaryColor primaryColor, bool isDark = false) + { + if (isDark) { + return sm_presetDarkPalettes[primaryColor]; + } + + return sm_presetPalettes[primaryColor]; + } + + public static IReadOnlyDictionary GetPresetPalettes(bool isDark = false) + { + if (isDark) { + return sm_presetDarkPalettes; + } + return sm_presetPalettes; + } + + private static void InitPalettes(bool isDark) + { + var allColors = PresetPrimaryColor.AllColorTypes(); + foreach (var presetColor in allColors) { + if (isDark) { + IReadOnlyList colorSequence = PaletteGenerator.GeneratePalette(presetColor.Color()); + sm_presetPalettes[presetColor] = new PaletteInfo + { + Primary = colorSequence[5], + ColorSequence = colorSequence + }; + } else { + IReadOnlyList colorSequence = PaletteGenerator.GeneratePalette(presetColor.Color(), + new PaletteGenerateOption + { + ThemeVariant = ThemeVariant.Dark, + BackgroundColor = Color.Parse("#141414") + }); + sm_presetDarkPalettes[presetColor] = new PaletteInfo + { + Primary = colorSequence[5], + ColorSequence = colorSequence + }; + } + } + } +} \ No newline at end of file diff --git a/src/AtomUI.Base/ColorSystem/PresetPrimaryColor.cs b/src/AtomUI.Base/ColorSystem/PresetPrimaryColor.cs new file mode 100644 index 0000000..9de95cc --- /dev/null +++ b/src/AtomUI.Base/ColorSystem/PresetPrimaryColor.cs @@ -0,0 +1,124 @@ +using Avalonia.Media; + +namespace AtomUI.ColorSystem; + +public class PresetPrimaryColor : IEquatable +{ + public enum ColorType + { + Red, + Volcano, + Orange, + Gold, + Yellow, + Lime, + Green, + Cyan, + Blue, + GeekBlue, + Purple, + Pink, + Magenta, + Grey, + } + + public static readonly PresetPrimaryColor Red = new PresetPrimaryColor(ColorType.Red); + public static readonly PresetPrimaryColor Volcano = new PresetPrimaryColor(ColorType.Volcano); + public static readonly PresetPrimaryColor Orange = new PresetPrimaryColor(ColorType.Orange); + public static readonly PresetPrimaryColor Gold = new PresetPrimaryColor(ColorType.Gold); + public static readonly PresetPrimaryColor Yellow = new PresetPrimaryColor(ColorType.Yellow); + public static readonly PresetPrimaryColor Lime = new PresetPrimaryColor(ColorType.Lime); + public static readonly PresetPrimaryColor Green = new PresetPrimaryColor(ColorType.Green); + public static readonly PresetPrimaryColor Cyan = new PresetPrimaryColor(ColorType.Cyan); + public static readonly PresetPrimaryColor Blue = new PresetPrimaryColor(ColorType.Blue); + public static readonly PresetPrimaryColor GeekBlue = new PresetPrimaryColor(ColorType.GeekBlue); + public static readonly PresetPrimaryColor Purple = new PresetPrimaryColor(ColorType.Purple); + public static readonly PresetPrimaryColor Pink = new PresetPrimaryColor(ColorType.Pink); + public static readonly PresetPrimaryColor Magenta = new PresetPrimaryColor(ColorType.Magenta); + public static readonly PresetPrimaryColor Grey = new PresetPrimaryColor(ColorType.Grey); + + public ColorType Type { get; } + + public PresetPrimaryColor(ColorType colorType) + { + Type = colorType; + } + + public string Name() + { + return Enum.GetName(typeof(ColorType), Type)!; + } + + public string RgbHex() + { + return Type switch + { + ColorType.Red => "#F5222D", + ColorType.Volcano => "#FA541C", + ColorType.Orange => "#FA8C16", + ColorType.Gold => "#FAAD14", + ColorType.Yellow => "#FADB14", + ColorType.Lime => "#A0D911", + ColorType.Green => "#52C41A", + ColorType.Cyan => "#13C2C2", + ColorType.Blue => "#1677FF", + ColorType.GeekBlue => "#2F54EB", + ColorType.Purple => "#722ED1", + ColorType.Magenta => "#EB2F96", + ColorType.Pink => "#EB2F96", + ColorType.Grey => "#666666", + _ => "#666666" + }; + } + + public Color Color() + { + return Avalonia.Media.Color.Parse(RgbHex()); + } + + public static IList AllColorTypes() + { + return new List + { + new PresetPrimaryColor(ColorType.Red), + new PresetPrimaryColor(ColorType.Volcano), + new PresetPrimaryColor(ColorType.Orange), + new PresetPrimaryColor(ColorType.Gold), + new PresetPrimaryColor(ColorType.Yellow), + new PresetPrimaryColor(ColorType.Lime), + new PresetPrimaryColor(ColorType.Green), + new PresetPrimaryColor(ColorType.Cyan), + new PresetPrimaryColor(ColorType.Blue), + new PresetPrimaryColor(ColorType.GeekBlue), + new PresetPrimaryColor(ColorType.Purple), + new PresetPrimaryColor(ColorType.Pink), + new PresetPrimaryColor(ColorType.Magenta), + new PresetPrimaryColor(ColorType.Grey), + }; + } + + public bool Equals(PresetPrimaryColor? other) + { + return other is not null && Type == other.Type; + } + + public override bool Equals(object? obj) + { + return obj is PresetPrimaryColor other && Equals(other); + } + + public override int GetHashCode() + { + return (int)Type; + } + + public static bool operator ==(PresetPrimaryColor left, PresetPrimaryColor right) + { + return left.Equals(right); + } + + public static bool operator !=(PresetPrimaryColor left, PresetPrimaryColor right) + { + return !left.Equals(right); + } +} diff --git a/src/AtomUI.Base/Common.cs b/src/AtomUI.Base/Common.cs new file mode 100644 index 0000000..c0fcb80 --- /dev/null +++ b/src/AtomUI.Base/Common.cs @@ -0,0 +1,81 @@ +using Avalonia.Media; + +namespace AtomUI; + +public enum TextDecorationLine + +{ + None, + Underline, + Overline, + LineThrough +} + +public enum LineStyle +{ + Solid, + Double, + Dotted, + Dashed, + Wavy, +} + +public enum Placement +{ + LeftTop, + Left, + LeftBottom, + + TopLeft, + Top, + TopRight, + + RightTop, + Right, + RightBottom, + + BottomLeft, + Bottom, + BottomRight, +} + +public enum Direction +{ + Left, + Top, + Right, + Bottom +} + +public enum Corner +{ + None = 0x00, + TopLeft = 0x01, + TopRight = 0x02, + BottomLeft = 0x04, + BottomRight = 0x08, + All = TopLeft | TopRight | BottomLeft | BottomRight +} + +public enum SizeType +{ + Large, + Middle, + Small +} + +// 文本修饰信息定义 +// 类似 CSS text-decoration +public class TextDecorationInfo +{ + public Color Color { get; set; } + public TextDecorationLine LineType { get; set; } = TextDecorationLine.None; + public LineStyle LineStyle { get; set; } = LineStyle.Solid; + public int Thickness { get; set; } = 1; +} + +public enum ColorNameFormat +{ + HexRgb, + HexArgb +} \ No newline at end of file diff --git a/src/AtomUI.Base/Utils/BindUtils.cs b/src/AtomUI.Base/Utils/BindUtils.cs new file mode 100644 index 0000000..386d240 --- /dev/null +++ b/src/AtomUI.Base/Utils/BindUtils.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; + +namespace AtomUI.Utils; + +public static class BindUtils +{ + public static IDisposable RelayBind(Control source, string sourcePropertyName, Control target, + string? targetPropertyName = null, + BindingMode mode = BindingMode.OneWay) + { + // TODO 看看这里的 sourcePropertyName 能不能优化掉 + if (targetPropertyName == null) { + targetPropertyName = sourcePropertyName; + } + + Type targetType = target.GetType(); + var targetUnderlyingPropertyName = $"{targetPropertyName}Property"; + var targetUnderlyingProperty = targetType.GetField(targetUnderlyingPropertyName, + BindingFlags.Public | BindingFlags.Static | + BindingFlags.FlattenHierarchy); + + if (targetUnderlyingProperty is null) { + throw new ArgumentException($"Relay target property {targetUnderlyingPropertyName} is not exist."); + } + + Type sourceType = source.GetType(); + var sourceUnderlyingProperty = sourceType.GetProperty(sourcePropertyName, + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.FlattenHierarchy); // 这里不需要加 Property 后缀 + + if (sourceUnderlyingProperty == null) { + throw new ArgumentException($"Relay source property {sourcePropertyName} is not exist."); + } + + var targetPropertyValue = targetUnderlyingProperty.GetValue(null); + if (targetPropertyValue is null) { + throw new ArgumentException($"Relay target property value is null."); + } + + return target.Bind((AvaloniaProperty)targetPropertyValue, new Binding(sourcePropertyName, mode) + { + Source = source + }); + } +} \ No newline at end of file diff --git a/src/AtomUI.Base/Utils/NumberUtils.cs b/src/AtomUI.Base/Utils/NumberUtils.cs new file mode 100644 index 0000000..8e421d3 --- /dev/null +++ b/src/AtomUI.Base/Utils/NumberUtils.cs @@ -0,0 +1,50 @@ +namespace AtomUI.Utils; + +public static class NumberUtils +{ + public static int Clamp(int value, int min, int max) + { + return (value < min) ? min : (value > max) ? max : value; + } + + public static float Clamp(float value, float min, float max) + { + return (value < min) ? min : (value > max) ? max : value; + } + + public static double Clamp(double value, double min, double max) + { + return (value < min) ? min : (value > max) ? max : value; + } + + public static decimal Clamp(decimal value, decimal min, decimal max) + { + return (value < min) ? min : (value > max) ? max : value; + } + + public static double RoundToFixedPoint(double value, int fixedCount) + { + var factor = Math.Pow(10, fixedCount); + return Math.Round(value * factor) / factor; + } + + public static bool FuzzyCompare(double p1, double p2) + { + return Math.Abs(p1 - p2) * 1000000000000d <= Math.Min(Math.Abs(p1), Math.Abs(p2)); + } + + public static bool FuzzyCompare(float p1, float p2) + { + return Math.Abs(p1 - p2) * 100000f <= Math.Min(Math.Abs(p1), Math.Abs(p2)); + } + + public static bool FuzzyIsNull(double d) + { + return Math.Abs(d) <= 0.000000000001d; + } + + public static bool FuzzyIsNull(float d) + { + return Math.Abs(d) <= 0.00001f; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Alert/Alert.cs b/src/AtomUI.Controls/Alert/Alert.cs new file mode 100644 index 0000000..23e20e9 --- /dev/null +++ b/src/AtomUI.Controls/Alert/Alert.cs @@ -0,0 +1,167 @@ +using AtomUI.Data; +using AtomUI.TokenSystem; +using Avalonia; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Avalonia.Metadata; + +namespace AtomUI.Controls; + +public enum AlertType +{ + Success, + Info, + Warning, + Error +} + +public partial class Alert : BorderedStyleControl, ITokenIdProvider +{ + string ITokenIdProvider.TokenId => nameof(Alert); + + public static readonly DirectProperty TypeProperty = + AvaloniaProperty.RegisterDirect(nameof(AlertType), + o => o.Type, + (o, v) => o.Type = v); + + public static readonly DirectProperty IsShowIconProperty = + AvaloniaProperty.RegisterDirect(nameof(IsShowIcon), + o => o.IsShowIcon, + (o, v) => o.IsShowIcon = v); + + public static readonly DirectProperty IsMessageMarqueEnabledProperty = + AvaloniaProperty.RegisterDirect(nameof(IsMessageMarqueEnabled), + o => o.IsMessageMarqueEnabled, + (o, v) => o.IsMessageMarqueEnabled = v); + + public static readonly DirectProperty IsClosableProperty = + AvaloniaProperty.RegisterDirect(nameof(IsClosable), + o => o.IsClosable, + (o, v) => o.IsClosable = v); + + + public static readonly DirectProperty CloseIconProperty = + AvaloniaProperty.RegisterDirect(nameof(CloseIcon), + o => o.CloseIcon, + (o, v) => o.CloseIcon = v); + + public static readonly DirectProperty MessageProperty = + AvaloniaProperty.RegisterDirect(nameof(Message), + o => o.Message, + (o, v) => o.Message = v); + + public static readonly DirectProperty DescriptionProperty = + AvaloniaProperty.RegisterDirect(nameof(Description), + o => o.Description, + (o, v) => o.Description = v); + + public static readonly DirectProperty ExtraActionProperty = + AvaloniaProperty.RegisterDirect(nameof(Description), + o => o._extraAction, + (o, v) => o._extraAction = v); + + private AlertType _alertType = AlertType.Success; + + public AlertType Type + { + get => _alertType; + set => SetAndRaise(TypeProperty, ref _alertType, value); + } + + private bool _isShowIcon = false; + + public bool IsShowIcon + { + get => _isShowIcon; + set => SetAndRaise(IsShowIconProperty, ref _isShowIcon, value); + } + + private bool _isMessageMarqueEnabled = false; + + public bool IsMessageMarqueEnabled + { + get => _isMessageMarqueEnabled; + set => SetAndRaise(IsMessageMarqueEnabledProperty, ref _isMessageMarqueEnabled, value); + } + + private bool _isClosable = false; + + public bool IsClosable + { + get => _isClosable; + set => SetAndRaise(IsClosableProperty, ref _isClosable, value); + } + + private PathIcon? _closeIcon; + + public PathIcon? CloseIcon + { + get => _closeIcon; + set => SetAndRaise(CloseIconProperty, ref _closeIcon, value); + } + + private string _message = string.Empty; + + [Content] + public string Message + { + get => _message; + set => SetAndRaise(MessageProperty, ref _message, value); + } + + private string? _description; + + public string? Description + { + get => _description; + set => SetAndRaise(DescriptionProperty, ref _description, value); + } + + private Control? _extraAction; + public Control? ExtraAction + { + get => _extraAction; + set => SetAndRaise(ExtraActionProperty, ref _extraAction, value); + } + + static Alert() + { + AffectsMeasure(IsClosableProperty, + IsShowIconProperty, + MessageProperty, + DescriptionProperty, + IsMessageMarqueEnabledProperty, + PaddingProperty, + ExtraActionProperty, + IsMessageMarqueEnabledProperty); + AffectsRender(TypeProperty); + } + + public Alert() + { + _tokenResourceBinder = new TokenResourceBinder(this); + _customStyle = this; + _customStyle.InitOnConstruct(); + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + if (!_initialized) { + _customStyle.SetupUi(); + _initialized = true; + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + _customStyle.HandlePropertyChangedForStyle(e); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _customStyle.ApplyRenderScalingAwareStyleConfig(); + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Alert/AlertStyle.cs b/src/AtomUI.Controls/Alert/AlertStyle.cs new file mode 100644 index 0000000..20e21fc --- /dev/null +++ b/src/AtomUI.Controls/Alert/AlertStyle.cs @@ -0,0 +1,294 @@ +using AtomUI.Data; +using AtomUI.Icon; +using AtomUI.Styling; +using AtomUI.Utils; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Layout; +using Avalonia.Media; + +namespace AtomUI.Controls; + +public partial class Alert : IControlCustomStyle +{ + private IControlCustomStyle _customStyle; + private TokenResourceBinder _tokenResourceBinder; + private bool _initialized = false; + private Grid? _mainLayout; + private Label? _messageLabel; + private MarqueeLabel? _messageMarqueeLabel; + private Label? _descriptionLabel; + private PathIcon? _icon; + private IconButton? _closeButton; + private StackPanel? _infoStack; + + void IControlCustomStyle.InitOnConstruct() + { + _mainLayout = new Grid() + { + RowDefinitions = + { + new RowDefinition(GridLength.Auto) + }, + ColumnDefinitions = + { + new ColumnDefinition(GridLength.Auto), + new ColumnDefinition(GridLength.Star), + new ColumnDefinition(GridLength.Auto), + new ColumnDefinition(GridLength.Auto) + } + }; + } + + void IControlCustomStyle.SetupUi() + { + HorizontalAlignment = HorizontalAlignment.Stretch; + _customStyle.ApplyFixedStyleConfig(); + CreateStructure(); + SetupPaddingStyleConfig(); + HandleDescriptionEnabled(!string.IsNullOrEmpty(_description)); + Child = _mainLayout; + } + + private void CreateStructure() + { + _descriptionLabel = new Label + { + Content = Description, + HorizontalAlignment = HorizontalAlignment.Stretch, + HorizontalContentAlignment = HorizontalAlignment.Left, + VerticalContentAlignment = VerticalAlignment.Center, + Padding = new Thickness(0) + }; + TextBlock.SetTextWrapping(_descriptionLabel, TextWrapping.Wrap); + _tokenResourceBinder.AddBinding(_descriptionLabel!, Label.MarginProperty, GlobalResourceKey.MarginXS); + _descriptionLabel.IsVisible = !string.IsNullOrEmpty(Description); + _infoStack = new StackPanel() + { + Orientation = Orientation.Vertical, + VerticalAlignment = VerticalAlignment.Center + }; + + HandleMessageMarqueEnabled(_isMessageMarqueEnabled); + SetupCloseButton(); + SetupTypeIcon(); + + _infoStack.Children.Add(_descriptionLabel); + + Grid.SetColumn(_infoStack, 1); + Grid.SetRow(_infoStack, 0); + _mainLayout!.Children.Add(_infoStack); + + + if (_extraAction is not null) { + _extraAction.VerticalAlignment = VerticalAlignment.Top; + Grid.SetColumn(_extraAction, 2); + Grid.SetRow(_extraAction, 0); + _tokenResourceBinder.AddBinding(_extraAction, MarginProperty, AlertResourceKey.ExtraElementMargin); + _mainLayout!.Children.Add(_extraAction); + } + } + + private Control GetMessageControl() + { + if (_isMessageMarqueEnabled) { + return _messageMarqueeLabel!; + } + + return _messageLabel!; + } + + public void SetupCloseButton() + { + if (_isClosable && _closeButton is null) { + if (_closeIcon is null) { + _closeIcon = new PathIcon + { + Kind = "CloseOutlined", + }; + _tokenResourceBinder.AddBinding(_closeIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon); + _tokenResourceBinder.AddBinding(_closeIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover); + } + + _closeButton = new IconButton + { + VerticalAlignment = VerticalAlignment.Top, + }; + _tokenResourceBinder.AddBinding(_closeButton, WidthProperty, GlobalResourceKey.IconSizeSM); + _tokenResourceBinder.AddBinding(_closeButton, HeightProperty, GlobalResourceKey.IconSizeSM); + _tokenResourceBinder.AddBinding(_closeButton, MarginProperty, AlertResourceKey.ExtraElementMargin); + Grid.SetRow(_closeButton, 0); + Grid.SetColumn(_closeButton, 3); + _mainLayout!.Children.Add(_closeButton); + BindUtils.RelayBind(this, "CloseIcon", _closeButton, "Icon"); + } else if (_closeButton is not null) { + _closeButton.IsVisible = _isClosable; + } + } + + void IControlCustomStyle.ApplyFixedStyleConfig() + { + ApplyAlertTypeStyleConfig(); + _tokenResourceBinder.AddBinding(FontSizeProperty, GlobalResourceKey.FontSize); + } + + private void SetupPaddingStyleConfig() + { + if (_descriptionLabel!.IsVisible) { + _tokenResourceBinder.AddBinding(PaddingProperty, AlertResourceKey.WithDescriptionPadding); + } else { + _tokenResourceBinder.AddBinding(PaddingProperty, AlertResourceKey.DefaultPadding); + } + } + + private void ApplyAlertTypeStyleConfig() + { + if (Type == AlertType.Success) { + _tokenResourceBinder.AddBinding(BackgroundProperty, GlobalResourceKey.ColorSuccessBg); + _tokenResourceBinder.AddBinding(BorderBrushProperty, GlobalResourceKey.ColorSuccessBorder); + } else if (Type == AlertType.Info) { + _tokenResourceBinder.AddBinding(BackgroundProperty, GlobalResourceKey.ColorInfoBg); + _tokenResourceBinder.AddBinding(BorderBrushProperty, GlobalResourceKey.ColorInfoBorder); + } else if (Type == AlertType.Warning) { + _tokenResourceBinder.AddBinding(BackgroundProperty, GlobalResourceKey.ColorWarningBg); + _tokenResourceBinder.AddBinding(BorderBrushProperty, GlobalResourceKey.ColorWarningBorder); + } else if (Type == AlertType.Error) { + _tokenResourceBinder.AddBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBg); + _tokenResourceBinder.AddBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorder); + } + _tokenResourceBinder.AddBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG); + } + + void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig() + { + _tokenResourceBinder.AddBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style, + new RenderScaleAwareThicknessConfigure(this)); + } + + void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e) + { + if (_initialized) { + if (e.Property == DescriptionProperty) { + var desc = e.GetNewValue(); + var enabled = !string.IsNullOrEmpty(desc); + HandleDescriptionEnabled(enabled); + HandleIconForDescriptionEnabled(enabled); + _descriptionLabel!.IsVisible = enabled; + } else if (e.Property == IsMessageMarqueEnabledProperty) { + var enabled = e.GetNewValue(); + HandleMessageMarqueEnabled(enabled); + } + } + } + + private void HandleDescriptionEnabled(bool enabled) + { + var messageControl = GetMessageControl(); + if (enabled) { + _tokenResourceBinder.AddBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSizeLG); + _tokenResourceBinder.AddBinding(messageControl, MarginProperty, AlertResourceKey.MessageWithDescriptionMargin); + if (_closeButton is not null) { + _closeButton.VerticalAlignment = VerticalAlignment.Top; + } + messageControl.VerticalAlignment = VerticalAlignment.Top; + } else { + _tokenResourceBinder.AddBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSize); + messageControl.Margin = new Thickness(); + if (_closeButton is not null) { + _closeButton.VerticalAlignment = VerticalAlignment.Center; + } + messageControl.VerticalAlignment = VerticalAlignment.Stretch; + } + } + + private void HandleIconForDescriptionEnabled(bool enabled) + { + if (_icon is not null && _isShowIcon) { + if (enabled) { + _tokenResourceBinder.AddBinding(_icon, WidthProperty, AlertResourceKey.WithDescriptionIconSize); + _tokenResourceBinder.AddBinding(_icon, HeightProperty, AlertResourceKey.WithDescriptionIconSize); + _tokenResourceBinder.AddBinding(_icon, MarginProperty, AlertResourceKey.IconWithDescriptionMargin); + _icon.VerticalAlignment = VerticalAlignment.Top; + } else { + _tokenResourceBinder.AddBinding(_icon, WidthProperty, AlertResourceKey.IconSize); + _tokenResourceBinder.AddBinding(_icon, HeightProperty, AlertResourceKey.IconSize); + _tokenResourceBinder.AddBinding(_icon, MarginProperty, AlertResourceKey.IconDefaultMargin); + _icon.VerticalAlignment = VerticalAlignment.Center; + } + } + } + + private void SetupTypeIcon() + { + if (_isShowIcon) { + if (_icon is null) { + var kind = string.Empty; + var resourceKey = string.Empty; + if (Type == AlertType.Success) { + kind = "CheckCircleFilled"; + resourceKey = GlobalResourceKey.ColorSuccess; + } else if (Type == AlertType.Info) { + kind = "InfoCircleFilled"; + resourceKey = GlobalResourceKey.ColorPrimary; + } else if (Type == AlertType.Warning) { + kind = "ExclamationCircleFilled"; + resourceKey = GlobalResourceKey.ColorWarning; + } else if (Type == AlertType.Error) { + kind = "CloseCircleFilled"; + resourceKey = GlobalResourceKey.ColorError; + } + + _icon = new PathIcon + { + Kind = kind, + }; + + _tokenResourceBinder.AddBinding(_icon, PathIcon.NormalFillBrushProperty, resourceKey); + + HandleIconForDescriptionEnabled(_descriptionLabel!.IsVisible); + + Grid.SetRow(_icon, 0); + Grid.SetColumn(_icon, 0); + _mainLayout!.Children.Add(_icon); + } + } else if (_icon is not null) { + _icon.IsVisible = false; + } + } + + private void HandleMessageMarqueEnabled(bool enabled) + { + if (!enabled) { + if (_messageLabel is null) { + _messageLabel = new Label + { + Content = Message, + HorizontalAlignment = HorizontalAlignment.Stretch, + HorizontalContentAlignment = HorizontalAlignment.Left, + VerticalContentAlignment = VerticalAlignment.Center, + Padding = new Thickness(0) + }; + TextBlock.SetTextWrapping(_messageLabel, TextWrapping.Wrap); + } + + if (_messageMarqueeLabel is not null) { + _infoStack!.Children.Remove(_messageMarqueeLabel); + } + _infoStack!.Children.Insert(0, _messageLabel); + } else { + if (_messageMarqueeLabel is null) { + _messageMarqueeLabel = new MarqueeLabel + { + Text = Message, + HorizontalAlignment = HorizontalAlignment.Stretch, + Padding = new Thickness(0) + }; + } + if (_messageLabel is not null) { + _infoStack!.Children.Remove(_messageLabel); + } + _infoStack!.Children.Insert(0, _messageMarqueeLabel); + } + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Alert/AlertToken.cs b/src/AtomUI.Controls/Alert/AlertToken.cs new file mode 100644 index 0000000..beabb19 --- /dev/null +++ b/src/AtomUI.Controls/Alert/AlertToken.cs @@ -0,0 +1,77 @@ +using AtomUI.TokenSystem; +using Avalonia; + +namespace AtomUI.Controls; + +[ControlDesignToken] +internal class AlertToken : AbstractControlDesignToken +{ + public const string ID = "Alert"; + + public AlertToken() + : base(ID) + { + } + + /// + /// 默认内间距 + /// + public Thickness DefaultPadding { get; set; } + + /// + /// 带有描述的内间距 + /// + public Thickness WithDescriptionPadding { get; set; } + + /// + /// 带有描述的 Message 外间距 + /// + public Thickness MessageWithDescriptionMargin { get; set; } + + /// + /// 图标默认外间距 + /// + public Thickness IconDefaultMargin { get; set; } + + /// + /// 图标带描述信息外间距 + /// + public Thickness IconWithDescriptionMargin { get; set; } + + /// + /// 没有描述时的图标尺寸 + /// + public double IconSize { get; set; } + + /// + /// 带有描述时的图标尺寸 + /// + public double WithDescriptionIconSize { get; set; } + + /// + /// 关闭按钮的大小 + /// + public double CloseIconSize { get; set; } + + /// + /// 额外元素的外间距 + /// + public Thickness ExtraElementMargin { get; set; } + + internal override void CalculateFromAlias() + { + base.CalculateFromAlias(); + const double paddingHorizontal = 12; // Fixed value here. + WithDescriptionIconSize = _globalToken.FontToken.FontSizeHeading3; + DefaultPadding = new Thickness(paddingHorizontal, _globalToken.PaddingContentVerticalSM); + WithDescriptionPadding = new Thickness(_globalToken.PaddingContentHorizontalLG, _globalToken.PaddingMD); + + MessageWithDescriptionMargin = new Thickness(0, 0, 0, _globalToken.MarginXS); + IconDefaultMargin = new Thickness(0, 0, _globalToken.MarginXS, 0); + IconWithDescriptionMargin = new Thickness(0, 0, _globalToken.MarginSM, 0); + ExtraElementMargin = new Thickness(_globalToken.MarginXS, 0, 0, 0); + + CloseIconSize = _globalToken.FontSizeIcon + 2; + IconSize = _globalToken.FontToken.FontSizeLG; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/AtomUI.Controls.csproj b/src/AtomUI.Controls/AtomUI.Controls.csproj new file mode 100644 index 0000000..813b773 --- /dev/null +++ b/src/AtomUI.Controls/AtomUI.Controls.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + AtomUI.Controls + true + GeneratedFiles + + + + + + + + + + + + + + diff --git a/src/AtomUI.Controls/BorderedStyleControl.cs b/src/AtomUI.Controls/BorderedStyleControl.cs new file mode 100644 index 0000000..578ac5f --- /dev/null +++ b/src/AtomUI.Controls/BorderedStyleControl.cs @@ -0,0 +1,117 @@ +using AtomUI.Utils; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Metadata; + +namespace AtomUI.Controls; + +public class BorderedStyleControl : StyledControl +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty ChildProperty = + AvaloniaProperty.Register(nameof(Child)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty BorderBrushProperty = + AvaloniaProperty.Register(nameof(BorderBrush)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty BorderThicknessProperty = + AvaloniaProperty.Register(nameof(BorderThickness)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty CornerRadiusProperty = + AvaloniaProperty.Register(nameof(CornerRadius)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty BoxShadowProperty = + AvaloniaProperty.Register(nameof(BoxShadow)); + + /// + /// Gets or sets a brush with which to paint the border. + /// + public IBrush? BorderBrush + { + get => GetValue(BorderBrushProperty); + set => SetValue(BorderBrushProperty, value); + } + + /// + /// Gets or sets the thickness of the border. + /// + public Thickness BorderThickness + { + get => GetValue(BorderThicknessProperty); + set => SetValue(BorderThicknessProperty, value); + } + + /// + /// Gets or sets the radius of the border rounded corners. + /// + public CornerRadius CornerRadius + { + get => GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + + /// + /// Gets or sets the box shadow effect parameters + /// + public BoxShadows BoxShadow + { + get => GetValue(BoxShadowProperty); + set => SetValue(BoxShadowProperty, value); + } + + /// + /// Gets or sets the decorated control. + /// + [Content] + protected Control? Child + { + get => GetValue(ChildProperty); + set => SetValue(ChildProperty, value); + } + + private Border? _border; + + static BorderedStyleControl() + { + AffectsMeasure(ChildProperty, PaddingProperty, BorderThicknessProperty); + AffectsRender(BorderBrushProperty); + } + + public BorderedStyleControl() + { + _border = new Border(); + LogicalChildren.Add(_border); + VisualChildren.Add(_border); + + BindUtils.RelayBind(this, "Background", _border); + BindUtils.RelayBind(this, "BackgroundSizing", _border); + BindUtils.RelayBind(this, "BorderBrush", _border); + BindUtils.RelayBind(this, "BorderThickness", _border); + BindUtils.RelayBind(this, "CornerRadius", _border); + BindUtils.RelayBind(this, "Child", _border); + BindUtils.RelayBind(this, "Padding", _border); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var targetRect = new Rect(new Point(BorderThickness.Left, BorderThickness.Top), + finalSize.Deflate(BorderThickness)); + _border!.Arrange(targetRect); + return finalSize; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Buttons/Button.cs b/src/AtomUI.Controls/Buttons/Button.cs new file mode 100644 index 0000000..e5d5390 --- /dev/null +++ b/src/AtomUI.Controls/Buttons/Button.cs @@ -0,0 +1,177 @@ +using AtomUI.Data; +using AtomUI.TokenSystem; +using Avalonia; +using Avalonia.LogicalTree; + +namespace AtomUI.Controls; + +using AvaloniaButton = Avalonia.Controls.Button; +using ButtonSizeType = SizeType; + +public enum ButtonType +{ + Default, + Primary, + Link, + Text +} + +public enum ButtonShape +{ + Default, + Circle, + Round, +} + +public partial class Button : AvaloniaButton, ITokenIdProvider, ISizeTypeAware +{ + // 需要改造 + public static readonly DirectProperty ButtonTypeProperty = + AvaloniaProperty.RegisterDirect(nameof(ButtonType), + o => o.ButtonType, + (o, v) => o.ButtonType = v, + ButtonType.Default); + + public static readonly DirectProperty ButtonShapeProperty = + AvaloniaProperty.RegisterDirect(nameof(Shape), + o => o.Shape, + (o, v) => o.Shape = v, + ButtonShape.Default); + + public static readonly DirectProperty IsDangerProperty = + AvaloniaProperty.RegisterDirect(nameof(IsDanger), + o => o.IsDanger, + (o, v) => o.IsDanger = v, + false); + + public static readonly DirectProperty IsGhostProperty = + AvaloniaProperty.RegisterDirect(nameof(IsGhost), + o => o.IsGhost, + (o, v) => o.IsGhost = v, + false); + + public static readonly DirectProperty SizeTypeProperty = + AvaloniaProperty.RegisterDirect(nameof(SizeType), + o => o.SizeType, + (o, v) => o.SizeType = v, + ButtonSizeType.Middle); + + public static readonly DirectProperty IconProperty + = AvaloniaProperty.RegisterDirect(nameof(Icon), + o => o.Icon, + (o, v) => o.Icon = v); + + public static readonly DirectProperty TextProperty + = AvaloniaProperty.RegisterDirect(nameof(Text), + o => o.Text, + (o, v) => o.Text = v, + string.Empty); + + private ButtonType _buttonType = ButtonType.Default; + + public ButtonType ButtonType + { + get => _buttonType; + set => SetAndRaise(ButtonTypeProperty, ref _buttonType, value); + } + + private ButtonShape _shape = ButtonShape.Default; + + public ButtonShape Shape + { + get => _shape; + set => SetAndRaise(ButtonShapeProperty, ref _shape, value); + } + + private bool _isDanger = false; + + public bool IsDanger + { + get => _isDanger; + set => SetAndRaise(IsDangerProperty, ref _isDanger, value); + } + + private bool _isGhost = false; + + public bool IsGhost + { + get => _isGhost; + set => SetAndRaise(IsGhostProperty, ref _isGhost, value); + } + + private ButtonSizeType _sizeType = ButtonSizeType.Middle; + + public ButtonSizeType SizeType + { + get => _sizeType; + set => SetAndRaise(SizeTypeProperty, ref _sizeType, value); + } + + private PathIcon? _icon; + + public PathIcon? Icon + { + get => _icon; + set => SetAndRaise(IconProperty, ref _icon, value); + } + + private string _text = string.Empty; + + public string Text + { + get => _text; + set => SetAndRaise(TextProperty, ref _text, value); + } + + string ITokenIdProvider.TokenId => nameof(Button); + + static Button() + { + AffectsMeasure