From 5cffe6cdba4b89b17b395b836a80093c01e785f3 Mon Sep 17 00:00:00 2001 From: Onebeld Date: Sun, 14 Jun 2026 14:16:46 +0500 Subject: [PATCH 01/15] Icons regardless of type, library updates, and other improvements --- PleasantUI.sln | 9 --- build/Package.props | 6 +- designer/PleasantUI.Designer/App.axaml | 8 --- designer/PleasantUI.Designer/App.axaml.cs | 19 ------- .../PleasantUI.Designer.csproj | 28 --------- designer/PleasantUI.Designer/Program.cs | 20 ------- designer/PleasantUI.Designer/app.manifest | 18 ------ ...antUI.MaterialIcons.SourceGenerator.csproj | 2 +- .../PleasantUI.Example.Desktop.csproj | 14 ++--- .../PleasantUI.Example/Assets/Images.axaml | 45 ++++++++++++++- .../ControlPageCardItemTemplate.axaml | 9 +-- .../PleasantUI.Example.csproj | 6 +- .../Properties/Localizations/App.resx | 2 +- .../Properties/Localizations/App.ru.resx | 2 +- .../PleasantUI.Example/Views/AboutView.axaml | 10 +--- .../Views/Pages/HomePageView.axaml | 25 ++++---- .../PleasantUI.MaterialIcons.csproj | 2 +- .../PleasantUI.ToolKit.csproj | 8 +-- src/PleasantUI/Assets/IconGeometries.axaml | 3 +- .../Controls/Chrome/PleasantTitleBar.cs | 50 +++------------- .../Controls/CommandBar/CommandBarButton.cs | 6 +- .../CommandBar/CommandBarToggleButton.cs | 4 +- .../Controls/IconControl/IconControl.cs | 31 ++++++++++ src/PleasantUI/Core/Attached/IconHelper.cs | 29 ++++++++++ src/PleasantUI/PleasantUI.csproj | 6 +- src/PleasantUI/Properties/AssemblyInfo.cs | 1 + .../ControlThemes/BasicControls/Button.axaml | 2 +- .../BasicControls/CalendarItem.axaml | 5 +- .../BasicControls/DataValidationErrors.axaml | 2 +- .../BasicControls/DatePicker.axaml | 1 - .../BasicControls/DateTimePickerShared.axaml | 5 +- .../BasicControls/MenuItem.axaml | 19 ++++--- .../BasicControls/ScrollBar.axaml | 10 ++-- .../BasicControls/SelectableTextBlock.axaml | 1 + .../BasicControls/ToggleButton.axaml | 2 - .../Styling/ControlThemes/Controls.axaml | 7 ++- .../PleasantControls/BreadcrumbBar.axaml | 1 - .../PleasantControls/CommandBar.axaml | 57 ++++--------------- .../PleasantControls/IconControl.axaml | 28 +++++++++ .../PleasantCaptionButtons.axaml | 26 ++++----- .../PleasantControls/PleasantTitleBar.axaml | 48 ++++++---------- 41 files changed, 256 insertions(+), 321 deletions(-) delete mode 100644 designer/PleasantUI.Designer/App.axaml delete mode 100644 designer/PleasantUI.Designer/App.axaml.cs delete mode 100644 designer/PleasantUI.Designer/PleasantUI.Designer.csproj delete mode 100644 designer/PleasantUI.Designer/Program.cs delete mode 100644 designer/PleasantUI.Designer/app.manifest create mode 100644 src/PleasantUI/Controls/IconControl/IconControl.cs create mode 100644 src/PleasantUI/Core/Attached/IconHelper.cs create mode 100644 src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml diff --git a/PleasantUI.sln b/PleasantUI.sln index 714bf104..e3c46a6e 100644 --- a/PleasantUI.sln +++ b/PleasantUI.sln @@ -12,10 +12,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.DataGrid", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.Example", "samples\PleasantUI.Example\PleasantUI.Example.csproj", "{59DC0F4B-421E-407D-9435-F6ED14765634}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "designer", "designer", "{EBAC1577-CFD7-477C-848E-86BFFC345D90}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.Designer", "designer\PleasantUI.Designer\PleasantUI.Designer.csproj", "{88C264B9-51D4-41CF-AEC0-24F5075FA6B8}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generators", "generators", "{941F7A66-EFCB-4141-97CA-A0B779ED1BB4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.MaterialIcons", "src\PleasantUI.MaterialIcons\PleasantUI.MaterialIcons.csproj", "{2ABE0576-837B-4D1B-970D-00B52BF0E078}" @@ -57,10 +53,6 @@ Global {59DC0F4B-421E-407D-9435-F6ED14765634}.Debug|Any CPU.Build.0 = Debug|Any CPU {59DC0F4B-421E-407D-9435-F6ED14765634}.Release|Any CPU.ActiveCfg = Release|Any CPU {59DC0F4B-421E-407D-9435-F6ED14765634}.Release|Any CPU.Build.0 = Release|Any CPU - {88C264B9-51D4-41CF-AEC0-24F5075FA6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88C264B9-51D4-41CF-AEC0-24F5075FA6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88C264B9-51D4-41CF-AEC0-24F5075FA6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88C264B9-51D4-41CF-AEC0-24F5075FA6B8}.Release|Any CPU.Build.0 = Release|Any CPU {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Debug|Any CPU.Build.0 = Debug|Any CPU {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -78,7 +70,6 @@ Global {71BFCFA6-B53E-4F28-819F-51CB26516B5A} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} {91F6CDD0-6925-4289-90EC-8BDD245F7BD0} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} {59DC0F4B-421E-407D-9435-F6ED14765634} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} - {88C264B9-51D4-41CF-AEC0-24F5075FA6B8} = {EBAC1577-CFD7-477C-848E-86BFFC345D90} {9FDFD065-7664-4E0C-B928-D7FC8B074EDD} = {941F7A66-EFCB-4141-97CA-A0B779ED1BB4} {2ABE0576-837B-4D1B-970D-00B52BF0E078} = {4BCCA866-A31E-49B1-AB79-B9C94651D295} EndGlobalSection diff --git a/build/Package.props b/build/Package.props index 8b02683c..123a496e 100644 --- a/build/Package.props +++ b/build/Package.props @@ -1,6 +1,6 @@  - net9.0 + net10.0 enable enable default @@ -28,9 +28,9 @@ - + - + diff --git a/designer/PleasantUI.Designer/App.axaml b/designer/PleasantUI.Designer/App.axaml deleted file mode 100644 index 84902959..00000000 --- a/designer/PleasantUI.Designer/App.axaml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/designer/PleasantUI.Designer/App.axaml.cs b/designer/PleasantUI.Designer/App.axaml.cs deleted file mode 100644 index 12a545e4..00000000 --- a/designer/PleasantUI.Designer/App.axaml.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; -using PleasantUI.Controls; - -namespace PleasantUI.Designer; - -public partial class App : Application -{ - public override void Initialize() => AvaloniaXamlLoader.Load(this); - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - desktop.MainWindow = new PleasantWindow(); - - base.OnFrameworkInitializationCompleted(); - } -} \ No newline at end of file diff --git a/designer/PleasantUI.Designer/PleasantUI.Designer.csproj b/designer/PleasantUI.Designer/PleasantUI.Designer.csproj deleted file mode 100644 index fba65afc..00000000 --- a/designer/PleasantUI.Designer/PleasantUI.Designer.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - WinExe - net9.0 - enable - true - app.manifest - true - Full - - - - embedded - true - - - - - - - - - - - - - - diff --git a/designer/PleasantUI.Designer/Program.cs b/designer/PleasantUI.Designer/Program.cs deleted file mode 100644 index a4df59c9..00000000 --- a/designer/PleasantUI.Designer/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Avalonia; -using System; - -namespace PleasantUI.Designer; - -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() - .StartWithClassicDesktopLifetime(args); - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .LogToTrace(); -} \ No newline at end of file diff --git a/designer/PleasantUI.Designer/app.manifest b/designer/PleasantUI.Designer/app.manifest deleted file mode 100644 index 4a26d06e..00000000 --- a/designer/PleasantUI.Designer/app.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj b/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj index 3568ce1c..19c05fe2 100644 --- a/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj +++ b/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj @@ -20,6 +20,6 @@ - + diff --git a/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj b/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj index 8db2d6c6..dc24231f 100644 --- a/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj +++ b/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj @@ -1,7 +1,7 @@  Exe - net9.0 + net10.0 enable enable true @@ -19,13 +19,13 @@ - - - - - + + + + + - + diff --git a/samples/PleasantUI.Example/Assets/Images.axaml b/samples/PleasantUI.Example/Assets/Images.axaml index 21763fa9..2d2063d1 100644 --- a/samples/PleasantUI.Example/Assets/Images.axaml +++ b/samples/PleasantUI.Example/Assets/Images.axaml @@ -3,7 +3,15 @@ - + + + + + + + + @@ -16,7 +24,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + F1 M603.4,67.6z M0,0z M148.2,54.3L183.7,54.3 183.7,66.5 133.1,66.5 133.1,1 182.5,1 182.5,13.2 148.1,13.2 148.1,27.4 178.5,27.4 178.5,39.1 148.2,39.1 148.2,54.2z M318.2,33.4A31.3,31.3,0,0,0,310.8,30.2C308.1,29.3 305.4,28.6 302.7,28 300,27.4 297.5,26.8 295.3,26.1 293,25.5 291.3,24.7 289.8,23.7 288.5,22.7 287.8,21.3 287.8,19.6 287.8,18.2 288.2,16.9 289,15.8 289.8,14.8 291.2,13.8 293,13.2 294.9,12.5 297.3,12.2 300.2,12.2 303.1,12.2 306.2,12.6 309.2,13.4 312.2,14.2 315.2,15.5 318.3,17.3L323,5.8C320,3.8 316.4,2.4 312.4,1.5 308.4,0.6 304.4,0 300.4,0 294.1,0 289,1 285,2.8 280.9,4.6 277.8,7 275.8,10.1 273.8,13.2 272.8,16.6 272.8,20.4 272.8,23.8 273.5,26.7 274.8,28.9 276.3,31.2 278.1,32.9 280.3,34.4 282.6,35.7 285,36.8 287.8,37.7 290.5,38.6 293.2,39.3 295.8,39.9 298.6,40.5 301.1,41.1 303.3,41.8 305.5,42.5 307.3,43.3 308.7,44.3A5,5,0,0,1,310.7,48.4C310.7,49.8 310.3,51 309.5,52.1 308.5,53.1 307.2,54 305.4,54.6A36.7,36.7,0,0,1,286.5,53.6C282.7,52.3,279.4,50.6,276.5,48.6L271.5,60C274.4,62.3,278.2,64,283,65.5A50,50,0,0,0,313.5,64.8C317.5,62.9 320.6,60.5 322.7,57.4 324.7,54.4 325.7,51 325.7,47.4 325.7,43.9 325.1,41.1 323.7,38.8 322.3,36.6 320.5,34.8 318.2,33.4z M236,1.1L265.2,66.5 249.3,66.5 243.6,52.5 213.3,52.5 207.4,66.5 191.9,66.5 221,1 235.9,1z M238.8,41L228.4,16 218,41 238.8,41z M87.2,1L72,1 72,66.4 120,66.4 120,54 87.2,54 87.2,1z M53.2,12C55.5,15.6 56.7,19.8 56.7,24.7 56.7,29.6 55.5,33.7 53.2,37.3A22.1,22.1,0,0,1,43.4,45.5A36.8,36.8,0,0,1,28.3,48.4L15,48.4 15,66.4 0,66.4 0,1 28.3,1C34.1,1 39.2,2 43.4,3.9 47.6,5.8 50.9,8.5 53.2,12z M41.3,24.8C41.3,21.2 40.2,18.4 37.9,16.4 35.6,14.4 32.1,13.4 27.4,13.4L15.1,13.4 15.1,36.1 27.4,36.1C32.1,36.1 35.6,35.1 37.9,33.1 40.2,31.1 41.4,28.4 41.4,24.8z M489.7,13.4L510.7,13.4 510.7,66.4 525.7,66.4 525.7,13.4 546.7,13.4 546.7,1.1 489.7,1.1 489.7,13.4z M581.2,18.9C581.2,21.9 580.6,24.1 579.3,25.5 578.1,26.8 576.3,27.5 574.1,27.5 571.9,27.5 570.1,26.8 568.8,25.5 567.5,24.1 566.9,21.9 566.9,18.9L566.9,1 559.5,1 559.5,19C559.5,23.7 560.8,27.4 563.3,29.9 565.9,32.4 569.5,33.7 574,33.7 578.5,33.7 582.2,32.4 584.7,29.9 587.3,27.4 588.6,23.7 588.6,19L588.6,0.999999999999993 581.2,0.999999999999993 581.2,19z M596,1L596,33.2 603.4,33.2 603.4,1 596,1z M462,40.8L429.6,1 417,1 417,66.4 432,66.4 432,26.8 464.5,66.5 477,66.5 477,1 462,1 462,40.7z M376.4,1L405.6,66.4 389.7,66.4 383.9,52.4 353.7,52.4 347.8,66.4 332.3,66.4 361.4,1 376.3,1z M379.2,40.9L368.8,15.9 358.4,40.9 379.2,40.9z diff --git a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml index e0c5f88f..ff2975ee 100644 --- a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml +++ b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml @@ -17,10 +17,11 @@ CornerRadius="{DynamicResource ControlCornerRadius}" Background="{DynamicResource ControlFillColor2}" VerticalAlignment="Top"> - + diff --git a/samples/PleasantUI.Example/PleasantUI.Example.csproj b/samples/PleasantUI.Example/PleasantUI.Example.csproj index 7a538881..db2b912b 100644 --- a/samples/PleasantUI.Example/PleasantUI.Example.csproj +++ b/samples/PleasantUI.Example/PleasantUI.Example.csproj @@ -1,6 +1,6 @@  - net9.0 + net10.0 enable enable Full @@ -22,8 +22,8 @@ - - + + diff --git a/samples/PleasantUI.Example/Properties/Localizations/App.resx b/samples/PleasantUI.Example/Properties/Localizations/App.resx index 0b9cc9a6..c7723f3c 100644 --- a/samples/PleasantUI.Example/Properties/Localizations/App.resx +++ b/samples/PleasantUI.Example/Properties/Localizations/App.resx @@ -22,7 +22,7 @@ Settings - Welcome to + Welcome! Theme diff --git a/samples/PleasantUI.Example/Properties/Localizations/App.ru.resx b/samples/PleasantUI.Example/Properties/Localizations/App.ru.resx index 7257aaa1..28c8b85c 100644 --- a/samples/PleasantUI.Example/Properties/Localizations/App.ru.resx +++ b/samples/PleasantUI.Example/Properties/Localizations/App.ru.resx @@ -15,7 +15,7 @@ Настройки - Добро пожаловать в + Добро пожаловать! Тема diff --git a/samples/PleasantUI.Example/Views/AboutView.axaml b/samples/PleasantUI.Example/Views/AboutView.axaml index 08a282f9..20c45d8a 100644 --- a/samples/PleasantUI.Example/Views/AboutView.axaml +++ b/samples/PleasantUI.Example/Views/AboutView.axaml @@ -18,15 +18,7 @@ Padding="20"> - - - - - + diff --git a/samples/PleasantUI.Example/Views/Pages/HomePageView.axaml b/samples/PleasantUI.Example/Views/Pages/HomePageView.axaml index 3a58ffb3..c3c05c3b 100644 --- a/samples/PleasantUI.Example/Views/Pages/HomePageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/HomePageView.axaml @@ -16,26 +16,26 @@ - - - + + + + + + + + + - - - - - - - + TextWrapping="Wrap" + Text="{Localize WelcomeToPleasantUI}" /> @@ -72,6 +72,5 @@ - diff --git a/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj b/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj index 79ebb767..03840820 100644 --- a/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj +++ b/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/PleasantUI.ToolKit/PleasantUI.ToolKit.csproj b/src/PleasantUI.ToolKit/PleasantUI.ToolKit.csproj index 97c3a2f1..8bc33d91 100644 --- a/src/PleasantUI.ToolKit/PleasantUI.ToolKit.csproj +++ b/src/PleasantUI.ToolKit/PleasantUI.ToolKit.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/PleasantUI/Assets/IconGeometries.axaml b/src/PleasantUI/Assets/IconGeometries.axaml index 00726091..bf3403fb 100644 --- a/src/PleasantUI/Assets/IconGeometries.axaml +++ b/src/PleasantUI/Assets/IconGeometries.axaml @@ -9,9 +9,8 @@ M5,0 L5,5 L0,5 L0,3.5 L3.5,3.5 L3.5,0 Z M10,0 L11.5,0 L11.5,3.5 L15,3.5 L15,5 L10,5 Z M0,10 L5,10 L5,15 L3.5,15 L3.5,11.5 L0,11.5 Z M10,10 L15,10 L15,11.5 L11.5,11.5 L11.5,15 L10,15 Z F1 M20,20z M0,0z M10,12.13L2.55,19.58Q2.13,20 1.48,20 0.84,20 0.42,19.58 0,19.16 0,18.52 0,17.87 0.42,17.45L7.87,10 0.42,2.55Q0,2.13 0,1.48 0,0.84 0.42,0.42 0.84,0 1.48,0 2.13,0 2.55,0.42L10,7.87 17.45,0.42Q17.87,0 18.52,0 19.16,0 19.58,0.42 20,0.84 20,1.48 20,2.13 19.58,2.55L12.13,10 19.58,17.45Q20,17.87 20,18.52 20,19.16 19.58,19.58 19.16,20 18.52,20 17.87,20 17.45,19.58z F1 M40,40z M0,0z M12.5,6L10,6A6.3,6.3,0,0,1,16.2,0L30.7,0A9.3,9.3,0,0,1,40,9.3L40,23.7A6.3,6.3,0,0,1,34,30L34,27.5A3.8,3.8,0,0,0,37.5,23.7L37.5,9.3A6.8,6.8,0,0,0,30.7,2.5L16.2,2.5A3.8,3.8,0,0,0,12.5,6z M6.2,8A6.3,6.3,0,0,0,0,14.3L0,33.7A6.3,6.3,0,0,0,6.3,40L25.7,40A6.3,6.3,0,0,0,32,33.7L32,14.3A6.3,6.3,0,0,0,25.7,8z M2.5,14.3A3.8,3.8,0,0,1,6.3,10.5L25.7,10.5A3.8,3.8,0,0,1,29.5,14.3L29.5,33.7A3.8,3.8,0,0,1,25.7,37.5L6.4,37.5A3.8,3.8,0,0,1,2.6,33.7z - + F1 M4.7,8.6z M0,0z M3.85,8.45L0.23,4.83C0.15,4.75 0.09,4.66 0.05,4.58 0.01,4.5 0,4.4 0,4.3 0,4.2 0.02,4.11 0.05,4.02 0.08,3.93 0.14,3.85 0.23,3.77L3.85,0.15C3.9,0.1 3.95,0.06 4.01,0.04 4.07,0.02 4.13,0 4.2,0 4.33,0 4.45,0.05 4.55,0.14 4.65,0.23 4.7,0.35 4.7,0.5L4.7,8.1C4.7,8.25 4.65,8.37 4.55,8.46 4.45,8.55 4.33,8.6 4.2,8.6 4.17,8.6 4.05,8.55 3.85,8.45z - F1 M12.4,10.1z M0,0z M10.6,10.1L1.7,10.1A1.7,1.7,0,0,1,0.3,7.4L4.7,0.8A1.7,1.7,0,0,1,7.6,0.8L12.1,7.4A1.8,1.8,0,0,1,10.6,10.1z M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z M8 3C5.79 3 4 4.79 4 7V14C4 15.1 4.9 16 6 16H9V20C9 21.1 9.9 22 11 22H13C14.1 22 15 21.1 15 20V16H18C19.1 16 20 15.1 20 14V3H8M8 5H12V7H14V5H15V9H17V5H18V10H6V7C6 5.9 6.9 5 8 5M6 14V12H18V14H6Z M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M14.59,8L12,10.59L9.41,8L8,9.41L10.59,12L8,14.59L9.41,16L12,13.41L14.59,16L16,14.59L13.41,12L16,9.41L14.59,8Z diff --git a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs index 1a994c3c..240df035 100644 --- a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs +++ b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs @@ -72,8 +72,8 @@ public enum Type private Border? _dragWindowBorder; private MenuItem? _expandMenuItem; - private Panel? _displayIcon; - private Panel? _displayTitle; + private IconControl? _displayIcon; + private IconControl? _displayTitle; private ContentPresenter? _leftTitleBarContent; private MenuItem? _reestablishMenuItem; @@ -141,10 +141,10 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _collapseMenuItem = e.NameScope.Get("PART_CollapseMenuItem"); _reestablishMenuItem = e.NameScope.Get("PART_ReestablishMenuItem"); - _displayIcon = e.NameScope.Find("PART_DisplayIcon"); - _displayTitle = e.NameScope.Get("PART_DisplayTitle"); + _displayIcon = e.NameScope.Get("PART_DisplayIcon"); + _displayTitle = e.NameScope.Get("PART_DisplayTitle"); _subtitle = e.NameScope.Get("PART_Subtitle"); - _titleBarGrid = e.NameScope.Find("PART_TitleBarGrid"); + _titleBarGrid = e.NameScope.Get("PART_TitleBarGrid"); _dragWindowBorder = e.NameScope.Get("PART_DragWindow"); _titlePanel = e.NameScope.Get("PART_TitlePanel"); @@ -287,48 +287,12 @@ private void SetDisplayIcon(object? obj) if (_displayIcon is null || obj is WindowIcon) return; - _displayIcon.Children.Clear(); - - switch (obj) - { - case Geometry geometry: - _displayIcon.Children.Add(new PathIcon { Data = geometry, Width = 16, Height = 16, [!ForegroundProperty] = _displayIcon[!TextElement.ForegroundProperty] }); - break; - case IImage icon: - _displayIcon.Children.Add(new Image { Source = icon, Width = 16, Height = 16 }); - break; - case Control control: - _displayIcon.Children.Add(control); - break; - - case null when _host?.Icon is not null: - _displayIcon.Children.Add(new Image { Source = _host?.Icon.ToBitmap(), Width = 16, Height = 16 }); - break; - } + _displayIcon.Icon = obj; } private void SetDisplayTitle(object? obj) { - if (_displayTitle is null) - return; - - _displayTitle.Children.Clear(); - - switch (obj) - { - case Geometry geometry: - _displayTitle.Children.Add(new PathIcon { Data = geometry, Height = 8, Width = double.NaN, [!ForegroundProperty] = _displayTitle[!TextElement.ForegroundProperty] }); - break; - case IImage icon: - _displayTitle.Children.Add(new Image { Source = icon, Height = 8, Width = double.NaN }); - break; - case Control control: - _displayTitle.Children.Add(control); - break; - case null when _host?.Title is not null: - _displayTitle.Children.Add(new TextBlock { Text = _host.Title }); - break; - } + _displayTitle?.Icon = obj; } private void PopulateTitleBar() diff --git a/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs b/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs index cab181f2..b4bd585d 100644 --- a/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs +++ b/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs @@ -30,8 +30,8 @@ public class CommandBarButton : Button, ICommandBarElement // ── Styled properties ───────────────────────────────────────────────────── /// Defines the property. - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon)); + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); /// Defines the property. public static readonly StyledProperty LabelProperty = @@ -58,7 +58,7 @@ public class CommandBarButton : Button, ICommandBarElement // ── CLR accessors ───────────────────────────────────────────────────────── /// Gets or sets the icon geometry displayed on the button. - public Geometry? Icon + public object? Icon { get => GetValue(IconProperty); set => SetValue(IconProperty, value); diff --git a/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs b/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs index caa22eaa..d2e2c3f8 100644 --- a/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs +++ b/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs @@ -21,7 +21,7 @@ public class CommandBarToggleButton : ToggleButton, ICommandBarElement // ── Styled properties ───────────────────────────────────────────────────── /// Defines the property. - public static readonly StyledProperty IconProperty = + public static readonly StyledProperty IconProperty = CommandBarButton.IconProperty.AddOwner(); /// Defines the property. @@ -49,7 +49,7 @@ public class CommandBarToggleButton : ToggleButton, ICommandBarElement // ── CLR accessors ───────────────────────────────────────────────────────── /// Gets or sets the icon geometry displayed on the button. - public Geometry? Icon + public object? Icon { get => GetValue(IconProperty); set => SetValue(IconProperty, value); diff --git a/src/PleasantUI/Controls/IconControl/IconControl.cs b/src/PleasantUI/Controls/IconControl/IconControl.cs new file mode 100644 index 00000000..578b0416 --- /dev/null +++ b/src/PleasantUI/Controls/IconControl/IconControl.cs @@ -0,0 +1,31 @@ +using Avalonia; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Data; +using Avalonia.Metadata; + +namespace PleasantUI.Controls; + +public class IconControl : TemplatedControl +{ + public static readonly StyledProperty IconTemplateProperty = + AvaloniaProperty.Register( + nameof(IconTemplate)); + + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); + + public IDataTemplate? IconTemplate + { + get => GetValue(IconTemplateProperty); + set => SetValue(IconTemplateProperty, value); + } + + [Content] + [DependsOn(nameof(IconTemplate))] + public object? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } +} \ No newline at end of file diff --git a/src/PleasantUI/Core/Attached/IconHelper.cs b/src/PleasantUI/Core/Attached/IconHelper.cs new file mode 100644 index 00000000..12ee21dd --- /dev/null +++ b/src/PleasantUI/Core/Attached/IconHelper.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; + +namespace PleasantUI.Core.Attached; + +public class IconHelper : AvaloniaObject +{ + public static readonly AttachedProperty WidthProperty = + AvaloniaProperty.RegisterAttached("Width", defaultBindingMode: BindingMode.TwoWay); + + public static readonly AttachedProperty HeightProperty = + AvaloniaProperty.RegisterAttached("Height", defaultBindingMode: BindingMode.TwoWay); + + public static readonly AttachedProperty FontSizeProperty = + AvaloniaProperty.RegisterAttached("FontSize", 1, defaultBindingMode: BindingMode.TwoWay); + + public static double GetWidth(AvaloniaObject obj) => obj.GetValue(WidthProperty); + + public static void SetWidth(AvaloniaObject obj, double value) => obj.SetValue(WidthProperty, value); + + public static double GetHeight(AvaloniaObject obj) => obj.GetValue(HeightProperty); + + public static void SetHeight(AvaloniaObject obj, double value) => obj.SetValue(HeightProperty, value); + + public static double GetFontSize(AvaloniaObject obj) => obj.GetValue(FontSizeProperty); + + public static void SetFontSize(AvaloniaObject obj, double value) => obj.SetValue(FontSizeProperty, value); +} \ No newline at end of file diff --git a/src/PleasantUI/PleasantUI.csproj b/src/PleasantUI/PleasantUI.csproj index 19f06e02..ad915a45 100644 --- a/src/PleasantUI/PleasantUI.csproj +++ b/src/PleasantUI/PleasantUI.csproj @@ -7,10 +7,10 @@ - + - - + + diff --git a/src/PleasantUI/Properties/AssemblyInfo.cs b/src/PleasantUI/Properties/AssemblyInfo.cs index 49ac7835..14b1400d 100644 --- a/src/PleasantUI/Properties/AssemblyInfo.cs +++ b/src/PleasantUI/Properties/AssemblyInfo.cs @@ -6,5 +6,6 @@ [assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Controls.Flyout")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Controls.Flyout.Presenters")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Core")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Core.Attached")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Converters")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI.Core.Extensions.Markup")] \ No newline at end of file diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml index b4df5823..8479de99 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml @@ -6,7 +6,7 @@ @@ -93,7 +92,7 @@ Theme="{DynamicResource PleasantCalendarButton}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Left"> - diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/DataValidationErrors.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/DataValidationErrors.axaml index 192220ee..c8a694c9 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/DataValidationErrors.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/DataValidationErrors.axaml @@ -16,7 +16,7 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/DateTimePickerShared.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/DateTimePickerShared.axaml index 45da64d1..89c0b438 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/DateTimePickerShared.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/DateTimePickerShared.axaml @@ -64,7 +64,7 @@ @@ -100,8 +100,7 @@ diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml index d84da91d..4c11d90c 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml @@ -54,6 +54,10 @@ + + + + @@ -78,13 +82,14 @@ - + @@ -103,8 +103,8 @@ @@ -190,8 +190,8 @@ Name="TrackRect" Fill="{DynamicResource ControlFillColor2}" Opacity="0" - RadiusY="{CompiledBinding Source={DynamicResource ScrollBarCornerRadius}, Converter={x:Static CornerRadiusFilterConverters.TopLeft}}" - RadiusX="{CompiledBinding Source={DynamicResource ScrollBarCornerRadius}, Converter={x:Static CornerRadiusFilterConverters.TopLeft}}" + RadiusY="{CompiledBinding Source={StaticResource ScrollBarCornerRadius}, Converter={x:Static CornerRadiusFilterConverters.TopLeft}}" + RadiusX="{CompiledBinding Source={StaticResource ScrollBarCornerRadius}, Converter={x:Static CornerRadiusFilterConverters.TopLeft}}" Grid.ColumnSpan="3" Margin="0"> diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/SelectableTextBlock.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/SelectableTextBlock.axaml index b098bb9e..07b44892 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/SelectableTextBlock.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/SelectableTextBlock.axaml @@ -15,6 +15,7 @@ + - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/BreadcrumbBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/BreadcrumbBar.axaml index 36aa15ff..be994aa4 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/BreadcrumbBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/BreadcrumbBar.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:PleasantUI.Controls" x:ClassModifier="internal"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml index 63bb89fa..ed444ce9 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:PleasantUI.Controls" x:ClassModifier="internal"> - @@ -27,7 +26,6 @@ - 40 64 24 @@ -94,14 +92,9 @@ 2. The "overflow" panel — a Grid with text + accelerator hint Exactly one is visible at a time. --> - + - - - - - @@ -134,8 +127,9 @@ Height="{DynamicResource CommandBarButtonIconSize}" Margin="11 0" HorizontalAlignment="Center"> - + + @@ -161,8 +155,7 @@ Margin="12 0 0 0" VerticalAlignment="Center" IsVisible="False"> - + @@ -192,20 +185,6 @@ - - - - - - - + BasedOn="{StaticResource {x:Type ToggleButton}}"> - + + + - + @@ -329,8 +308,7 @@ Margin="12 0 0 0" VerticalAlignment="Center" IsVisible="False"> - + @@ -353,24 +331,12 @@ Margin="0 0 12 0" IsVisible="False" /> - - - - - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml new file mode 100644 index 00000000..3934fa50 --- /dev/null +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml index eff6207d..ceafa5c5 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml @@ -29,19 +29,19 @@ @@ -61,27 +61,27 @@ diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantTitleBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantTitleBar.axaml index 45fd60cd..18755511 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantTitleBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantTitleBar.axaml @@ -8,30 +8,20 @@ - - - - - + - - - - + Header="{Localize CollapseMenu, Default='C_ollapse'}" Icon="{DynamicResource SubtractRegular}" + IconHelper.Width="12" + IconHelper.Height="12" /> - - - - + Header="{Localize ExpandMenu, Default='_Expand'}" Icon="{DynamicResource MaximizeRegular}" + IconHelper.Width="12" + IconHelper.Height="12" /> - - - - + InputGesture="Ctrl+Q" Icon="{DynamicResource DismissRegular}" + IconHelper.Width="12" + IconHelper.Height="12" /> @@ -66,9 +52,9 @@ Spacing="5" VerticalAlignment="Center" IsHitTestVisible="False"> - + - + Date: Sun, 14 Jun 2026 20:33:42 +0500 Subject: [PATCH 02/15] Replace PathIcon with IconControl where possible and various fixes --- samples/PleasantUI.Example/MainView.axaml.cs | 7 +- .../Properties/Localizations/Library.resx | 3 + .../Properties/Localizations/Library.ru.resx | 3 + .../Pages/ToolKitPages/MessageBoxViewModel.cs | 4 +- .../PleasantUI.Example/Views/HomeView.axaml | 3 +- .../Pages/ControlPages/TextBoxPageView.axaml | 9 +- .../InstallWizardPageView.axaml | 7 +- .../ItemListPanelPageView.axaml | 2 - .../Views/SettingsView.axaml | 2 +- .../Controls/NoticeDialog/NoticeDialog.axaml | 11 +- .../NoticeDialog/NoticeDialog.axaml.cs | 5 +- .../ThemeColorItemTemplate.axaml | 22 ++-- .../Enums/MessageBoxStyle.cs | 2 +- .../Enums/PleasantDialogResult.cs | 2 +- src/PleasantUI.ToolKit/MessageBox.axaml.cs | 6 +- .../PleasantDialog.axaml.cs | 4 +- .../Structures/MessageBoxButton.cs | 2 +- .../Structures/MessageBoxResult.cs | 2 +- .../Structures/PleasantDialogButton.cs | 5 +- .../PleasantDialogClosingEventArgs.cs | 2 +- .../Structures/PleasantDialogCommand.cs | 4 +- .../ThemeEditorWindow.axaml | 8 +- .../Controls/Drawer/PleasantDrawer.cs | 11 ++ .../InformationBlock/InformationBlock.cs | 7 +- .../OptionsDisplayItem/OptionsDisplayItem.cs | 6 +- .../SplashScreen/PleasantSplashScreen.cs | 6 +- .../Controls/TreeViewPanel/TreeViewSection.cs | 6 +- src/PleasantUI/Core/Attached/IconHelper.cs | 35 ++++++ src/PleasantUI/Core/Helpers/WindowHelper.cs | 34 +++--- .../BasicControls/MenuItem.axaml | 4 +- .../ControlThemes/BasicControls/TextBox.axaml | 35 ++---- .../Styling/ControlThemes/Controls.axaml | 2 + .../PleasantControls/CommandBar.axaml | 7 +- .../PleasantControls/DashboardCard.axaml | 1 - .../PleasantControls/IconControl.axaml | 4 +- .../PleasantControls/InformationBlock.axaml | 10 +- .../PleasantControls/InstallWizard.axaml | 6 +- .../PleasantControls/MarkedTextBox.axaml | 60 ++++------ .../PleasantControls/OptionsDisplayItem.axaml | 25 ++--- .../PleasantControls/PleasantDrawer.axaml | 20 ++-- .../PleasantHorizontallySplittedView.axaml | 1 - .../PleasantControls/PleasantMiniWindow.axaml | 28 ++--- .../PleasantControls/PleasantReDock.axaml | 1 - .../PleasantControls/PleasantSideBar.axaml | 1 - .../PleasantSplashScreen.axaml | 16 ++- .../PleasantControls/PleasantTrayPopup.axaml | 2 - .../PleasantControls/PopConfirm.axaml | 3 +- .../PleasantControls/SelectionList.axaml | 1 - .../PleasantControls/TreeViewPanel.axaml | 103 ++++++++---------- 49 files changed, 282 insertions(+), 268 deletions(-) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Enums/MessageBoxStyle.cs (90%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Enums/PleasantDialogResult.cs (85%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Structures/MessageBoxButton.cs (95%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Structures/MessageBoxResult.cs (93%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Structures/PleasantDialogButton.cs (96%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Structures/PleasantDialogClosingEventArgs.cs (93%) rename src/{PleasantUI/Core => PleasantUI.ToolKit}/Structures/PleasantDialogCommand.cs (95%) diff --git a/samples/PleasantUI.Example/MainView.axaml.cs b/samples/PleasantUI.Example/MainView.axaml.cs index 66e16d65..33ad5530 100644 --- a/samples/PleasantUI.Example/MainView.axaml.cs +++ b/samples/PleasantUI.Example/MainView.axaml.cs @@ -33,8 +33,7 @@ private void OnNavigationSelectionChanged(object? sender, SelectionChangedEventA if (DataContext is not AppViewModel vm) return; if (e.AddedItems.Count == 0) return; - var selected = e.AddedItems[0] as NavigationViewItem; - if (selected is null) return; + if (e.AddedItems[0] is not NavigationViewItem selected) return; // Switching to a top-level item (About, Settings, or Home directly) — // clear any previously selected leaf so it doesn't stay highlighted. @@ -49,10 +48,10 @@ private void OnNavigationSelectionChanged(object? sender, SelectionChangedEventA } // Leaf item — navigate to the corresponding page - var page = selected.Tag as string switch + IPage? page = (selected.Tag as string) switch { // Basic controls - "Button" => (IPage)new ButtonPage(), + "Button" => new ButtonPage(), "Checkbox" => new CheckBoxPage(), "Progress" => new ProgressPage(), "Calendar" => new CalendarPage(), diff --git a/samples/PleasantUI.Example/Properties/Localizations/Library.resx b/samples/PleasantUI.Example/Properties/Localizations/Library.resx index 0fdbd4da..1498a07f 100644 --- a/samples/PleasantUI.Example/Properties/Localizations/Library.resx +++ b/samples/PleasantUI.Example/Properties/Localizations/Library.resx @@ -444,4 +444,7 @@ IsEmailRequired=True — the Send button is blocked until a valid email is entered. Open (email required) Last result: + + Attach over windows + \ No newline at end of file diff --git a/samples/PleasantUI.Example/Properties/Localizations/Library.ru.resx b/samples/PleasantUI.Example/Properties/Localizations/Library.ru.resx index d707fafd..85e3704a 100644 --- a/samples/PleasantUI.Example/Properties/Localizations/Library.ru.resx +++ b/samples/PleasantUI.Example/Properties/Localizations/Library.ru.resx @@ -437,4 +437,7 @@ IsEmailRequired=True — кнопка «Отправить» заблокирована до ввода корректного email. Открыть (email обязателен) Последний результат: + + Закрепить поверх окон + \ No newline at end of file diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs index 93b42fc9..2d7d08e0 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs @@ -1,10 +1,10 @@ using Avalonia.Controls; using Avalonia.Layout; using PleasantUI.Core; -using PleasantUI.Core.Enums; using PleasantUI.Core.Localization; -using PleasantUI.Core.Structures; using PleasantUI.ToolKit; +using PleasantUI.ToolKit.Enums; +using PleasantUI.ToolKit.Structures; namespace PleasantUI.Example.ViewModels.Pages.ToolKitPages; diff --git a/samples/PleasantUI.Example/Views/HomeView.axaml b/samples/PleasantUI.Example/Views/HomeView.axaml index f40437cf..b1c4610d 100644 --- a/samples/PleasantUI.Example/Views/HomeView.axaml +++ b/samples/PleasantUI.Example/Views/HomeView.axaml @@ -21,8 +21,7 @@ - + + + + - + - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/InstallWizardPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/InstallWizardPageView.axaml index 41800b4a..062ad9b6 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/InstallWizardPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/InstallWizardPageView.axaml @@ -57,10 +57,9 @@ - - - + Height="420" + AppIcon="{DynamicResource PleasantUIForeground}" + IconHelper.Width="130"> diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/ItemListPanelPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/ItemListPanelPageView.axaml index b5ea9a91..a4d11c2b 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/ItemListPanelPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/ItemListPanelPageView.axaml @@ -5,7 +5,6 @@ xmlns:controls="clr-namespace:PleasantUI.Controls;assembly=PleasantUI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="900" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.ItemListPanelPageView"> - - diff --git a/samples/PleasantUI.Example/Views/SettingsView.axaml b/samples/PleasantUI.Example/Views/SettingsView.axaml index 15e0ecc0..6941336f 100644 --- a/samples/PleasantUI.Example/Views/SettingsView.axaml +++ b/samples/PleasantUI.Example/Views/SettingsView.axaml @@ -93,7 +93,7 @@ Command="{CompiledBinding CreateThemeAsync}" Width="40" Height="40"> - + diff --git a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml index 35629f47..3bae85e4 100644 --- a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml +++ b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml @@ -25,11 +25,11 @@ Background="#22FFFFFF" Margin="0 0 12 0" VerticalAlignment="Center"> - + - diff --git a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml.cs b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml.cs index 3ea3dd97..685b6c64 100644 --- a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml.cs +++ b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Styling; +using PleasantUI.Controls; using PleasantUI.Core; using PleasantUI.Core.Localization; @@ -99,7 +100,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang private void UpdateSeverity() { var headerBorder = this.FindControl("HeaderBorder"); - var severityIcon = this.FindControl("SeverityIcon"); + var severityIcon = this.FindControl("SeverityIcon"); var titleText = this.FindControl("TitleText"); if (headerBorder is null || severityIcon is null || titleText is null) @@ -108,7 +109,7 @@ private void UpdateSeverity() var severity = Severity; headerBorder.Background = GetSeverityHeaderBrush(severity); - severityIcon.Data = severity switch + severityIcon.Icon = severity switch { NoticeSeverity.Info => InfoIconGeometry, NoticeSeverity.Warning => WarningIconGeometry, diff --git a/src/PleasantUI.ToolKit/DataTemplates/ThemeColorItemTemplate.axaml b/src/PleasantUI.ToolKit/DataTemplates/ThemeColorItemTemplate.axaml index baa81ae2..ccc40951 100644 --- a/src/PleasantUI.ToolKit/DataTemplates/ThemeColorItemTemplate.axaml +++ b/src/PleasantUI.ToolKit/DataTemplates/ThemeColorItemTemplate.axaml @@ -12,26 +12,18 @@ - - - - + Command="{CompiledBinding CopyColorAsync}" + Icon="{DynamicResource CopyRegular}" /> + - - - - + Command="{CompiledBinding PasteColorAsync}" + Icon="{DynamicResource PasteRegular}" /> - - - - + Command="{CompiledBinding ChangeColorAsync}" + Icon="{DynamicResource EditRegular}" /> diff --git a/src/PleasantUI/Core/Enums/MessageBoxStyle.cs b/src/PleasantUI.ToolKit/Enums/MessageBoxStyle.cs similarity index 90% rename from src/PleasantUI/Core/Enums/MessageBoxStyle.cs rename to src/PleasantUI.ToolKit/Enums/MessageBoxStyle.cs index b4b61767..c552d60a 100644 --- a/src/PleasantUI/Core/Enums/MessageBoxStyle.cs +++ b/src/PleasantUI.ToolKit/Enums/MessageBoxStyle.cs @@ -1,4 +1,4 @@ -namespace PleasantUI.Core.Enums; +namespace PleasantUI.ToolKit.Enums; /// /// Controls the visual style of a . diff --git a/src/PleasantUI/Core/Enums/PleasantDialogResult.cs b/src/PleasantUI.ToolKit/Enums/PleasantDialogResult.cs similarity index 85% rename from src/PleasantUI/Core/Enums/PleasantDialogResult.cs rename to src/PleasantUI.ToolKit/Enums/PleasantDialogResult.cs index 1e8f95d9..0084437a 100644 --- a/src/PleasantUI/Core/Enums/PleasantDialogResult.cs +++ b/src/PleasantUI.ToolKit/Enums/PleasantDialogResult.cs @@ -1,4 +1,4 @@ -namespace PleasantUI.Core.Enums; +namespace PleasantUI.ToolKit.Enums; /// /// Standard result values for a . diff --git a/src/PleasantUI.ToolKit/MessageBox.axaml.cs b/src/PleasantUI.ToolKit/MessageBox.axaml.cs index 1a5b6cb7..afc5591c 100644 --- a/src/PleasantUI.ToolKit/MessageBox.axaml.cs +++ b/src/PleasantUI.ToolKit/MessageBox.axaml.cs @@ -7,11 +7,11 @@ using Avalonia.Media; using Avalonia.Styling; using PleasantUI.Controls; -using PleasantUI.Core.Enums; using PleasantUI.Core.Extensions; using PleasantUI.Core.Interfaces; using PleasantUI.Core.Localization; -using PleasantUI.Core.Structures; +using PleasantUI.ToolKit.Enums; +using PleasantUI.ToolKit.Structures; namespace PleasantUI.ToolKit; @@ -20,7 +20,7 @@ namespace PleasantUI.ToolKit; /// public sealed partial class MessageBox : ContentDialog { - private MessageBox() => InitializeComponent(); + public MessageBox() => InitializeComponent(); /// /// Displays a message box and returns the clicked button's result string. diff --git a/src/PleasantUI.ToolKit/PleasantDialog.axaml.cs b/src/PleasantUI.ToolKit/PleasantDialog.axaml.cs index 49ab637b..bdacaa21 100644 --- a/src/PleasantUI.ToolKit/PleasantDialog.axaml.cs +++ b/src/PleasantUI.ToolKit/PleasantDialog.axaml.cs @@ -7,11 +7,11 @@ using Avalonia.Media; using Avalonia.Styling; using PleasantUI.Controls; -using PleasantUI.Core.Enums; using PleasantUI.Core.Extensions; using PleasantUI.Core.Interfaces; using PleasantUI.Core.Localization; -using PleasantUI.Core.Structures; +using PleasantUI.ToolKit.Enums; +using PleasantUI.ToolKit.Structures; namespace PleasantUI.ToolKit; diff --git a/src/PleasantUI/Core/Structures/MessageBoxButton.cs b/src/PleasantUI.ToolKit/Structures/MessageBoxButton.cs similarity index 95% rename from src/PleasantUI/Core/Structures/MessageBoxButton.cs rename to src/PleasantUI.ToolKit/Structures/MessageBoxButton.cs index 734f250f..272c3100 100644 --- a/src/PleasantUI/Core/Structures/MessageBoxButton.cs +++ b/src/PleasantUI.ToolKit/Structures/MessageBoxButton.cs @@ -1,4 +1,4 @@ -namespace PleasantUI.Core.Structures; +namespace PleasantUI.ToolKit.Structures; /// /// Defines the structure of the button for the message box diff --git a/src/PleasantUI/Core/Structures/MessageBoxResult.cs b/src/PleasantUI.ToolKit/Structures/MessageBoxResult.cs similarity index 93% rename from src/PleasantUI/Core/Structures/MessageBoxResult.cs rename to src/PleasantUI.ToolKit/Structures/MessageBoxResult.cs index 7d0b9448..266e730d 100644 --- a/src/PleasantUI/Core/Structures/MessageBoxResult.cs +++ b/src/PleasantUI.ToolKit/Structures/MessageBoxResult.cs @@ -1,4 +1,4 @@ -namespace PleasantUI.Core.Structures; +namespace PleasantUI.ToolKit.Structures; /// /// Result returned by . diff --git a/src/PleasantUI/Core/Structures/PleasantDialogButton.cs b/src/PleasantUI.ToolKit/Structures/PleasantDialogButton.cs similarity index 96% rename from src/PleasantUI/Core/Structures/PleasantDialogButton.cs rename to src/PleasantUI.ToolKit/Structures/PleasantDialogButton.cs index 1b6febfe..eb440cca 100644 --- a/src/PleasantUI/Core/Structures/PleasantDialogButton.cs +++ b/src/PleasantUI.ToolKit/Structures/PleasantDialogButton.cs @@ -1,8 +1,7 @@ using System.Windows.Input; +using PleasantUI.ToolKit.Enums; -using PleasantUI.Core.Enums; - -namespace PleasantUI.Core.Structures; +namespace PleasantUI.ToolKit.Structures; /// /// Represents a button in a . diff --git a/src/PleasantUI/Core/Structures/PleasantDialogClosingEventArgs.cs b/src/PleasantUI.ToolKit/Structures/PleasantDialogClosingEventArgs.cs similarity index 93% rename from src/PleasantUI/Core/Structures/PleasantDialogClosingEventArgs.cs rename to src/PleasantUI.ToolKit/Structures/PleasantDialogClosingEventArgs.cs index 4d960051..c9372c70 100644 --- a/src/PleasantUI/Core/Structures/PleasantDialogClosingEventArgs.cs +++ b/src/PleasantUI.ToolKit/Structures/PleasantDialogClosingEventArgs.cs @@ -1,4 +1,4 @@ -namespace PleasantUI.Core.Structures; +namespace PleasantUI.ToolKit.Structures; /// /// Event args for the event. diff --git a/src/PleasantUI/Core/Structures/PleasantDialogCommand.cs b/src/PleasantUI.ToolKit/Structures/PleasantDialogCommand.cs similarity index 95% rename from src/PleasantUI/Core/Structures/PleasantDialogCommand.cs rename to src/PleasantUI.ToolKit/Structures/PleasantDialogCommand.cs index 49e08518..29134891 100644 --- a/src/PleasantUI/Core/Structures/PleasantDialogCommand.cs +++ b/src/PleasantUI.ToolKit/Structures/PleasantDialogCommand.cs @@ -1,6 +1,6 @@ -using PleasantUI.Core.Enums; +using PleasantUI.ToolKit.Enums; -namespace PleasantUI.Core.Structures; +namespace PleasantUI.ToolKit.Structures; /// /// Represents a command item (radio button, checkbox, or command link) in a diff --git a/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml b/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml index ecd41cb7..344b9bf4 100644 --- a/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml +++ b/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml @@ -62,7 +62,7 @@ - + @@ -179,14 +179,14 @@ IsChecked="True" GroupName="ColorsData" ToolTip.Tip="{Localize Colors, Default='Colors'}"> - + - + diff --git a/src/PleasantUI/Controls/Drawer/PleasantDrawer.cs b/src/PleasantUI/Controls/Drawer/PleasantDrawer.cs index 12d6030a..a5d42148 100644 --- a/src/PleasantUI/Controls/Drawer/PleasantDrawer.cs +++ b/src/PleasantUI/Controls/Drawer/PleasantDrawer.cs @@ -55,6 +55,10 @@ public class PleasantDrawer : PleasantPopupElement /// Defines the property. public static readonly StyledProperty TitleProperty = AvaloniaProperty.Register(nameof(Title)); + + /// Defines the property. + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); /// Defines the property. public static readonly StyledProperty PositionProperty = @@ -104,6 +108,13 @@ public string? Title get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); } + + /// + public object? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } /// Gets or sets which edge the drawer slides in from. public DrawerPosition Position diff --git a/src/PleasantUI/Controls/InformationBlock/InformationBlock.cs b/src/PleasantUI/Controls/InformationBlock/InformationBlock.cs index f5db06a5..39623bbf 100644 --- a/src/PleasantUI/Controls/InformationBlock/InformationBlock.cs +++ b/src/PleasantUI/Controls/InformationBlock/InformationBlock.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Media; namespace PleasantUI.Controls; @@ -12,13 +11,13 @@ public class InformationBlock : ContentControl /// /// Defines the property. /// - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon)); + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); /// /// Represents the icon associated with the count of items, indicating the number of items associated with that icon. /// - public Geometry Icon + public object? Icon { get => GetValue(IconProperty); set => SetValue(IconProperty, value); diff --git a/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs b/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs index a08a3074..8087cb89 100644 --- a/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs +++ b/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs @@ -33,8 +33,8 @@ public class OptionsDisplayItem : TemplatedControl /// /// Defines the property. /// - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon)); + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); /// /// Defines the property. @@ -106,7 +106,7 @@ public string Description /// /// Gets or sets the icon geometry. /// - public Geometry Icon + public object? Icon { get => GetValue(IconProperty); set => SetValue(IconProperty, value); diff --git a/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs b/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs index 4c1cd8c7..5dffcb96 100644 --- a/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs +++ b/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs @@ -19,8 +19,8 @@ public class PleasantSplashScreen : TemplatedControl AvaloniaProperty.Register(nameof(AppName)); /// Defines the property. - public static readonly StyledProperty AppIconProperty = - AvaloniaProperty.Register(nameof(AppIcon)); + public static readonly StyledProperty AppIconProperty = + AvaloniaProperty.Register(nameof(AppIcon)); /// Defines the property. public static readonly StyledProperty SplashContentProperty = @@ -41,7 +41,7 @@ public string? AppName } /// Image displayed in the splash screen. - public IImage? AppIcon + public object? AppIcon { get => GetValue(AppIconProperty); set => SetValue(AppIconProperty, value); diff --git a/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs b/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs index 821cc85f..d2b51067 100644 --- a/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs +++ b/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs @@ -29,8 +29,8 @@ public class TreeViewSection : HeaderedItemsControl // ── Styled properties ───────────────────────────────────────────────────── /// Defines the property. - public static readonly StyledProperty SectionIconProperty = - AvaloniaProperty.Register(nameof(SectionIcon)); + public static readonly StyledProperty SectionIconProperty = + AvaloniaProperty.Register(nameof(SectionIcon)); /// Defines the property. public static readonly StyledProperty IconBackgroundProperty = @@ -68,7 +68,7 @@ public class TreeViewSection : HeaderedItemsControl // ── CLR accessors ───────────────────────────────────────────────────────── /// Gets or sets the icon geometry shown in the section header. - public Geometry? SectionIcon + public object? SectionIcon { get => GetValue(SectionIconProperty); set => SetValue(SectionIconProperty, value); diff --git a/src/PleasantUI/Core/Attached/IconHelper.cs b/src/PleasantUI/Core/Attached/IconHelper.cs index 12ee21dd..01a3c10a 100644 --- a/src/PleasantUI/Core/Attached/IconHelper.cs +++ b/src/PleasantUI/Core/Attached/IconHelper.cs @@ -4,6 +4,9 @@ namespace PleasantUI.Core.Attached; +/// +/// A container class for attached properties for working with icons +/// public class IconHelper : AvaloniaObject { public static readonly AttachedProperty WidthProperty = @@ -14,16 +17,48 @@ public class IconHelper : AvaloniaObject public static readonly AttachedProperty FontSizeProperty = AvaloniaProperty.RegisterAttached("FontSize", 1, defaultBindingMode: BindingMode.TwoWay); + + static IconHelper() { } + /// + /// Gets the width of the icon + /// + /// The current instance of the container class + /// Icon width public static double GetWidth(AvaloniaObject obj) => obj.GetValue(WidthProperty); + /// + /// Sets the width of the icon + /// + /// The current instance of the container class + /// Icon width public static void SetWidth(AvaloniaObject obj, double value) => obj.SetValue(WidthProperty, value); + /// + /// Gets the height of the icon + /// + /// The current instance of the container class + /// Icon height public static double GetHeight(AvaloniaObject obj) => obj.GetValue(HeightProperty); + /// + /// Sets the height of the icon + /// + /// The current instance of the container class + /// Icon height public static void SetHeight(AvaloniaObject obj, double value) => obj.SetValue(HeightProperty, value); + /// + /// Gets the icon font size for a converted + /// + /// The current instance of the container class + /// public static double GetFontSize(AvaloniaObject obj) => obj.GetValue(FontSizeProperty); + /// + /// Sets the icon font size for a converted + /// + /// The current instance of the container class + /// Icon font size public static void SetFontSize(AvaloniaObject obj, double value) => obj.SetValue(FontSizeProperty, value); } \ No newline at end of file diff --git a/src/PleasantUI/Core/Helpers/WindowHelper.cs b/src/PleasantUI/Core/Helpers/WindowHelper.cs index 840ade3d..030f9cc0 100644 --- a/src/PleasantUI/Core/Helpers/WindowHelper.cs +++ b/src/PleasantUI/Core/Helpers/WindowHelper.cs @@ -32,28 +32,28 @@ public static class WindowHelper if (topLevel is not null) return topLevel as IPleasantWindow; - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + switch (Application.Current?.ApplicationLifetime) { - IReadOnlyList windows = desktopLifetime.Windows; - - for (int i = 0; i < windows.Count; i++) + case IClassicDesktopStyleApplicationLifetime desktopLifetime: { - if (!windows[i].IsActive) continue; + IReadOnlyList windows = desktopLifetime.Windows; + + foreach (Window t in windows) + { + if (!t.IsActive) continue; - topLevel = windows[i]; - break; - } + topLevel = t; + break; + } - topLevel ??= desktopLifetime.MainWindow; + topLevel ??= desktopLifetime.MainWindow; - return topLevel as IPleasantWindow; - } - - if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) - { - return singleViewLifetime.MainView as IPleasantWindow; + return topLevel as IPleasantWindow; + } + case ISingleViewApplicationLifetime singleViewLifetime: + return singleViewLifetime.MainView as IPleasantWindow; + default: + return null; } - - return null; } } \ No newline at end of file diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml index 4c11d90c..d9568911 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml @@ -54,8 +54,8 @@ - - + + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml index c70668e8..44900316 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml @@ -44,36 +44,23 @@ - - - - + InputGesture="{x:Static TextBox.CutGesture}" + Foreground="{DynamicResource TextFillColor1}" + Icon="{DynamicResource CutRegular}" /> + - - - - + InputGesture="{x:Static TextBox.CopyGesture}" + Foreground="{DynamicResource TextFillColor1}" + Icon="{DynamicResource CopyRegular}" /> + - - - - + InputGesture="{x:Static TextBox.PasteGesture}" + Foreground="{DynamicResource TextFillColor1}" + Icon="{DynamicResource PasteRegular}" /> diff --git a/src/PleasantUI/Styling/ControlThemes/Controls.axaml b/src/PleasantUI/Styling/ControlThemes/Controls.axaml index e873f47a..ad4885a5 100644 --- a/src/PleasantUI/Styling/ControlThemes/Controls.axaml +++ b/src/PleasantUI/Styling/ControlThemes/Controls.axaml @@ -115,6 +115,8 @@ - @@ -209,7 +208,7 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml index 53ac9fa4..cbb546c0 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml @@ -4,7 +4,7 @@ x:ClassModifier="internal"> - + @@ -44,19 +44,25 @@ CornerRadius="8 0 0 8" ClipToBounds="True"> - - + + + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantHorizontallySplittedView.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantHorizontallySplittedView.axaml index 38c3545a..e20c0518 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantHorizontallySplittedView.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantHorizontallySplittedView.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:PleasantUI.Controls.Docking" x:ClassModifier="internal"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml index 19c6f1d3..47b3cf54 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml @@ -1,6 +1,12 @@  + + + + + + @@ -39,11 +45,8 @@ Margin="4" Theme="{DynamicResource AppBarToggleButtonTheme}" IsChecked="{CompiledBinding $parent[PleasantMiniWindow].Topmost}" - ToolTip.Tip="{DynamicResource FastenOverTheWindows}"> - - - + ToolTip.Tip="{Localize AttachOverWindows, Default='Attach over windows'}"> + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantReDock.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantReDock.axaml index 5ee57f43..2bafc57c 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantReDock.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantReDock.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:PleasantUI.Controls.Docking" x:ClassModifier="internal"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSideBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSideBar.axaml index ceaab7f5..06997eb6 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSideBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSideBar.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:PleasantUI.Controls.Docking" x:ClassModifier="internal"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml index 18f61d1f..228d5067 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml @@ -1,7 +1,12 @@ - + + + + + + @@ -21,11 +26,16 @@ IsVisible="{TemplateBinding SplashContent, Converter={x:Static ObjectConverters.IsNotNull}}" /> - + IsVisible="{TemplateBinding AppIcon, Converter={x:Static ObjectConverters.IsNotNull}}" />--> + - @@ -153,5 +152,4 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml index a72b9ffd..eb07a3c6 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml @@ -80,8 +80,7 @@ Padding="5" Margin="8 0 0 0" VerticalAlignment="Top"> - + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/SelectionList.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/SelectionList.axaml index 9d006c37..5befb008 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/SelectionList.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/SelectionList.axaml @@ -1,7 +1,6 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml index 806c7041..30b42ac6 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml @@ -4,30 +4,27 @@ x:ClassModifier="internal"> - + - - - - + IsLoading="False" + SectionIcon="{DynamicResource CutRegular}"/> - + - @@ -37,11 +34,11 @@ CornerRadius="{DynamicResource ControlCornerRadius}" Background="{TemplateBinding IconBackground}" IsVisible="{TemplateBinding SectionIcon, Converter={x:Static ObjectConverters.IsNotNull}}"> - + @@ -67,7 +64,7 @@ - + - - + @@ -170,41 +165,36 @@ - - - - - - - - + + + + + + + + + + @@ -225,7 +215,7 @@ Padding="6" ToolTip.Tip="Collapse all" IsVisible="{TemplateBinding ShowCollapseButton}"> - @@ -239,7 +229,7 @@ Padding="16 12 16 16"> - + @@ -249,5 +239,4 @@ - From 1be17e283f1a490bca820146eb953ded7b5e1e7c Mon Sep 17 00:00:00 2001 From: Onebeld Date: Wed, 17 Jun 2026 01:06:26 +0500 Subject: [PATCH 03/15] SmoothScrollViewer, MaterialIcons generator have been removed, IconControl improvements and other changes --- PleasantUI.sln | 18 - build/Package.props | 6 +- .../MaterialIconsSourceGenerator.cs | 59 -- ...antUI.MaterialIcons.SourceGenerator.csproj | 25 - .../Properties/launchSettings.json | 9 - .../StringExtensions.cs | 17 - samples/PleasantUI.Example.Desktop/App.axaml | 18 + .../PleasantUI.Example/Assets/Images.axaml | 1 + .../ControlPageCardItemTemplate.axaml | 12 +- .../Factories/ControlPageCardsFactory.cs | 72 +- samples/PleasantUI.Example/MainView.axaml | 83 +- .../Models/ControlPageCard.cs | 5 +- .../PleasantUI.Example.csproj | 2 +- .../Pages/ToolKitPages/MessageBoxViewModel.cs | 15 +- .../PleasantUI.Example/Views/AboutView.axaml | 164 ++-- .../PleasantUI.Example/Views/HomeView.axaml | 10 +- .../Pages/ControlPages/ButtonPageView.axaml | 2 +- .../Pages/ControlPages/PinCodePageView.axaml | 5 +- .../BreadcrumbBarPageView.axaml | 6 +- .../CommandBarPageView.axaml | 5 +- .../CrashReportDialogPageView.axaml | 5 +- .../DashboardCardPageView.axaml | 5 +- .../DownloadPanelPageView.axaml | 5 +- .../InformationBlockPageView.axaml | 5 +- .../InstallWizardPageView.axaml | 5 +- .../ItemListPanelPageView.axaml | 3 +- .../OptionsDisplayItemPageView.axaml | 11 +- .../PathPickerPageView.axaml | 5 +- .../PleasantDrawerPageView.axaml | 7 +- .../PleasantMenuPageView.axaml | 6 +- .../PleasantMenuPageView.axaml.cs | 46 +- .../PleasantMiniWindowPageView.axaml | 6 +- .../PleasantSnackbarPageView.axaml | 5 +- .../PleasantSnackbarPageView.axaml.cs | 32 +- .../PopConfirmPageView.axaml | 5 +- .../PropertyGridPageView.axaml | 5 +- .../StepDialogPageView.axaml | 6 +- .../TimelinePageView.axaml | 5 +- .../ToolKitPages/DockingPageView.axaml.cs | 26 +- .../ToolKitPages/MessageBoxPageView.axaml | 4 +- .../ToolKitPages/NoticeDialogPageView.axaml | 4 +- .../Views/SettingsView.axaml | 29 +- src/PleasantUI.MaterialIcons/MaterialIcons.cs | 3 - .../PleasantUI.MaterialIcons.csproj | 17 - .../Properties/AssemblyInfo.cs | 3 - .../Controls/NoticeDialog/NoticeDialog.axaml | 8 +- .../Controls/TerminalPanel/TerminalPanel.cs | 6 +- src/PleasantUI.ToolKit/MessageBox.axaml | 4 +- src/PleasantUI.ToolKit/PleasantDialog.axaml | 4 +- .../CrashReportDialog.axaml | 24 +- .../PleasantToolKitControls/StepDialog.axaml | 10 +- .../TerminalPanel.axaml | 12 +- .../ThemeEditorWindow.axaml | 16 +- src/PleasantUI/Assets/IconGeometries.axaml | 11 - .../Controls/IconControl/IconControl.cs | 2 + .../NavigationView/NavigationViewItem.cs | 14 +- .../Controls/PleasantMenu/PleasantMenu.cs | 8 +- .../PleasantMenu/PleasantMenuFooterItem.cs | 6 +- .../Controls/PleasantMenu/PleasantMenuItem.cs | 6 +- .../PleasantTabView/PleasantTabView.cs | 2 +- .../SmoothScrollContentPresenter.cs | 774 ---------------- .../SmoothScrollViewer/SmoothScrollViewer.cs | 851 ------------------ .../Controls/Snackbar/PleasantSnackbar.cs | 6 +- .../Snackbar/PleasantSnackbarOptions.cs | 2 +- src/PleasantUI/Core/Attached/IconHelper.cs | 23 +- .../Core/Attached/ScrollViewerExtensions.cs | 339 +++++++ .../DataTemplates/IconTemplateSelector.cs | 46 + .../Core/Extensions/BinarySearchExtension.cs | 57 ++ src/PleasantUI/PleasantTheme.axaml.cs | 8 +- .../BasicControls/ButtonSpinner.axaml | 8 +- .../BasicControls/Carousel.axaml | 4 +- .../BasicControls/ComboBox.axaml | 4 +- .../BasicControls/ContextMenu.axaml | 4 +- .../BasicControls/DatePicker.axaml | 16 +- .../BasicControls/FlyoutPresenter.axaml | 4 +- .../ControlThemes/BasicControls/ListBox.axaml | 4 +- .../ControlThemes/BasicControls/Menu.axaml | 4 +- .../BasicControls/MenuItem.axaml | 4 +- .../BasicControls/ScrollViewer.axaml | 4 + .../ControlThemes/BasicControls/TextBox.axaml | 8 +- .../BasicControls/TimePicker.axaml | 16 +- .../BasicControls/TreeView.axaml | 4 +- .../Styling/ControlThemes/Controls.axaml | 1 - .../PleasantControls/CommandBar.axaml | 4 +- .../PleasantControls/ContentDialog.axaml | 4 +- .../PleasantControls/DashboardCard.axaml | 6 +- .../PleasantControls/IconControl.axaml | 5 +- .../PleasantControls/MarkedTextBox.axaml | 4 +- .../PleasantControls/NavigationView.axaml | 4 +- .../PleasantControls/NavigationViewItem.axaml | 48 +- .../PleasantControls/OptionsDisplayItem.axaml | 2 + .../PleasantControls/PleasantDrawer.axaml | 4 +- .../PleasantControls/PleasantTabView.axaml | 4 +- .../PleasantControls/PleasantTitleBar.axaml | 8 +- .../PleasantControls/PropertyGrid.axaml | 4 +- .../PleasantControls/SmoothScrollViewer.axaml | 96 -- .../PleasantControls/TreeViewPanel.axaml | 4 +- .../Styling/VGUIControlThemes.axaml | 4 +- 98 files changed, 953 insertions(+), 2359 deletions(-) delete mode 100644 generators/PleasantUI.MaterialIcons.SourceGenerator/MaterialIconsSourceGenerator.cs delete mode 100644 generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj delete mode 100644 generators/PleasantUI.MaterialIcons.SourceGenerator/Properties/launchSettings.json delete mode 100644 generators/PleasantUI.MaterialIcons.SourceGenerator/StringExtensions.cs delete mode 100644 src/PleasantUI.MaterialIcons/MaterialIcons.cs delete mode 100644 src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj delete mode 100644 src/PleasantUI.MaterialIcons/Properties/AssemblyInfo.cs delete mode 100644 src/PleasantUI/Controls/SmoothScrollViewer/SmoothScrollContentPresenter.cs delete mode 100644 src/PleasantUI/Controls/SmoothScrollViewer/SmoothScrollViewer.cs create mode 100644 src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs create mode 100644 src/PleasantUI/Core/DataTemplates/IconTemplateSelector.cs create mode 100644 src/PleasantUI/Core/Extensions/BinarySearchExtension.cs delete mode 100644 src/PleasantUI/Styling/ControlThemes/PleasantControls/SmoothScrollViewer.axaml diff --git a/PleasantUI.sln b/PleasantUI.sln index e3c46a6e..53b99838 100644 --- a/PleasantUI.sln +++ b/PleasantUI.sln @@ -12,12 +12,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.DataGrid", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.Example", "samples\PleasantUI.Example\PleasantUI.Example.csproj", "{59DC0F4B-421E-407D-9435-F6ED14765634}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generators", "generators", "{941F7A66-EFCB-4141-97CA-A0B779ED1BB4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.MaterialIcons", "src\PleasantUI.MaterialIcons\PleasantUI.MaterialIcons.csproj", "{2ABE0576-837B-4D1B-970D-00B52BF0E078}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.MaterialIcons.SourceGenerator", "generators\PleasantUI.MaterialIcons.SourceGenerator\PleasantUI.MaterialIcons.SourceGenerator.csproj", "{9FDFD065-7664-4E0C-B928-D7FC8B074EDD}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PleasantUI.ToolKit", "src\PleasantUI.ToolKit\PleasantUI.ToolKit.csproj", "{722B39BB-1CBF-4762-A5C5-439F4EB3A781}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D6E493DE-5180-4BA0-B122-D155A5934D08}" @@ -25,8 +19,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D6E493DE build\Package.props = build\Package.props EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{4BCCA866-A31E-49B1-AB79-B9C94651D295}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -53,14 +45,6 @@ Global {59DC0F4B-421E-407D-9435-F6ED14765634}.Debug|Any CPU.Build.0 = Debug|Any CPU {59DC0F4B-421E-407D-9435-F6ED14765634}.Release|Any CPU.ActiveCfg = Release|Any CPU {59DC0F4B-421E-407D-9435-F6ED14765634}.Release|Any CPU.Build.0 = Release|Any CPU - {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2ABE0576-837B-4D1B-970D-00B52BF0E078}.Release|Any CPU.Build.0 = Release|Any CPU - {9FDFD065-7664-4E0C-B928-D7FC8B074EDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9FDFD065-7664-4E0C-B928-D7FC8B074EDD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9FDFD065-7664-4E0C-B928-D7FC8B074EDD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9FDFD065-7664-4E0C-B928-D7FC8B074EDD}.Release|Any CPU.Build.0 = Release|Any CPU {722B39BB-1CBF-4762-A5C5-439F4EB3A781}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {722B39BB-1CBF-4762-A5C5-439F4EB3A781}.Debug|Any CPU.Build.0 = Debug|Any CPU {722B39BB-1CBF-4762-A5C5-439F4EB3A781}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -70,7 +54,5 @@ Global {71BFCFA6-B53E-4F28-819F-51CB26516B5A} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} {91F6CDD0-6925-4289-90EC-8BDD245F7BD0} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} {59DC0F4B-421E-407D-9435-F6ED14765634} = {BEE78D8A-ED3D-4D34-9B29-43D5BB953D40} - {9FDFD065-7664-4E0C-B928-D7FC8B074EDD} = {941F7A66-EFCB-4141-97CA-A0B779ED1BB4} - {2ABE0576-837B-4D1B-970D-00B52BF0E078} = {4BCCA866-A31E-49B1-AB79-B9C94651D295} EndGlobalSection EndGlobal diff --git a/build/Package.props b/build/Package.props index 123a496e..fe55c845 100644 --- a/build/Package.props +++ b/build/Package.props @@ -4,14 +4,14 @@ enable enable default - 5.2.2 + 5.3.0 Dmitry Zhutkov (Onebeld), Andrey Savich (pieckenst) Dmitry Zhutkov (Onebeld) theme, design, xaml, library, ui, gui, control, csharp, styled-components, interface, dotnet, nuget, style, avalonia, controls, user-interface, styles, avaloniaui, pleasant, graphical-user-interface PleasantUI is a cross-platform UI theme and control library for Avalonia, inspired by Microsoft Fluent Design and the WinUI/UWP visual language. It provides complete re-styled themes for all standard Avalonia controls — buttons, checkboxes, sliders, text boxes, data grids, calendars, combo boxes, and more — along with a suite of custom Pleasant controls including NavigationView, PleasantTabView, ProgressRing, OptionsDisplayItem, PleasantSnackbar, InformationBlock, ContentDialog, and a full theme editor with custom theme support. Supports light, dark, and multiple accent color themes out of the box, with a reactive localization system and desktop/mobile adaptive layouts. Onebeld - 5.2.2.0 - 5.2.2.0 + 5.3.0.0 + 5.3.0.0 $(PackageVersion) true https://github.com/Onebeld/PleasantUI diff --git a/generators/PleasantUI.MaterialIcons.SourceGenerator/MaterialIconsSourceGenerator.cs b/generators/PleasantUI.MaterialIcons.SourceGenerator/MaterialIconsSourceGenerator.cs deleted file mode 100644 index 156abb2e..00000000 --- a/generators/PleasantUI.MaterialIcons.SourceGenerator/MaterialIconsSourceGenerator.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.IO; -using System.Net.Http; -using System.Text; -using Microsoft.CodeAnalysis; -using System.Text.Json.Nodes; -using System.Threading.Tasks; - -namespace PleasantUI.MaterialIcons.SourceGenerator; - -[Generator] -public class MaterialIconsSourceGenerator : IIncrementalGenerator -{ - const string MaterialIconsFetchApi = "https://dev.materialdesignicons.com/api/package/38EF63D0-4744-11E4-B3CF-842B2B6CFE1B"; - - private JsonNode? _json; - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - using HttpClient httpClient = new(); - - Task task = Task.Run(() => httpClient.GetStreamAsync(MaterialIconsFetchApi)); - task.Wait(); - - Stream? dataStream = task.Result; - JsonNode? json = JsonNode.Parse(dataStream); - - _json = json ?? throw new Exception("Unable to load JSON"); - - if (_json is null) - throw new NullReferenceException("JSON is null"); - - JsonArray icons = _json.Root["icons"]!.AsArray(); - - const string generated = """ - // - using Avalonia.Media; - - namespace PleasantUI; - - public static partial class MaterialIcons - { - """; - - StringBuilder stringBuilder = new(generated); - - foreach (JsonNode? icon in icons) - { - string name = icon!["name"]!.GetValue().Underscore().Pascalize(); - string data = icon["data"]!.GetValue(); - - stringBuilder.Append($"\tpublic static Geometry {name} = Geometry.Parse(\"{data}\");\n"); - } - - stringBuilder.Append("}\n"); - - context.RegisterPostInitializationOutput(ctx => ctx.AddSource("MaterialIcons.g.cs", stringBuilder.ToString())); - } -} \ No newline at end of file diff --git a/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj b/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj deleted file mode 100644 index 19c05fe2..00000000 --- a/generators/PleasantUI.MaterialIcons.SourceGenerator/PleasantUI.MaterialIcons.SourceGenerator.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - netstandard2.0 - false - enable - latest - - true - true - - PleasantUI.MaterialIcons.SourceGenerator - PleasantUI.MaterialIcons.SourceGenerator - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - diff --git a/generators/PleasantUI.MaterialIcons.SourceGenerator/Properties/launchSettings.json b/generators/PleasantUI.MaterialIcons.SourceGenerator/Properties/launchSettings.json deleted file mode 100644 index ef93413a..00000000 --- a/generators/PleasantUI.MaterialIcons.SourceGenerator/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "DebugRoslynSourceGenerator": { - "commandName": "DebugRoslynComponent", - "targetProject": "../../src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj" - } - } -} \ No newline at end of file diff --git a/generators/PleasantUI.MaterialIcons.SourceGenerator/StringExtensions.cs b/generators/PleasantUI.MaterialIcons.SourceGenerator/StringExtensions.cs deleted file mode 100644 index d61bf56d..00000000 --- a/generators/PleasantUI.MaterialIcons.SourceGenerator/StringExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.RegularExpressions; - -namespace PleasantUI.MaterialIcons.SourceGenerator; - -public static class StringExtensions -{ - public static string Underscore(this string input) { - return Regex.Replace( - Regex.Replace( - Regex.Replace(input, @"([\p{Lu}]+)([\p{Lu}][\p{Ll}])", "$1_$2"), @"([\p{Ll}\d])([\p{Lu}])", "$1_$2"), @"[-\s]", "_").ToLower(); - } - - public static string Pascalize(this string input) - { - return Regex.Replace(input, "(?:^|_| +)(.)", match => match.Groups[1].Value.ToUpper()); - } -} \ No newline at end of file diff --git a/samples/PleasantUI.Example.Desktop/App.axaml b/samples/PleasantUI.Example.Desktop/App.axaml index 185878a6..feb7f5c1 100644 --- a/samples/PleasantUI.Example.Desktop/App.axaml +++ b/samples/PleasantUI.Example.Desktop/App.axaml @@ -1,5 +1,8 @@ @@ -14,11 +17,26 @@ + + + + + + + + + + diff --git a/samples/PleasantUI.Example/Assets/Images.axaml b/samples/PleasantUI.Example/Assets/Images.axaml index 2d2063d1..d197a239 100644 --- a/samples/PleasantUI.Example/Assets/Images.axaml +++ b/samples/PleasantUI.Example/Assets/Images.axaml @@ -72,4 +72,5 @@ F1 M603.4,67.6z M0,0z M148.2,54.3L183.7,54.3 183.7,66.5 133.1,66.5 133.1,1 182.5,1 182.5,13.2 148.1,13.2 148.1,27.4 178.5,27.4 178.5,39.1 148.2,39.1 148.2,54.2z M318.2,33.4A31.3,31.3,0,0,0,310.8,30.2C308.1,29.3 305.4,28.6 302.7,28 300,27.4 297.5,26.8 295.3,26.1 293,25.5 291.3,24.7 289.8,23.7 288.5,22.7 287.8,21.3 287.8,19.6 287.8,18.2 288.2,16.9 289,15.8 289.8,14.8 291.2,13.8 293,13.2 294.9,12.5 297.3,12.2 300.2,12.2 303.1,12.2 306.2,12.6 309.2,13.4 312.2,14.2 315.2,15.5 318.3,17.3L323,5.8C320,3.8 316.4,2.4 312.4,1.5 308.4,0.6 304.4,0 300.4,0 294.1,0 289,1 285,2.8 280.9,4.6 277.8,7 275.8,10.1 273.8,13.2 272.8,16.6 272.8,20.4 272.8,23.8 273.5,26.7 274.8,28.9 276.3,31.2 278.1,32.9 280.3,34.4 282.6,35.7 285,36.8 287.8,37.7 290.5,38.6 293.2,39.3 295.8,39.9 298.6,40.5 301.1,41.1 303.3,41.8 305.5,42.5 307.3,43.3 308.7,44.3A5,5,0,0,1,310.7,48.4C310.7,49.8 310.3,51 309.5,52.1 308.5,53.1 307.2,54 305.4,54.6A36.7,36.7,0,0,1,286.5,53.6C282.7,52.3,279.4,50.6,276.5,48.6L271.5,60C274.4,62.3,278.2,64,283,65.5A50,50,0,0,0,313.5,64.8C317.5,62.9 320.6,60.5 322.7,57.4 324.7,54.4 325.7,51 325.7,47.4 325.7,43.9 325.1,41.1 323.7,38.8 322.3,36.6 320.5,34.8 318.2,33.4z M236,1.1L265.2,66.5 249.3,66.5 243.6,52.5 213.3,52.5 207.4,66.5 191.9,66.5 221,1 235.9,1z M238.8,41L228.4,16 218,41 238.8,41z M87.2,1L72,1 72,66.4 120,66.4 120,54 87.2,54 87.2,1z M53.2,12C55.5,15.6 56.7,19.8 56.7,24.7 56.7,29.6 55.5,33.7 53.2,37.3A22.1,22.1,0,0,1,43.4,45.5A36.8,36.8,0,0,1,28.3,48.4L15,48.4 15,66.4 0,66.4 0,1 28.3,1C34.1,1 39.2,2 43.4,3.9 47.6,5.8 50.9,8.5 53.2,12z M41.3,24.8C41.3,21.2 40.2,18.4 37.9,16.4 35.6,14.4 32.1,13.4 27.4,13.4L15.1,13.4 15.1,36.1 27.4,36.1C32.1,36.1 35.6,35.1 37.9,33.1 40.2,31.1 41.4,28.4 41.4,24.8z M489.7,13.4L510.7,13.4 510.7,66.4 525.7,66.4 525.7,13.4 546.7,13.4 546.7,1.1 489.7,1.1 489.7,13.4z M581.2,18.9C581.2,21.9 580.6,24.1 579.3,25.5 578.1,26.8 576.3,27.5 574.1,27.5 571.9,27.5 570.1,26.8 568.8,25.5 567.5,24.1 566.9,21.9 566.9,18.9L566.9,1 559.5,1 559.5,19C559.5,23.7 560.8,27.4 563.3,29.9 565.9,32.4 569.5,33.7 574,33.7 578.5,33.7 582.2,32.4 584.7,29.9 587.3,27.4 588.6,23.7 588.6,19L588.6,0.999999999999993 581.2,0.999999999999993 581.2,19z M596,1L596,33.2 603.4,33.2 603.4,1 596,1z M462,40.8L429.6,1 417,1 417,66.4 432,66.4 432,26.8 464.5,66.5 477,66.5 477,1 462,1 462,40.7z M376.4,1L405.6,66.4 389.7,66.4 383.9,52.4 353.7,52.4 347.8,66.4 332.3,66.4 361.4,1 376.3,1z M379.2,40.9L368.8,15.9 358.4,40.9 379.2,40.9z F1 M434.6,39.2z M0,0z M122,5.6A19.8,19.8,0,0,0,117.6,27.4A20,20,0,0,0,136.6,39.2A19.6,19.6,0,0,0,155.6,11.8C150.4,-1.2,131.6,-3.7,121.9,5.6z M136.6,32C127.3,32.1 121.6,23 125,14.6 129,4.8 144,4.7 148,14.6 151.1,23 146,32.1 136.6,32z M197.3,0L205.5,0 205.5,39.2 198.8,39.2 178.8,14.6 178.8,39.2 170.6,39.2 170.6,0 177.3,0 197.3,24.6 197.3,0z M297,25A9.7,9.7,0,0,0,291.4,18.7C294.1,16.8 295.7,13.7 295.5,10.5 295.3,2.8 288.7,0 281,0L262.5,0 262.5,39.2 282.1,39.2C291.8,39.4,299.3,34.8,297.1,25.1z M270.8,6.9L280.2,6.9C283.8,6.9 287.2,7.7 287.2,11.5 287.2,15.3 283.8,16 280.2,16.1L270.8,16.1 270.8,6.8z M281.6,32.5L270.8,32.5 270.8,22.8 281.6,22.8C284.1,22.8 286,23.2 287.3,24 288.5,24.8 289.1,26 289.1,27.7 289.1,31.8 285.5,32.4 281.6,32.5z M319.4,32.1L340.8,32.1 340.8,39.3 311,39.3 311,0 340,0 340,7.2 319.4,7.2 319.4,15.8 337.7,15.8 337.7,22.8 319.4,22.8 319.4,32z M362.7,32.1L382.5,32.1 382.5,39.3 354.4,39.3 354.4,0 362.7,0 362.7,32z M431.9,9.4C428.2,3,421,0,413.5,0L396,0 396,39.2 413.3,39.2C417.3,39.2 421.1,38.4 424.3,36.8 434.3,32.1 437.3,18.5 431.8,9.3z M424.7,26.2C422.2,30.2,417.9,32.1,413,32.1L404.4,32.1 404.4,7.2 413,7.2C415.7,7.2 418.1,7.7 420,8.8 422,9.8 423.6,11.2 424.7,13 426.9,17.1 426.9,22 424.7,26.1z M227.5,32L249,32 249,39.2 219.2,39.2 219.2,0 248.2,0 248.2,7.2 227.5,7.2 227.5,15.8 245.8,15.8 245.8,22.8 227.5,22.8 227.5,32z M14.4,6.3C7.8,4.3 4.4,0 4.4,0 4.4,0 3.9,5.5 8.2,9.4L0,15C11.2,18.7 11.2,18.3 16.7,23.3 15.1,23.7 13.4,23.6 11.8,23 13.3,25.5 17.2,27.6 22.2,27.3 45.9,43.2 78.9,43.3 102.5,27.7A74.8,74.8,0,0,0,14.4,6.3z M21.8,20.1A64,64,0,0,1,41.8,13.7A17.9,17.9,0,0,0,42.2,30.5A61.6,61.6,0,0,1,21.8,20z M65.3,33.5A71,71,0,0,1,51,32.6C44.9,28.6 43.3,18.8 48.1,13.1 55.9,12.6 63.8,13.5 71.3,15.6 74.3,22.2 71.8,30.1 65.3,33.6z M74,32.6C77.3,28.3 78.5,22.8 77.2,17.6 83,19.8 88.2,22.6 93.1,26.5 87.1,29.5 80.7,31.6 74,32.6z M65.4,21.2C65.6,29.9,52.6,30.2,52.4,21.5A6.5,6.5,0,0,1,61.4,15.5C58.9,18.1,62,22.7,65.4,21.2z + M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z \ No newline at end of file diff --git a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml index ff2975ee..3781953a 100644 --- a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml +++ b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml @@ -1,10 +1,11 @@  - + diff --git a/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml b/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml index 36436fa5..8cf834e6 100644 --- a/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml @@ -8,7 +8,7 @@ - - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMenuPageView.axaml.cs b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMenuPageView.axaml.cs index 197ccd51..994d04c1 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMenuPageView.axaml.cs +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMenuPageView.axaml.cs @@ -1,4 +1,6 @@ using Avalonia.Controls; +using Material.Icons; +using Material.Icons.Avalonia; using PleasantUI.Controls; using PleasantUI.Core.Localization; @@ -23,29 +25,29 @@ private void BuildMenus() PleasantMenuItem[] items = [ - MakeItem(MaterialIcons.FolderOpenOutline, labelKey: "Open", labelDefault: "Open", tipKey: "OpenTip", tipDefault: "Open a file"), - MakeItem(MaterialIcons.ContentSaveOutline, labelKey: "Save", labelDefault: "Save", tipKey: "SaveTip", tipDefault: "Save current file"), - MakeItem(MaterialIcons.PlusCircleOutline, labelKey: "New", labelDefault: "New", tipKey: "NewTip", tipDefault: "Create new file"), - MakeItem(MaterialIcons.WrenchOutline, labelKey: "Tools", labelDefault: "Tools", tipKey: "ToolsTip", tipDefault: "Open tools"), - MakeItem(MaterialIcons.HomeOutline, labelKey: "Home", labelDefault: "Home", tipKey: "HomeTip", tipDefault: "Go to home screen"), - MakeItem(MaterialIcons.DeleteOutline, labelKey: "Delete", labelDefault: "Delete", tipKey: "DeleteTip", tipDefault: "Delete selected", isEnabled: false) + MakeItem(CreateIcon(MaterialIconKind.FolderOpenOutline), labelKey: "Open", labelDefault: "Open", tipKey: "OpenTip", tipDefault: "Open a file"), + MakeItem(CreateIcon(MaterialIconKind.ContentSaveOutline), labelKey: "Save", labelDefault: "Save", tipKey: "SaveTip", tipDefault: "Save current file"), + MakeItem(CreateIcon(MaterialIconKind.PlusCircleOutline), labelKey: "New", labelDefault: "New", tipKey: "NewTip", tipDefault: "Create new file"), + MakeItem(CreateIcon(MaterialIconKind.WrenchOutline), labelKey: "Tools", labelDefault: "Tools", tipKey: "ToolsTip", tipDefault: "Open tools"), + MakeItem(CreateIcon(MaterialIconKind.HomeOutline), labelKey: "Home", labelDefault: "Home", tipKey: "HomeTip", tipDefault: "Go to home screen"), + MakeItem(CreateIcon(MaterialIconKind.DeleteOutline), labelKey: "Delete", labelDefault: "Delete", tipKey: "DeleteTip", tipDefault: "Delete selected", isEnabled: false) ]; PleasantMenuFooterItem[] footerItems = [ - MakeFooterItem(MaterialIcons.CogOutline, tipKey: "Settings", tipDefault: "Settings", alignRight: false), - MakeFooterItem(MaterialIcons.InformationOutline, tipKey: "About", tipDefault: "About", alignRight: true), - MakeFooterItem(MaterialIcons.ExitToApp, tipKey: "Exit", tipDefault: "Exit", alignRight: true) + MakeFooterItem(CreateIcon(MaterialIconKind.CogOutline), tipKey: "Settings", tipDefault: "Settings", alignRight: false), + MakeFooterItem(CreateIcon(MaterialIconKind.InformationOutline), tipKey: "About", tipDefault: "About", alignRight: true), + MakeFooterItem(CreateIcon(MaterialIconKind.ExitToApp), tipKey: "Exit", tipDefault: "Exit", alignRight: true) ]; - var badges = new StackPanel + StackPanel badges = new() { Orientation = Avalonia.Layout.Orientation.Horizontal, Spacing = 6, Children = { - MakeBadge(MaterialIcons.FolderOutline, "3", tipKey: "OpenFiles", tipDefault: "Open files"), - MakeBadge(MaterialIcons.PackageVariantClosed, "5", tipKey: "Modules", tipDefault: "Loaded modules") + MakeBadge(CreateIcon(MaterialIconKind.FolderOutline), "3", tipKey: "OpenFiles", tipDefault: "Open files"), + MakeBadge(CreateIcon(MaterialIconKind.PackageVariantClosed), "5", tipKey: "Modules", tipDefault: "Loaded modules") } }; @@ -67,14 +69,14 @@ protected override void ReinitializeComponent() } private static PleasantMenuItem MakeItem( - Avalonia.Media.Geometry? icon, + object? icon, string labelKey, string labelDefault, string tipKey, string tipDefault, bool isEnabled = true) { - var item = new PleasantMenuItem + PleasantMenuItem item = new() { Icon = icon, IsEnabled = isEnabled, @@ -87,12 +89,12 @@ private static PleasantMenuItem MakeItem( } private static PleasantMenuFooterItem MakeFooterItem( - Avalonia.Media.Geometry? icon, + object? icon, string tipKey, string tipDefault, bool alignRight) { - var item = new PleasantMenuFooterItem + PleasantMenuFooterItem item = new() { Icon = icon, AlignRight = alignRight @@ -102,9 +104,9 @@ private static PleasantMenuFooterItem MakeFooterItem( return item; } - private static InformationBlock MakeBadge(Avalonia.Media.Geometry icon, string content, string tipKey, string tipDefault) + private static InformationBlock MakeBadge(object icon, string content, string tipKey, string tipDefault) { - var badge = new InformationBlock + InformationBlock badge = new() { Icon = icon, Content = content @@ -113,4 +115,12 @@ private static InformationBlock MakeBadge(Avalonia.Media.Geometry icon, string c badge.Bind(ToolTip.TipProperty, LocalizeBinding.Create(tipKey, context: "PleasantMenu", @default: tipDefault)); return badge; } + + private MaterialIcon CreateIcon(MaterialIconKind kind) + { + return new MaterialIcon() + { + Kind = kind + }; + } } diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml index 9ea79264..1e9b42ae 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml @@ -4,8 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.PleasantMiniWindowPageView"> - - + - - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml index 755e22ca..0921b950 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml @@ -4,8 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.PleasantSnackbarPageView"> - - + @@ -111,5 +110,5 @@ - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml.cs b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml.cs index b7a5c73e..cfe35a71 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml.cs +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantSnackbarPageView.axaml.cs @@ -1,6 +1,8 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Notifications; +using Material.Icons; +using Material.Icons.Avalonia; using PleasantUI.Controls; using PleasantUI.Core.Localization; @@ -28,36 +30,36 @@ private void WireHandlers() { BtnInformation.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("InformationMsg", "This is an informational message.")) - { Icon = MaterialIcons.InformationOutline, NotificationType = NotificationType.Information }); + { Icon = CreateIcon(MaterialIconKind.InformationOutline), NotificationType = NotificationType.Information }); BtnSuccess.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("SuccessMsg", "Operation completed successfully.")) - { Icon = MaterialIcons.CheckCircleOutline, NotificationType = NotificationType.Success }); + { Icon = CreateIcon(MaterialIconKind.CheckCircleOutline), NotificationType = NotificationType.Success }); BtnWarning.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("WarningMsg", "Disk space is running low.")) - { Icon = MaterialIcons.AlertOutline, NotificationType = NotificationType.Warning }); + { Icon = CreateIcon(MaterialIconKind.AlertOutline), NotificationType = NotificationType.Warning }); BtnError.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("ErrorMsg", "Failed to save the file.")) - { Icon = MaterialIcons.CloseCircleOutline, NotificationType = NotificationType.Error }); + { Icon = CreateIcon(MaterialIconKind.CloseCircleOutline), NotificationType = NotificationType.Error }); BtnLongMessage.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("LongMsg", "Your export has finished. The file has been saved to your Downloads folder and is ready to open.")) - { Icon = MaterialIcons.FileExportOutline, NotificationType = NotificationType.Information, IsClosable = true, TimeSpan = TimeSpan.FromSeconds(8) }); + { Icon = CreateIcon(MaterialIconKind.FileExportOutline), NotificationType = NotificationType.Information, IsClosable = true, TimeSpan = TimeSpan.FromSeconds(8) }); BtnWithTitle.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("WithTitleMsg", "Your changes have been saved to the cloud.")) - { Title = T("WithTitleTitle", "Saved"), Icon = MaterialIcons.CloudCheckOutline, NotificationType = NotificationType.Success }); + { Title = T("WithTitleTitle", "Saved"), Icon = CreateIcon(MaterialIconKind.CloudCheckOutline), NotificationType = NotificationType.Success }); BtnWithAction.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("WithActionMsg", "Item moved to trash.")) { - Icon = MaterialIcons.DeleteOutline, NotificationType = NotificationType.Information, + Icon = CreateIcon(MaterialIconKind.DeleteOutline), NotificationType = NotificationType.Information, ButtonText = T("Undo", "Undo"), ButtonAction = () => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("UndoneMsg", "Action undone.")) - { Icon = MaterialIcons.UndoVariant, NotificationType = NotificationType.Success }) + { Icon = CreateIcon(MaterialIconKind.UndoVariant), NotificationType = NotificationType.Success }) }); BtnWithCustomAction.Click += (_, _) => @@ -74,19 +76,27 @@ private void WireHandlers() PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("WithCustomActionMsg", "New update available.")) - { Icon = MaterialIcons.AccessPointCheck, NotificationType = NotificationType.Information, ActionButton = actionBtn }); + { Icon = CreateIcon(MaterialIconKind.AccessPointCheck), NotificationType = NotificationType.Information, ActionButton = actionBtn }); }; BtnClosable.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("ClosableMsg", "This snackbar has a close button.")) - { Title = T("ClosableTitle", "Dismissable"), Icon = MaterialIcons.InformationOutline, NotificationType = NotificationType.Information, IsClosable = true, TimeSpan = TimeSpan.FromSeconds(10) }); + { Title = T("ClosableTitle", "Dismissable"), Icon = CreateIcon(MaterialIconKind.InformationOutline), NotificationType = NotificationType.Information, IsClosable = true, TimeSpan = TimeSpan.FromSeconds(10) }); BtnWithEvents.Click += (_, _) => PleasantSnackbar.Show(PleasantUiExampleApp.Main, new PleasantSnackbarOptions(T("WithEventsMsg", "Tap or wait to dismiss — events are tracked.")) { - Icon = MaterialIcons.BellOutline, NotificationType = NotificationType.Information, IsClosable = true, + Icon = CreateIcon(MaterialIconKind.BellOutline), NotificationType = NotificationType.Information, IsClosable = true, Closing = (_, args) => { LastEventText.Text = $"Closing ({args.Reason})"; }, Closed = (_, args) => { LastEventText.Text = $"Closed ({args.Reason})"; } }); } + + private MaterialIcon CreateIcon(MaterialIconKind kind) + { + return new MaterialIcon() + { + Kind = kind + }; + } } diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PopConfirmPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PopConfirmPageView.axaml index ffc52890..77f590fb 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PopConfirmPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PopConfirmPageView.axaml @@ -5,8 +5,7 @@ xmlns:controls="clr-namespace:PleasantUI.Controls;assembly=PleasantUI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="900" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.PopConfirmPageView"> - - + - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PropertyGridPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PropertyGridPageView.axaml index 397a2080..b44684bf 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PropertyGridPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PropertyGridPageView.axaml @@ -5,8 +5,7 @@ xmlns:controls="clr-namespace:PleasantUI.Controls;assembly=PleasantUI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="900" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.PropertyGridPageView"> - - + - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/StepDialogPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/StepDialogPageView.axaml index afb69781..f6403dc9 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/StepDialogPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/StepDialogPageView.axaml @@ -4,8 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="900" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.StepDialogPageView"> - - + - - + diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/TimelinePageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/TimelinePageView.axaml index 036a1420..41259d8f 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/TimelinePageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/TimelinePageView.axaml @@ -4,8 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="900" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.TimelinePageView"> - - + @@ -128,5 +127,5 @@ - + diff --git a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/DockingPageView.axaml.cs b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/DockingPageView.axaml.cs index d0fe65da..53d8d396 100644 --- a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/DockingPageView.axaml.cs +++ b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/DockingPageView.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Media; +using Material.Icons; using PleasantUI.Core.Localization; namespace PleasantUI.Example.Views.Pages.ToolkitPages; @@ -37,23 +38,23 @@ protected override void ReinitializeComponent() private void Setup() { - _explorerItem = MakeItem("Explorer", "FolderRegular", true); - _propertiesItem = MakeItem("Properties", "SettingsRegular", true); + _explorerItem = MakeItem("Explorer", MaterialIconKind.Folder, true); + _propertiesItem = MakeItem("Properties", MaterialIconKind.Settings, true); _leftUpperTop = new AvaloniaList { _explorerItem, - MakeItem("Search", "SearchRegular"), - MakeItem("Source Control", "BranchRegular"), + MakeItem("Search", MaterialIconKind.Search), + MakeItem("Source Control", MaterialIconKind.SourceBranch), }; _leftLowerBottom = new AvaloniaList { - MakeItem("Extensions", "PuzzlePieceRegular"), + MakeItem("Extensions", MaterialIconKind.Puzzle), }; _rightUpperTop = new AvaloniaList { _propertiesItem, - MakeItem("Outline", "ListRegular"), + MakeItem("Outline", MaterialIconKind.ListBox), }; _rightLowerBottom = new AvaloniaList(); @@ -152,14 +153,9 @@ private Control CreatePropertiesContent() }; } - private static ToolItem MakeItem(string label, string iconKey, bool isVisible = false) + private static ToolItem MakeItem(string label, object icon, bool isVisible = false) { - // Resolve the Geometry from app resources at construction time - Geometry? geo = null; - if (Application.Current?.TryGetResource(iconKey, null, out var res) == true && res is Geometry g) - geo = g; - - return new ToolItem(label, geo, isVisible); + return new ToolItem(label, icon, isVisible); } } @@ -169,7 +165,7 @@ public sealed class ToolItem : INotifyPropertyChanged private bool _isVisible; public string Label { get; } - public Geometry? IconData { get; } + public object? IconData { get; } public bool IsVisible { @@ -182,7 +178,7 @@ public bool IsVisible } } - public ToolItem(string label, Geometry? iconData, bool isVisible = false) + public ToolItem(string label, object? iconData, bool isVisible = false) { Label = label; IconData = iconData; diff --git a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/MessageBoxPageView.axaml b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/MessageBoxPageView.axaml index 17d75874..9e46e0da 100644 --- a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/MessageBoxPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/MessageBoxPageView.axaml @@ -10,7 +10,7 @@ - + @@ -127,5 +127,5 @@ - + diff --git a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/NoticeDialogPageView.axaml b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/NoticeDialogPageView.axaml index fe8e894e..437c8ae7 100644 --- a/samples/PleasantUI.Example/Views/Pages/ToolKitPages/NoticeDialogPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/ToolKitPages/NoticeDialogPageView.axaml @@ -10,7 +10,7 @@ - + @@ -73,5 +73,5 @@ - + diff --git a/samples/PleasantUI.Example/Views/SettingsView.axaml b/samples/PleasantUI.Example/Views/SettingsView.axaml index 6941336f..e5b42a4f 100644 --- a/samples/PleasantUI.Example/Views/SettingsView.axaml +++ b/samples/PleasantUI.Example/Views/SettingsView.axaml @@ -9,6 +9,8 @@ xmlns:userControls="clr-namespace:PleasantUI.ToolKit.UserControls;assembly=PleasantUI.ToolKit" xmlns:example="clr-namespace:PleasantUI.Example" xmlns:structures="clr-namespace:PleasantUI.Example.Structures" + xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" + xmlns:icons="clr-namespace:Material.Icons;assembly=Material.Icons" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="PleasantUI.Example.Views.SettingsView" x:DataType="pages:SettingsViewModel"> @@ -16,13 +18,20 @@ - + + + + + - + - - - + @@ -127,7 +138,8 @@ Height="30" Command="{CompiledBinding $parent[views:SettingsView].((pages:SettingsViewModel)DataContext).EditThemeAsync}" CommandParameter="{CompiledBinding}"> - + @@ -156,5 +169,5 @@ - + \ No newline at end of file diff --git a/src/PleasantUI.MaterialIcons/MaterialIcons.cs b/src/PleasantUI.MaterialIcons/MaterialIcons.cs deleted file mode 100644 index a49dbde4..00000000 --- a/src/PleasantUI.MaterialIcons/MaterialIcons.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace PleasantUI; - -public partial class MaterialIcons; \ No newline at end of file diff --git a/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj b/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj deleted file mode 100644 index 03840820..00000000 --- a/src/PleasantUI.MaterialIcons/PleasantUI.MaterialIcons.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - PleasantUI.MaterialIcons - PleasantUI.MaterialIcons - net9.0 - - - - - - - - - - - - diff --git a/src/PleasantUI.MaterialIcons/Properties/AssemblyInfo.cs b/src/PleasantUI.MaterialIcons/Properties/AssemblyInfo.cs deleted file mode 100644 index 07d505b2..00000000 --- a/src/PleasantUI.MaterialIcons/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "PleasantUI")] \ No newline at end of file diff --git a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml index 3bae85e4..a6a94daf 100644 --- a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml +++ b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.axaml @@ -42,9 +42,9 @@ - + @@ -92,7 +92,7 @@ FontStyle="Italic" IsVisible="False" /> - + diff --git a/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs b/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs index a11f0953..5ed99db7 100644 --- a/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs +++ b/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs @@ -18,7 +18,7 @@ namespace PleasantUI.ToolKit.Controls; [TemplatePart(PART_InputBox, typeof(TextBox))] [TemplatePart(PART_ClearButton, typeof(Button))] [TemplatePart(PART_CloseButton, typeof(Button))] -[TemplatePart(PART_ScrollViewer, typeof(SmoothScrollViewer))] +[TemplatePart(PART_ScrollViewer, typeof(ScrollViewer))] [PseudoClasses(PC_Running, PC_HasOutput)] public class TerminalPanel : TemplatedControl { @@ -196,7 +196,7 @@ public string InputPlaceholder private TextBox? _inputBox; private Button? _clearButton; private Button? _closeButton; - private SmoothScrollViewer? _scrollViewer; + private ScrollViewer? _scrollViewer; private readonly StringBuilder _outputBuffer = new(); private int _lineCount; @@ -213,7 +213,7 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _inputBox = e.NameScope.Find(PART_InputBox); _clearButton = e.NameScope.Find diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml index ede69e4f..3ab84c3b 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml @@ -92,7 +92,7 @@ MaxWidth="{TemplateBinding MaxWidth}" MinHeight="{TemplateBinding MinHeight}" CornerRadius="{TemplateBinding CornerRadius}"> - - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml index a0f67e19..eb30e8a7 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml @@ -31,13 +31,13 @@ - - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/Menu.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/Menu.axaml index 37d98b87..328bfc53 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/Menu.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/Menu.axaml @@ -52,12 +52,12 @@ MinHeight="32" HorizontalAlignment="Stretch" CornerRadius="{DynamicResource ControlCornerRadius}"> - + - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml index d9568911..2b0ef469 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/MenuItem.axaml @@ -157,13 +157,13 @@ MinHeight="32" HorizontalAlignment="Stretch" CornerRadius="{DynamicResource ControlCornerRadius}"> - - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml index 203d6a83..dbf0afd6 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml @@ -20,6 +20,10 @@ + + + + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml index 44900316..63abec65 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml @@ -86,7 +86,7 @@ FontWeight="SemiBold" DockPanel.Dock="Top" /> - @@ -114,12 +114,12 @@ HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> - + - - + + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/TimePicker.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/TimePicker.axaml index f6790bdf..0fa424c4 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/TimePicker.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/TimePicker.axaml @@ -141,13 +141,13 @@ - - + - - + @@ -170,13 +170,13 @@ - - + @@ -185,13 +185,13 @@ - - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/TreeView.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/TreeView.axaml index 4792e2ca..366f4c40 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/TreeView.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/TreeView.axaml @@ -28,14 +28,14 @@ - - + diff --git a/src/PleasantUI/Styling/ControlThemes/Controls.axaml b/src/PleasantUI/Styling/ControlThemes/Controls.axaml index ad4885a5..1c73af92 100644 --- a/src/PleasantUI/Styling/ControlThemes/Controls.axaml +++ b/src/PleasantUI/Styling/ControlThemes/Controls.axaml @@ -100,7 +100,6 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml index 4240dce8..a82f16d9 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml @@ -403,11 +403,11 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}"> - - + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml index 7ff069dd..b9617ab3 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml @@ -9,12 +9,12 @@ Margin="50" MinWidth="400" MinHeight="180"> - + - + From a425bcf3791075371c0786f65f38731a47fefa14 Mon Sep 17 00:00:00 2001 From: Onebeld Date: Sun, 21 Jun 2026 01:31:23 +0500 Subject: [PATCH 05/15] More detailed attribution, improved ScrollViewerExtensions, removal of redundant functionality, and more --- README.md | 15 +- .../PleasantUI.Example.Desktop/App.axaml.cs | 6 +- samples/PleasantUI.Example/AppSettings.cs | 22 + .../Factories/ControlPageCardsFactory.cs | 1 - samples/PleasantUI.Example/MainView.axaml | 2 +- .../Models/ControlPageCard.cs | 1 - .../PleasantUIExampleApp.cs | 5 +- .../ViewModels/Pages/SettingsViewModel.cs | 6 +- .../ToolKitPages/NoticeDialogViewModel.cs | 47 +- .../PleasantUI.Example/Views/AboutView.axaml | 14 - .../Views/AboutView.axaml.cs | 26 - .../Pages/ControlPages/PinCodePageView.axaml | 132 +++-- .../BreadcrumbBarPageView.axaml | 376 +++++++------- .../BreadcrumbBarPageView.axaml.cs | 1 - .../CommandBarPageView.axaml | 484 +++++++++--------- .../CommandBarPageView.axaml.cs | 2 - .../CrashReportDialogPageView.axaml | 94 ++-- .../CrashReportDialogPageView.axaml.cs | 1 - .../DashboardCardPageView.axaml | 144 +++--- .../DashboardCardPageView.axaml.cs | 2 - .../DownloadPanelPageView.axaml | 90 ++-- .../DownloadPanelPageView.axaml.cs | 1 - .../InstallWizardPageView.axaml | 398 +++++++------- .../ItemListPanelPageView.axaml | 18 +- .../ItemListPanelPageView.axaml.cs | 2 - .../LogViewerPanelPageView.axaml | 1 - .../LogViewerPanelPageView.axaml.cs | 2 - .../PathPickerPageView.axaml | 234 +++++---- .../PleasantDrawerPageView.axaml | 136 ++--- .../PleasantMenuPageView.axaml | 103 ++-- .../PleasantMiniWindowPageView.axaml | 102 ++-- .../PleasantMiniWindowPageView.axaml.cs | 1 - .../PleasantSnackbarPageView.axaml | 186 ++++--- .../PopConfirmPageView.axaml | 203 ++++---- .../PropertyGridPageView.axaml | 105 ++-- .../PropertyGridPageView.axaml.cs | 2 - .../StepDialogPageView.axaml | 77 ++- .../StepDialogPageView.axaml.cs | 5 +- .../TerminalPanelPageView.axaml | 3 - .../TerminalPanelPageView.axaml.cs | 1 - .../TimelinePageView.axaml | 237 ++++----- .../TreeViewPanelPageView.axaml | 2 - .../TreeViewPanelPageView.axaml.cs | 2 - .../Pages/ToolKitPages/DockingPageView.axaml | 4 - .../ToolKitPages/MessageBoxPageView.axaml | 210 ++++---- .../ToolKitPages/NoticeDialogPageView.axaml | 116 ++--- .../Controls/DownloadPanel/ChunkInfo.cs | 1 - .../Controls/DownloadPanel/DownloadPanel.cs | 1 - .../Controls/LogViewerPanel/LogViewerPanel.cs | 1 - .../NoticeDialog/NoticeDialog.axaml.cs | 63 +-- .../Controls/NoticeDialog/NoticeDialog.cs | 21 - .../Controls/StepDialog/StepItem.cs | 1 - .../Controls/TerminalPanel/TerminalPanel.cs | 1 - .../PleasantUIToolKit.axaml.cs | 2 - src/PleasantUI/Assets/IconGeometries.axaml | 3 - .../BackdropBlurControl/BackdropBlurBorder.cs | 63 ++- .../Controls/BreadcrumbBar/BreadcrumbBar.cs | 1 - .../BreadcrumbBar/BreadcrumbBarItem.cs | 1 - .../BreadcrumbBar/BreadcrumbBarPanel.cs | 1 - .../Controls/Chrome/PleasantCaptionButtons.cs | 13 +- .../Controls/Chrome/PleasantTitleBar.cs | 6 - .../Controls/CommandBar/CommandBarButton.cs | 3 - .../CommandBar/CommandBarToggleButton.cs | 1 - .../Controls/ContentDialog/ContentDialog.cs | 2 +- .../Controls/IconControl/IconControl.cs | 1 - .../Controls/ItemListPanel/ItemListPanel.cs | 1 - .../Controls/NavigationView/NavigationView.cs | 58 ++- .../NavigationView/NavigationViewItem.cs | 49 +- .../NavigationViewSubMenuControl.cs | 7 +- .../OptionsDisplayItem/OptionsDisplayItem.cs | 1 - .../Controls/PathPicker/PathPicker.cs | 1 - .../PleasantDatePicker/PleasantDatePicker.cs | 1 - .../PleasantMenu/PleasantMenuFooterItem.cs | 1 - .../Controls/PleasantMenu/PleasantMenuItem.cs | 1 - .../PleasantPopupElement.cs | 1 - .../PleasantTabView/PleasantTabItem.cs | 14 +- .../PleasantTabView/PleasantTabView.cs | 14 +- .../PleasantTrayPopup/PleasantTrayPopup.cs | 1 - .../PleasantWindow/PleasantWindowBase.cs | 1 - .../Controls/ProgressRing/ProgressRing.cs | 18 +- src/PleasantUI/Controls/Ripple/Ripple.cs | 14 +- .../Controls/Ripple/RippleEffect.cs | 16 +- .../Controls/Ripple/RippleHandler.cs | 14 +- .../Controls/ShadowBorder/ShadowBorder.cs | 25 +- .../Snackbar/PleasantSnackbarOptions.cs | 1 - .../SplashScreen/PleasantSplashScreen.cs | 1 - .../Controls/TreeViewPanel/TreeViewSection.cs | 1 - .../Utils/RealizedWrappedElements.cs | 14 +- .../VirtualizingWrapPanel/Utils/UVSize.cs | 14 +- .../VirtualizingWrapPanel.cs | 14 +- .../ProgressBarIndicatorConverter.cs | 1 - .../Core/Attached/ScrollViewerExtensions.cs | 188 ++++--- .../Core/Extensions/BinarySearchExtension.cs | 57 --- .../Extensions/Markup/LocalizeExtension.cs | 28 +- .../PleasantSettingsGenerationContext.cs | 1 - .../Extensions/BinarySearchExtension.cs | 66 +++ src/PleasantUI/Core/Localization/Localizer.cs | 34 +- src/PleasantUI/Core/PleasantSettings.cs | 166 +----- .../Core/Settings/AppVersionSettings.cs | 66 --- src/PleasantUI/PleasantTheme.axaml.cs | 5 - .../ControlThemes/BasicControls/ListBox.axaml | 4 +- .../BasicControls/ScrollViewer.axaml | 12 +- .../PleasantControls/NavigationView.axaml | 10 +- .../PleasantControls/NavigationViewItem.axaml | 18 +- .../PleasantControls/PleasantTitleBar.axaml | 3 +- 105 files changed, 2312 insertions(+), 2438 deletions(-) create mode 100644 samples/PleasantUI.Example/AppSettings.cs delete mode 100644 src/PleasantUI/Core/Extensions/BinarySearchExtension.cs create mode 100644 src/PleasantUI/Core/Internal/Extensions/BinarySearchExtension.cs delete mode 100644 src/PleasantUI/Core/Settings/AppVersionSettings.cs diff --git a/README.md b/README.md index 7cb5f909..6f1894e9 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,6 @@ Controls built from scratch that go beyond what Avalonia ships: | `InformationBlock` | Compact pill-shaped label combining an icon and a value | ![Imgur](https://i.imgur.com/SX0BZ2k.png) | | `MarkedTextBox` / `MarkedNumericUpDown` | Input controls with inline label/unit markers | None | | `RippleEffect` | Material-style ripple click feedback | ![Imgur](https://i.imgur.com/2WtIPvj.gif) | -| `SmoothScrollViewer` | ScrollViewer with inertia gesture support | None | | `PleasantMiniWindow` | Lightweight floating window | None | | `Timeline` | Displays a list of events in chronological order along a vertical axis. Supports four layout modes, custom icons, and five severity types. | ![Imgur](https://i.imgur.com/DGP3nDR.png) | | `InstallWizard` | A multi-step installation wizard with a sidebar step list, progress bar, and Back / Next / Cancel navigation. | ![Imgur](https://i.imgur.com/bbgKH3L.png) | @@ -311,9 +310,17 @@ string title = Localizer.TrDefault("DialogTitle", "Confirm"); ## ❤️ Credits -- [Avalonia](https://github.com/AvaloniaUI/Avalonia) -- Some controls inspired by PieroCastillo's [Aura.UI](https://github.com/PieroCastillo/Aura.UI) -- [ProgressRing](https://github.com/ymg2006/FluentAvalonia.ProgressRing) by ymg2006 +### Main +- [Avalonia](https://github.com/AvaloniaUI/Avalonia) under license from MIT - Built with [JetBrains Rider](https://www.jetbrains.com/rider/) +### Controls and Code +- [Aura.UI](https://github.com/PieroCastillo/Aura.UI) (PieroCastillo) - NavigationView and AuraTabView under license from MIT +- [ProgressRing](https://github.com/ymg2006/FluentAvalonia.ProgressRing) (ymg2006) - under license from MIT +- [RichTextKit](https://github.com/toptensoftware/RichTextKit) (toptensoftware) - implementation of BinarySearch under license from Apache 2.0 +- [weather](https://github.com/rocksdanister/weather) (rocksdanister) - implementation of BackdropBlurBorder under license from MIT +- [Xaml.Behaviors.Interactions.Animated](https://github.com/Egolds/Xaml.Behaviors.Interactions.Animated) (Egolds) - implementation of VerticalScrollViewerAnimatedBehavior under license from MIT +- [BilibiliClient](https://github.com/afunc233/BilibiliClient) (afunc233) - implementation of VirtualizingWrapPanel +- [Material.Avalonia](https://github.com/AvaloniaCommunity/Material.Avalonia) (CommunityAvalonia) - implementation of Ripple Effect under license from MIT + diff --git a/samples/PleasantUI.Example.Desktop/App.axaml.cs b/samples/PleasantUI.Example.Desktop/App.axaml.cs index 41a5070e..8029333c 100644 --- a/samples/PleasantUI.Example.Desktop/App.axaml.cs +++ b/samples/PleasantUI.Example.Desktop/App.axaml.cs @@ -127,7 +127,7 @@ private PleasantTrayPopup BuildTrayPopup() // Content: version info var content = new StackPanel { - Margin = new Avalonia.Thickness(14, 8), + Margin = new Thickness(14, 8), Spacing = 4, Children = { @@ -147,9 +147,9 @@ private PleasantTrayPopup BuildTrayPopup() } }; - ((IResourceHost)this).TryFindResource("PleasantUILogo", out var appIconResource); + this.TryFindResource("PleasantUILogo", out object? appIconResource); - var popup = new PleasantTrayPopup + PleasantTrayPopup popup = new PleasantTrayPopup { Width = 280, AppTitle = "PleasantUI Example", diff --git a/samples/PleasantUI.Example/AppSettings.cs b/samples/PleasantUI.Example/AppSettings.cs new file mode 100644 index 00000000..a8361b97 --- /dev/null +++ b/samples/PleasantUI.Example/AppSettings.cs @@ -0,0 +1,22 @@ +using System.Runtime.Serialization; +using PleasantUI.Core; + +namespace PleasantUI.Example; + +public class AppSettings : ViewModelBase +{ + /// + /// Gets the singleton instance of the AppSettings class. + /// + public static AppSettings? Current { get; set; } + + /// + /// Gets or sets the current language code (e.g., "en", "ru"). + /// + [DataMember] + public string Language + { + get; + set => SetProperty(ref field, value); + } = "en"; +} \ No newline at end of file diff --git a/samples/PleasantUI.Example/Factories/ControlPageCardsFactory.cs b/samples/PleasantUI.Example/Factories/ControlPageCardsFactory.cs index 1471039d..de85e74e 100644 --- a/samples/PleasantUI.Example/Factories/ControlPageCardsFactory.cs +++ b/samples/PleasantUI.Example/Factories/ControlPageCardsFactory.cs @@ -1,6 +1,5 @@ using Avalonia.Collections; using Material.Icons; -using Material.Icons.Avalonia; using PleasantUI.Example.Models; using PleasantUI.Example.Pages.BasicControls; using PleasantUI.Example.Pages.PleasantControls; diff --git a/samples/PleasantUI.Example/MainView.axaml b/samples/PleasantUI.Example/MainView.axaml index 78b4bba8..e8a35792 100644 --- a/samples/PleasantUI.Example/MainView.axaml +++ b/samples/PleasantUI.Example/MainView.axaml @@ -85,7 +85,7 @@ - + diff --git a/samples/PleasantUI.Example/Models/ControlPageCard.cs b/samples/PleasantUI.Example/Models/ControlPageCard.cs index 95151d0a..03e82be2 100644 --- a/samples/PleasantUI.Example/Models/ControlPageCard.cs +++ b/samples/PleasantUI.Example/Models/ControlPageCard.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Diagnostics; -using Avalonia.Media; using Material.Icons; using PleasantUI.Core.Localization; using PleasantUI.Example.Interfaces; diff --git a/samples/PleasantUI.Example/PleasantUIExampleApp.cs b/samples/PleasantUI.Example/PleasantUIExampleApp.cs index a9ae249e..508d71a3 100644 --- a/samples/PleasantUI.Example/PleasantUIExampleApp.cs +++ b/samples/PleasantUI.Example/PleasantUIExampleApp.cs @@ -2,7 +2,6 @@ using Avalonia; using Avalonia.Controls; using PleasantUI.Controls; -using PleasantUI.Core; using PleasantUI.Core.Interfaces; using PleasantUI.Core.Localization; using PleasantUI.Example.Structures; @@ -47,9 +46,9 @@ protected void InitializeFromSettings() if (Design.IsDesignMode) return; // Now PleasantSettings.Current is available — apply persisted language. - if (PleasantSettings.Current is not null && !string.IsNullOrEmpty(PleasantSettings.Current.Language)) + if (AppSettings.Current is not null && !string.IsNullOrEmpty(AppSettings.Current.Language)) { - LanguageKey = PleasantSettings.Current.Language; + LanguageKey = AppSettings.Current.Language; } // Re-apply language now that we know the persisted value. diff --git a/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs index cb090e46..b82e7c53 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs @@ -17,7 +17,7 @@ public SettingsViewModel() public bool IsFullScreenButtonVisible { - get => PleasantUiExampleApp.Main is PleasantWindow w && w.IsFullScreenButtonVisible; + get => PleasantUiExampleApp.Main is PleasantWindow { IsFullScreenButtonVisible: true }; set { if (PleasantUiExampleApp.Main is PleasantWindow w) @@ -48,8 +48,8 @@ public Language SelectedLanguage PleasantUiExampleApp.LanguageKey = value.Key; // Persist language to settings - if (PleasantSettings.Current is not null) - PleasantSettings.Current.Language = value.Key; + if (AppSettings.Current is not null) + AppSettings.Current.Language = value.Key; Localizer.ChangeLang(value.Key); RaisePropertyChanged(); diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs index 18ecabf7..03647843 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs @@ -7,13 +7,11 @@ namespace PleasantUI.Example.ViewModels.Pages.ToolKitPages; public class NoticeDialogViewModel : ViewModelBase { - private string _lastResult = "—"; - public string LastResult { - get => _lastResult; - set => SetProperty(ref _lastResult, value); - } + get; + set => SetProperty(ref field, value); + } = "—"; // Resolves a key under the NoticeDialog/ context with a hardcoded fallback private static string T(string key, string fallback) => @@ -21,7 +19,7 @@ private static string T(string key, string fallback) => public async Task ShowInfo() { - var dialog = new NoticeDialog + NoticeDialog dialog = new NoticeDialog { Title = T("InfoTitle", "Information"), Message = T("InfoMessage", "Your changes have been saved successfully."), @@ -30,13 +28,13 @@ public async Task ShowInfo() }; dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); - await dialog.ShowAsync(PleasantUiExampleApp.Main as Avalonia.Controls.TopLevel); + await dialog.ShowAsync(PleasantUiExampleApp.Main as TopLevel); LastResult = T("InfoResult", "Info dialog closed"); } public async Task ShowWarning() { - var dialog = new NoticeDialog + NoticeDialog dialog = new NoticeDialog { Title = T("WarningTitle", "Warning"), Message = T("WarningMessage", "The file you are trying to open is larger than 100MB. Opening it may slow down the application."), @@ -47,13 +45,13 @@ public async Task ShowWarning() dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); dialog.SecondaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); - await dialog.ShowAsync(PleasantUiExampleApp.Main as Avalonia.Controls.TopLevel); + await dialog.ShowAsync(PleasantUiExampleApp.Main as TopLevel); LastResult = T("WarningResult", "Warning dialog closed"); } public async Task ShowError() { - var dialog = new NoticeDialog + NoticeDialog dialog = new NoticeDialog { Title = T("ErrorTitle", "Error"), Message = T("ErrorMessage", "An unexpected error occurred while processing your request. Please try again later."), @@ -62,13 +60,13 @@ public async Task ShowError() }; dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); - await dialog.ShowAsync(PleasantUiExampleApp.Main as Avalonia.Controls.TopLevel); + await dialog.ShowAsync(PleasantUiExampleApp.Main as TopLevel); LastResult = T("ErrorResult", "Error dialog closed"); } public async Task ShowSuccess() { - var dialog = new NoticeDialog + NoticeDialog dialog = new NoticeDialog { Title = T("SuccessTitle", "Success"), Message = T("SuccessMessage", "Your account has been created successfully. You can now log in."), @@ -77,42 +75,29 @@ public async Task ShowSuccess() }; dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); - await dialog.ShowAsync(PleasantUiExampleApp.Main as Avalonia.Controls.TopLevel); + await dialog.ShowAsync(PleasantUiExampleApp.Main as TopLevel); LastResult = T("SuccessResult", "Success dialog closed"); } public async Task ShowWorkInProgress() { - var versionType = PleasantUI.Core.PleasantSettings.VersionType; - string version = PleasantUI.Core.PleasantSettings.InformationalVersion; // Get version-specific message - string message = versionType switch - { - PleasantUI.Core.PleasantVersionType.Stable => T("WipStableMessage", "This is a stable release of the application. All features are fully tested and ready for production use."), - PleasantUI.Core.PleasantVersionType.BugFix => T("WipBugfixMessage", "This is a bugfix release containing important fixes for issues found in the stable version."), - PleasantUI.Core.PleasantVersionType.Alpha => T("WipAlphaMessage", "This is an alpha release. Features may be incomplete and unstable. Use with caution."), - PleasantUI.Core.PleasantVersionType.Beta => T("WipBetaMessage", "This is a beta release. Features are mostly complete but may still contain bugs."), - PleasantUI.Core.PleasantVersionType.ReleaseCandidate => T("WipRcMessage", "This is a release candidate. It is close to the final release but may still have issues."), - PleasantUI.Core.PleasantVersionType.Canary => T("WipCanaryMessage", "This is a canary build with the latest changes. It may be unstable and is intended for testing purposes only."), - _ => T("WipDefaultMessage", "This version of the application is in active development. Features may change or be removed in future releases.") - }; + string message = T("WipStableMessage", + "This is a stable release of the application. All features are fully tested and ready for production use."); - var dialog = new NoticeDialog + NoticeDialog dialog = new NoticeDialog { Title = T("WipTitle", "Development Version"), Message = message, NoticeFooterText = T("WipFooter", "- Development Team"), PrimaryButtonText = T("Ok", "OK"), Severity = NoticeSeverity.WorkInProgress, - Version = version, - VersionType = PleasantUI.Core.PleasantSettings.VersionTypeDescription, - VersionTypeEnum = PleasantUI.Core.PleasantSettings.VersionType, VersionLabel = T("VersionLabel", "Version") }; dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); - await dialog.ShowAsync(PleasantUiExampleApp.Main as Avalonia.Controls.TopLevel); - LastResult = $"{T("WipResult", "Work in progress dialog shown")}: {versionType}"; + await dialog.ShowAsync(PleasantUiExampleApp.Main as TopLevel); + LastResult = $"{T("WipResult", "Work in progress dialog shown")}"; } } diff --git a/samples/PleasantUI.Example/Views/AboutView.axaml b/samples/PleasantUI.Example/Views/AboutView.axaml index efd3b283..7bfb1ab5 100644 --- a/samples/PleasantUI.Example/Views/AboutView.axaml +++ b/samples/PleasantUI.Example/Views/AboutView.axaml @@ -30,20 +30,6 @@ - - - - - diff --git a/samples/PleasantUI.Example/Views/AboutView.axaml.cs b/samples/PleasantUI.Example/Views/AboutView.axaml.cs index 47c1a7c5..82620c7e 100644 --- a/samples/PleasantUI.Example/Views/AboutView.axaml.cs +++ b/samples/PleasantUI.Example/Views/AboutView.axaml.cs @@ -1,6 +1,4 @@ using Avalonia.Controls; -using Avalonia.Media; -using PleasantUI.Core; using System.Diagnostics; namespace PleasantUI.Example.Views; @@ -10,30 +8,6 @@ public partial class AboutView : UserControl public AboutView() { InitializeComponent(); - - VersionTextBlock.Text = PleasantSettings.InformationalVersion; - - // Set version type badge - var badgeTextBlock = VersionTypeBadge.Child as TextBlock; - if (badgeTextBlock != null) - { - badgeTextBlock.Text = PleasantSettings.VersionTypeDescription; - } - VersionTypeBadge.Background = GetVersionTypeColor(PleasantSettings.VersionType); - } - - private IBrush GetVersionTypeColor(PleasantVersionType versionType) - { - return versionType switch - { - PleasantVersionType.Stable => new SolidColorBrush(Color.Parse("#107C10")), // Green - PleasantVersionType.BugFix => new SolidColorBrush(Color.Parse("#008272")), // Teal - PleasantVersionType.Alpha => new SolidColorBrush(Color.Parse("#E81123")), // Red - PleasantVersionType.Beta => new SolidColorBrush(Color.Parse("#FF8C00")), // Orange - PleasantVersionType.ReleaseCandidate => new SolidColorBrush(Color.Parse("#8A2BE2")), // Purple - PleasantVersionType.Canary => new SolidColorBrush(Color.Parse("#FFB900")), // Yellow - _ => new SolidColorBrush(Color.Parse("#605E5C")) // Gray - }; } private void GitHubButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) diff --git a/samples/PleasantUI.Example/Views/Pages/ControlPages/PinCodePageView.axaml b/samples/PleasantUI.Example/Views/Pages/ControlPages/PinCodePageView.axaml index cc086f4a..1d4d6262 100644 --- a/samples/PleasantUI.Example/Views/Pages/ControlPages/PinCodePageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/ControlPages/PinCodePageView.axaml @@ -4,79 +4,77 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="PleasantUI.Example.Views.Pages.ControlPages.PinCodePageView"> - - + - - - - - - - - - - - - - - - - - - - + + + - + VerticalAlignment="Center" /> + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - + + - - + - + + - - - - - - - + + + \ No newline at end of file diff --git a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml index 1e9b42ae..d7bdf658 100644 --- a/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/PleasantControlPages/PleasantMiniWindowPageView.axaml @@ -4,52 +4,64 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="PleasantUI.Example.Views.Pages.PleasantControlPages.PleasantMiniWindowPageView"> - - + - + - - - - - - - public partial class NoticeDialog { - private const double ProgressBarVisibleHeight = 20.0; - // ── Severity icon geometries ─────────────────────────────────────────────── private static readonly Geometry InfoIconGeometry = Geometry.Parse("M11 2a9 9 0 1 0 0 18 9 9 0 0 0 0-18zm0 16a7 7 0 1 1 0-14 7 7 0 0 1 0 14zm1-11h-2v2h2V7zm0 4h-2v4h2v-4z"); @@ -59,22 +56,9 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) UpdateLocalization(); UpdateVersionBadge(); UpdateButtons(); - - // Ensure version badge background is set after template is applied as failsafe - if (!string.IsNullOrEmpty(VersionType)) - { - var versionTypeBadge = this.FindControl("VersionTypeBadge"); - if (versionTypeBadge is not null) - { - var brush = VersionTypeEnum.HasValue - ? GetVersionTypeBrushFromEnum(VersionTypeEnum.Value) - : GetVersionTypeBrushFromString(VersionType); - VersionTypeBadgeBackground = brush; - versionTypeBadge.Background = brush; - } - } } + /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); @@ -83,7 +67,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang UpdateSeverity(); else if (change.Property == TitleProperty || change.Property == MessageProperty || change.Property == NoticeFooterTextProperty) UpdateLocalization(); - else if (change.Property == VersionProperty || change.Property == VersionTypeProperty || change.Property == VersionLabelProperty || change.Property == VersionTypeEnumProperty) + else if (change.Property == VersionProperty || change.Property == VersionTypeProperty || change.Property == VersionLabelProperty) UpdateVersionBadge(); else if (change.Property == VersionTypeBadgeBackgroundProperty) { @@ -99,9 +83,9 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang private void UpdateSeverity() { - var headerBorder = this.FindControl("HeaderBorder"); - var severityIcon = this.FindControl("SeverityIcon"); - var titleText = this.FindControl("TitleText"); + Border? headerBorder = this.FindControl("HeaderBorder"); + IconControl? severityIcon = this.FindControl("SeverityIcon"); + TextBlock? titleText = this.FindControl("TitleText"); if (headerBorder is null || severityIcon is null || titleText is null) return; @@ -170,12 +154,6 @@ private void UpdateVersionBadge() if (!string.IsNullOrEmpty(VersionType)) { versionTypeText.Text = VersionType; - // Prefer strongly-typed enum for accurate color resolution; fall back to string normalization - var brush = VersionTypeEnum.HasValue - ? GetVersionTypeBrushFromEnum(VersionTypeEnum.Value) - : GetVersionTypeBrushFromString(VersionType); - VersionTypeBadgeBackground = brush; - versionTypeBadge.Background = brush; versionTypeBadge.IsVisible = true; } else @@ -189,37 +167,6 @@ private void UpdateVersionBadge() } } - private static IBrush GetVersionTypeBrushFromEnum(PleasantVersionType versionType) - { - return versionType switch - { - PleasantVersionType.Stable => new SolidColorBrush(Color.Parse("#107C10")), - PleasantVersionType.BugFix => new SolidColorBrush(Color.Parse("#008272")), - PleasantVersionType.Alpha => new SolidColorBrush(Color.Parse("#E81123")), - PleasantVersionType.Beta => new SolidColorBrush(Color.Parse("#FF8C00")), - PleasantVersionType.ReleaseCandidate => new SolidColorBrush(Color.Parse("#8A2BE2")), - PleasantVersionType.Canary => new SolidColorBrush(Color.Parse("#FFB900")), - _ => new SolidColorBrush(Color.Parse("#605E5C")) - }; - } - - private static IBrush GetVersionTypeBrushFromString(string versionType) - { - // Normalize: lowercase, strip spaces and hyphens - var normalized = versionType.ToLowerInvariant().Replace(" ", "").Replace("-", ""); - - return normalized switch - { - "stable" or "stablerelease" => new SolidColorBrush(Color.Parse("#107C10")), - "bugfix" or "fix" or "bugfixrelease" => new SolidColorBrush(Color.Parse("#008272")), - "alpha" or "alphaprerelease" or "alphaprerelease" => new SolidColorBrush(Color.Parse("#E81123")), - "beta" or "betaprerelease" => new SolidColorBrush(Color.Parse("#FF8C00")), - "rc" or "releasecandidate" => new SolidColorBrush(Color.Parse("#8A2BE2")), - "canary" or "canarybuild" => new SolidColorBrush(Color.Parse("#FFB900")), - _ => new SolidColorBrush(Color.Parse("#605E5C")) - }; - } - private void UpdateButtons() { var buttonsHost = this.FindControl("ButtonsHost"); diff --git a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.cs b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.cs index 23c38422..b29dccb6 100644 --- a/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.cs +++ b/src/PleasantUI.ToolKit/Controls/NoticeDialog/NoticeDialog.cs @@ -1,13 +1,6 @@ using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Presenters; -using Avalonia.Controls.Primitives; -using Avalonia.Input; using Avalonia.Media; -using Avalonia.Styling; using PleasantUI.Controls; -using PleasantUI.Core; -using PleasantUI.Core.Localization; namespace PleasantUI.ToolKit.Controls; @@ -77,10 +70,6 @@ public partial class NoticeDialog : ContentDialog public static readonly StyledProperty VersionTypeBadgeBackgroundProperty = AvaloniaProperty.Register(nameof(VersionTypeBadgeBackground)); - /// Defines the property. - public static readonly StyledProperty VersionTypeEnumProperty = - AvaloniaProperty.Register(nameof(VersionTypeEnum)); - // ── CLR accessors ───────────────────────────────────────────────────────── /// Gets or sets the dialog title. Supports localization keys. @@ -153,16 +142,6 @@ public IBrush? VersionTypeBadgeBackground set => SetValue(VersionTypeBadgeBackgroundProperty, value); } - /// - /// Gets or sets the strongly-typed version type enum. When set, badge color is resolved - /// from the enum directly (same logic as AboutView), bypassing string normalization. - /// - public PleasantVersionType? VersionTypeEnum - { - get => GetValue(VersionTypeEnumProperty); - set => SetValue(VersionTypeEnumProperty, value); - } - // ── Events ──────────────────────────────────────────────────────────────── /// Raised when the primary button is clicked. diff --git a/src/PleasantUI.ToolKit/Controls/StepDialog/StepItem.cs b/src/PleasantUI.ToolKit/Controls/StepDialog/StepItem.cs index e0c4d28d..fc8bbce3 100644 --- a/src/PleasantUI.ToolKit/Controls/StepDialog/StepItem.cs +++ b/src/PleasantUI.ToolKit/Controls/StepDialog/StepItem.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Templates; namespace PleasantUI.ToolKit.Controls; diff --git a/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs b/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs index 5ed99db7..57253afd 100644 --- a/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs +++ b/src/PleasantUI.ToolKit/Controls/TerminalPanel/TerminalPanel.cs @@ -6,7 +6,6 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; -using PleasantUI.Controls; namespace PleasantUI.ToolKit.Controls; diff --git a/src/PleasantUI.ToolKit/PleasantUIToolKit.axaml.cs b/src/PleasantUI.ToolKit/PleasantUIToolKit.axaml.cs index ec2da229..8953fe7a 100644 --- a/src/PleasantUI.ToolKit/PleasantUIToolKit.axaml.cs +++ b/src/PleasantUI.ToolKit/PleasantUIToolKit.axaml.cs @@ -1,8 +1,6 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; -using Avalonia.Styling; using PleasantUI.Core; namespace PleasantUI.ToolKit; diff --git a/src/PleasantUI/Assets/IconGeometries.axaml b/src/PleasantUI/Assets/IconGeometries.axaml index d9d422d7..0d7334dc 100644 --- a/src/PleasantUI/Assets/IconGeometries.axaml +++ b/src/PleasantUI/Assets/IconGeometries.axaml @@ -38,11 +38,8 @@ M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16 M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z - M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z - M6,2A3,3 0 0,1 9,5C9,6.28 8.19,7.38 7.06,7.81C7.15,8.27 7.39,8.83 8,9.63C8.84,10.67 10,11.83 10,13.41V13.44C10.93,13.17 12,13 13,13C14,13 15.07,13.17 16,13.44V13.41C16,11.83 17.16,10.67 18,9.63C18.61,8.83 18.85,8.27 18.94,7.81C17.81,7.38 17,6.28 17,5A3,3 0 0,1 20,2A3,3 0 0,1 23,5C23,6.28 22.19,7.38 21.06,7.81C21,8.5 20.6,9.28 19.75,10.36C19.16,11.1 18.5,11.86 18.17,12.63C19.26,13.06 20,14.13 20,15.41A3,3 0 0,1 17,18.41A3,3 0 0,1 14,15.41C14,14.38 14.56,13.5 15.38,13.03C14.55,12.85 13.77,12.77 13,12.77C12.23,12.77 11.45,12.85 10.62,13.03C11.44,13.5 12,14.38 12,15.41A3,3 0 0,1 9,18.41A3,3 0 0,1 6,15.41C6,14.13 6.74,13.06 7.83,12.63C7.5,11.86 6.84,11.1 6.25,10.36C5.4,9.28 5,8.5 4.94,7.81C3.81,7.38 3,6.28 3,5A3,3 0 0,1 6,2Z - M20.5,11H19V7C19,5.89 18.1,5 17,5H13V3.5A2.5,2.5 0 0,0 10.5,1A2.5,2.5 0 0,0 8,3.5V5H4A2,2 0 0,0 2,7V10.8H3.5C5,10.8 6.2,12 6.2,13.5C6.2,15 5,16.2 3.5,16.2H2V20A2,2 0 0,0 4,22H7.8V20.5C7.8,19 9,17.8 10.5,17.8C12,17.8 13.2,19 13.2,20.5V22H17A2,2 0 0,0 19,20V16H20.5A2.5,2.5 0 0,0 23,13.5A2.5,2.5 0 0,0 20.5,11Z M3,5H21V7H3V5M3,11H21V13H3V11M3,17H21V19H3V17Z M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z diff --git a/src/PleasantUI/Controls/BackdropBlurControl/BackdropBlurBorder.cs b/src/PleasantUI/Controls/BackdropBlurControl/BackdropBlurBorder.cs index 0ddee965..4b32a5be 100644 --- a/src/PleasantUI/Controls/BackdropBlurControl/BackdropBlurBorder.cs +++ b/src/PleasantUI/Controls/BackdropBlurControl/BackdropBlurBorder.cs @@ -1,4 +1,18 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2024 Dani John (rocksdanister) + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/rocksdanister/weather/blob/main/src/Drizzle.UI.Avalonia/UserControls/BackdropBlurControl.cs + * + * Changes: + * 1. Discontinued from use ExperimentalAcrylicMaterial. + * 2. Added the ability to set a normal and rounded border. + * 3. Added render refresh timer to fix artifact. + */ + +using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Media; @@ -9,8 +23,6 @@ namespace PleasantUI.Controls; -// Reference: https://github.com/rocksdanister/weather/blob/main/src/Drizzle.UI.Avalonia/UserControls/BackdropBlurControl.cs - /// /// A control that blurs the background. /// @@ -19,6 +31,8 @@ namespace PleasantUI.Controls; /// public class BackdropBlurBorder : Decorator { + private DispatcherTimer? _refreshTimer; + /// /// Defines the property. /// @@ -103,6 +117,11 @@ static BackdropBlurBorder() AffectsMeasure(BorderThicknessProperty); } + + public BackdropBlurBorder() + { + InitializeRefreshTimer(); + } /// public override void Render(DrawingContext context) @@ -110,10 +129,6 @@ public override void Render(DrawingContext context) context.Custom(new BlurBehindRenderOperation(Background, TintOpacity, BlurRadius, new Rect(default, Bounds.Size), GetMaxCornerRadius(CornerRadius), GetMaxThickness(BorderThickness), BorderBrush)); - - // We update every frame because there are artifacts when animating the color of the controls (sharp lines). - // This is the best we could find, but expensive - Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); } /// @@ -135,6 +150,39 @@ protected override Size ArrangeOverride(Size finalSize) { return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness); } + + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + StartRefreshTimer(); + } + + /// + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + StopRefreshTimer(); + } + + private void InitializeRefreshTimer() + { + _refreshTimer = new DispatcherTimer(DispatcherPriority.Render) + { + Interval = TimeSpan.FromMilliseconds(25) + }; + _refreshTimer.Tick += (s, e) => InvalidateVisual(); + } + + private void StartRefreshTimer() + { + _refreshTimer?.Start(); + } + + private void StopRefreshTimer() + { + _refreshTimer?.Stop(); + } private double GetMaxThickness(Thickness thickness) { @@ -254,6 +302,7 @@ public void Render(ImmediateDrawingContext context) { blurSnapPaint.Shader = blurSnapShader; blurSnapPaint.IsAntialias = true; + lease.SkCanvas.DrawRect(0, 0, width, height, blurSnapPaint); } diff --git a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBar.cs b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBar.cs index 0361a9eb..d0e2e88a 100644 --- a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBar.cs +++ b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBar.cs @@ -1,4 +1,3 @@ -using System.Collections; using System.Collections.Specialized; using Avalonia; using Avalonia.Automation; diff --git a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarItem.cs b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarItem.cs index e43438e3..c9a49749 100644 --- a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarItem.cs +++ b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarItem.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Avalonia; using Avalonia.Automation; using Avalonia.Automation.Peers; diff --git a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarPanel.cs b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarPanel.cs index c4c84cc1..6216d02f 100644 --- a/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarPanel.cs +++ b/src/PleasantUI/Controls/BreadcrumbBar/BreadcrumbBarPanel.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Layout; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/Chrome/PleasantCaptionButtons.cs b/src/PleasantUI/Controls/Chrome/PleasantCaptionButtons.cs index 73d476e4..e143953b 100644 --- a/src/PleasantUI/Controls/Chrome/PleasantCaptionButtons.cs +++ b/src/PleasantUI/Controls/Chrome/PleasantCaptionButtons.cs @@ -148,7 +148,7 @@ private void UpdateButtonVisibility() if (Host is null || _closeButton is null || _maximizeButton is null || _minimizeButton is null) return; - var state = Host.WindowState; + WindowState state = Host.WindowState; bool isFullScreen = state == WindowState.FullScreen; // In fullscreen: only Close and FullScreen button are relevant @@ -167,25 +167,25 @@ private void UpdateButtonVisibility() { case Type.None: _closeButton.IsVisible = _maximizeButton.IsVisible = _minimizeButton.IsVisible = false; - if (_fullScreenButton is not null) _fullScreenButton.IsVisible = false; + _fullScreenButton?.IsVisible = false; return; case Type.Close: _minimizeButton.IsVisible = false; _maximizeButton.IsVisible = false; _closeButton.IsVisible = Host.IsCloseButtonVisible; - if (_fullScreenButton is not null) _fullScreenButton.IsVisible = false; + _fullScreenButton?.IsVisible = false; return; case Type.CloseAndCollapse: _maximizeButton.IsVisible = false; _minimizeButton.IsVisible = Host.CanMinimize && Host.IsMinimizeButtonVisible; _closeButton.IsVisible = Host.IsCloseButtonVisible; - if (_fullScreenButton is not null) _fullScreenButton.IsVisible = false; + _fullScreenButton?.IsVisible = false; return; case Type.CloseAndExpand: _minimizeButton.IsVisible = false; _maximizeButton.IsVisible = Host.CanResize && Host.IsRestoreButtonVisible; _closeButton.IsVisible = Host.IsCloseButtonVisible; - if (_fullScreenButton is not null) _fullScreenButton.IsVisible = false; + _fullScreenButton?.IsVisible = false; return; } @@ -193,8 +193,7 @@ private void UpdateButtonVisibility() _closeButton.IsVisible = Host.IsCloseButtonVisible; _maximizeButton.IsVisible = Host.CanResize && Host.IsRestoreButtonVisible; _minimizeButton.IsVisible = Host.CanMinimize && Host.IsMinimizeButtonVisible; - if (_fullScreenButton is not null) - _fullScreenButton.IsVisible = Host.IsFullScreenButtonVisible; + _fullScreenButton?.IsVisible = Host.IsFullScreenButtonVisible; } /// diff --git a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs index 240df035..d75776bb 100644 --- a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs +++ b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs @@ -1,14 +1,11 @@ using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Documents; using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Input; -using Avalonia.Media; using Avalonia.Reactive; -using PleasantUI.Core.Extensions; using PleasantUI.Core.Internal.Reactive; using Path = Avalonia.Controls.Shapes.Path; @@ -164,9 +161,6 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _expandMenuItem.Click += (_, _) => window.WindowState = WindowState.Maximized; _collapseMenuItem.Click += (_, _) => window.WindowState = WindowState.Minimized; - _dragWindowBorder.PointerPressed += OnDragWindowBorderOnPointerPressed; - _dragWindowBorder.DoubleTapped += OnDragWindowBorderOnDoubleTapped; - Attach(window); } } diff --git a/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs b/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs index b4bd585d..3a8e289b 100644 --- a/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs +++ b/src/PleasantUI/Controls/CommandBar/CommandBarButton.cs @@ -1,11 +1,8 @@ using System.Diagnostics; -using System.Windows.Input; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; -using Avalonia.Input; using Avalonia.LogicalTree; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs b/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs index d2e2c3f8..d0cbbc3a 100644 --- a/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs +++ b/src/PleasantUI/Controls/CommandBar/CommandBarToggleButton.cs @@ -4,7 +4,6 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs b/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs index f142f8c6..4fcc5cf0 100644 --- a/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs +++ b/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs @@ -185,7 +185,7 @@ public Animation? CloseAnimation /// /// The window to show the dialog on. /// A task that represents the asynchronous show operation. - public Task ShowAsync(TopLevel topLevel) => ShowAsyncCoreForTopLevel(topLevel); + public Task ShowAsync(TopLevel? topLevel) => ShowAsyncCoreForTopLevel(topLevel); /// /// Shows the dialog asynchronously. diff --git a/src/PleasantUI/Controls/IconControl/IconControl.cs b/src/PleasantUI/Controls/IconControl/IconControl.cs index 29b5138b..c917cafc 100644 --- a/src/PleasantUI/Controls/IconControl/IconControl.cs +++ b/src/PleasantUI/Controls/IconControl/IconControl.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Data; using Avalonia.Metadata; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/ItemListPanel/ItemListPanel.cs b/src/PleasantUI/Controls/ItemListPanel/ItemListPanel.cs index f5154363..4dadabf6 100644 --- a/src/PleasantUI/Controls/ItemListPanel/ItemListPanel.cs +++ b/src/PleasantUI/Controls/ItemListPanel/ItemListPanel.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Specialized; using Avalonia; using Avalonia.Collections; using Avalonia.Controls; diff --git a/src/PleasantUI/Controls/NavigationView/NavigationView.cs b/src/PleasantUI/Controls/NavigationView/NavigationView.cs index 4482128f..0d96946f 100644 --- a/src/PleasantUI/Controls/NavigationView/NavigationView.cs +++ b/src/PleasantUI/Controls/NavigationView/NavigationView.cs @@ -1,5 +1,14 @@ -using System.Diagnostics; -using System.Linq; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2024 PieroCastillo + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/Navigation/NavigationView/NavigationView.Properties.cs + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/Navigation/NavigationView/NavigationView.cs + */ + +using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Input; using Avalonia; @@ -47,7 +56,7 @@ public class NavigationView : TreeView { private const double LittleWidth = 1005; private const double VeryLittleWidth = 650; - private double titleBarHeight; + private double _titleBarHeight; private Border? _container; private Grid? _mainGrid; @@ -57,7 +66,6 @@ public class NavigationView : TreeView private DockPanel? _topBottomLayout; private Button? _backButton; - private ICommand? _backButtonCommand; private PleasantWindow? _window; @@ -79,10 +87,6 @@ public class NavigationView : TreeView private Button? _headerItem; - private IEnumerable? _itemsAsStrings; - - private object? _selectedContent; - // Separate item collections for top/bottom nav bar — never shared with the left-pane Items. private readonly Avalonia.Collections.AvaloniaList _topItems = new(); private readonly Avalonia.Collections.AvaloniaList _bottomItems = new(); @@ -203,13 +207,13 @@ public class NavigationView : TreeView /// so it sits flush below the titlebar rather than overlapping it. /// public static readonly StyledProperty ButtonsPanelOffsetProperty = - AvaloniaProperty.Register(nameof(ButtonsPanelOffset), false); + AvaloniaProperty.Register(nameof(ButtonsPanelOffset)); /// /// Defines the property. /// public static readonly StyledProperty PositionProperty = - AvaloniaProperty.Register(nameof(Position), NavigationViewPosition.Left); + AvaloniaProperty.Register(nameof(Position)); /// /// Defines the property. @@ -248,8 +252,8 @@ public Geometry Icon /// public object? SelectedContent { - get => _selectedContent; - private set => SetAndRaise(SelectedContentProperty, ref _selectedContent, value); + get; + private set => SetAndRaise(SelectedContentProperty, ref field, value); } /// @@ -418,8 +422,8 @@ public Animation? TransitionAnimation /// public IEnumerable? ItemsAsStrings { - get => _itemsAsStrings; - private set => SetAndRaise(ItemsAsStringsProperty, ref _itemsAsStrings, value); + get; + private set => SetAndRaise(ItemsAsStringsProperty, ref field, value); } /// @@ -472,8 +476,8 @@ public bool BindWindowSettings /// public ICommand? BackButtonCommand { - get => _backButtonCommand; - set => SetAndRaise(BackButtonCommandProperty, ref _backButtonCommand, value); + get; + set => SetAndRaise(BackButtonCommandProperty, ref field, value); } static NavigationView() @@ -552,8 +556,8 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) if (TopLevel.GetTopLevel(this) is PleasantWindow window) { _window = window; - titleBarHeight = window.TitleBarHeight; - Debug.WriteLine($"[NavigationView] OnApplyTemplate PleasantWindow found titleBarHeight={titleBarHeight}"); + _titleBarHeight = window.TitleBarHeight; + Debug.WriteLine($"[NavigationView] OnApplyTemplate PleasantWindow found titleBarHeight={_titleBarHeight}"); UpdateMacNavigationLayout(window); UpdateContainerTitleHeight(window); UpdateMarginPanel(); @@ -563,7 +567,7 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) window.GetObservable(PleasantWindow.TitleBarHeightProperty) .Subscribe(new AnonymousObserver(h => { - titleBarHeight = h; + _titleBarHeight = h; UpdateContainerTitleHeight(window); UpdateMarginPanel(); UpdateTopBottomLayout(window); @@ -654,7 +658,7 @@ private void UpdateMacNavigationLayout(PleasantWindow window) { _mainGrid.RowDefinitions.Insert(0, new RowDefinition { Height = new GridLength(_headerItem.Height, GridUnitType.Pixel) }); } - _stackPanelButtons.Margin = new Thickness(5, titleBarHeight + 6, 5, 5); + _stackPanelButtons.Margin = new Thickness(5, _titleBarHeight + 6, 5, 5); Grid.SetRow(_marginPanel, 2); Grid.SetRow(_dockPanel, 3); } @@ -665,7 +669,7 @@ private void UpdateContainerTitleHeight(PleasantWindow window) { if (_container == null) return; - Thickness margin = window.EnableCustomTitleBar ? new Thickness(8, titleBarHeight + 1, 8, 8) : new Thickness(0); + Thickness margin = window.EnableCustomTitleBar ? new Thickness(8, _titleBarHeight + 1, 8, 8) : new Thickness(0); Debug.WriteLine($"[NavigationView] UpdateContainerTitleHeight enableCustomTitleBar={window.EnableCustomTitleBar} margin={margin}"); _container.CornerRadius = new CornerRadius(8); @@ -714,8 +718,8 @@ private void UpdateTopBottomLayout(PleasantWindow window) // For Top: push the entire DockPanel down by titleBarHeight so the bar clears the titlebar. // For Bottom: same — the content area starts below the titlebar, bar docks to the bottom. - _topBottomLayout.Margin = new Thickness(0, titleBarHeight, 0, 0); - Debug.WriteLine($"[NavigationView] UpdateTopBottomLayout position={Position} titleBarHeight={titleBarHeight} → margin=0,{titleBarHeight},0,0"); + _topBottomLayout.Margin = new Thickness(0, _titleBarHeight, 0, 0); + Debug.WriteLine($"[NavigationView] UpdateTopBottomLayout position={Position} titleBarHeight={_titleBarHeight} → margin=0,{_titleBarHeight},0,0"); } /// @@ -763,7 +767,7 @@ private void UpdateMarginPanel() double buttonsHeight = noBackButton ? 37 + 5 // hamburger + bottom margin : 37 + 5 + 37 + 5; // back + spacing + hamburger + bottom margin - result = titleBarHeight + 5 + buttonsHeight + 5; // top margin + buttons + gap + result = _titleBarHeight + 5 + buttonsHeight + 5; // top margin + buttons + gap } else { @@ -771,18 +775,18 @@ private void UpdateMarginPanel() const double baselineTitleBarHeight = 44.0; double baseHeight = noBackButton ? 60.0 : 90.0; double delta = baseHeight - baselineTitleBarHeight; - result = Math.Max(titleBarHeight + delta, baseHeight); + result = Math.Max(_titleBarHeight + delta, baseHeight); } _marginPanel.Height = result; if (_stackPanelButtons != null) { - double topMargin = ButtonsPanelOffset ? titleBarHeight + 5 : 5; + double topMargin = ButtonsPanelOffset ? _titleBarHeight + 5 : 5; _stackPanelButtons.Margin = new Thickness(5, topMargin, 5, 5); } - Debug.WriteLine($"[NavigationView] UpdateMarginPanel titleBarHeight={titleBarHeight} noBack={noBackButton} offset={ButtonsPanelOffset} → {result}"); + Debug.WriteLine($"[NavigationView] UpdateMarginPanel titleBarHeight={_titleBarHeight} noBack={noBackButton} offset={ButtonsPanelOffset} → {result}"); } private void OnBoundsChanged(Rect rect) diff --git a/src/PleasantUI/Controls/NavigationView/NavigationViewItem.cs b/src/PleasantUI/Controls/NavigationView/NavigationViewItem.cs index f5a24970..e73821fa 100644 --- a/src/PleasantUI/Controls/NavigationView/NavigationViewItem.cs +++ b/src/PleasantUI/Controls/NavigationView/NavigationViewItem.cs @@ -1,4 +1,15 @@ -using System.Diagnostics; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2024 PieroCastillo + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/Navigation/NavigationView/NavigationViewItemBase.Properties.cs + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/Navigation/NavigationView/NavigationViewItemBase.cs + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/Navigation/NavigationView/NavigationViewItem.cs + */ + +using System.Diagnostics; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; @@ -19,14 +30,6 @@ namespace PleasantUI.Controls; [TemplatePart("PART_Popup", typeof(Popup))] public class NavigationViewItem : TreeViewItem { - private object? _content; - private double _externalLength; - private object? _icon; - - private int _navigationViewDistance; - private object _title = "Title"; - private bool _isSubMenuOpen; - private Popup? _popup; private ScrollViewer? _popupScrollViewer; private NavigationViewSubMenuControl? _subMenuControl; @@ -141,8 +144,8 @@ public class NavigationViewItem : TreeViewItem /// public object? Content { - get => _content; - set => SetAndRaise(ContentProperty, ref _content, value); + get; + set => SetAndRaise(ContentProperty, ref field, value); } /// @@ -153,8 +156,8 @@ public object? Content /// public object? Icon { - get => _icon; - set => SetAndRaise(IconProperty, ref _icon, value); + get; + set => SetAndRaise(IconProperty, ref field, value); } /// @@ -169,9 +172,9 @@ public object? Icon /// public object Title { - get => _title; - set => SetAndRaise(TitleProperty, ref _title, value); - } + get; + set => SetAndRaise(TitleProperty, ref field, value); + } = "Title"; /// /// Gets or sets a value indicating whether the property is open. @@ -217,8 +220,8 @@ public ClickMode ClickMode /// public int NavigationViewDistance { - get => _navigationViewDistance; - protected set => SetAndRaise(LevelProperty, ref _navigationViewDistance, value); + get; + protected set => SetAndRaise(LevelProperty, ref field, value); } /// @@ -257,8 +260,8 @@ public double OpenPaneLength /// public double ExternalLength { - get => _externalLength; - private set => SetAndRaise(ExternalLengthProperty, ref _externalLength, value); + get; + private set => SetAndRaise(ExternalLengthProperty, ref field, value); } /// @@ -267,8 +270,8 @@ public double ExternalLength /// public bool IsSubMenuOpen { - get => _isSubMenuOpen; - set => SetAndRaise(IsSubMenuOpenProperty, ref _isSubMenuOpen, value); + get; + set => SetAndRaise(IsSubMenuOpenProperty, ref field, value); } /// @@ -563,7 +566,7 @@ private void RemovePopupItemsPresenter() Debug.WriteLine($"[NavItem] RemovePopupItemsPresenter header={Header} subMenuControl={_subMenuControl is not null}"); if (_subMenuControl is null) return; - if (_popupScrollViewer?.Content == _subMenuControl) + if (Equals(_popupScrollViewer?.Content, _subMenuControl)) _popupScrollViewer.Content = null; _subMenuControl.ItemsSource = null; diff --git a/src/PleasantUI/Controls/NavigationView/NavigationViewSubMenuControl.cs b/src/PleasantUI/Controls/NavigationView/NavigationViewSubMenuControl.cs index e9e6e9c7..5fbab056 100644 --- a/src/PleasantUI/Controls/NavigationView/NavigationViewSubMenuControl.cs +++ b/src/PleasantUI/Controls/NavigationView/NavigationViewSubMenuControl.cs @@ -2,8 +2,6 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; -using Avalonia.Layout; -using Avalonia.LogicalTree; using PleasantUI.Core.Extensions; namespace PleasantUI.Controls; @@ -15,8 +13,6 @@ namespace PleasantUI.Controls; /// public class NavigationViewSubMenuControl : ItemsControl { - private ItemsPresenter? _itemsPresenter; - /// /// Defines the property. /// @@ -43,10 +39,11 @@ public NavigationViewSubMenuControl() Classes.Add("navigationViewSubMenu"); } + /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - _itemsPresenter = e.NameScope.Find("PART_ItemsPresenter"); + e.NameScope.Find("PART_ItemsPresenter"); } /// diff --git a/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs b/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs index 8087cb89..02709c9e 100644 --- a/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs +++ b/src/PleasantUI/Controls/OptionsDisplayItem/OptionsDisplayItem.cs @@ -5,7 +5,6 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PathPicker/PathPicker.cs b/src/PleasantUI/Controls/PathPicker/PathPicker.cs index 36c1850a..5d457ae6 100644 --- a/src/PleasantUI/Controls/PathPicker/PathPicker.cs +++ b/src/PleasantUI/Controls/PathPicker/PathPicker.cs @@ -8,7 +8,6 @@ using Avalonia.Data; using Avalonia.Interactivity; using Avalonia.Layout; -using Avalonia.Logging; using Avalonia.Platform.Storage; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PleasantDatePicker/PleasantDatePicker.cs b/src/PleasantUI/Controls/PleasantDatePicker/PleasantDatePicker.cs index 784b1b1f..b5abfdcb 100644 --- a/src/PleasantUI/Controls/PleasantDatePicker/PleasantDatePicker.cs +++ b/src/PleasantUI/Controls/PleasantDatePicker/PleasantDatePicker.cs @@ -1,4 +1,3 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; diff --git a/src/PleasantUI/Controls/PleasantMenu/PleasantMenuFooterItem.cs b/src/PleasantUI/Controls/PleasantMenu/PleasantMenuFooterItem.cs index 07e67b6d..74ab980f 100644 --- a/src/PleasantUI/Controls/PleasantMenu/PleasantMenuFooterItem.cs +++ b/src/PleasantUI/Controls/PleasantMenu/PleasantMenuFooterItem.cs @@ -1,6 +1,5 @@ using System.Windows.Input; using Avalonia; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PleasantMenu/PleasantMenuItem.cs b/src/PleasantUI/Controls/PleasantMenu/PleasantMenuItem.cs index eccbb71d..e09fc427 100644 --- a/src/PleasantUI/Controls/PleasantMenu/PleasantMenuItem.cs +++ b/src/PleasantUI/Controls/PleasantMenu/PleasantMenuItem.cs @@ -1,6 +1,5 @@ using System.Windows.Input; using Avalonia; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PleasantModalWindow/PleasantPopupElement.cs b/src/PleasantUI/Controls/PleasantModalWindow/PleasantPopupElement.cs index 6051612b..5df3437d 100644 --- a/src/PleasantUI/Controls/PleasantModalWindow/PleasantPopupElement.cs +++ b/src/PleasantUI/Controls/PleasantModalWindow/PleasantPopupElement.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Primitives; -using PleasantUI.Core.Settings.Providers.Interfaces; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PleasantTabView/PleasantTabItem.cs b/src/PleasantUI/Controls/PleasantTabView/PleasantTabItem.cs index 79ce7bf5..95d8395f 100644 --- a/src/PleasantUI/Controls/PleasantTabView/PleasantTabItem.cs +++ b/src/PleasantUI/Controls/PleasantTabView/PleasantTabItem.cs @@ -1,4 +1,13 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2024 PieroCastillo + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/AuraTabItem/AuraTabItem.cs + */ + +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; @@ -11,9 +20,6 @@ namespace PleasantUI.Controls; /// /// An item in a . /// -/// -/// Reference: https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/AuraTabItem/AuraTabItem.cs -/// [TemplatePart("PART_CloseButton", typeof(Button))] public class PleasantTabItem : TabItem { diff --git a/src/PleasantUI/Controls/PleasantTabView/PleasantTabView.cs b/src/PleasantUI/Controls/PleasantTabView/PleasantTabView.cs index 67723070..a7fea2e8 100644 --- a/src/PleasantUI/Controls/PleasantTabView/PleasantTabView.cs +++ b/src/PleasantUI/Controls/PleasantTabView/PleasantTabView.cs @@ -1,4 +1,13 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2024 PieroCastillo + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/AuraTabView/AuraTabView.cs + */ + +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; @@ -10,9 +19,6 @@ namespace PleasantUI.Controls; /// /// A with a smooth scroll viewer and an additional button to add items. /// -/// -/// Reference: https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/AuraTabView/AuraTabView.cs -/// [TemplatePart("PART_ScrollViewer", typeof(ScrollViewer))] [TemplatePart("PART_AdderButton", typeof(Button))] [TemplatePart("PART_InternalGrid", typeof(Grid))] diff --git a/src/PleasantUI/Controls/PleasantTrayPopup/PleasantTrayPopup.cs b/src/PleasantUI/Controls/PleasantTrayPopup/PleasantTrayPopup.cs index bad792f1..8be3fa86 100644 --- a/src/PleasantUI/Controls/PleasantTrayPopup/PleasantTrayPopup.cs +++ b/src/PleasantUI/Controls/PleasantTrayPopup/PleasantTrayPopup.cs @@ -3,7 +3,6 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Media; -using System.Linq; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/PleasantWindow/PleasantWindowBase.cs b/src/PleasantUI/Controls/PleasantWindow/PleasantWindowBase.cs index fb61eda3..d25969be 100644 --- a/src/PleasantUI/Controls/PleasantWindow/PleasantWindowBase.cs +++ b/src/PleasantUI/Controls/PleasantWindow/PleasantWindowBase.cs @@ -4,7 +4,6 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Reactive; -using Avalonia.Platform; using Avalonia.Threading; using PleasantUI.Core; using PleasantUI.Core.Interfaces; diff --git a/src/PleasantUI/Controls/ProgressRing/ProgressRing.cs b/src/PleasantUI/Controls/ProgressRing/ProgressRing.cs index 054efb83..65c62be3 100644 --- a/src/PleasantUI/Controls/ProgressRing/ProgressRing.cs +++ b/src/PleasantUI/Controls/ProgressRing/ProgressRing.cs @@ -1,4 +1,13 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2023 ymg2006 + * SPDX-License-Identifier: MIT + * + * Modified from original source: + * https://github.com/ymg2006/FluentAvalonia.ProgressRing/blob/main/FluentAvalonia.ProgressRing/UI/Controls/ProgressRing/ProgressRing.cs + */ + +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; @@ -8,9 +17,6 @@ namespace PleasantUI.Controls; /// /// A control used to indicate the progress of an operation. /// -/// -/// Reference: https://github.com/ymg2006/FluentAvalonia.ProgressRing -/// [PseudoClasses(":preserveaspect", ":indeterminate")] public class ProgressRing : RangeBase { @@ -129,9 +135,9 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang base.OnPropertyChanged(change); if (change.Property == IsIndeterminateProperty) - PseudoClasses.Set(":indeterminate", change.NewValue as bool? ?? default); + PseudoClasses.Set(":indeterminate", change.NewValue as bool? ?? false); else if (change.Property == PreserveAspectProperty) - UpdatePseudoClasses(null, change.NewValue as bool? ?? default); + UpdatePseudoClasses(null, change.NewValue as bool? ?? false); } private static void OnValuePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e) diff --git a/src/PleasantUI/Controls/Ripple/Ripple.cs b/src/PleasantUI/Controls/Ripple/Ripple.cs index 13a2a214..ae39fdd9 100644 --- a/src/PleasantUI/Controls/Ripple/Ripple.cs +++ b/src/PleasantUI/Controls/Ripple/Ripple.cs @@ -1,13 +1,19 @@ -using Avalonia.Animation.Easings; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2023 AvaloniaCommunity + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/Ripple.cs + */ + +using Avalonia.Animation.Easings; namespace PleasantUI.Controls; /// /// Provides static properties for configuring ripple effects. /// -/// -/// Reference: https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/Ripple.cs -/// public static class Ripple { /// diff --git a/src/PleasantUI/Controls/Ripple/RippleEffect.cs b/src/PleasantUI/Controls/Ripple/RippleEffect.cs index 0942c542..057713f2 100644 --- a/src/PleasantUI/Controls/Ripple/RippleEffect.cs +++ b/src/PleasantUI/Controls/Ripple/RippleEffect.cs @@ -1,4 +1,13 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2025 AvaloniaCommunity + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/RippleEffect.cs + */ + +using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; @@ -11,9 +20,6 @@ namespace PleasantUI.Controls; /// /// A control that provides a ripple effect on pointer interaction. /// -/// -/// Reference: https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/RippleEffect.cs -/// public class RippleEffect : ContentControl { private CompositionContainerVisual? _container; @@ -144,7 +150,7 @@ protected override void OnSizeChanged(SizeChangedEventArgs e) if (newSize != default) { container.Size = newSize; - foreach (CompositionVisual? child in container.Children) child.Size = newSize; + foreach (CompositionVisual child in container.Children) child.Size = newSize; } } } diff --git a/src/PleasantUI/Controls/Ripple/RippleHandler.cs b/src/PleasantUI/Controls/Ripple/RippleHandler.cs index eef5cec0..fafffa09 100644 --- a/src/PleasantUI/Controls/Ripple/RippleHandler.cs +++ b/src/PleasantUI/Controls/Ripple/RippleHandler.cs @@ -1,13 +1,19 @@ -using Avalonia; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2025 AvaloniaCommunity + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/RippleHandler.cs + */ + +using Avalonia; using Avalonia.Animation.Easings; using Avalonia.Media; using Avalonia.Rendering.Composition; namespace PleasantUI.Controls; -/// -/// Reference: https://github.com/AvaloniaCommunity/Material.Avalonia/blob/master/Material.Ripple/RippleHandler.cs -/// internal class RippleHandler : CompositionCustomVisualHandler { public static readonly object FirstStepMessage = new(), SecondStepMessage = new(); diff --git a/src/PleasantUI/Controls/ShadowBorder/ShadowBorder.cs b/src/PleasantUI/Controls/ShadowBorder/ShadowBorder.cs index 56f0c4bc..993532ab 100644 --- a/src/PleasantUI/Controls/ShadowBorder/ShadowBorder.cs +++ b/src/PleasantUI/Controls/ShadowBorder/ShadowBorder.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Media; using Avalonia.Media.Imaging; -using Avalonia.Platform; using SkiaSharp; namespace PleasantUI.Controls; @@ -57,8 +56,13 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { base.OnPropertyChanged(change); + if (change.Property == OffsetProperty) + { + InvalidateVisual(); + return; + } + if (change.Property != ShadowColorProperty && - change.Property != BlurRadiusProperty && change.Property != OffsetProperty && change.Property != CornerRadiusProperty) return; @@ -113,7 +117,7 @@ private void EnsureShadowBitmap(Size contentSize) return; _shadowBitmap?.Dispose(); - _shadowBitmap = CreateShadowBitmap(contentSize, BlurRadius, ShadowColor, CornerRadius, Opacity); + _shadowBitmap = CreateShadowBitmap(contentSize, BlurRadius, ShadowColor, CornerRadius); _lastContentSize = contentSize; _lastBlur = BlurRadius; @@ -122,7 +126,7 @@ private void EnsureShadowBitmap(Size contentSize) _lastCorner = CornerRadius; } - private Bitmap CreateShadowBitmap(Size contentSizeDip, double blurDip, Color color, CornerRadius corner, double opacity) + private Bitmap CreateShadowBitmap(Size contentSizeDip, double blurDip, Color color, CornerRadius corner) { double scale = 1.0; if (TopLevel.GetTopLevel(this) is { } topLevel) @@ -169,15 +173,10 @@ private Bitmap CreateShadowBitmap(Size contentSizeDip, double blurDip, Color col canvas.DrawRect(rect, paint); } - IntPtr pixelsPtr = skBitmap.GetPixels(); - - return new Bitmap( - PixelFormat.Bgra8888, - AlphaFormat.Premul, - pixelsPtr, - new PixelSize(skBitmap.Width, skBitmap.Height), - new Vector(96.0 * scale, 96.0 * scale), - skBitmap.RowBytes); + using SKData data = skBitmap.Encode(SKEncodedImageFormat.Png, 100); + using Stream? stream = data.AsStream(); + + return new Bitmap(stream); } private static double ComputePad(double blur) diff --git a/src/PleasantUI/Controls/Snackbar/PleasantSnackbarOptions.cs b/src/PleasantUI/Controls/Snackbar/PleasantSnackbarOptions.cs index 902715fe..bcb87d47 100644 --- a/src/PleasantUI/Controls/Snackbar/PleasantSnackbarOptions.cs +++ b/src/PleasantUI/Controls/Snackbar/PleasantSnackbarOptions.cs @@ -1,6 +1,5 @@ using Avalonia.Controls; using Avalonia.Controls.Notifications; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs b/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs index 5dffcb96..dcf6c204 100644 --- a/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs +++ b/src/PleasantUI/Controls/SplashScreen/PleasantSplashScreen.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.Controls.Primitives; -using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs b/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs index d2b51067..7ad81212 100644 --- a/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs +++ b/src/PleasantUI/Controls/TreeViewPanel/TreeViewSection.cs @@ -4,7 +4,6 @@ using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Templates; using Avalonia.Media; namespace PleasantUI.Controls; diff --git a/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/RealizedWrappedElements.cs b/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/RealizedWrappedElements.cs index 3bf3432e..625a2951 100644 --- a/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/RealizedWrappedElements.cs +++ b/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/RealizedWrappedElements.cs @@ -1,4 +1,13 @@ -using Avalonia.Controls; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2023 afunc233 + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/RealizedWrappedElements.cs + */ + +using Avalonia.Controls; using Avalonia.Layout; using PleasantUI.Core.Extensions; @@ -9,9 +18,6 @@ namespace PleasantUI.Controls.Utils; /// in a stack layout, continuing on the next line when layout reaches the end, such as /// . /// -/// -/// Reference: https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/RealizedWrappedElements.cs -/// internal class RealizedWrappedElements { private List? _elements; diff --git a/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/UVSize.cs b/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/UVSize.cs index 4780ffda..91a44795 100644 --- a/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/UVSize.cs +++ b/src/PleasantUI/Controls/VirtualizingWrapPanel/Utils/UVSize.cs @@ -1,10 +1,16 @@ -using Avalonia.Layout; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2023 afunc233 + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/UVSize.cs + */ + +using Avalonia.Layout; namespace PleasantUI.Controls.Utils; -/// -/// Reference: https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/UVSize.cs -/// internal struct UvSize { internal UvSize(Orientation orientation, double width, double height) diff --git a/src/PleasantUI/Controls/VirtualizingWrapPanel/VirtualizingWrapPanel.cs b/src/PleasantUI/Controls/VirtualizingWrapPanel/VirtualizingWrapPanel.cs index f7343df6..242dec76 100644 --- a/src/PleasantUI/Controls/VirtualizingWrapPanel/VirtualizingWrapPanel.cs +++ b/src/PleasantUI/Controls/VirtualizingWrapPanel/VirtualizingWrapPanel.cs @@ -1,4 +1,13 @@ -using System.Collections.Specialized; +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2023 afunc233 + * SPDX-License-Identifier: MIT + * + * Derived from: + * https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/VirtualizingWarpPanel.cs + */ + +using System.Collections.Specialized; using System.Diagnostics; using Avalonia; using Avalonia.Controls; @@ -16,9 +25,6 @@ namespace PleasantUI.Controls; /// Subsequent ordering happens sequentially from top to bottom or from right to left, /// depending on the value of the property. /// -/// -/// Reference: https://github.com/afunc233/BilibiliClient/blob/599d7451e9187e7a967cbbb0cdbbd6a428493672/src/BilibiliClient/Controls/VirtualizingWarpPanel.cs -/// public class VirtualizingWrapPanel : VirtualizingPanel { private static readonly Rect InvalidViewport = new(double.PositiveInfinity, double.PositiveInfinity, 0, 0); diff --git a/src/PleasantUI/Converters/ProgressBarIndicatorConverter.cs b/src/PleasantUI/Converters/ProgressBarIndicatorConverter.cs index 49178cc0..dc3c1784 100644 --- a/src/PleasantUI/Converters/ProgressBarIndicatorConverter.cs +++ b/src/PleasantUI/Converters/ProgressBarIndicatorConverter.cs @@ -1,4 +1,3 @@ -using Avalonia.Data; using Avalonia.Data.Converters; using System.Globalization; diff --git a/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs b/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs index cdeb0603..40e0f7e3 100644 --- a/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs +++ b/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs @@ -3,13 +3,16 @@ * SPDX-FileCopyrightText: 2025 Egolds * SPDX-License-Identifier: MIT * - * SPDX-FileComment: A modified version of the VerticalScrollViewerAnimatedBehavior.cs component - * (original: https://github.com/Egolds/Xaml.Behaviors.Interactions.Animated/blob/master/Xaml.Behaviors.Interactions.Animated/ScrollViewer/VerticalScrollViewerAnimatedBehavior.cs) + * Modified from original source: + * https://github.com/Egolds/Xaml.Behaviors.Interactions.Animated/blob/master/Xaml.Behaviors.Interactions.Animated/ScrollViewer/VerticalScrollViewerAnimatedBehavior.cs + * * Changes: * 1. Renamed to ScrollViewerExtensions instead of VerticalScrollViewerAnimatedBehavior. * 2. Attached properties were used instead of Xaml.Behaviors.Interactivity. * 3. Smooth horizontal scrolling has been implemented. * 4. Instead of await Task.Delay(), DispatcherTimer was used. + * 5. Turning smooth scrolling on or off. + * 6. Possibility of horizontal scrolling using the mouse wheel. */ using Avalonia; @@ -22,7 +25,7 @@ using Avalonia.Layout; using Avalonia.Threading; using Avalonia.VisualTree; -using PleasantUI.Core.Extensions; +using PleasantUI.Core.Internal.Extensions; namespace PleasantUI.Core.Attached; @@ -66,6 +69,12 @@ public enum ScrollOrientation /// public static readonly AttachedProperty EnableAnimatedScrollProperty = AvaloniaProperty.RegisterAttached("EnableAnimatedScroll", typeof(ScrollViewerExtensions)); + + /// + /// Defines the HandleCustomScroll attached property. + /// + public static readonly AttachedProperty HandleCustomScrollProperty = + AvaloniaProperty.RegisterAttached("HandleCustomScroll", typeof(ScrollViewerExtensions)); /// /// Defines the ScrollStepSize attached property. @@ -104,6 +113,10 @@ public enum ScrollOrientation /// Original control /// Smooth scrolling is enabled public static void SetEnableAnimatedScroll(ScrollViewer obj, bool value) => obj.SetValue(EnableAnimatedScrollProperty, value); + + public static bool GetHandleCustomScroll(ScrollViewer obj) => obj.GetValue(HandleCustomScrollProperty); + + public static void SetHandleCustomScroll(ScrollViewer obj, bool value) => obj.SetValue(HandleCustomScrollProperty, value); /// /// Gets the scroll step size for the scroll type. @@ -149,10 +162,10 @@ public enum ScrollOrientation static ScrollViewerExtensions() { - EnableAnimatedScrollProperty.Changed.AddClassHandler(OnEnableAnimatedScrollChanged); + HandleCustomScrollProperty.Changed.AddClassHandler(OnHandleCustomScrollChanged); } - private static void OnEnableAnimatedScrollChanged(ScrollViewer sender, AvaloniaPropertyChangedEventArgs e) + private static void OnHandleCustomScrollChanged(ScrollViewer sender, AvaloniaPropertyChangedEventArgs e) { if (e.NewValue is true) { @@ -188,47 +201,12 @@ private static void ScrollViewer_Loaded(object? sender, RoutedEventArgs e) private static void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e) { - if (sender is not ScrollViewer scrollViewer) return; + if (sender is not ScrollViewer currentScrollViewer) return; - ScrollAnimationState? state = scrollViewer.GetValue(AnimationStateProperty); - if (state == null) return; - - state.ScrollContentPresenter ??= scrollViewer.Presenter as ScrollContentPresenter; - - ScrollContentPresenter? scp = state.ScrollContentPresenter; - if (scp == null) return; - - object? src = e.Source; - while (src != null && !Equals(src, scp)) - { - if (src is ScrollContentPresenter scp2) - { - if (scp2 == scp) break; - - bool isAtYMax = e.Delta.Y > 0 ? scp2.Offset.Y == 0 : Math.Abs(scp2.Offset.Y - (scp2.Extent.Height - scp2.Viewport.Height)) < 0.1; - bool isAtXMax = e.Delta.X > 0 ? scp2.Offset.X == 0 : Math.Abs(scp2.Offset.X - (scp2.Extent.Width - scp2.Viewport.Width)) < 0.1; - - if (isAtYMax && isAtXMax) - src = scp2.GetVisualParent(); - else - return; - } - else if (src is Visual visual) - { - src = visual.GetVisualParent(); - } - } - - if (!Equals(src, scp)) - { - e.Handled = !(src as ScrollContentPresenter)?.IsScrollChainingEnabled ?? false; - return; - } - double deltaX = e.Delta.X; double deltaY = e.Delta.Y; - - ScrollOrientation orientation = GetScrollOrientation(scrollViewer); + + ScrollOrientation orientation = GetScrollOrientation(currentScrollViewer); bool isShiftPressed = e.KeyModifiers.HasFlag(KeyModifiers.Shift); if (orientation == ScrollOrientation.Horizontal) @@ -250,50 +228,109 @@ private static void OnPointerWheelChanged(object? sender, PointerWheelEventArgs deltaY = 0; } } + + Vector realDelta = new(deltaX, deltaY); + + ScrollViewer? activeChildScrollViewer = null; + IEnumerable descendants = currentScrollViewer.GetVisualDescendants(); + foreach (Visual visual in descendants) + { + if (visual is ScrollViewer childScrollViewer && childScrollViewer != currentScrollViewer) + { + if (childScrollViewer.IsPointerOver && GetHandleCustomScroll(childScrollViewer)) + { + activeChildScrollViewer = childScrollViewer; + break; + } + } + } - double maxOffsetY = scp.Extent.Height - scp.Viewport.Height; - double maxOffsetX = scp.Extent.Width - scp.Viewport.Width; + if (activeChildScrollViewer?.Presenter is ScrollContentPresenter childScp) + { + if (CanScroll(childScp, realDelta)) + return; + } + + ScrollAnimationState? state = currentScrollViewer.GetValue(AnimationStateProperty); + if (state == null) return; + + state.ScrollContentPresenter ??= currentScrollViewer.Presenter as ScrollContentPresenter; + + ScrollContentPresenter? currentScp = state.ScrollContentPresenter; + if (currentScp == null) return; + + double maxOffsetY = currentScp.Extent.Height - currentScp.Viewport.Height; + double maxOffsetX = currentScp.Extent.Width - currentScp.Viewport.Width; - ILogicalScrollable? scrollable = scp.Child as ILogicalScrollable; + ILogicalScrollable? scrollable = currentScp.Child as ILogicalScrollable; bool isLogical = scrollable?.IsLogicalScrollEnabled == true; - double stepSize = GetScrollStepSize(scrollViewer); + double stepSize = GetScrollStepSize(currentScrollViewer); double stepX = 0; double stepY = 0; - if (deltaY != 0 && scp.Extent.Height > scp.Viewport.Height) + if (realDelta.Y != 0 && currentScp.Extent.Height > currentScp.Viewport.Height) { double height = isLogical ? scrollable!.ScrollSize.Height : stepSize; - double targetY = Math.Clamp(scp.Offset.Y + (-deltaY * height), 0, maxOffsetY); - Vector snapped = SnapOffset(scp, new Vector(scp.Offset.X, targetY), new Vector(0, deltaY), true); - stepY = snapped.Y - scp.Offset.Y; + double targetY = Math.Clamp(currentScp.Offset.Y + (-realDelta.Y * height), 0, maxOffsetY); + stepY = targetY - currentScp.Offset.Y; } - if (deltaX != 0 && scp.Extent.Width > scp.Viewport.Width) + if (realDelta.X != 0 && currentScp.Extent.Width > currentScp.Viewport.Width) { double width = isLogical ? scrollable!.ScrollSize.Width : stepSize; - double targetX = Math.Clamp(scp.Offset.X + (-deltaX * width), 0, maxOffsetX); - Vector snapped = SnapOffset(scp, new Vector(targetX, scp.Offset.Y), new Vector(deltaX, 0), true); - stepX = snapped.X - scp.Offset.X; + double targetX = Math.Clamp(currentScp.Offset.X + (-realDelta.X * width), 0, maxOffsetX); + stepX = targetX - currentScp.Offset.X; } if (stepX != 0 || stepY != 0) { - ChangeSize changeSize = GetScrollChangeSize(scrollViewer); + if (!GetEnableAnimatedScroll(currentScrollViewer) && activeChildScrollViewer != null) + { + if (activeChildScrollViewer.Presenter is ScrollContentPresenter childScp1 && CanScroll(childScp1, realDelta)) + return; + } + + ChangeSize changeSize = GetScrollChangeSize(currentScrollViewer); double finalDeltaX = stepX; double finalDeltaY = stepY; if (changeSize == ChangeSize.Page) { - if (deltaX != 0) finalDeltaX = deltaX > 0 ? -scrollViewer.Bounds.Width : scrollViewer.Bounds.Width; - if (deltaY != 0) finalDeltaY = deltaY > 0 ? -scrollViewer.Bounds.Height : scrollViewer.Bounds.Height; + if (realDelta.X != 0) finalDeltaX = realDelta.X > 0 ? -currentScrollViewer.Bounds.Width : currentScrollViewer.Bounds.Width; + if (realDelta.Y != 0) finalDeltaY = realDelta.Y > 0 ? -currentScrollViewer.Bounds.Height : currentScrollViewer.Bounds.Height; + } + + if (GetEnableAnimatedScroll(currentScrollViewer)) + state.AnimateScroll(finalDeltaX, finalDeltaY); + else + { + state.Reset(); + currentScrollViewer.Offset = new Vector( + Math.Clamp(currentScrollViewer.Offset.X + finalDeltaX, 0, maxOffsetX), + Math.Clamp(currentScrollViewer.Offset.Y + finalDeltaY, 0, maxOffsetY) + ); } - state.AnimateScroll(finalDeltaX, finalDeltaY); + e.Handled = true; } + else + { + if (!currentScp.IsScrollChainingEnabled) + e.Handled = true; + } + } + + private static bool CanScroll(ScrollContentPresenter scp, Vector delta) + { + const double epsilon = 0.5; + + if (delta.Y > 0 && scp.Offset.Y > epsilon) return true; + if (delta.Y < 0 && scp.Offset.Y < (scp.Extent.Height - scp.Viewport.Height) - epsilon) return true; + if (delta.X > 0 && scp.Offset.X > epsilon) return true; + if (delta.X < 0 && scp.Offset.X < (scp.Extent.Width - scp.Viewport.Width) - epsilon) return true; - bool offsetChanged = (stepX != 0 || stepY != 0); - e.Handled = !scp.IsScrollChainingEnabled || offsetChanged; + return false; } private static Vector SnapOffset(ScrollContentPresenter scp, Vector offset, Vector direction = default, bool snapToNext = false) @@ -433,15 +470,30 @@ public void AnimateScroll(double deltaX, double deltaY) Math.Max(0, _scrollViewer.Extent.Height - _scrollViewer.Bounds.Height) ); - if (_isAnimating) + if (!_isAnimating) { - double elapsedTime = (currentTime - _animationStartTime).TotalMilliseconds; - double progress = Math.Min(elapsedTime / AnimationDuration, 1.0); - - SineEaseOut easing = new(); - double easedProgress = easing.Ease(progress); + _startOffset = _scrollViewer.Offset; + _targetOffset = _startOffset; + } - _startOffset += easedProgress * (_targetOffset - _startOffset); + if (_isAnimating) + { + Vector currentDirection = _targetOffset - _startOffset; + + if ((deltaX > 0 && currentDirection.X < 0) || (deltaX < 0 && currentDirection.X > 0) || + (deltaY > 0 && currentDirection.Y < 0) || (deltaY < 0 && currentDirection.Y > 0)) + { + _startOffset = _scrollViewer.Offset; + _targetOffset = _startOffset; + } + else + { + double elapsedTime = (currentTime - _animationStartTime).TotalMilliseconds; + double progress = Math.Min(elapsedTime / AnimationDuration, 1.0); + SineEaseOut easing = new(); + _startOffset += easing.Ease(progress) * (_targetOffset - _startOffset); + } + _targetOffset = new Vector( Math.Clamp(_targetOffset.X + deltaX, 0, maxOffset.X), Math.Clamp(_targetOffset.Y + deltaY, 0, maxOffset.Y) diff --git a/src/PleasantUI/Core/Extensions/BinarySearchExtension.cs b/src/PleasantUI/Core/Extensions/BinarySearchExtension.cs deleted file mode 100644 index a7535a28..00000000 --- a/src/PleasantUI/Core/Extensions/BinarySearchExtension.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace PleasantUI.Core.Extensions; - -internal static class BinarySearchExtension - { - private static int GetMedian(int low, int hi) - { - System.Diagnostics.Debug.Assert(low <= hi); - System.Diagnostics.Debug.Assert(hi - low >= 0, "Length overflow!"); - return low + (hi - low >> 1); - } - - /// - /// Performs a binary search on the entire contents of an IReadOnlyList - /// - /// The list element type - /// The list to be searched - /// The value to search for - /// The comparer - /// The index of the found item; otherwise the bitwise complement of the index of the next larger item - public static int BinarySearch(this IReadOnlyList list, T value, IComparer comparer) - { - return list.BinarySearch(0, list.Count, value, comparer); - } - - /// - /// Performs a binary search on a subset of an IReadOnlyList - /// - /// The list element type - /// The list to be searched - /// The start of the range to be searched - /// The length of the range to be searched - /// The value to search for - /// A comparer - /// The index of the found item; otherwise the bitwise complement of the index of the next larger item - public static int BinarySearch(this IReadOnlyList list, int index, int length, T value, IComparer comparer) - { - // Based on this: https://referencesource.microsoft.com/#mscorlib/system/array.cs,957 - var lo = index; - var hi = index + length - 1; - while (lo <= hi) - { - var i = GetMedian(lo, hi); - var c = comparer.Compare(list[i], value); - if (c == 0) - return i; - if (c < 0) - { - lo = i + 1; - } - else - { - hi = i - 1; - } - } - return ~lo; - } - } \ No newline at end of file diff --git a/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs b/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs index 3189bfb2..66059277 100644 --- a/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs +++ b/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs @@ -140,15 +140,13 @@ string Resolve() // LocalizeKeyObservable fires PropertyChanged on every language change. // Use a reflection Binding — reliable for non-AvaloniaObject INPC sources. - var observable = new LocalizeKeyObservable(Resolve); - var binding = new Binding - { - Source = observable, - Path = nameof(LocalizeKeyObservable.Value), - Mode = BindingMode.OneWay, - FallbackValue = resolvedKey, - TargetNullValue = resolvedKey - }; + LocalizeKeyObservable observable = new(Resolve); + CompiledBinding binding = CompiledBinding.Create( + (LocalizeKeyObservable item) => item.Value, + observable, + mode: BindingMode.OneWay, + fallbackValue: resolvedKey, + targetNullValue: resolvedKey); if (_bindings is null || _bindings.Length <= 0) return binding; @@ -165,13 +163,11 @@ string Resolve() // Add a language-change trigger so the MultiBinding re-evaluates on every // language switch. Without this the converter only fires when the key binding // itself changes, so the UI stays frozen on the original language. - var langTrigger = new LocalizeKeyObservable(() => Localizer.Instance.CurrentLanguage ?? string.Empty); - var langBinding = new Binding - { - Source = langTrigger, - Path = nameof(LocalizeKeyObservable.Value), - Mode = BindingMode.OneWay - }; + LocalizeKeyObservable langTrigger = new(() => Localizer.Instance.CurrentLanguage); + CompiledBinding langBinding = CompiledBinding.Create( + (LocalizeKeyObservable item) => item.Value, + langTrigger, + mode: BindingMode.OneWay); BindingBase[] bindingBases = GetBindingsWithLang(binding, langBinding); return new MultiBinding diff --git a/src/PleasantUI/Core/GenerationContexts/PleasantSettingsGenerationContext.cs b/src/PleasantUI/Core/GenerationContexts/PleasantSettingsGenerationContext.cs index 5df53d4f..6ba3364d 100644 --- a/src/PleasantUI/Core/GenerationContexts/PleasantSettingsGenerationContext.cs +++ b/src/PleasantUI/Core/GenerationContexts/PleasantSettingsGenerationContext.cs @@ -4,5 +4,4 @@ namespace PleasantUI.Core.GenerationContexts; [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(PleasantSettings))] -[JsonSerializable(typeof(PleasantUI.Core.Settings.AppVersionSettings))] internal partial class PleasantSettingsGenerationContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/PleasantUI/Core/Internal/Extensions/BinarySearchExtension.cs b/src/PleasantUI/Core/Internal/Extensions/BinarySearchExtension.cs new file mode 100644 index 00000000..e1934dfe --- /dev/null +++ b/src/PleasantUI/Core/Internal/Extensions/BinarySearchExtension.cs @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Dmitry Zhutkov (Onebeld) + * SPDX-FileCopyrightText: 2020 Brad Robinson (toptensoftware) + * SPDX-License-Identifier: Apache-2.0 + * + * Derived from: + * https://github.com/toptensoftware/RichTextKit/blob/8038a9b252c03fc9ee4807e5db3817a3628626e0/Topten.RichTextKit/Utils/BinarySearchExtension.cs + */ + +namespace PleasantUI.Core.Internal.Extensions; + +internal static class BinarySearchExtension +{ + private static int GetMedian(int low, int hi) + { + System.Diagnostics.Debug.Assert(low <= hi); + System.Diagnostics.Debug.Assert(hi - low >= 0, "Length overflow!"); + return low + (hi - low >> 1); + } + + /// + /// Performs a binary search on the entire contents of an IReadOnlyList + /// + /// The list element type + /// The list to be searched + /// The value to search for + /// The comparer + /// The index of the found item; otherwise the bitwise complement of the index of the next larger item + public static int BinarySearch(this IReadOnlyList list, T value, IComparer comparer) + { + return list.BinarySearch(0, list.Count, value, comparer); + } + + /// + /// Performs a binary search on a subset of an IReadOnlyList + /// + /// The list element type + /// The list to be searched + /// The start of the range to be searched + /// The length of the range to be searched + /// The value to search for + /// A comparer + /// The index of the found item; otherwise the bitwise complement of the index of the next larger item + public static int BinarySearch(this IReadOnlyList list, int index, int length, T value, IComparer comparer) + { + // Based on this: https://referencesource.microsoft.com/#mscorlib/system/array.cs,957 + int lo = index; + int hi = index + length - 1; + while (lo <= hi) + { + int i = GetMedian(lo, hi); + int c = comparer.Compare(list[i], value); + if (c == 0) return i; + if (c < 0) + { + lo = i + 1; + } + else + { + hi = i - 1; + } + } + + return ~lo; + } +} \ No newline at end of file diff --git a/src/PleasantUI/Core/Localization/Localizer.cs b/src/PleasantUI/Core/Localization/Localizer.cs index 026224e1..69b72f7b 100644 --- a/src/PleasantUI/Core/Localization/Localizer.cs +++ b/src/PleasantUI/Core/Localization/Localizer.cs @@ -15,7 +15,7 @@ public class Localizer : ILocalizer, INotifyPropertyChanged private const string IndexerName = "Item"; private const string IndexerArrayName = "Item[]"; - private static readonly List? ResourceManagers = new(); + private static readonly List? ResourceManagers = []; // Strong references to all LocalizeKeyObservable instances — prevents GC from // collecting them and silently killing their LocalizationChanged subscriptions. @@ -56,20 +56,20 @@ public string this[string key] { get { - if (_resources == null || !_resources.Any()) + if (_resources == null || _resources.Count == 0) return ""; - string? row = GetExpression(key); + string row = GetExpression(key); if (string.IsNullOrEmpty(row)) return $""; - string? ret = row?.Replace(@"\\n", "\n"); + string ret = row.Replace(@"\\n", "\n"); if (string.IsNullOrEmpty(ret)) ret = $"Localize:{key}"; - return ret!; + return ret; } } @@ -101,6 +101,14 @@ public static string Tr(string? key, string? context = null, params object[] arg return string.Format(expression, args); } + /// + /// Translates the specified key. If the key is not found, the original text will be displayed. + /// + /// The key to look up. + /// Original text + /// The context of the translation. + /// The arguments to pass to the translation. + /// The translated string. public static string TrDefault(string? key, string? defaultString = null, string? context = null, params object[] args) { if (key is null) @@ -143,13 +151,13 @@ public static string TrDefault(string? key, string? defaultString = null, string /// True if the key is found, false otherwise. public bool TryGetString(string key, out string expression) { - if (_resources == null || !_resources.Any()) + if (_resources == null || _resources.Count == 0) { expression = ""; return false; } - string? row = GetExpression(key); + string row = GetExpression(key); if (string.IsNullOrEmpty(row)) { @@ -157,12 +165,12 @@ public bool TryGetString(string key, out string expression) return false; } - string? ret = row?.Replace(@"\\n", "\n"); + string ret = row.Replace(@"\\n", "\n"); if (string.IsNullOrEmpty(ret)) ret = $"Localize:{key}"; - expression = ret!; + expression = ret; return true; } @@ -256,7 +264,7 @@ public void ChangeLanguage(string language = "en") language = DefaultLanguage; Interlocked.Exchange(ref _isChangingLanguage, 1); - Debug.WriteLine($"[Localizer] ChangeLanguage → \"{language}\" (subscribers: {LocalizationChanged?.GetInvocationList().Length ?? 0}, observables: {AliveObservables.Count})"); + Debug.WriteLine($"[Localizer] ChangeLanguage → \"{language}\" (subscribers: {LocalizationChanged?.GetInvocationList().Length ?? 0})"); CultureInfo culture = new(language); CultureInfo.CurrentCulture = culture; @@ -291,16 +299,16 @@ public void ChangeLanguage(string language = "en") } /// - public string? GetExpression(string key) + public string GetExpression(string key) { if (_resources == null) return string.Empty; - foreach (ResourceManager? resource in _resources) + foreach (ResourceManager resource in _resources) { string? row; try { - row = resource?.GetString(key); + row = resource.GetString(key); } catch (MissingManifestResourceException) { diff --git a/src/PleasantUI/Core/PleasantSettings.cs b/src/PleasantUI/Core/PleasantSettings.cs index 9c683a6f..2292eb26 100644 --- a/src/PleasantUI/Core/PleasantSettings.cs +++ b/src/PleasantUI/Core/PleasantSettings.cs @@ -1,63 +1,16 @@ -using System.Reflection; -using System.Runtime.Serialization; +using System.Runtime.Serialization; using Avalonia.Collections; using PleasantUI.Core.Settings; namespace PleasantUI.Core; -/// -/// Represents the type of PleasantUI version. -/// -public enum PleasantVersionType -{ - /// - /// Stable release (e.g., 5.2.1) - /// - Stable, - - /// - /// Bug fix release (e.g., 5.2.1-fix) - /// - BugFix, - - /// - /// Alpha pre-release (e.g., 5.2.1-alpha) - /// - Alpha, - - /// - /// Beta pre-release (e.g., 5.2.1-beta) - /// - Beta, - - /// - /// Release candidate (e.g., 5.2.1-rc) - /// - ReleaseCandidate, - - /// - /// Canary build (e.g., 5.2.1-canary-20260410-161942) - /// - Canary -} - /// /// Represents the settings for the PleasantUI library. This class manages various settings related to themes, windows /// and accent colors. /// public class PleasantSettings : ViewModelBase { - private AvaloniaList _colorPalettes = []; - - private uint _numericalAccentColor; - private bool _preferUserAccentColor; - - private string _theme = "System"; - private Guid? _customThemeId; - private string _language = "en"; - private WindowSettings _windowSettings; - private AppVersionSettings _appVersion; /// /// Gets the singleton instance of the PleasantSettings class. @@ -65,64 +18,14 @@ public class PleasantSettings : ViewModelBase public static PleasantSettings? Current { get; set; } /// - /// Gets the version string from the assembly (numeric format). + /// Gets or sets whether effects that make the interface more visually appealing are enabled. /// - public static string Version => - Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Unknown"; - - /// - /// Gets the informational version from the assembly (includes pre-release tags). - /// Uses InformationalVersion which is set from PackageVersion in Package.props. - /// - public static string InformationalVersion => - Assembly.GetExecutingAssembly() - .GetCustomAttribute()?.InformationalVersion ?? Version; - - /// - /// Gets the type of the current PleasantUI version based on the version string. - /// - public static PleasantVersionType VersionType - { - get - { - string version = InformationalVersion; - - if (version.Contains("-canary-")) - return PleasantVersionType.Canary; - - if (version.EndsWith("-fix")) - return PleasantVersionType.BugFix; - - if (version.Contains("-alpha")) - return PleasantVersionType.Alpha; - - if (version.Contains("-beta")) - return PleasantVersionType.Beta; - - if (version.Contains("-rc")) - return PleasantVersionType.ReleaseCandidate; - - // If it contains a hyphen but none of the above, treat as generic pre-release - if (version.Contains("-")) - return PleasantVersionType.Alpha; - - return PleasantVersionType.Stable; - } - } - - /// - /// Gets a display-friendly description of the version type. - /// - public static string VersionTypeDescription => VersionType switch + [DataMember] + public bool EnableEffects { - PleasantVersionType.Stable => "Stable Release", - PleasantVersionType.BugFix => "Bug Fix Release", - PleasantVersionType.Alpha => "Alpha Pre-Release", - PleasantVersionType.Beta => "Beta Pre-Release", - PleasantVersionType.ReleaseCandidate => "Release Candidate", - PleasantVersionType.Canary => "Canary Build", - _ => "Unknown" - }; + get; + set => SetProperty(ref field, value); + } = true; /// /// Gets or sets the color in numerical form @@ -130,8 +33,8 @@ public static PleasantVersionType VersionType [DataMember] public uint NumericalAccentColor { - get => _numericalAccentColor; - set => SetProperty(ref _numericalAccentColor, value); + get; + set => SetProperty(ref field, value); } /// @@ -140,8 +43,8 @@ public uint NumericalAccentColor [DataMember] public bool PreferUserAccentColor { - get => _preferUserAccentColor; - set => SetProperty(ref _preferUserAccentColor, value); + get; + set => SetProperty(ref field, value); } /// @@ -150,9 +53,9 @@ public bool PreferUserAccentColor [DataMember] public string Theme { - get => _theme; - set => SetProperty(ref _theme, value); - } + get; + set => SetProperty(ref field, value); + } = "System"; /// /// Gets or sets the ID of the custom theme. @@ -160,18 +63,8 @@ public string Theme [DataMember] public Guid? CustomThemeId { - get => _customThemeId; - set => SetProperty(ref _customThemeId, value); - } - - /// - /// Gets or sets the current language code (e.g., "en", "ru"). - /// - [DataMember] - public string Language - { - get => _language; - set => SetProperty(ref _language, value); + get; + set => SetProperty(ref field, value); } /// @@ -196,29 +89,9 @@ public WindowSettings WindowSettings [DataMember] public AvaloniaList ColorPalettes { - get => _colorPalettes; - set => SetProperty(ref _colorPalettes, value); - } - - // ── App-specific version (separate from PleasantUI library version) ──────── - - /// - /// Gets or sets application-specific version information. - /// Use this to store and display your app's own version, release channel, - /// and version label — independently of the PleasantUI library version. - /// - [DataMember] - public AppVersionSettings AppVersion - { - get => _appVersion; - set - { - if (value is null) - throw new NullReferenceException("AppVersion is null"); - - SetProperty(ref _appVersion, value); - } - } + get; + set => SetProperty(ref field, value); + } = []; /// /// Initializes a new instance of the class @@ -227,6 +100,5 @@ public AppVersionSettings AppVersion public PleasantSettings() { _windowSettings = new WindowSettings(); - _appVersion = new AppVersionSettings(); } } \ No newline at end of file diff --git a/src/PleasantUI/Core/Settings/AppVersionSettings.cs b/src/PleasantUI/Core/Settings/AppVersionSettings.cs deleted file mode 100644 index 0f81638c..00000000 --- a/src/PleasantUI/Core/Settings/AppVersionSettings.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Runtime.Serialization; - -namespace PleasantUI.Core.Settings; - -/// -/// Stores application-specific version information inside . -/// Reuses for release-channel semantics while keeping -/// app versioning fully separate from the PleasantUI library version. -/// -public class AppVersionSettings : ViewModelBase -{ - private string _version = string.Empty; - private PleasantVersionType _versionType = PleasantVersionType.Stable; - private string _versionLabel = "Version"; - - /// - /// Gets or sets the application version string (e.g., "1.0.0", "2.3.1-beta"). - /// - [DataMember] - public string Version - { - get => _version; - set => SetProperty(ref _version, value); - } - - /// - /// Gets or sets the release channel / version type for the application. - /// Reuses — Stable, BugFix, Alpha, Beta, RC, Canary. - /// - [DataMember] - public PleasantVersionType VersionType - { - get => _versionType; - set - { - if (SetProperty(ref _versionType, value)) - RaisePropertyChanged(nameof(VersionTypeDescription)); - } - } - - /// - /// Gets or sets a custom label shown next to the version string (e.g., "Version", "App Version"). - /// Defaults to "Version". - /// - [DataMember] - public string VersionLabel - { - get => _versionLabel; - set => SetProperty(ref _versionLabel, value); - } - - /// - /// Gets a display-friendly description of . - /// Derived — not serialized. - /// - public string VersionTypeDescription => VersionType switch - { - PleasantVersionType.Stable => "Stable Release", - PleasantVersionType.BugFix => "Bug Fix Release", - PleasantVersionType.Alpha => "Alpha Pre-Release", - PleasantVersionType.Beta => "Beta Pre-Release", - PleasantVersionType.ReleaseCandidate => "Release Candidate", - PleasantVersionType.Canary => "Canary Build", - _ => "Unknown" - }; -} diff --git a/src/PleasantUI/PleasantTheme.axaml.cs b/src/PleasantUI/PleasantTheme.axaml.cs index 82c999a1..959ca327 100644 --- a/src/PleasantUI/PleasantTheme.axaml.cs +++ b/src/PleasantUI/PleasantTheme.axaml.cs @@ -304,11 +304,6 @@ private void PleasantSettingsOnPropertyChanged(object? sender, PropertyChangedEv _settingsProvider.Save(PleasantSettings.Current, Path.Combine(PleasantDirectories.Settings, PleasantFileNames.Settings)); break; - case nameof(PleasantSettings.Current.Language): - // Persist language changes immediately as well. - _settingsProvider.Save(PleasantSettings.Current, - Path.Combine(PleasantDirectories.Settings, PleasantFileNames.Settings)); - break; case nameof(PleasantSettings.Current.NumericalAccentColor): UpdateAccentColors(Color.FromUInt32(PleasantSettings.Current.NumericalAccentColor)); break; diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml index eb30e8a7..fd231e8a 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ListBox.axaml @@ -32,8 +32,8 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> + HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}" + VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"> diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml index dbf0afd6..149c72cf 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ScrollViewer.axaml @@ -20,7 +20,8 @@ - + + @@ -37,12 +38,13 @@ VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}" HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}" VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}" - Padding="{TemplateBinding Padding}"> + Padding="{TemplateBinding Padding}" + ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}"> + CanHorizontallyScroll="{Binding CanHorizontallyScroll, ElementName=PART_ContentPresenter}" + CanVerticallyScroll="{Binding CanVerticallyScroll, ElementName=PART_ContentPresenter}" + IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_ContentPresenter}" /> - + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/NavigationViewItem.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/NavigationViewItem.axaml index 9de00066..3ed5db23 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/NavigationViewItem.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/NavigationViewItem.axaml @@ -104,16 +104,14 @@ - + @@ -271,7 +269,7 @@ - - - diff --git a/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj b/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj index dc24231f..cdf33c89 100644 --- a/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj +++ b/samples/PleasantUI.Example.Desktop/PleasantUI.Example.Desktop.csproj @@ -19,13 +19,13 @@ - - - + + + - + diff --git a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml index 3781953a..734f673a 100644 --- a/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml +++ b/samples/PleasantUI.Example/DataTemplates/ControlPageCardItemTemplate.axaml @@ -18,7 +18,7 @@ CornerRadius="{DynamicResource ControlCornerRadius}" Background="{DynamicResource ControlFillColor2}" VerticalAlignment="Top"> - - + diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs index 62c17f2b..5ab0f51b 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs @@ -141,7 +141,7 @@ public async Task ShowCustomContent() Spacing = 8, Children = { - new IconControl() + new PleasantIcon() { Icon = CreateIcon(MaterialIconKind.InformationOutline), Width = 16, diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs index 03647843..65d324f4 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs @@ -66,7 +66,7 @@ public async Task ShowError() public async Task ShowSuccess() { - NoticeDialog dialog = new NoticeDialog + NoticeDialog dialog = new() { Title = T("SuccessTitle", "Success"), Message = T("SuccessMessage", "Your account has been created successfully. You can now log in."), @@ -86,14 +86,12 @@ public async Task ShowWorkInProgress() string message = T("WipStableMessage", "This is a stable release of the application. All features are fully tested and ready for production use."); - NoticeDialog dialog = new NoticeDialog + NoticeDialog dialog = new() { Title = T("WipTitle", "Development Version"), Message = message, NoticeFooterText = T("WipFooter", "- Development Team"), - PrimaryButtonText = T("Ok", "OK"), - Severity = NoticeSeverity.WorkInProgress, - VersionLabel = T("VersionLabel", "Version") + PrimaryButtonText = T("Ok", "OK") }; dialog.PrimaryButtonClicked += (_, _) => _ = dialog.CloseAsync(); diff --git a/samples/PleasantUI.Example/Views/AboutView.axaml b/samples/PleasantUI.Example/Views/AboutView.axaml index 7bfb1ab5..09b67244 100644 --- a/samples/PleasantUI.Example/Views/AboutView.axaml +++ b/samples/PleasantUI.Example/Views/AboutView.axaml @@ -13,12 +13,12 @@ Theme="{DynamicResource TitleTextBlockTheme}" /> - - + @@ -46,15 +46,15 @@ Margin="0 5 0 0" /> - + - - - - + - + + + + + + + + + + + + + + + diff --git a/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml b/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml index 1426feeb..fe1dd596 100644 --- a/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml +++ b/src/PleasantUI.ToolKit/ThemeEditorWindow.axaml @@ -3,9 +3,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:PleasantUI.ToolKit.ViewModels" - xmlns:models="clr-namespace:PleasantUI.Core.Models;assembly=PleasantUI" - xmlns:toolKit="clr-namespace:PleasantUI.ToolKit" - xmlns:userControls="clr-namespace:PleasantUI.ToolKit.UserControls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="PleasantUI.ToolKit.ThemeEditorWindow" Design.Height="600" @@ -20,9 +17,11 @@ + + @@ -62,7 +61,7 @@ - + @@ -179,14 +178,14 @@ IsChecked="True" GroupName="ColorsData" ToolTip.Tip="{Localize Colors, Default='Colors'}"> - + - + @@ -222,27 +221,8 @@ - - - - - - - + @@ -260,29 +240,8 @@ - - - - - - - + - + @@ -324,12 +283,12 @@ diff --git a/src/PleasantUI/Assets/IconGeometries.axaml b/src/PleasantUI/Assets/IconGeometries.axaml index 0d7334dc..b626f69d 100644 --- a/src/PleasantUI/Assets/IconGeometries.axaml +++ b/src/PleasantUI/Assets/IconGeometries.axaml @@ -45,7 +45,8 @@ M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z - + M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z + M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z M2,5.27L3.28,4L20,20.72L18.73,22L15.65,18.92C14.5,19.3 13.28,19.5 12,19.5C7,19.5 2.73,16.39 1,12C1.69,10.24 2.79,8.69 4.19,7.46L2,5.27M12,9A3,3 0 0,1 15,12C15,12.35 14.94,12.69 14.83,13L11,9.17C11.31,9.06 11.65,9 12,9M12,4.5C17,4.5 21.27,7.61 23,12C22.18,14.08 20.79,15.88 19,17.19L17.58,15.76C18.94,14.82 20.06,13.54 20.82,12C19.17,8.64 15.76,6.5 12,6.5C10.91,6.5 9.84,6.68 8.84,7L7.3,5.47C8.74,4.85 10.33,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C12.69,17.5 13.37,17.43 14,17.29L11.72,15C10.29,14.85 9.15,13.71 9,12.28L5.6,8.87C4.61,9.72 3.78,10.78 3.18,12Z \ No newline at end of file diff --git a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs index d75776bb..03afe089 100644 --- a/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs +++ b/src/PleasantUI/Controls/Chrome/PleasantTitleBar.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Reactive; using PleasantUI.Core.Internal.Reactive; using Path = Avalonia.Controls.Shapes.Path; @@ -69,8 +70,8 @@ public enum Type private Border? _dragWindowBorder; private MenuItem? _expandMenuItem; - private IconControl? _displayIcon; - private IconControl? _displayTitle; + private PleasantIcon? _displayIcon; + private PleasantIcon? _displayTitle; private ContentPresenter? _leftTitleBarContent; private MenuItem? _reestablishMenuItem; @@ -138,8 +139,8 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _collapseMenuItem = e.NameScope.Get("PART_CollapseMenuItem"); _reestablishMenuItem = e.NameScope.Get("PART_ReestablishMenuItem"); - _displayIcon = e.NameScope.Get("PART_DisplayIcon"); - _displayTitle = e.NameScope.Get("PART_DisplayTitle"); + _displayIcon = e.NameScope.Get("PART_DisplayIcon"); + _displayTitle = e.NameScope.Get("PART_DisplayTitle"); _subtitle = e.NameScope.Get("PART_Subtitle"); _titleBarGrid = e.NameScope.Get("PART_TitleBarGrid"); _dragWindowBorder = e.NameScope.Get("PART_DragWindow"); @@ -262,20 +263,6 @@ private void Attach(PleasantWindow host) }; } - private void OnDragWindowBorderOnPointerPressed(object? _, PointerPressedEventArgs args) - { - if (args.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - _host?.BeginMoveDrag(args); - } - - private void OnDragWindowBorderOnDoubleTapped(object? o, TappedEventArgs tappedEventArgs) - { - if (_host is null || !_host.CanResize) return; - _host.WindowState = _host.WindowState == WindowState.Maximized - ? WindowState.Normal - : WindowState.Maximized; - } - private void SetDisplayIcon(object? obj) { if (_displayIcon is null || obj is WindowIcon) diff --git a/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs b/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs index 4fcc5cf0..64c7a587 100644 --- a/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs +++ b/src/PleasantUI/Controls/ContentDialog/ContentDialog.cs @@ -25,10 +25,7 @@ public class ContentDialog : PleasantPopupElement, ICustomKeyboardNavigation private IInputElement? _lastFocus; private Border? _modalBackground; private Panel? _panel; - - private bool _isClosed; - private bool _isClosing; - + private AvaloniaList? _modalWindows; /// @@ -94,7 +91,7 @@ public object BottomPanelContent get => GetValue(BottomPanelContentProperty); set => SetValue(BottomPanelContentProperty, value); } - + /// /// Gets or sets a value indicating that the window has been closed. /// @@ -103,8 +100,8 @@ public object BottomPanelContent /// public bool IsClosed { - get => _isClosed; - set => SetAndRaise(IsClosedProperty, ref _isClosed, value); + get; + set => SetAndRaise(IsClosedProperty, ref field, value); } /// @@ -115,10 +112,10 @@ public bool IsClosed /// public bool IsClosing { - get => _isClosing; - set => SetAndRaise(IsClosingProperty, ref _isClosing, value); + get; + set => SetAndRaise(IsClosingProperty, ref field, value); } - + /// /// Set the animation when opening a modal window. /// @@ -297,7 +294,7 @@ private async Task CloseAsync(bool ignoreCancel) base.ShowCoreForTopLevel(topLevel); _modalWindows?.Add(this); - _lastFocus = topLevel?.FocusManager?.GetFocusedElement(); + _lastFocus = topLevel?.FocusManager.GetFocusedElement(); Closed += (_, _) => { @@ -373,7 +370,7 @@ protected override async void OnLoaded(RoutedEventArgs e) try { if (ShowBackgroundAnimation is not null && _modalBackground is not null) - await ShowBackgroundAnimation.RunAsync(_modalBackground); + _ = ShowBackgroundAnimation.RunAsync(_modalBackground); if (OpenAnimation is not null) await OpenAnimation.RunAsync(this); diff --git a/src/PleasantUI/Controls/ModalWindowHost/ModalWindowHost.cs b/src/PleasantUI/Controls/ModalWindowHost/ModalWindowHost.cs index 9761dd72..6a8b4937 100644 --- a/src/PleasantUI/Controls/ModalWindowHost/ModalWindowHost.cs +++ b/src/PleasantUI/Controls/ModalWindowHost/ModalWindowHost.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Layout; using Avalonia.Reactive; namespace PleasantUI.Controls; @@ -13,38 +12,6 @@ public class ModalWindowHost : ContentControl { private IDisposable? _rootBoundsWatcher; - /// - /// Defines the property. - /// - public static readonly StyledProperty HorizontalContentAlignmentProperty = - AvaloniaProperty.Register(nameof(HorizontalContentAlignment), HorizontalAlignment.Center); - - /// - /// Defines the property. - /// - public static readonly StyledProperty VerticalContentAlignmentProperty = - AvaloniaProperty.Register(nameof(VerticalContentAlignment), VerticalAlignment.Center); - - /// - /// Gets or sets the horizontal alignment of the content within the host. - /// This controls how the overlay is positioned in the OverlayLayer. - /// - public HorizontalAlignment HorizontalContentAlignment - { - get => GetValue(HorizontalContentAlignmentProperty); - set => SetValue(HorizontalContentAlignmentProperty, value); - } - - /// - /// Gets or sets the vertical alignment of the content within the host. - /// This controls how the overlay is positioned in the OverlayLayer. - /// - public VerticalAlignment VerticalContentAlignment - { - get => GetValue(VerticalContentAlignmentProperty); - set => SetValue(VerticalContentAlignmentProperty, value); - } - /// /// Initializes a new instance of the class. /// @@ -67,7 +34,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - if (e.Root is Control root) + if (e.RootVisual is Control root) _rootBoundsWatcher = root.GetObservable(BoundsProperty).Subscribe(new AnonymousObserver(_ => OnRootBoundsChanged())); } diff --git a/src/PleasantUI/Controls/IconControl/IconControl.cs b/src/PleasantUI/Controls/PleasantIcon/PleasantIcon.cs similarity index 74% rename from src/PleasantUI/Controls/IconControl/IconControl.cs rename to src/PleasantUI/Controls/PleasantIcon/PleasantIcon.cs index c917cafc..93d7a53b 100644 --- a/src/PleasantUI/Controls/IconControl/IconControl.cs +++ b/src/PleasantUI/Controls/PleasantIcon/PleasantIcon.cs @@ -5,16 +5,14 @@ namespace PleasantUI.Controls; -public class IconControl : TemplatedControl +public class PleasantIcon : TemplatedControl { - private DataTemplates? _dataTemplates; - public static readonly StyledProperty IconTemplateProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(IconTemplate)); public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon)); + AvaloniaProperty.Register(nameof(Icon)); public IDataTemplate? IconTemplate { diff --git a/src/PleasantUI/Controls/PleasantMenu/PleasantMenu.cs b/src/PleasantUI/Controls/PleasantMenu/PleasantMenu.cs index d546bcde..af3b37d5 100644 --- a/src/PleasantUI/Controls/PleasantMenu/PleasantMenu.cs +++ b/src/PleasantUI/Controls/PleasantMenu/PleasantMenu.cs @@ -125,7 +125,7 @@ private Button BuildItemButton(PleasantMenuItem item) if (item.SecondaryCommand is not null) return BuildSplitButton(item); - var icon = new IconControl() + var icon = new PleasantIcon() { Width = 20, Height = 20, @@ -171,7 +171,7 @@ private Button BuildItemButton(PleasantMenuItem item) private Button BuildSplitButton(PleasantMenuItem item) { - var icon = new IconControl() { Width = 20, Height = 20, Icon = item.Icon }; + var icon = new PleasantIcon() { Width = 20, Height = 20, Icon = item.Icon }; var mainBtn = new Button { @@ -252,7 +252,7 @@ private void RebuildFooter() foreach (var fi in _footerItems) { - var icon = new IconControl() { Width = 16, Height = 16, Icon = fi.Icon }; + var icon = new PleasantIcon() { Width = 16, Height = 16, Icon = fi.Icon }; var btn = new Button { diff --git a/src/PleasantUI/Converters/ColorToTransparentConverter.cs b/src/PleasantUI/Converters/ColorToTransparentConverter.cs index 9feddd67..559f7147 100644 --- a/src/PleasantUI/Converters/ColorToTransparentConverter.cs +++ b/src/PleasantUI/Converters/ColorToTransparentConverter.cs @@ -5,24 +5,20 @@ namespace PleasantUI.Converters; -/// -/// Converts a color to a transparent color by setting its alpha channel to zero. -/// -public class ColorToTransparentConverter : IMultiValueConverter +public class ColorToTransparentConverter : IValueConverter { - /// - /// Converts a color to a transparent brush. - /// - /// An array where the first element is expected to be a . - /// The target type of the binding (not used). - /// An optional parameter (not used). - /// The culture to use in the converter (not used). - /// A with a transparent version of the input color, or if the conversion fails. - public object Convert(IList values, Type targetType, object? parameter, CultureInfo culture) + public static readonly ColorToTransparentConverter Instance = new(); + + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - if (values[0] is Color color) + if (value is Color color) return new SolidColorBrush(Color.FromArgb(0, color.R, color.G, color.B)); return AvaloniaProperty.UnsetValue; } -} + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/PleasantUI/Converters/ColorToTransparentMultiConverter.cs b/src/PleasantUI/Converters/ColorToTransparentMultiConverter.cs new file mode 100644 index 00000000..2d96d979 --- /dev/null +++ b/src/PleasantUI/Converters/ColorToTransparentMultiConverter.cs @@ -0,0 +1,30 @@ +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace PleasantUI.Converters; + +/// +/// Converts a color to a transparent color by setting its alpha channel to zero. +/// +public class ColorToTransparentMultiConverter : IMultiValueConverter +{ + public static readonly ColorToTransparentMultiConverter Instance = new(); + + /// + /// Converts a color to a transparent brush. + /// + /// An array where the first element is expected to be a . + /// The target type of the binding (not used). + /// An optional parameter (not used). + /// The culture to use in the converter (not used). + /// A with a transparent version of the input color, or if the conversion fails. + public object Convert(IList values, Type targetType, object? parameter, CultureInfo culture) + { + if (values[0] is Color color) + return new SolidColorBrush(Color.FromArgb(0, color.R, color.G, color.B)); + + return AvaloniaProperty.UnsetValue; + } +} diff --git a/src/PleasantUI/Converters/ForegroundBasedAccentConverter.cs b/src/PleasantUI/Converters/ForegroundBasedAccentConverter.cs index 19c64484..536c3862 100644 --- a/src/PleasantUI/Converters/ForegroundBasedAccentConverter.cs +++ b/src/PleasantUI/Converters/ForegroundBasedAccentConverter.cs @@ -11,6 +11,8 @@ namespace PleasantUI.Converters; /// public class ForegroundBasedAccentConverter : IMultiValueConverter { + public static readonly ForegroundBasedAccentConverter Instance = new(); + /// /// Determines an appropriate accent color (black or white) based on the luminance of the input color. /// diff --git a/src/PleasantUI/Converters/TranslateConverter.cs b/src/PleasantUI/Converters/TranslateConverter.cs index 7f14459d..73e2efb8 100644 --- a/src/PleasantUI/Converters/TranslateConverter.cs +++ b/src/PleasantUI/Converters/TranslateConverter.cs @@ -10,6 +10,8 @@ namespace PleasantUI.Converters; /// public class TranslateConverter : IMultiValueConverter { + public static readonly TranslateConverter Instance = new(); + /// /// Converts a list of values to a translated string. /// diff --git a/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs b/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs index 40e0f7e3..93ea1e65 100644 --- a/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs +++ b/src/PleasantUI/Core/Attached/ScrollViewerExtensions.cs @@ -114,8 +114,18 @@ public enum ScrollOrientation /// Smooth scrolling is enabled public static void SetEnableAnimatedScroll(ScrollViewer obj, bool value) => obj.SetValue(EnableAnimatedScrollProperty, value); + /// + /// Gets whether custom behavior is enabled for the . + /// + /// Original control + /// Custom behavior is enabled public static bool GetHandleCustomScroll(ScrollViewer obj) => obj.GetValue(HandleCustomScrollProperty); + /// + /// Sets whether custom behavior is enabled for the . + /// + /// Original control + /// Custom behavior is enabled public static void SetHandleCustomScroll(ScrollViewer obj, bool value) => obj.SetValue(HandleCustomScrollProperty, value); /// @@ -333,103 +343,6 @@ private static bool CanScroll(ScrollContentPresenter scp, Vector delta) return false; } - private static Vector SnapOffset(ScrollContentPresenter scp, Vector offset, Vector direction = default, bool snapToNext = false) - { - IScrollSnapPointsInfo? scrollable = GetScrollSnapPointsInfo(scp); - if (scrollable == null || scp.VerticalSnapPointsType == SnapPointsType.None) - return offset; - - Vector diff = GetAlignmentDiff(scp); - bool areVerticalSnapPointsRegular = scrollable.AreVerticalSnapPointsRegular; - IReadOnlyList? verticalSnapPoints = null; - double verticalSnapPoint = 0; - double verticalSnapPointOffset = 0; - - if (!areVerticalSnapPointsRegular) - verticalSnapPoints = - scrollable.GetIrregularSnapPoints(Orientation.Vertical, scp.VerticalSnapPointsAlignment); - else - verticalSnapPoint = scrollable.GetRegularSnapPoints(Orientation.Vertical, scp.VerticalSnapPointsAlignment, - out verticalSnapPointOffset); - - if ((!areVerticalSnapPointsRegular && (!(verticalSnapPoints?.Count > 0))) || (snapToNext && direction.Y == 0)) - return offset; - - Vector estimatedOffset = new(offset.X, offset.Y + diff.Y); - double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0; - - if (areVerticalSnapPointsRegular) - { - previousSnapPoint = (int)(estimatedOffset.Y / verticalSnapPoint) * verticalSnapPoint + - verticalSnapPointOffset; - nextSnapPoint = previousSnapPoint + verticalSnapPoint; - midPoint = (previousSnapPoint + nextSnapPoint) / 2; - } - else if (verticalSnapPoints?.Count > 0) - { - (previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(verticalSnapPoints, estimatedOffset.Y); - midPoint = (previousSnapPoint + nextSnapPoint) / 2; - } - - double nearestSnapPoint = snapToNext - ? (direction.Y > 0 ? previousSnapPoint : nextSnapPoint) - : estimatedOffset.Y < midPoint - ? previousSnapPoint - : nextSnapPoint; - - offset = new Vector(offset.X, nearestSnapPoint - diff.Y); - - return offset; - } - - private static IScrollSnapPointsInfo? GetScrollSnapPointsInfo(ScrollContentPresenter scp) - { - object? scrollable = scp.Content switch - { - ItemsControl itemsControl => itemsControl.Presenter?.Panel, - ItemsPresenter itemsPresenter => itemsPresenter.Panel, - _ => scp.Content - }; - - return scrollable as IScrollSnapPointsInfo; - } - - private static Vector GetAlignmentDiff(ScrollContentPresenter scp) - { - Vector vector = default; - switch (scp.VerticalSnapPointsAlignment) - { - case SnapPointsAlignment.Center: vector += new Vector(0, scp.Viewport.Height / 2); break; - case SnapPointsAlignment.Far: vector += new Vector(0, scp.Viewport.Height); break; - } - - switch (scp.HorizontalSnapPointsAlignment) - { - case SnapPointsAlignment.Center: vector += new Vector(scp.Viewport.Width / 2, 0); break; - case SnapPointsAlignment.Far: vector += new Vector(scp.Viewport.Width, 0); break; - } - - return vector; - } - - private static (double previous, double next) FindNearestSnapPoint(IReadOnlyList snapPoints, double value) - { - int point = snapPoints.BinarySearch(value, Comparer.Default); - double previousSnapPoint, nextSnapPoint; - if (point < 0) - { - point = ~point; - previousSnapPoint = snapPoints[Math.Max(0, point - 1)]; - nextSnapPoint = point >= snapPoints.Count ? snapPoints[^1] : snapPoints[Math.Max(0, point)]; - } - else - { - previousSnapPoint = nextSnapPoint = snapPoints[Math.Max(0, point)]; - } - - return (previousSnapPoint, nextSnapPoint); - } - private class ScrollAnimationState { private const double AnimationDuration = 100; diff --git a/src/PleasantUI/Core/DataTemplates/IconTemplateSelector.cs b/src/PleasantUI/Core/DataTemplates/IconTemplateSelector.cs index 032bc479..bd5fb9c4 100644 --- a/src/PleasantUI/Core/DataTemplates/IconTemplateSelector.cs +++ b/src/PleasantUI/Core/DataTemplates/IconTemplateSelector.cs @@ -8,38 +8,31 @@ namespace PleasantUI.Core.DataTemplates; public class IconTemplateSelector : IDataTemplate { [Content] - public Dictionary AvailableTemplates { get; } = new Dictionary(); + public Dictionary AvailableTemplates { get; } = new(); - // Метод Match вызывается Avalonia автоматически для каждого шаблона в коллекции. - // Мы просто используем базовую проверку типов, которая уже заложена в DataTemplates. + /// public bool Match(object? data) { if (data == null) return false; - // Проверяем, есть ли внутри нашей коллекции шаблон, - // у которого DataType совпадает с типом пришедшего Enum (или объекта) - foreach (var template in AvailableTemplates.Values) - { - if (template.Match(data)) - { - return true; - } - } - - return false; + if (AvailableTemplates.Values.Any(template => template.Match(data))) + return true; + + return data is Control; } + /// public Control? Build(object? param) { if (param == null) return null; - var element = AvailableTemplates.Values.FirstOrDefault(item => item.Match(param)); + IDataTemplate? element = AvailableTemplates.Values.FirstOrDefault(item => item.Match(param)); - // Извлекаем объект и приводим его к IDataTemplate во время выполнения if (element != null) - { return element.Build(param); - } + + if (param is Control control) + return control; return new TextBlock { Text = param.ToString() }; } diff --git a/src/PleasantUI/Core/Extensions/Markup/ColorToTransparentExtension.cs b/src/PleasantUI/Core/Extensions/Markup/ColorToTransparentExtension.cs index b26b5b71..8ede2ec1 100644 --- a/src/PleasantUI/Core/Extensions/Markup/ColorToTransparentExtension.cs +++ b/src/PleasantUI/Core/Extensions/Markup/ColorToTransparentExtension.cs @@ -46,7 +46,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) MultiBinding multiBinding = new() { Bindings = { extension }, - Converter = new ColorToTransparentConverter() + Converter = ColorToTransparentMultiConverter.Instance }; return multiBinding; diff --git a/src/PleasantUI/Core/Extensions/Markup/ForegroundBasedAccentExtension.cs b/src/PleasantUI/Core/Extensions/Markup/ForegroundBasedExtension.cs similarity index 84% rename from src/PleasantUI/Core/Extensions/Markup/ForegroundBasedAccentExtension.cs rename to src/PleasantUI/Core/Extensions/Markup/ForegroundBasedExtension.cs index c13a3178..59dae69d 100644 --- a/src/PleasantUI/Core/Extensions/Markup/ForegroundBasedAccentExtension.cs +++ b/src/PleasantUI/Core/Extensions/Markup/ForegroundBasedExtension.cs @@ -8,7 +8,7 @@ namespace PleasantUI.Core.Extensions.Markup; /// /// A markup extension that provides a foreground-based accent color using a resource key. /// -public class ForegroundBasedAccentExtension : MarkupExtension +public class ForegroundBasedExtension : MarkupExtension { /// /// Gets or sets the resource key used to retrieve the accent color. @@ -16,17 +16,17 @@ public class ForegroundBasedAccentExtension : MarkupExtension public object? ResourceKey { get; set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ForegroundBasedAccentExtension() + public ForegroundBasedExtension() { } /// - /// Initializes a new instance of the class with a specified resource key. + /// Initializes a new instance of the class with a specified resource key. /// /// The resource key used to retrieve the accent color. - public ForegroundBasedAccentExtension(object resourceKey) + public ForegroundBasedExtension(object resourceKey) { ResourceKey = resourceKey ?? throw new ArgumentNullException(nameof(resourceKey), "ResourceKey cannot be null."); } @@ -47,7 +47,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) MultiBinding multiBinding = new() { Bindings = { extension }, - Converter = new ForegroundBasedAccentConverter() + Converter = ForegroundBasedAccentConverter.Instance }; return multiBinding; diff --git a/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs b/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs index 66059277..c2c9329c 100644 --- a/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs +++ b/src/PleasantUI/Core/Extensions/Markup/LocalizeExtension.cs @@ -155,7 +155,7 @@ string Resolve() return new MultiBinding { Bindings = bindingBases, - Converter = new TranslateConverter() + Converter = TranslateConverter.Instance }; } else if (Key is BindingBase binding) diff --git a/src/PleasantUI/PleasantUI.csproj b/src/PleasantUI/PleasantUI.csproj index ad915a45..7de113d9 100644 --- a/src/PleasantUI/PleasantUI.csproj +++ b/src/PleasantUI/PleasantUI.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml index 8479de99..37a1fe7f 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/Button.axaml @@ -72,7 +72,7 @@ - + \ No newline at end of file diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ButtonSpinner.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ButtonSpinner.axaml index 25bbea04..88382df5 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ButtonSpinner.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ButtonSpinner.axaml @@ -21,7 +21,7 @@ - + @@ -50,15 +50,6 @@ VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> - + + - + + + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml index 3ab84c3b..d0ecbe4b 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/FlyoutPresenter.axaml @@ -29,24 +29,6 @@ BlurRadius="5" Offset="0 1" ShadowColor="#3C000000"> - - - - - - + @@ -34,6 +34,7 @@ @@ -106,7 +106,7 @@ - - - @@ -101,7 +117,7 @@ BorderBrush="{TemplateBinding BorderBrush}"> diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml index 63abec65..bbd768e5 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/TextBox.axaml @@ -24,10 +24,10 @@ - - - + + + @@ -137,15 +137,15 @@ CornerRadius="{DynamicResource RoundedControlCornerRadius}" IsChecked="{CompiledBinding $parent[TextBox].RevealPassword, Mode=TwoWay}"> - - - + @@ -159,9 +159,7 @@ Margin="5" CornerRadius="{DynamicResource RoundedControlCornerRadius}" Command="{CompiledBinding $parent[TextBox].Clear}"> - + diff --git a/src/PleasantUI/Styling/ControlThemes/BasicControls/ToggleButton.axaml b/src/PleasantUI/Styling/ControlThemes/BasicControls/ToggleButton.axaml index e19e6440..fb0c25f4 100644 --- a/src/PleasantUI/Styling/ControlThemes/BasicControls/ToggleButton.axaml +++ b/src/PleasantUI/Styling/ControlThemes/BasicControls/ToggleButton.axaml @@ -15,7 +15,7 @@ - + - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml index a82f16d9..8e3f6b27 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/CommandBar.axaml @@ -129,7 +129,7 @@ HorizontalAlignment="Center"> - + @@ -155,7 +155,7 @@ Margin="12 0 0 0" VerticalAlignment="Center" IsVisible="False"> - + @@ -282,7 +282,7 @@ Height="{DynamicResource CommandBarButtonIconSize}" Margin="11 0" HorizontalAlignment="Center"> - + @@ -308,7 +308,7 @@ Margin="12 0 0 0" VerticalAlignment="Center" IsVisible="False"> - + @@ -474,7 +474,7 @@ Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" Height="{DynamicResource CommandBarCompactHeight}"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml index b9617ab3..49a95431 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/ContentDialog.axaml @@ -28,6 +28,9 @@ + + + @@ -44,18 +47,18 @@ - + BorderThickness="{TemplateBinding BorderThickness}"> diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml deleted file mode 100644 index 59c5fc9a..00000000 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/IconControl.axaml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/InformationBlock.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/InformationBlock.axaml index 05c2a649..fdba3886 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/InformationBlock.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/InformationBlock.axaml @@ -22,7 +22,7 @@ BorderThickness="1" CornerRadius="{TemplateBinding CornerRadius}"> - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/InstallWizard.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/InstallWizard.axaml index af0a93c1..e747a3e1 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/InstallWizard.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/InstallWizard.axaml @@ -50,7 +50,7 @@ - - + @@ -45,6 +45,7 @@ DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" ButtonSpinnerLocation="{TemplateBinding ButtonSpinnerLocation}"> + + + + + @@ -17,8 +22,10 @@ - - + + + + @@ -26,6 +33,12 @@ + + + + + + @@ -66,7 +79,7 @@ Grid.Column="0" Background="{DynamicResource ControlFillColor2}" Width="35"> - + + + @@ -124,7 +142,6 @@ - - - - - - @@ -371,7 +371,7 @@ - @@ -417,7 +417,7 @@ HorizontalAlignment="Center" CornerRadius="4" Background="{DynamicResource AccentLightColor2}" /> - - @@ -470,7 +470,7 @@ - - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/OptionsDisplayItem.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/OptionsDisplayItem.axaml index ccdafb27..77d28bd9 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/OptionsDisplayItem.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/OptionsDisplayItem.axaml @@ -47,7 +47,7 @@ ClipToBounds="True"> - - - @@ -210,7 +210,7 @@ - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml index ceafa5c5..e0b09831 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantCaptionButtons.axaml @@ -22,64 +22,57 @@ - - - - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml index 2dd152aa..88b55c17 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantDrawer.axaml @@ -45,7 +45,7 @@ ClipToBounds="True"> - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml index 47b3cf54..0c284b9c 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantMiniWindow.axaml @@ -46,7 +46,7 @@ Theme="{DynamicResource AppBarToggleButtonTheme}" IsChecked="{CompiledBinding $parent[PleasantMiniWindow].Topmost}" ToolTip.Tip="{Localize AttachOverWindows, Default='Attach over windows'}"> - + - + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml index 228d5067..ef3e12c0 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PleasantSplashScreen.axaml @@ -31,7 +31,7 @@ Width="96" Height="96" Stretch="Uniform" IsVisible="{TemplateBinding AppIcon, Converter={x:Static ObjectConverters.IsNotNull}}" />--> - - + - - - - + + + + - - diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml index eb07a3c6..d2f48aa9 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/PopConfirm.axaml @@ -80,7 +80,7 @@ Padding="5" Margin="8 0 0 0" VerticalAlignment="Top"> - + diff --git a/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml b/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml index c7899612..d8d90945 100644 --- a/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml +++ b/src/PleasantUI/Styling/ControlThemes/PleasantControls/TreeViewPanel.axaml @@ -34,7 +34,7 @@ CornerRadius="{DynamicResource ControlCornerRadius}" Background="{TemplateBinding IconBackground}" IsVisible="{TemplateBinding SectionIcon, Converter={x:Static ObjectConverters.IsNotNull}}"> - - - @@ -203,7 +203,7 @@ Padding="6" ToolTip.Tip="Expand all" IsVisible="{TemplateBinding ShowExpandButton}"> - @@ -215,7 +215,7 @@ Padding="6" ToolTip.Tip="Collapse all" IsVisible="{TemplateBinding ShowCollapseButton}"> - diff --git a/src/PleasantUI/Styling/PleasantThemes.axaml b/src/PleasantUI/Styling/PleasantThemes.axaml index 5a088a2e..7b9acd80 100644 --- a/src/PleasantUI/Styling/PleasantThemes.axaml +++ b/src/PleasantUI/Styling/PleasantThemes.axaml @@ -173,20 +173,20 @@ #DFE0B8B8 #ABE0B8B8 - #4C69242F - #40993D4D + #4C802B39 + #59993D4D #B2802D3A - #C9B63269 - #C9C83774 - #C9DE3D81 + #C9802249 + #C98C2651 + #C9A62D5F #59C41B51 #FF2E1015 - #FF4A1A22 - #FF571E28 - #FF692430 + #FF3B151B + #FF45191F + #FF521D24 #C42B1C #0F7B0F diff --git a/src/PleasantUI/Styling/VGUIControlThemes.axaml b/src/PleasantUI/Styling/VGUIControlThemes.axaml index a95c2e20..e0a5b3cb 100644 --- a/src/PleasantUI/Styling/VGUIControlThemes.axaml +++ b/src/PleasantUI/Styling/VGUIControlThemes.axaml @@ -104,7 +104,7 @@ - + - - - - - diff --git a/samples/PleasantUI.Example/ViewModels/AppViewModel.cs b/samples/PleasantUI.Example/ViewModels/AppViewModel.cs index 07ed41e1..78461108 100644 --- a/samples/PleasantUI.Example/ViewModels/AppViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/AppViewModel.cs @@ -13,18 +13,11 @@ namespace PleasantUI.Example.ViewModels; public class AppViewModel : ViewModelBase { - private readonly IEventAggregator _eventAggregator; private readonly ControlPageCardsFactory _factory; - private IPage _page = null!; - private bool _isForwardAnimation = true; - // Localized header strings — updated directly on language change so // {CompiledBinding} in HomePageView always gets the correct value. private string _welcomeText = string.Empty; - private string _basicControlsText = string.Empty; - private string _pleasantControlsText = string.Empty; - private string _toolKitText = string.Empty; public AvaloniaList BasicControlPageCards { get; } = []; public AvaloniaList PleasantControlPageCards { get; } = []; @@ -32,15 +25,15 @@ public class AppViewModel : ViewModelBase public IPage Page { - get => _page; - set => SetProperty(ref _page, value); + get; + set => SetProperty(ref field, value); } public bool IsForwardAnimation { - get => _isForwardAnimation; - set => SetProperty(ref _isForwardAnimation, value); - } + get; + set => SetProperty(ref field, value); + } = true; public string WelcomeText { @@ -50,25 +43,24 @@ public string WelcomeText public string BasicControlsText { - get => _basicControlsText; - private set => SetProperty(ref _basicControlsText, value); - } + get; + private set => SetProperty(ref field, value); + } = string.Empty; public string PleasantControlsText { - get => _pleasantControlsText; - private set => SetProperty(ref _pleasantControlsText, value); - } + get; + private set => SetProperty(ref field, value); + } = string.Empty; public string ToolKitText { - get => _toolKitText; - private set => SetProperty(ref _toolKitText, value); - } + get; + private set => SetProperty(ref field, value); + } = string.Empty; public AppViewModel(IEventAggregator eventAggregator) { - _eventAggregator = eventAggregator; _factory = new ControlPageCardsFactory(eventAggregator); BasicControlPageCards.AddRange(_factory.CreateBasicControlPageCards()); @@ -77,7 +69,7 @@ public AppViewModel(IEventAggregator eventAggregator) Page = new HomePage(); - _eventAggregator.Subscribe(async message => + eventAggregator.Subscribe(async message => { ChangePage(message.Page); await Task.CompletedTask; @@ -103,9 +95,9 @@ private void Rebuild() RefreshLocalizedTexts(); // Rebuild card collections with fresh instances that read the new language - var newBasic = _factory.CreateBasicControlPageCards().ToList(); - var newPleasant = _factory.CreatePleasantControlPageCards().ToList(); - var newToolkit = _factory.CreateToolkitControlPageCards().ToList(); + List newBasic = _factory.CreateBasicControlPageCards().ToList(); + List newPleasant = _factory.CreatePleasantControlPageCards().ToList(); + List newToolkit = _factory.CreateToolkitControlPageCards().ToList(); BasicControlPageCards.Clear(); PleasantControlPageCards.Clear(); diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/DataGridViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/DataGridViewModel.cs index 4e4dc6ca..e4dcf50b 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/DataGridViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/DataGridViewModel.cs @@ -6,42 +6,36 @@ namespace PleasantUI.Example.ViewModels.Pages.ControlPages; public class DataGridViewModel : ViewModelBase { - private bool _showGridLines; - private bool _canUserReorderColumns = true; - private bool _canUserResizeColumns = true; - private bool _canUserSortColumns = true; - private bool _showRowDetails; - public AvaloniaList DataModels { get; } public bool ShowGridLines { - get => _showGridLines; - set => SetProperty(ref _showGridLines, value); + get; + set => SetProperty(ref field, value); } public bool CanUserReorderColumns { - get => _canUserReorderColumns; - set => SetProperty(ref _canUserReorderColumns, value); - } + get; + set => SetProperty(ref field, value); + } = true; public bool CanUserResizeColumns { - get => _canUserResizeColumns; - set => SetProperty(ref _canUserResizeColumns, value); - } + get; + set => SetProperty(ref field, value); + } = true; public bool CanUserSortColumns { - get => _canUserSortColumns; - set => SetProperty(ref _canUserSortColumns, value); - } + get; + set => SetProperty(ref field, value); + } = true; public bool ShowRowDetails { - get => _showRowDetails; - set => SetProperty(ref _showRowDetails, value); + get; + set => SetProperty(ref field, value); } public DataGridViewModel() diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/ProgressRingViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/ProgressRingViewModel.cs index dac67bca..1ffa0d4b 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/ProgressRingViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ControlPages/ProgressRingViewModel.cs @@ -4,19 +4,15 @@ namespace PleasantUI.Example.ViewModels.Pages.ControlPages; public partial class ProgressRingViewModel : ViewModelBase { - private double _value = 25; - - private bool _isIndeterminate; - public double Value { - get => _value; - set => SetProperty(ref _value, value); - } + get; + set => SetProperty(ref field, value); + } = 25; public bool IsIndeterminate { - get => _isIndeterminate; - set => SetProperty(ref _isIndeterminate, value); + get; + set => SetProperty(ref field, value); } } \ No newline at end of file diff --git a/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs index b82e7c53..ac6cae95 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/SettingsViewModel.cs @@ -1,4 +1,5 @@ -using PleasantUI.Controls; +using Avalonia.Media; +using PleasantUI.Controls; using PleasantUI.Core; using PleasantUI.Core.Localization; using PleasantUI.Core.Models; @@ -9,6 +10,8 @@ namespace PleasantUI.Example.ViewModels.Pages; public partial class SettingsViewModel : ViewModelBase { + public List Fonts { get; set; } = FontManager.Current.SystemFonts.OrderBy(f => f.Name).ToList(); + public SettingsViewModel() { // Re-raise SelectedLanguage when language changes so the ComboBox stays in sync diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs index 5ab0f51b..f0086281 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/MessageBoxViewModel.cs @@ -13,13 +13,11 @@ namespace PleasantUI.Example.ViewModels.Pages.ToolKitPages; public class MessageBoxViewModel : ViewModelBase { - private string _lastResult = "—"; - public string LastResult { - get => _lastResult; - set => SetProperty(ref _lastResult, value); - } + get; + set => SetProperty(ref field, value); + } = "—"; // Resolves a key under the MessageBox/ context with a hardcoded fallback private static string T(string key, string fallback) => @@ -126,11 +124,11 @@ public async Task ShowDanger() public async Task ShowCustomContent() { // Build extra content: warning icon + description + radio buttons - var option1 = new RadioButton { Content = T("CustomOption1", "Keep existing data"), GroupName = "MBOptions", IsChecked = true }; - var option2 = new RadioButton { Content = T("CustomOption2", "Replace with new data"), GroupName = "MBOptions" }; - var option3 = new RadioButton { Content = T("CustomOption3", "Merge both"), GroupName = "MBOptions" }; + RadioButton option1 = new() { Content = T("CustomOption1", "Keep existing data"), GroupName = "MBOptions", IsChecked = true }; + RadioButton option2 = new() { Content = T("CustomOption2", "Replace with new data"), GroupName = "MBOptions" }; + RadioButton option3 = new() { Content = T("CustomOption3", "Merge both"), GroupName = "MBOptions" }; - var panel = new StackPanel + StackPanel panel = new() { Spacing = 8, Children = @@ -161,7 +159,7 @@ public async Task ShowCustomContent() } }; - var result = await MessageBox.Show( + MessageBoxResult result = await MessageBox.Show( PleasantUiExampleApp.Main, T("CustomTitle", "Data conflict"), T("CustomText", "A file with this name already exists. How would you like to proceed?"), @@ -187,9 +185,9 @@ private static string TD(string key, string fallback) => public async Task ShowPleasantDialogRich() { - var remember = new PleasantDialogCheckBox { Text = TD("RememberChoice", "Remember my choice") }; + PleasantDialogCheckBox remember = new() { Text = TD("RememberChoice", "Remember my choice") }; - var result = await PleasantDialog.Show( + object? result = await PleasantDialog.Show( PleasantUiExampleApp.Main, header: TD("RichTitle", "Sync settings"), body: TD("RichBody", "Choose how your settings should be synchronized across devices."), @@ -224,9 +222,9 @@ public async Task ShowPleasantDialogRich() public async Task ShowPleasantDialogProgress() { PleasantDialog? dialogRef = null; - var cts = new CancellationTokenSource(); + CancellationTokenSource cts = new(); - var result = await PleasantDialog.Show( + object? result = await PleasantDialog.Show( PleasantUiExampleApp.Main, header: TD("ProgressTitle", "Processing"), body: TD("ProgressBody", "Please wait while the operation completes…"), @@ -259,7 +257,7 @@ public async Task ShowPleasantDialogProgress() { d.SetProgressBarState(captured); // Update subheader text to show percentage - var sub = d.FindControl("SubHeaderText"); + TextBlock? sub = d.FindControl("SubHeaderText"); if (sub is not null) { sub.Text = $"{captured}%"; @@ -283,7 +281,7 @@ public async Task ShowPleasantDialogProgress() public async Task ShowPleasantDialogDanger() { - var result = await PleasantDialog.Show( + object? result = await PleasantDialog.Show( PleasantUiExampleApp.Main, header: TD("DangerTitle", "Permanently delete account"), body: TD("DangerBody", "This will remove all your data, settings, and history. This cannot be undone."), diff --git a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs index 65d324f4..ce660141 100644 --- a/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs +++ b/samples/PleasantUI.Example/ViewModels/Pages/ToolKitPages/NoticeDialogViewModel.cs @@ -19,7 +19,7 @@ private static string T(string key, string fallback) => public async Task ShowInfo() { - NoticeDialog dialog = new NoticeDialog + NoticeDialog dialog = new() { Title = T("InfoTitle", "Information"), Message = T("InfoMessage", "Your changes have been saved successfully."), @@ -34,7 +34,7 @@ public async Task ShowInfo() public async Task ShowWarning() { - NoticeDialog dialog = new NoticeDialog + NoticeDialog dialog = new() { Title = T("WarningTitle", "Warning"), Message = T("WarningMessage", "The file you are trying to open is larger than 100MB. Opening it may slow down the application."), @@ -51,7 +51,7 @@ public async Task ShowWarning() public async Task ShowError() { - NoticeDialog dialog = new NoticeDialog + NoticeDialog dialog = new() { Title = T("ErrorTitle", "Error"), Message = T("ErrorMessage", "An unexpected error occurred while processing your request. Please try again later."), diff --git a/samples/PleasantUI.Example/Views/AboutView.axaml b/samples/PleasantUI.Example/Views/AboutView.axaml index 09b67244..2649415c 100644 --- a/samples/PleasantUI.Example/Views/AboutView.axaml +++ b/samples/PleasantUI.Example/Views/AboutView.axaml @@ -6,7 +6,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="PleasantUI.Example.Views.AboutView"> - + diff --git a/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml b/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml index 60379f78..9146f1f1 100644 --- a/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml +++ b/samples/PleasantUI.Example/Views/Pages/ControlPages/ButtonPageView.axaml @@ -26,124 +26,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + +