mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-29 18:38:16 +08:00
优化项目代码格式
This commit is contained in:
parent
80638627ee
commit
171734f757
@ -1,27 +1,19 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_ASSIGNMENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_VARIABLES/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_TUPLE_COMPONENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTIPLE_DECLARATION/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_CONSTRAINS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_REGION/@EntryValue">2</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_TYPE/@EntryValue">2</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_ASSIGNMENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_BINARY_EXPRESSIONS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_COMMENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_NESTED_TERNARY/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_PROPERTY_PATTERNS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_SWITCH_EXPRESSIONS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_SWITCH_SECTIONS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_VARIABLES/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OUTDENT_COMMAS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8715f408_002D64e6_002D4969_002Daba2_002D79f56ef672fa/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Avalonia Stuff"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="PART_" Suffix="" Style="AaBb"><ExtraRule Prefix="STATE_" Suffix="" Style="AaBb" /></Policy></Policy></s:String>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/XamlStyler/SearchToDriveRoot/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=adorner/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=adorners/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_TYPE/@EntryValue">0</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_ACCESSOR/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64></wpf:ResourceDictionary>
|
117
README.md
117
README.md
@ -6,21 +6,23 @@
|
||||
|
||||
#### Introduce
|
||||
|
||||
AtomUI is an implementation of Ant Design based on .NET technology, dedicated to bringing the excellent and efficient design language and experience of Ant Design to the Avalonia/.NET cross-platform desktop software development field.
|
||||
AtomUI is an implementation of Ant Design based on .NET technology, dedicated to bringing the excellent and efficient
|
||||
design language and experience of Ant Design to the Avalonia/.NET cross-platform desktop software development field.
|
||||
|
||||
Welcome to communicate and give suggestions to AtomUI, thank you for giving the project a Star.
|
||||
|
||||
|
||||
#### Control library completion
|
||||
|
||||
##### General
|
||||
| Control Name | Status |
|
||||
|:-------------|:-----------|
|
||||
| Button | Completed ✅|
|
||||
| PathIcon | Completed ✅|
|
||||
| SpiltButton | Completed ✅|
|
||||
|
||||
| Control Name | Status |
|
||||
|:-------------|:------------|
|
||||
| Button | Completed ✅ |
|
||||
| PathIcon | Completed ✅ |
|
||||
| SpiltButton | Completed ✅ |
|
||||
|
||||
##### Navigation
|
||||
|
||||
| Control Name | Status |
|
||||
|:-------------|:------------|
|
||||
| Breadcrumb | TODO |
|
||||
@ -30,6 +32,7 @@ Welcome to communicate and give suggestions to AtomUI, thank you for giving the
|
||||
| Steps | TODO |
|
||||
|
||||
##### Data Entry
|
||||
|
||||
| Control Name | Status |
|
||||
|:----------------|:---------------|
|
||||
| AutoComplete | TODO |
|
||||
@ -53,46 +56,48 @@ Welcome to communicate and give suggestions to AtomUI, thank you for giving the
|
||||
| Upload | Need Review 🤔 |
|
||||
|
||||
##### Data Display
|
||||
| Control Name | Status |
|
||||
|:---------------|:---------------|
|
||||
| Avatar | TODO |
|
||||
| Badge | Completed ✅ |
|
||||
| Calendar | TODO |
|
||||
| Card | TODO |
|
||||
| GroupBox | Completed ✅ |
|
||||
| Carousel | Developing 💪 |
|
||||
| Collapse | Completed ✅ |
|
||||
| Expander | Completed ✅ |
|
||||
| Descriptions | TODO |
|
||||
| EmptyIndicator | Completed ✅ |
|
||||
| Image | TODO |
|
||||
| ListBox | Completed ✅ |
|
||||
| Popover | Completed ✅ |
|
||||
| QRCode | TODO |
|
||||
| Segmented | Completed ✅ |
|
||||
| Statistic | TODO |
|
||||
| Table | TODO |
|
||||
| TabControl | Completed ✅ |
|
||||
| Tag | Completed ✅ |
|
||||
| Timeline | Developing 💪 |
|
||||
| Tooltip | Completed ✅ |
|
||||
| Tour | TODO |
|
||||
| Tree | Completed ✅ |
|
||||
|
||||
| Control Name | Status |
|
||||
|:---------------|:--------------|
|
||||
| Avatar | TODO |
|
||||
| Badge | Completed ✅ |
|
||||
| Calendar | TODO |
|
||||
| Card | TODO |
|
||||
| GroupBox | Completed ✅ |
|
||||
| Carousel | Developing 💪 |
|
||||
| Collapse | Completed ✅ |
|
||||
| Expander | Completed ✅ |
|
||||
| Descriptions | TODO |
|
||||
| EmptyIndicator | Completed ✅ |
|
||||
| Image | TODO |
|
||||
| ListBox | Completed ✅ |
|
||||
| Popover | Completed ✅ |
|
||||
| QRCode | TODO |
|
||||
| Segmented | Completed ✅ |
|
||||
| Statistic | TODO |
|
||||
| Table | TODO |
|
||||
| TabControl | Completed ✅ |
|
||||
| Tag | Completed ✅ |
|
||||
| Timeline | Developing 💪 |
|
||||
| Tooltip | Completed ✅ |
|
||||
| Tour | TODO |
|
||||
| Tree | Completed ✅ |
|
||||
|
||||
##### Feedback
|
||||
| Control Name | Status |
|
||||
|:-------------------|:--------------|
|
||||
| Alert | Completed ✅ |
|
||||
| Drawer | Developing 💪 |
|
||||
| Message | Completed ✅ |
|
||||
| Modal | Developing 💪 |
|
||||
| Notification | Completed ✅ |
|
||||
| Popconfirm | Completed ✅ |
|
||||
| ProgressBar | Completed ✅ |
|
||||
| Result | Need Review 🤔 |
|
||||
| Skeleton | TODO |
|
||||
| LoadingIndicator | Completed ✅ |
|
||||
| Watermark | Developing 💪 |
|
||||
|
||||
| Control Name | Status |
|
||||
|:-----------------|:---------------|
|
||||
| Alert | Completed ✅ |
|
||||
| Drawer | Developing 💪 |
|
||||
| Message | Completed ✅ |
|
||||
| Modal | Developing 💪 |
|
||||
| Notification | Completed ✅ |
|
||||
| Popconfirm | Completed ✅ |
|
||||
| ProgressBar | Completed ✅ |
|
||||
| Result | Need Review 🤔 |
|
||||
| Skeleton | TODO |
|
||||
| LoadingIndicator | Completed ✅ |
|
||||
| Watermark | Developing 💪 |
|
||||
|
||||
#### Some screenshots of the running effect
|
||||
|
||||
@ -101,22 +106,35 @@ Welcome to communicate and give suggestions to AtomUI, thank you for giving the
|
||||
![Button](docs/images/controls/ButtonControl.png)
|
||||
|
||||
#### Progress bar control
|
||||
|
||||
![Progress](docs/images/controls/ProgressBarControl.png)
|
||||
|
||||
#### Slider control
|
||||
|
||||
![Slider](docs/images/controls/SliderControl.png)
|
||||
|
||||
<div style="height:50px"></div>
|
||||
|
||||
#### License Description
|
||||
The project source code is only free for personal learning and communication or open source projects that follow GPLv3. <strong>Commercial applications (including but not limited to internal company projects, commercial projects developed by individuals using AtomUI, and outsourced projects) require the purchase of a commercial license</strong>. Please contact: Qinpai Software for authorization matters.
|
||||
|
||||
The project source code is only free for personal learning and communication or open source projects that follow
|
||||
GPLv3. <strong>Commercial applications (including but not limited to internal company projects, commercial projects
|
||||
developed by individuals using AtomUI, and outsourced projects) require the purchase of a commercial license</strong>.
|
||||
Please contact: Qinpai Software for authorization matters.
|
||||
|
||||
#### About the Jiachen Project
|
||||
|
||||
<p align="center">
|
||||
<img src="./docs/images/jiachenjihua.png" width="300" />
|
||||
</p>
|
||||
|
||||
The Jiachen Project (RISC-V Prosperity 2036) was born on New Year's Eve 2024. It was jointly initiated by several domestic RISC-V software and chip teams and has attracted dozens of domestic and foreign companies engaged in RISC-V product and software development to join. We believe that the RISC-V ecosystem is entering the initial stage of unprecedented explosive growth: in 2025, RISC-V may welcome more than 1 million RISC-V application developers, and at the same time RISC-V will enter the world's top 500 supercomputers in 2025 and the top 10 in 2030. We are in a golden age of computer architecture and basic software systems, and the open instruction set architecture has brought a large number of new scientific problems and engineering challenges.
|
||||
The Jiachen Project (RISC-V Prosperity 2036) was born on New Year's Eve 2024. It was jointly initiated by several
|
||||
domestic RISC-V software and chip teams and has attracted dozens of domestic and foreign companies engaged in RISC-V
|
||||
product and software development to join. We believe that the RISC-V ecosystem is entering the initial stage of
|
||||
unprecedented explosive growth: in 2025, RISC-V may welcome more than 1 million RISC-V application developers, and at
|
||||
the same time RISC-V will enter the world's top 500 supercomputers in 2025 and the top 10 in 2030. We are in a golden
|
||||
age of computer architecture and basic software systems, and the open instruction set architecture has brought a large
|
||||
number of new scientific problems and engineering challenges.
|
||||
|
||||
#### About Chinware
|
||||
|
||||
@ -124,4 +142,7 @@ The Jiachen Project (RISC-V Prosperity 2036) was born on New Year's Eve 2024. It
|
||||
<img src="./docs/images/Chinware.png" width="300" />
|
||||
</p>
|
||||
|
||||
Chinware Technologies Ltd. is a technology company dedicated to the development of productivity tool software. Since its inception, it has been determined to deepen its roots in the field of tool software, practice the spirit of continuous improvement in research and development, and strive to launch high-quality productivity tool software to serve developers at home and abroad, improve developers' work efficiency, and create commercial value and social value.
|
||||
Chinware Technologies Ltd. is a technology company dedicated to the development of productivity tool software. Since its
|
||||
inception, it has been determined to deepen its roots in the field of tool software, practice the spirit of continuous
|
||||
improvement in research and development, and strive to launch high-quality productivity tool software to serve
|
||||
developers at home and abroad, improve developers' work efficiency, and create commercial value and social value.
|
19
Untitled.DotSettings
Normal file
19
Untitled.DotSettings
Normal file
@ -0,0 +1,19 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTIPLE_DECLARATION/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_CONSTRAINS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_TUPLE_COMPONENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_ACCESSOR/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_TYPE/@EntryValue">0</s:Int64>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_ASSIGNMENTS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_VARIABLES/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>
|
@ -1,31 +1,31 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TrimMode>copyused</TrimMode>
|
||||
<BuiltInComInteropSupport>false</BuiltInComInteropSupport>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TrimMode>copyused</TrimMode>
|
||||
<BuiltInComInteropSupport>false</BuiltInComInteropSupport>
|
||||
|
||||
<!-- 'bin/ref/' https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/compiler-options/output#producereferenceassembly -->
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<!-- 'net6.0' https://learn.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#appendtargetframeworktooutputpath -->
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<!-- 'win-x64' https://learn.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#appendruntimeidentifiertooutputpath -->
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<!-- 'bin/ref/' https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/compiler-options/output#producereferenceassembly -->
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<!-- 'net6.0' https://learn.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#appendtargetframeworktooutputpath -->
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<!-- 'win-x64' https://learn.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#appendruntimeidentifiertooutputpath -->
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
|
||||
<IsTestProject Condition="$(MSBuildProjectFullPath.Contains('test')) and ($(MSBuildProjectName.EndsWith('.Tests')) or $(MSBuildProjectName.EndsWith('.TestBase')))">true</IsTestProject>
|
||||
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
|
||||
<NoWarn>$(NoWarn);CS1591;CS0436;CS7035</NoWarn>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<WarningsAsErrors>Nullable</WarningsAsErrors>
|
||||
<IsTestProject Condition="$(MSBuildProjectFullPath.Contains('test')) and ($(MSBuildProjectName.EndsWith('.Tests')) or $(MSBuildProjectName.EndsWith('.TestBase')))">true</IsTestProject>
|
||||
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
|
||||
<NoWarn>$(NoWarn);CS1591;CS0436;CS7035</NoWarn>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<WarningsAsErrors>Nullable</WarningsAsErrors>
|
||||
|
||||
<!-- enable private APIs -->
|
||||
<AvaloniaAccessUnstablePrivateApis>true</AvaloniaAccessUnstablePrivateApis>
|
||||
<!-- Mind to specify the exact Avalonia version needed for your package to work -->
|
||||
<Avalonia_I_Want_To_Use_Private_Apis_In_Nuget_Package_And_Promise_To_Pin_The_Exact_Avalonia_Version_In_Package_Dependency>true</Avalonia_I_Want_To_Use_Private_Apis_In_Nuget_Package_And_Promise_To_Pin_The_Exact_Avalonia_Version_In_Package_Dependency>
|
||||
|
||||
</PropertyGroup>
|
||||
<!-- enable private APIs -->
|
||||
<AvaloniaAccessUnstablePrivateApis>true</AvaloniaAccessUnstablePrivateApis>
|
||||
<!-- Mind to specify the exact Avalonia version needed for your package to work -->
|
||||
<Avalonia_I_Want_To_Use_Private_Apis_In_Nuget_Package_And_Promise_To_Pin_The_Exact_Avalonia_Version_In_Package_Dependency>true</Avalonia_I_Want_To_Use_Private_Apis_In_Nuget_Package_And_Promise_To_Pin_The_Exact_Avalonia_Version_In_Package_Dependency>
|
||||
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<DefineConstants>$(DefineConstants);INTERNAL_USING</DefineConstants>
|
||||
|
||||
</PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants);INTERNAL_USING</DefineConstants>
|
||||
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,33 +1,33 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">True</GeneratePackageOnBuild>
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">True</GeneratePackageOnBuild>
|
||||
|
||||
<PackageId>$(MSBuildProjectName)</PackageId>
|
||||
|
||||
<!--Nuget-->
|
||||
<Title>AtomUI</Title>
|
||||
<Author>Chinware Technologies Ltd.</Author>
|
||||
<Authors>$(Author)</Authors>
|
||||
|
||||
<Description>AtomUI is an implementation of Ant Design based on .NET technology, and is committed to bringing Ant Design's excellent and efficient design language and experience to the Avalonia/.NET cross-platform desktop software development field.</Description>
|
||||
<PackageTags>avalonia;antdesign;ui;control</PackageTags>
|
||||
|
||||
<ProjectUrl>https://atomui.net</ProjectUrl>
|
||||
<RepositoryUrl>https://github.com/chinware/AtomUI</RepositoryUrl>
|
||||
<PackageProjectUrl>https://atomui.net</PackageProjectUrl>
|
||||
<Company>Chinware Technologies Ltd.</Company>
|
||||
<Copyright>Copyright ©2018-2024, Chinware Technologies Ltd, All Rights Reserved.</Copyright>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>$(NugetPackageVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<PackageId>$(MSBuildProjectName)</PackageId>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\logo.png" Visible="False" Pack="True" PackagePath="\"/>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\LICENSE" Visible="False" Pack="True" PackagePath="\"/>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\README.md" Visible="False" Pack="True" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
<!--Nuget-->
|
||||
<Title>AtomUI</Title>
|
||||
<Author>Chinware Technologies Ltd.</Author>
|
||||
<Authors>$(Author)</Authors>
|
||||
|
||||
<Description>AtomUI is an implementation of Ant Design based on .NET technology, and is committed to bringing Ant Design's excellent and efficient design language and experience to the Avalonia/.NET cross-platform desktop software development field.</Description>
|
||||
<PackageTags>avalonia;antdesign;ui;control</PackageTags>
|
||||
|
||||
<ProjectUrl>https://atomui.net</ProjectUrl>
|
||||
<RepositoryUrl>https://github.com/chinware/AtomUI</RepositoryUrl>
|
||||
<PackageProjectUrl>https://atomui.net</PackageProjectUrl>
|
||||
<Company>Chinware Technologies Ltd.</Company>
|
||||
<Copyright>Copyright ©2018-2024, Chinware Technologies Ltd, All Rights Reserved.</Copyright>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>$(NugetPackageVersion)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\logo.png" Visible="False" Pack="True" PackagePath="\"/>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\LICENSE" Visible="False" Pack="True" PackagePath="\"/>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\README.md" Visible="False" Pack="True" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<OutputPath>$(MSBuildThisFileDirectory)..\_output\App\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
|
||||
<OutputPath>$(MSBuildThisFileDirectory)..\_output\App\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<OutputPath>$(ProjectDir)bin\$(Configuration)\</OutputPath>
|
||||
<OutputPath>$(ProjectDir)bin\$(Configuration)\</OutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\_output\Nuget\</PackageOutputPath>
|
||||
<OutputPath>$(MSBuildThisFileDirectory)..\_output\Bin\$(Configuration)\</OutputPath>
|
||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\_output\Nuget\</PackageOutputPath>
|
||||
<OutputPath>$(MSBuildThisFileDirectory)..\_output\Bin\$(Configuration)\</OutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,15 +1,15 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
<NoWarn>$(NoWarn);CS7035</NoWarn>
|
||||
<NoWarn>$(NoWarn);CS7035</NoWarn>
|
||||
|
||||
<AvaloniaVersionThatLibrariesUsed>11.1.2</AvaloniaVersionThatLibrariesUsed>
|
||||
<AvaloniaVersionThatSampleUsed>11.1.2</AvaloniaVersionThatSampleUsed>
|
||||
<LibVersion>0.0.1</LibVersion>
|
||||
<NugetPackageVersion>$(LibVersion)-local.1</NugetPackageVersion>
|
||||
<NugetPackageVersion>$(LibVersion)-preview.1</NugetPackageVersion>
|
||||
<FileVersion>$([System.DateTime]::Now.ToString("yyyy.MM.dd HH:mm:ss"))</FileVersion>
|
||||
<AssemblyVersion>$(LibVersion)</AssemblyVersion>
|
||||
<AvaloniaVersionThatLibrariesUsed>11.1.2</AvaloniaVersionThatLibrariesUsed>
|
||||
<AvaloniaVersionThatSampleUsed>11.1.2</AvaloniaVersionThatSampleUsed>
|
||||
<LibVersion>0.0.1</LibVersion>
|
||||
<NugetPackageVersion>$(LibVersion)-local.1</NugetPackageVersion>
|
||||
<NugetPackageVersion>$(LibVersion)-preview.1</NugetPackageVersion>
|
||||
<FileVersion>$([System.DateTime]::Now.ToString("yyyy.MM.dd HH:mm:ss"))</FileVersion>
|
||||
<AssemblyVersion>$(LibVersion)</AssemblyVersion>
|
||||
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -44,9 +44,13 @@ public class ShowCasePanel : Control
|
||||
{
|
||||
var control = Children[i];
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
_leftContainer.Children.Add(control);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rightContainer.Children.Add(control);
|
||||
}
|
||||
}
|
||||
|
||||
var scrollView = new ScrollViewer
|
||||
|
@ -30,6 +30,9 @@ public class ColorItemControl : TemplatedControl
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
if (DataContext is ColorItemViewModel v) WeakReferenceMessenger.Default.Send(v);
|
||||
if (DataContext is ColorItemViewModel v)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(v);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,10 @@ public class IconGallery : TemplatedControl
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
if (DataContext is ColorItemViewModel v) WeakReferenceMessenger.Default.Send(v);
|
||||
if (DataContext is ColorItemViewModel v)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(v);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
@ -40,7 +43,11 @@ public class IconGallery : TemplatedControl
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
if (!_initialized)
|
||||
{
|
||||
if (IconThemeType.HasValue) _galleryModel.LoadThemeIcons(IconThemeType.Value);
|
||||
if (IconThemeType.HasValue)
|
||||
{
|
||||
_galleryModel.LoadThemeIcons(IconThemeType.Value);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,13 @@ internal class Program
|
||||
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UseManagedSystemDialogs()
|
||||
.UsePlatformDetect()
|
||||
.UseAtomUI()
|
||||
.UseManagedSystemDialogs()
|
||||
.UsePlatformDetect()
|
||||
.UseAtomUI()
|
||||
#if DEBUG
|
||||
.UseDevToolsForAvalonia()
|
||||
.UseDevToolsForAvalonia()
|
||||
#endif
|
||||
.UseIconPackage<AntDesignIconPackage>(true)
|
||||
.With(new Win32PlatformOptions())
|
||||
.LogToTrace();
|
||||
.UseIconPackage<AntDesignIconPackage>(true)
|
||||
.With(new Win32PlatformOptions())
|
||||
.LogToTrace();
|
||||
}
|
@ -32,11 +32,17 @@ public partial class ButtonShowCase : UserControl
|
||||
private void HandleButtonSizeTypeOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
ButtonSizeType = SizeType.Large;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
ButtonSizeType = SizeType.Middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
ButtonSizeType = SizeType.Small;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLoadingBtnClick(object? sender, RoutedEventArgs args)
|
||||
|
@ -25,13 +25,23 @@ public partial class ButtonSpinnerShowCase : UserControl
|
||||
{
|
||||
var value = Array.IndexOf(_spinnerItems, textBlock.Text);
|
||||
if (e.Direction == SpinDirection.Increase)
|
||||
{
|
||||
value++;
|
||||
}
|
||||
else
|
||||
{
|
||||
value--;
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
value = _spinnerItems.Length - 1;
|
||||
else if (value >= _spinnerItems.Length) value = 0;
|
||||
{
|
||||
value = _spinnerItems.Length - 1;
|
||||
}
|
||||
else if (value >= _spinnerItems.Length)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
|
||||
textBlock.Text = _spinnerItems[value];
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,12 @@ public partial class CollapseShowCase : UserControl
|
||||
private void HandleExpandButtonPosOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
CollapseExpandIconPosition = CollapseExpandIconPosition.Start;
|
||||
else if (args.Index == 1) CollapseExpandIconPosition = CollapseExpandIconPosition.End;
|
||||
{
|
||||
CollapseExpandIconPosition = CollapseExpandIconPosition.Start;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
CollapseExpandIconPosition = CollapseExpandIconPosition.End;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,15 @@ public partial class DrawerShowCase : UserControl
|
||||
|
||||
private void Button_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Button button) return;
|
||||
if (sender is not Button button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Drawer.GetDrawer(button) is not { } drawer) return;
|
||||
if (Drawer.GetDrawer(button) is not { } drawer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
drawer.IsOpen = false;
|
||||
}
|
||||
|
@ -35,18 +35,32 @@ public partial class ExpanderShowCase : UserControl
|
||||
private void HandleExpandButtonPosOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
ToggleIconPosition = ExpanderIconPosition.Start;
|
||||
else if (args.Index == 1) ToggleIconPosition = ExpanderIconPosition.End;
|
||||
{
|
||||
ToggleIconPosition = ExpanderIconPosition.Start;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
ToggleIconPosition = ExpanderIconPosition.End;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleExpandDirectionOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
ExpandDirection = ExpandDirection.Down;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
ExpandDirection = ExpandDirection.Up;
|
||||
}
|
||||
else if (args.Index == 2)
|
||||
ExpandDirection = ExpandDirection.Left;
|
||||
else if (args.Index == 3) ExpandDirection = ExpandDirection.Right;
|
||||
{
|
||||
ExpandDirection = ExpandDirection.Left;
|
||||
}
|
||||
else if (args.Index == 3)
|
||||
{
|
||||
ExpandDirection = ExpandDirection.Right;
|
||||
}
|
||||
}
|
||||
}
|
@ -28,9 +28,13 @@ public partial class NotificationShowCase : UserControl
|
||||
if (_basicManager is not null)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
_basicManager.IsPauseOnHover = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_basicManager.IsPauseOnHover = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,13 @@
|
||||
<UserControl x:Class="AtomUI.Demo.Desktop.ShowCase.Overview"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:Avalonia.Markup.Xaml.Converters;assembly=Avalonia.Markup.Xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="1000"
|
||||
d:DesignWidth="1920"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Border.CodeBlock">
|
||||
|
@ -128,8 +128,12 @@ public partial class ProgressBarShowCase : UserControl
|
||||
{
|
||||
ToggleStatus = !ToggleStatus;
|
||||
if (ToggleStatus)
|
||||
{
|
||||
ToggleDisabledText = "Disable";
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleDisabledText = "Enable";
|
||||
}
|
||||
}
|
||||
}
|
@ -18,12 +18,18 @@ public partial class RadioButtonShowCase : UserControl
|
||||
|
||||
public static void ToggleDisabledStatus(object arg)
|
||||
{
|
||||
var btn = (arg as Button)!;
|
||||
var btn = (arg as Button)!;
|
||||
var stackPanel = btn.Parent as StackPanel;
|
||||
var radioBtn1 = stackPanel?.FindControl<RadioButton>("ToggleDisabledRadioUnChecked");
|
||||
var radioBtn2 = stackPanel?.FindControl<RadioButton>("ToggleDisabledRadioChecked");
|
||||
if (radioBtn1 != null) radioBtn1.IsEnabled = !radioBtn1.IsEnabled;
|
||||
var radioBtn1 = stackPanel?.FindControl<RadioButton>("ToggleDisabledRadioUnChecked");
|
||||
var radioBtn2 = stackPanel?.FindControl<RadioButton>("ToggleDisabledRadioChecked");
|
||||
if (radioBtn1 != null)
|
||||
{
|
||||
radioBtn1.IsEnabled = !radioBtn1.IsEnabled;
|
||||
}
|
||||
|
||||
if (radioBtn2 != null) radioBtn2.IsEnabled = !radioBtn2.IsEnabled;
|
||||
if (radioBtn2 != null)
|
||||
{
|
||||
radioBtn2.IsEnabled = !radioBtn2.IsEnabled;
|
||||
}
|
||||
}
|
||||
}
|
@ -19,12 +19,18 @@ public partial class SwitchShowCase : UserControl
|
||||
|
||||
public static void ToggleLoadingStatus(object arg)
|
||||
{
|
||||
var btn = (arg as Button)!;
|
||||
var stackPanel = btn.Parent as StackPanel;
|
||||
var toggleSwitchDefault = stackPanel?.Children[0] as ToggleSwitch;
|
||||
var toggleSwitchSmall = stackPanel?.Children[1] as ToggleSwitch;
|
||||
if (toggleSwitchDefault is not null) toggleSwitchDefault.IsLoading = !toggleSwitchDefault.IsLoading;
|
||||
var btn = (arg as Button)!;
|
||||
var stackPanel = btn.Parent as StackPanel;
|
||||
var toggleSwitchDefault = stackPanel?.Children[0] as ToggleSwitch;
|
||||
var toggleSwitchSmall = stackPanel?.Children[1] as ToggleSwitch;
|
||||
if (toggleSwitchDefault is not null)
|
||||
{
|
||||
toggleSwitchDefault.IsLoading = !toggleSwitchDefault.IsLoading;
|
||||
}
|
||||
|
||||
if (toggleSwitchSmall is not null) toggleSwitchSmall.IsLoading = !toggleSwitchSmall.IsLoading;
|
||||
if (toggleSwitchSmall is not null)
|
||||
{
|
||||
toggleSwitchSmall.IsLoading = !toggleSwitchSmall.IsLoading;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,6 @@ public partial class TabControlShowCase : UserControl
|
||||
AddTabDemoTabControl.AddTabRequest += HandleTabControlAddTabRequest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region TabStrip
|
||||
|
||||
public static readonly StyledProperty<Dock> PositionTabStripPlacementProperty =
|
||||
@ -56,8 +54,6 @@ public partial class TabControlShowCase : UserControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region TabControl
|
||||
|
||||
public static readonly StyledProperty<Dock> PositionTabControlPlacementProperty =
|
||||
@ -89,42 +85,62 @@ public partial class TabControlShowCase : UserControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region TabStrip
|
||||
|
||||
private void HandleTabStripPlacementOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
PositionTabStripPlacement = Dock.Top;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
PositionTabStripPlacement = Dock.Bottom;
|
||||
}
|
||||
else if (args.Index == 2)
|
||||
{
|
||||
PositionTabStripPlacement = Dock.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
PositionTabStripPlacement = Dock.Right;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCardTabStripPlacementOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
PositionCardTabStripPlacement = Dock.Top;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
PositionCardTabStripPlacement = Dock.Bottom;
|
||||
}
|
||||
else if (args.Index == 2)
|
||||
{
|
||||
PositionCardTabStripPlacement = Dock.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
PositionCardTabStripPlacement = Dock.Right;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTabStripSizeTypeOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
SizeTypeTabStrip = SizeType.Small;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
SizeTypeTabStrip = SizeType.Middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
SizeTypeTabStrip = SizeType.Large;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTabStripAddTabRequest(object? sender, RoutedEventArgs args)
|
||||
@ -139,42 +155,62 @@ public partial class TabControlShowCase : UserControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region TabControl
|
||||
|
||||
private void HandleTabControlPlacementOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
PositionTabControlPlacement = Dock.Top;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
PositionTabControlPlacement = Dock.Bottom;
|
||||
}
|
||||
else if (args.Index == 2)
|
||||
{
|
||||
PositionTabControlPlacement = Dock.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
PositionTabControlPlacement = Dock.Right;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCardTabControlPlacementOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
PositionCardTabControlPlacement = Dock.Top;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
PositionCardTabControlPlacement = Dock.Bottom;
|
||||
}
|
||||
else if (args.Index == 2)
|
||||
{
|
||||
PositionCardTabControlPlacement = Dock.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
PositionCardTabControlPlacement = Dock.Right;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTabControlSizeTypeOptionCheckedChanged(object? sender, OptionCheckedChangedEventArgs args)
|
||||
{
|
||||
if (args.Index == 0)
|
||||
{
|
||||
SizeTypeTabControl = SizeType.Small;
|
||||
}
|
||||
else if (args.Index == 1)
|
||||
{
|
||||
SizeTypeTabControl = SizeType.Middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
SizeTypeTabControl = SizeType.Large;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTabControlAddTabRequest(object? sender, RoutedEventArgs args)
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace AtomUI.Demo.Desktop.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Xaml markup to get the enum values.
|
||||
/// Xaml markup to get the enum values.
|
||||
/// </summary>
|
||||
/// TODO 优化性能时可以考虑缓存类型和列表
|
||||
public class EnumExtension : MarkupExtension
|
||||
|
@ -115,9 +115,13 @@ public class CheckBoxShowCaseModel : ObservableObject
|
||||
if (ControlledCheckBoxCheckedStatus.HasValue)
|
||||
{
|
||||
if (ControlledCheckBoxCheckedStatus.Value)
|
||||
{
|
||||
CheckStatusBtnText = "UnCheck";
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckStatusBtnText = "Check";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -128,18 +132,28 @@ public class CheckBoxShowCaseModel : ObservableObject
|
||||
private void SetupEnabledBtnText()
|
||||
{
|
||||
if (ControlledCheckBoxEnabledStatus)
|
||||
{
|
||||
EnableStatusBtnText = "Disable";
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableStatusBtnText = "Enable";
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupControlledCheckBoxText()
|
||||
{
|
||||
var checkedText = "UnChecked";
|
||||
if (ControlledCheckBoxCheckedStatus.HasValue && ControlledCheckBoxCheckedStatus.Value) checkedText = "Checked";
|
||||
if (ControlledCheckBoxCheckedStatus.HasValue && ControlledCheckBoxCheckedStatus.Value)
|
||||
{
|
||||
checkedText = "Checked";
|
||||
}
|
||||
|
||||
var enabledText = "Disabled";
|
||||
if (ControlledCheckBoxEnabledStatus) enabledText = "Enabled";
|
||||
var enabledText = "Disabled";
|
||||
if (ControlledCheckBoxEnabledStatus)
|
||||
{
|
||||
enabledText = "Enabled";
|
||||
}
|
||||
|
||||
ControlledCheckBoxText = $"{checkedText}-{enabledText}";
|
||||
}
|
||||
@ -163,10 +177,16 @@ public class CheckBoxShowCaseModel : ObservableObject
|
||||
public void CheckedItemStatusHandler(object arg)
|
||||
{
|
||||
if (OrangeCheckedStatus && PearCheckedStatus && AppleCheckedStatus)
|
||||
{
|
||||
CheckedAllStatus = true;
|
||||
}
|
||||
else if (!OrangeCheckedStatus && !PearCheckedStatus && !AppleCheckedStatus)
|
||||
{
|
||||
CheckedAllStatus = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckedAllStatus = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,6 @@ public class DataGridDemoViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Song
|
||||
{
|
||||
public Song(string title, string artist, int m, int s, string album, int countOfComment, int netEaseId)
|
||||
@ -59,71 +58,70 @@ public class Song
|
||||
|
||||
public static List<Song> Songs { get; set; } = new()
|
||||
{
|
||||
new("好肚有肚(feat.李玲玉)", "熊猫堂ProducePandas", 2, 50, "A.S.I.A", 730, 1487039339),
|
||||
new("荒诞秀", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 639, 1487037601),
|
||||
new("长大", "熊猫堂ProducePandas", 4, 6, "A.S.I.A", 1114, 1487037690),
|
||||
new("招财猫(feat.纪粹希(G-Tracy))", "熊猫堂ProducePandas", 3, 37, "A.S.I.A", 361, 1487039632),
|
||||
new("千转", "熊猫堂ProducePandas", 4, 0, "A.S.I.A", 1115, 1477312398),
|
||||
new("辣辣辣", "熊猫堂ProducePandas", 3, 24, "A.S.I.A", 1873, 1465043716),
|
||||
new("碎碎念", "熊猫堂ProducePandas", 3, 25, "A.S.I.A", 676, 1474142064),
|
||||
new("盘他", "熊猫堂ProducePandas", 2, 16, "A.S.I.A", 365, 1481652786),
|
||||
new("Na Na Na", "熊猫堂ProducePandas", 3, 26, "A.S.I.A", 312, 1469022662),
|
||||
new("Indigo", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 137, 1487039517),
|
||||
new("饕餮人间", "熊猫堂ProducePandas", 3, 20, "饕餮人间", 1295, 1499584605),
|
||||
new("七步咙咚呛", "熊猫堂ProducePandas", 3, 10, "七步咙咚呛", 175, 1809095152),
|
||||
new("大惊小怪", "熊猫堂ProducePandas", 3, 32, "大惊小怪", 10420, 1847477425),
|
||||
new("工具人", "熊猫堂ProducePandas", 2, 46, "大惊小怪", 1135, 1847476499),
|
||||
new("以梦为马", "熊猫堂ProducePandas", 4, 19, "大惊小怪", 18361, 1836034373),
|
||||
new("以梦为马(Piano Version)", "熊猫堂ProducePandas", 3, 4, "大惊小怪", 570, 1847477423),
|
||||
new("The ONE", "熊猫堂ProducePandas", 2, 58, "The ONE", 1508, 1864329424),
|
||||
new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "The ONE", 385, 1864329429),
|
||||
new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 19, "以梦为马 (壮志骄阳版)", 161, 1865138896),
|
||||
new("New Horse", "熊猫堂ProducePandas", 2, 30, "emo了", 643, 1887021307),
|
||||
new("不例外", "熊猫堂ProducePandas", 3, 31, "emo了", 1818, 1887022665),
|
||||
new("满意", "熊猫堂ProducePandas", 4, 32, "emo了", 1081, 1882433472),
|
||||
new("就算与全世界为敌也要跟你在一起", "熊猫堂ProducePandas", 3, 32, "emo了", 2119, 1881759960),
|
||||
new("The ONE", "熊猫堂ProducePandas", 2, 58, "emo了", 67, 1887022648),
|
||||
new("口香糖", "熊猫堂ProducePandas", 3, 10, "emo了", 2181, 1885502254),
|
||||
new("Suuuuuuper Mario", "熊猫堂ProducePandas", 3, 32, "emo了", 1010, 1887021318),
|
||||
new("饕餮人间", "熊猫堂ProducePandas", 3, 22, "emo了", 109, 1887021320),
|
||||
new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 21, "emo了", 34, 1887022666),
|
||||
new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "emo了", 27, 1887022646),
|
||||
new("满意(DJheap九天版)", "熊猫堂ProducePandas", 4, 31, "emo了", 31, 1901605941),
|
||||
new("一眼万年", "熊猫堂ProducePandas", 3, 54, "一眼万年", 20, 1922599361),
|
||||
new("冲刺", "熊猫堂ProducePandas", 3, 49, "冲刺吧", 1006, 1932878194),
|
||||
new("滴答滴", "熊猫堂ProducePandas", 2, 30, "爱的赏味期限", 86, 1957515790),
|
||||
new("热带季风", "熊猫堂ProducePandas", 2, 45, "爱的赏味期限", 212, 1957514964),
|
||||
new("渣", "熊猫堂ProducePandas", 3, 28, "爱的赏味期限", 22, 1957514965),
|
||||
new("独特", "熊猫堂ProducePandas", 3, 33, "爱的赏味期限", 62, 1957514966),
|
||||
new("雨后", "熊猫堂ProducePandas", 4, 15, "爱的赏味期限", 23, 1957514967),
|
||||
new("然后然后", "熊猫堂ProducePandas", 3, 50, "爱的赏味期限", 108, 1957514968),
|
||||
new("丢", "熊猫堂ProducePandas", 3, 26, "爱的赏味期限", 30, 1957515792),
|
||||
new("热带疾风(FACEVOID桃心连哥 Remix)", "熊猫堂ProducePandas", 3, 23, "爱的赏味期限", 55, 1957515793),
|
||||
new("COSMIC ANTHEM -Japanese Ver.-", "熊猫堂ProducePandas", 3, 11, "COSMIC ANTHEM / 手紙", 0, 1977171493),
|
||||
new("手紙 (「長大-You Raise Me Up-」-Japanese Ver.-)", "熊猫堂ProducePandas", 4, 11, "COSMIC ANTHEM / 手紙", 0,
|
||||
new Song("好肚有肚(feat.李玲玉)", "熊猫堂ProducePandas", 2, 50, "A.S.I.A", 730, 1487039339),
|
||||
new Song("荒诞秀", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 639, 1487037601),
|
||||
new Song("长大", "熊猫堂ProducePandas", 4, 6, "A.S.I.A", 1114, 1487037690),
|
||||
new Song("招财猫(feat.纪粹希(G-Tracy))", "熊猫堂ProducePandas", 3, 37, "A.S.I.A", 361, 1487039632),
|
||||
new Song("千转", "熊猫堂ProducePandas", 4, 0, "A.S.I.A", 1115, 1477312398),
|
||||
new Song("辣辣辣", "熊猫堂ProducePandas", 3, 24, "A.S.I.A", 1873, 1465043716),
|
||||
new Song("碎碎念", "熊猫堂ProducePandas", 3, 25, "A.S.I.A", 676, 1474142064),
|
||||
new Song("盘他", "熊猫堂ProducePandas", 2, 16, "A.S.I.A", 365, 1481652786),
|
||||
new Song("Na Na Na", "熊猫堂ProducePandas", 3, 26, "A.S.I.A", 312, 1469022662),
|
||||
new Song("Indigo", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 137, 1487039517),
|
||||
new Song("饕餮人间", "熊猫堂ProducePandas", 3, 20, "饕餮人间", 1295, 1499584605),
|
||||
new Song("七步咙咚呛", "熊猫堂ProducePandas", 3, 10, "七步咙咚呛", 175, 1809095152),
|
||||
new Song("大惊小怪", "熊猫堂ProducePandas", 3, 32, "大惊小怪", 10420, 1847477425),
|
||||
new Song("工具人", "熊猫堂ProducePandas", 2, 46, "大惊小怪", 1135, 1847476499),
|
||||
new Song("以梦为马", "熊猫堂ProducePandas", 4, 19, "大惊小怪", 18361, 1836034373),
|
||||
new Song("以梦为马(Piano Version)", "熊猫堂ProducePandas", 3, 4, "大惊小怪", 570, 1847477423),
|
||||
new Song("The ONE", "熊猫堂ProducePandas", 2, 58, "The ONE", 1508, 1864329424),
|
||||
new Song("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "The ONE", 385, 1864329429),
|
||||
new Song("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 19, "以梦为马 (壮志骄阳版)", 161, 1865138896),
|
||||
new Song("New Horse", "熊猫堂ProducePandas", 2, 30, "emo了", 643, 1887021307),
|
||||
new Song("不例外", "熊猫堂ProducePandas", 3, 31, "emo了", 1818, 1887022665),
|
||||
new Song("满意", "熊猫堂ProducePandas", 4, 32, "emo了", 1081, 1882433472),
|
||||
new Song("就算与全世界为敌也要跟你在一起", "熊猫堂ProducePandas", 3, 32, "emo了", 2119, 1881759960),
|
||||
new Song("The ONE", "熊猫堂ProducePandas", 2, 58, "emo了", 67, 1887022648),
|
||||
new Song("口香糖", "熊猫堂ProducePandas", 3, 10, "emo了", 2181, 1885502254),
|
||||
new Song("Suuuuuuper Mario", "熊猫堂ProducePandas", 3, 32, "emo了", 1010, 1887021318),
|
||||
new Song("饕餮人间", "熊猫堂ProducePandas", 3, 22, "emo了", 109, 1887021320),
|
||||
new Song("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 21, "emo了", 34, 1887022666),
|
||||
new Song("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "emo了", 27, 1887022646),
|
||||
new Song("满意(DJheap九天版)", "熊猫堂ProducePandas", 4, 31, "emo了", 31, 1901605941),
|
||||
new Song("一眼万年", "熊猫堂ProducePandas", 3, 54, "一眼万年", 20, 1922599361),
|
||||
new Song("冲刺", "熊猫堂ProducePandas", 3, 49, "冲刺吧", 1006, 1932878194),
|
||||
new Song("滴答滴", "熊猫堂ProducePandas", 2, 30, "爱的赏味期限", 86, 1957515790),
|
||||
new Song("热带季风", "熊猫堂ProducePandas", 2, 45, "爱的赏味期限", 212, 1957514964),
|
||||
new Song("渣", "熊猫堂ProducePandas", 3, 28, "爱的赏味期限", 22, 1957514965),
|
||||
new Song("独特", "熊猫堂ProducePandas", 3, 33, "爱的赏味期限", 62, 1957514966),
|
||||
new Song("雨后", "熊猫堂ProducePandas", 4, 15, "爱的赏味期限", 23, 1957514967),
|
||||
new Song("然后然后", "熊猫堂ProducePandas", 3, 50, "爱的赏味期限", 108, 1957514968),
|
||||
new Song("丢", "熊猫堂ProducePandas", 3, 26, "爱的赏味期限", 30, 1957515792),
|
||||
new Song("热带疾风(FACEVOID桃心连哥 Remix)", "熊猫堂ProducePandas", 3, 23, "爱的赏味期限", 55, 1957515793),
|
||||
new Song("COSMIC ANTHEM -Japanese Ver.-", "熊猫堂ProducePandas", 3, 11, "COSMIC ANTHEM / 手紙", 0, 1977171493),
|
||||
new Song("手紙 (「長大-You Raise Me Up-」-Japanese Ver.-)", "熊猫堂ProducePandas", 4, 11, "COSMIC ANTHEM / 手紙", 0,
|
||||
1977171494),
|
||||
new("COSMIC ANTHEM -Chinese Ver.-", "熊猫堂ProducePandas", 3, 31, "COSMIC ANTHEM / 手紙", 0, 1977172202),
|
||||
new("世界晚安", "熊猫堂ProducePandas", 2, 59, "世界晚安", 652, 1985063377),
|
||||
new("世界晚安(泰文版)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 134, 1987842504),
|
||||
new("世界晚安(钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 76, 1990475933),
|
||||
new("世界晚安(泰文钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 29, 1990475934),
|
||||
new("世界晚安(DJ沈念版)", "熊猫堂ProducePandas", 3, 9, "世界晚安", 34, 2014263184),
|
||||
new("世界晚安(钢琴配乐)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 11, 2014263185),
|
||||
new("明年也要好好长大", "熊猫堂ProducePandas", 3, 12, "明年也要好好长大", 0, 2010515162),
|
||||
new("320万年前(DJ沈念版)", "熊猫堂ProducePandas", 3, 21, "320万年前", 8, 2055888636),
|
||||
new("320万年前", "熊猫堂ProducePandas", 3, 7, "W.O.R.L.D.", 329, 2049770469),
|
||||
new("隐德来希", "熊猫堂ProducePandas", 3, 3, "W.O.R.L.D.", 594, 2061317924),
|
||||
new("孔明", "熊猫堂ProducePandas", 3, 59, "W.O.R.L.D.", 91, 2063175274),
|
||||
new("锦鲤卟噜噜", "熊猫堂ProducePandas", 3, 5, "W.O.R.L.D.", 67, 2059208262),
|
||||
new("指鹿为马", "熊猫堂ProducePandas", 3, 12, "W.O.R.L.D.", 74, 2063175272),
|
||||
new("热带季风Remix", "熊猫堂ProducePandas", 3, 22, "W.O.R.L.D.", 23, 2063173319),
|
||||
new("加州梦境", "熊猫堂ProducePandas", 2, 56, "W.O.R.L.D.", 1662, 2063173324),
|
||||
new("渐近自由", "熊猫堂ProducePandas", 4, 19, "W.O.R.L.D.", 124, 2063173321),
|
||||
new("世界所有的烂漫", "熊猫堂ProducePandas", 3, 30, "W.O.R.L.D.", 335, 2053388775)
|
||||
new Song("COSMIC ANTHEM -Chinese Ver.-", "熊猫堂ProducePandas", 3, 31, "COSMIC ANTHEM / 手紙", 0, 1977172202),
|
||||
new Song("世界晚安", "熊猫堂ProducePandas", 2, 59, "世界晚安", 652, 1985063377),
|
||||
new Song("世界晚安(泰文版)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 134, 1987842504),
|
||||
new Song("世界晚安(钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 76, 1990475933),
|
||||
new Song("世界晚安(泰文钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 29, 1990475934),
|
||||
new Song("世界晚安(DJ沈念版)", "熊猫堂ProducePandas", 3, 9, "世界晚安", 34, 2014263184),
|
||||
new Song("世界晚安(钢琴配乐)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 11, 2014263185),
|
||||
new Song("明年也要好好长大", "熊猫堂ProducePandas", 3, 12, "明年也要好好长大", 0, 2010515162),
|
||||
new Song("320万年前(DJ沈念版)", "熊猫堂ProducePandas", 3, 21, "320万年前", 8, 2055888636),
|
||||
new Song("320万年前", "熊猫堂ProducePandas", 3, 7, "W.O.R.L.D.", 329, 2049770469),
|
||||
new Song("隐德来希", "熊猫堂ProducePandas", 3, 3, "W.O.R.L.D.", 594, 2061317924),
|
||||
new Song("孔明", "熊猫堂ProducePandas", 3, 59, "W.O.R.L.D.", 91, 2063175274),
|
||||
new Song("锦鲤卟噜噜", "熊猫堂ProducePandas", 3, 5, "W.O.R.L.D.", 67, 2059208262),
|
||||
new Song("指鹿为马", "熊猫堂ProducePandas", 3, 12, "W.O.R.L.D.", 74, 2063175272),
|
||||
new Song("热带季风Remix", "熊猫堂ProducePandas", 3, 22, "W.O.R.L.D.", 23, 2063173319),
|
||||
new Song("加州梦境", "熊猫堂ProducePandas", 2, 56, "W.O.R.L.D.", 1662, 2063173324),
|
||||
new Song("渐近自由", "熊猫堂ProducePandas", 4, 19, "W.O.R.L.D.", 124, 2063173321),
|
||||
new Song("世界所有的烂漫", "熊猫堂ProducePandas", 3, 30, "W.O.R.L.D.", 335, 2053388775)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public class SongViewModel : ObservableObject
|
||||
{
|
||||
private string? _album;
|
||||
|
@ -21,7 +21,6 @@ public class PaletteMetaItem
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class PaletteDemoViewModel : ObservableObject
|
||||
{
|
||||
private readonly PaletteMetaItem[] _presetPaletteInfos =
|
||||
@ -156,7 +155,6 @@ public class PaletteDemoViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ColorGroupViewModel : ObservableObject
|
||||
{
|
||||
private ObservableCollection<ColorListViewModel>? _colorList;
|
||||
@ -168,7 +166,6 @@ public class ColorGroupViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ColorListViewModel : ObservableObject
|
||||
{
|
||||
private ObservableCollection<ColorItemViewModel>? _colors;
|
||||
@ -196,7 +193,6 @@ public class ColorListViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ColorItemViewModel : ObservableObject
|
||||
{
|
||||
private IBrush _brush = null!;
|
||||
@ -213,9 +209,13 @@ public class ColorItemViewModel : ObservableObject
|
||||
Brush = brush;
|
||||
Hex = brush.ToString()!.ToUpperInvariant();
|
||||
if ((light && index < 5) || (!light && index >= 5))
|
||||
{
|
||||
TextBrush = Brushes.Black;
|
||||
}
|
||||
else
|
||||
{
|
||||
TextBrush = Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
public IBrush Brush
|
||||
|
@ -29,7 +29,6 @@ public class IconInfoItemModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class IconGalleryModel : ObservableObject
|
||||
{
|
||||
private readonly IconThemeType? _iconThemeType;
|
||||
@ -39,7 +38,10 @@ public class IconGalleryModel : ObservableObject
|
||||
public IconGalleryModel(IconThemeType? iconThemeType = null)
|
||||
{
|
||||
_iconThemeType = iconThemeType;
|
||||
if (_iconThemeType.HasValue) LoadThemeIcons(_iconThemeType.Value);
|
||||
if (_iconThemeType.HasValue)
|
||||
{
|
||||
LoadThemeIcons(_iconThemeType.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<IconInfoItemModel>? IconInfos
|
||||
@ -51,7 +53,10 @@ public class IconGalleryModel : ObservableObject
|
||||
public void LoadThemeIcons(IconThemeType iconThemeType)
|
||||
{
|
||||
var iconPackage = IconManager.Current.GetIconProvider<AntDesignIconPackage>();
|
||||
if (iconPackage is null) return;
|
||||
if (iconPackage is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IconInfos = new ObservableCollection<IconInfoItemModel>();
|
||||
var iconInfos = iconPackage.GetIconInfos(iconThemeType);
|
||||
|
@ -11,7 +11,6 @@ public enum TextDecorationLine
|
||||
LineThrough
|
||||
}
|
||||
|
||||
|
||||
public enum LineStyle
|
||||
{
|
||||
Solid,
|
||||
@ -21,7 +20,6 @@ public enum LineStyle
|
||||
Wavy
|
||||
}
|
||||
|
||||
|
||||
public enum Direction
|
||||
{
|
||||
Left,
|
||||
@ -30,7 +28,6 @@ public enum Direction
|
||||
Bottom
|
||||
}
|
||||
|
||||
|
||||
public enum Corner
|
||||
{
|
||||
None = 0x00,
|
||||
@ -41,7 +38,6 @@ public enum Corner
|
||||
All = TopLeft | TopRight | BottomLeft | BottomRight
|
||||
}
|
||||
|
||||
|
||||
public enum SizeType
|
||||
{
|
||||
Large,
|
||||
@ -49,7 +45,6 @@ public enum SizeType
|
||||
Small
|
||||
}
|
||||
|
||||
|
||||
// 文本修饰信息定义
|
||||
// 类似 CSS text-decoration
|
||||
public class TextDecorationInfo
|
||||
@ -60,7 +55,6 @@ public class TextDecorationInfo
|
||||
public int Thickness { get; set; } = 1;
|
||||
}
|
||||
|
||||
|
||||
public enum ColorNameFormat
|
||||
{
|
||||
HexRgb,
|
||||
|
@ -6,18 +6,26 @@ namespace AtomUI.Data;
|
||||
public static class BindUtils
|
||||
{
|
||||
public static IDisposable RelayBind(AvaloniaObject source, AvaloniaProperty sourceProperty, AvaloniaObject target,
|
||||
AvaloniaProperty? targetProperty = null,
|
||||
BindingMode mode = BindingMode.Default)
|
||||
AvaloniaProperty? targetProperty = null,
|
||||
BindingMode mode = BindingMode.Default)
|
||||
{
|
||||
targetProperty ??= sourceProperty;
|
||||
var registry = AvaloniaPropertyRegistry.Instance;
|
||||
if (!sourceProperty.IsAttached)
|
||||
{
|
||||
if (!registry.IsRegistered(source.GetType(), sourceProperty))
|
||||
{
|
||||
throw new ArgumentException($"Relay source property is not registered for: {source.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetProperty.IsAttached)
|
||||
{
|
||||
if (!registry.IsRegistered(target.GetType(), targetProperty))
|
||||
{
|
||||
throw new ArgumentException($"Relay target property is not registered for: {target.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
var descriptor = new IndexerDescriptor
|
||||
{
|
||||
@ -30,17 +38,22 @@ public static class BindUtils
|
||||
}
|
||||
|
||||
public static IDisposable RelayBind<TSource, TResult>(AvaloniaObject source,
|
||||
AvaloniaProperty<TSource> sourceProperty,
|
||||
AvaloniaObject target, AvaloniaProperty<TResult> targetProperty,
|
||||
Func<TSource, TResult> converter,
|
||||
BindingPriority priority = BindingPriority.Template)
|
||||
AvaloniaProperty<TSource> sourceProperty,
|
||||
AvaloniaObject target,
|
||||
AvaloniaProperty<TResult> targetProperty,
|
||||
Func<TSource, TResult> converter,
|
||||
BindingPriority priority = BindingPriority.Template)
|
||||
{
|
||||
var registry = AvaloniaPropertyRegistry.Instance;
|
||||
if (!registry.IsRegistered(source.GetType(), sourceProperty))
|
||||
{
|
||||
throw new ArgumentException($"Relay source property is not registered for: {source.GetType().Name}.");
|
||||
}
|
||||
|
||||
if (!registry.IsRegistered(target.GetType(), targetProperty))
|
||||
{
|
||||
throw new ArgumentException($"Relay target property is not registered for: {target.GetType().Name}.");
|
||||
}
|
||||
|
||||
return target.Bind(targetProperty, source.GetObservable(sourceProperty, converter), priority);
|
||||
}
|
||||
|
@ -15,7 +15,11 @@ internal static class XYFocusHelpers
|
||||
XYFocusNavigationModes modes,
|
||||
KeyDeviceType? keyDeviceType)
|
||||
{
|
||||
if (!keyDeviceType.HasValue) return true;
|
||||
if (!keyDeviceType.HasValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyDeviceType.GetValueOrDefault())
|
||||
{
|
||||
case KeyDeviceType.Keyboard:
|
||||
|
@ -30,7 +30,10 @@ internal class TransformTrackingHelper : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_visual == null) return;
|
||||
if (_visual == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnsubscribeFromParents();
|
||||
_visual.AttachedToVisualTree -= OnAttachedToVisualTree;
|
||||
@ -46,7 +49,10 @@ internal class TransformTrackingHelper : IDisposable
|
||||
{
|
||||
visual.AttachedToVisualTree += OnAttachedToVisualTree;
|
||||
visual.DetachedFromVisualTree -= OnDetachedFromVisualTree;
|
||||
if (visual.GetVisualRoot() is not null) SubscribeToParents();
|
||||
if (visual.GetVisualRoot() is not null)
|
||||
{
|
||||
SubscribeToParents();
|
||||
}
|
||||
|
||||
UpdateMatrix();
|
||||
}
|
||||
@ -74,7 +80,11 @@ internal class TransformTrackingHelper : IDisposable
|
||||
|
||||
private void UnsubscribeFromParents()
|
||||
{
|
||||
foreach (var v in _propertyChangedSubscriptions) v.PropertyChanged -= _propertyChangedHandler;
|
||||
foreach (var v in _propertyChangedSubscriptions)
|
||||
{
|
||||
v.PropertyChanged -= _propertyChangedHandler;
|
||||
}
|
||||
|
||||
_propertyChangedSubscriptions.Clear();
|
||||
}
|
||||
|
||||
@ -82,7 +92,9 @@ internal class TransformTrackingHelper : IDisposable
|
||||
{
|
||||
Matrix? matrix = null;
|
||||
if (_visual != null && _visual.GetVisualRoot() != null)
|
||||
{
|
||||
matrix = _visual.TransformToVisual((Visual)_visual.GetVisualRoot()!);
|
||||
}
|
||||
|
||||
if (Matrix != matrix)
|
||||
{
|
||||
@ -99,7 +111,10 @@ internal class TransformTrackingHelper : IDisposable
|
||||
|
||||
private void EnqueueForUpdate()
|
||||
{
|
||||
if (_queuedForUpdate) return;
|
||||
if (_queuedForUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queuedForUpdate = true;
|
||||
var priority = (DispatcherPriority)AfterRenderFieldInfo.GetValue(null)!;
|
||||
@ -109,7 +124,10 @@ internal class TransformTrackingHelper : IDisposable
|
||||
private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
e.TryGetProperty<bool>("IsEffectiveValueChange", out var isEffectiveValueChange);
|
||||
if (isEffectiveValueChange && e.Property == Visual.BoundsProperty) EnqueueForUpdate();
|
||||
if (isEffectiveValueChange && e.Property == Visual.BoundsProperty)
|
||||
{
|
||||
EnqueueForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs)
|
||||
|
@ -13,10 +13,10 @@ public static class BoxShadowExtensions
|
||||
var spreadRadius = boxShadow.Spread;
|
||||
|
||||
var value = Math.Max(blurRadius + spreadRadius, 0.0); // 可以正负抵消
|
||||
var left = Math.Max(value - offsetX, 0.0);
|
||||
var right = Math.Max(value + offsetX, 0.0);
|
||||
var top = Math.Max(value - offsetY, 0.0);
|
||||
var bottom = Math.Max(value + offsetY, 0.0);
|
||||
var left = Math.Max(value - offsetX, 0.0);
|
||||
var right = Math.Max(value + offsetX, 0.0);
|
||||
var top = Math.Max(value - offsetY, 0.0);
|
||||
var bottom = Math.Max(value + offsetY, 0.0);
|
||||
return new Thickness(left, top, right, bottom);
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ public static class ColorExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the perceived brightness of the color, from 0-255.
|
||||
/// Returns the perceived brightness of the color, from 0-255.
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
@ -118,7 +118,7 @@ public static class ColorExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the perceived luminance of a color, from 0-1.
|
||||
/// Returns the perceived luminance of a color, from 0-1.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static double GetLuminance(this Color color)
|
||||
@ -131,24 +131,37 @@ public static class ColorExtensions
|
||||
double g = 0;
|
||||
double b = 0;
|
||||
if (MathUtils.LessThanOrClose(rsRGB, 0.03928))
|
||||
{
|
||||
r = rsRGB / 12.92;
|
||||
}
|
||||
else
|
||||
|
||||
// eslint-disable-next-line prefer-exponentiation-operator
|
||||
{
|
||||
r = Math.Pow((rsRGB + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
if (gsRGB <= 0.03928)
|
||||
{
|
||||
g = gsRGB / 12.92;
|
||||
}
|
||||
else
|
||||
|
||||
// eslint-disable-next-line prefer-exponentiation-operator
|
||||
{
|
||||
g = Math.Pow((gsRGB + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
if (bsRGB <= 0.03928)
|
||||
{
|
||||
b = bsRGB / 12.92;
|
||||
}
|
||||
else
|
||||
|
||||
// eslint-disable-next-line prefer-exponentiation-operator
|
||||
{
|
||||
b = Math.Pow((bsRGB + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
}
|
||||
|
@ -9,35 +9,31 @@ public enum WCAG2Level
|
||||
AAA
|
||||
}
|
||||
|
||||
|
||||
public enum WCAG2Size
|
||||
{
|
||||
Large,
|
||||
Small
|
||||
}
|
||||
|
||||
|
||||
public class WCAG2Parms
|
||||
{
|
||||
public WCAG2Level Level { get; set; } = WCAG2Level.AA;
|
||||
public WCAG2Size Size { get; set; } = WCAG2Size.Small;
|
||||
}
|
||||
|
||||
|
||||
public class WCAG2FallbackParms : WCAG2Parms
|
||||
{
|
||||
public bool IncludeFallbackColors { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public static class ColorUtils
|
||||
{
|
||||
public static Color FromRgbF(double alpha, double red, double green, double blue)
|
||||
{
|
||||
return Color.FromArgb((byte)Math.Round(alpha * 255d),
|
||||
(byte)Math.Round(red * 255d),
|
||||
(byte)Math.Round(green * 255d),
|
||||
(byte)Math.Round(blue * 255d));
|
||||
(byte)Math.Round(red * 255d),
|
||||
(byte)Math.Round(green * 255d),
|
||||
(byte)Math.Round(blue * 255d));
|
||||
}
|
||||
|
||||
public static Color FromRgbF(double red, double green, double blue)
|
||||
@ -123,7 +119,10 @@ public static class ColorUtils
|
||||
var fG = frontColor.GetGreenF();
|
||||
var fB = frontColor.GetBlueF();
|
||||
var originAlpha = frontColor.GetAlphaF();
|
||||
if (originAlpha < 1d) return frontColor;
|
||||
if (originAlpha < 1d)
|
||||
{
|
||||
return frontColor;
|
||||
}
|
||||
|
||||
var bR = backgroundColor.GetRedF();
|
||||
var bG = backgroundColor.GetGreenF();
|
||||
@ -135,7 +134,9 @@ public static class ColorUtils
|
||||
var g = Math.Round((fG - bG * (1d - fA)) / fA);
|
||||
var b = Math.Round((fB - bB * (1d - fA)) / fA);
|
||||
if (IsStableColor(r) && IsStableColor(g) && IsStableColor(b))
|
||||
{
|
||||
return FromRgbF(Math.Round(fA * 100d) / 100d, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
@ -145,7 +146,10 @@ public static class ColorUtils
|
||||
|
||||
public static Color ParseCssRgbColor(string colorExpr)
|
||||
{
|
||||
if (TryParseCssRgbColor(colorExpr, out var color)) return color;
|
||||
if (TryParseCssRgbColor(colorExpr, out var color))
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
throw new FormatException($"Invalid color string: '{colorExpr}'.");
|
||||
}
|
||||
@ -153,39 +157,63 @@ public static class ColorUtils
|
||||
public static bool TryParseCssRgbColor(string? colorExpr, out Color color)
|
||||
{
|
||||
color = default;
|
||||
if (string.IsNullOrEmpty(colorExpr)) return false;
|
||||
if (string.IsNullOrEmpty(colorExpr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (colorExpr[0] == '#') return Color.TryParse(colorExpr, out color);
|
||||
if (colorExpr[0] == '#')
|
||||
{
|
||||
return Color.TryParse(colorExpr, out color);
|
||||
}
|
||||
|
||||
var isRgba = colorExpr.StartsWith("rgba", StringComparison.InvariantCultureIgnoreCase);
|
||||
var isRgb = false;
|
||||
if (!isRgba) isRgb = colorExpr.StartsWith("rgb", StringComparison.InvariantCultureIgnoreCase);
|
||||
var isRgba = colorExpr.StartsWith("rgba", StringComparison.InvariantCultureIgnoreCase);
|
||||
var isRgb = false;
|
||||
if (!isRgba)
|
||||
{
|
||||
isRgb = colorExpr.StartsWith("rgb", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
if (isRgb || isRgba)
|
||||
{
|
||||
var leftParen = colorExpr.IndexOf('(');
|
||||
var rightParen = colorExpr.IndexOf(')');
|
||||
if (leftParen == -1 || rightParen == -1) return false;
|
||||
if (leftParen == -1 || rightParen == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var parts = new List<string>(colorExpr.Substring(leftParen + 1, rightParen - leftParen - 1)
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
if (isRgb)
|
||||
{
|
||||
if (parts.Count != 3) return false;
|
||||
if (parts.Count != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add("255");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parts.Count != 4) return false;
|
||||
if (parts.Count != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var rgbaValues = new List<int>();
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (int.TryParse(part, out var partValue))
|
||||
{
|
||||
rgbaValues.Add(partValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
color = Color.FromArgb((byte)rgbaValues[0], (byte)rgbaValues[1], (byte)rgbaValues[2], (byte)rgbaValues[3]);
|
||||
return true;
|
||||
@ -197,8 +225,8 @@ public static class ColorUtils
|
||||
/// Readability Functions
|
||||
/// ---------------------
|
||||
/// <http:// www.w3.org/ TR/2008/ REC-WCAG20-20081211/# contrast-ratiodef ( WCAG Version 2)
|
||||
/// AKA ` contrast`
|
||||
/// Analyze the 2 colors and returns the color contrast defined by ( WCAG Version
|
||||
/// AKA ` contrast`
|
||||
/// Analyze the 2 colors and returns the color contrast defined by ( WCAG Version
|
||||
/// 2
|
||||
/// )
|
||||
public static double Readability(Color color1, Color color2)
|
||||
@ -222,14 +250,20 @@ public static class ColorUtils
|
||||
var readabilityLevel = Readability(color1, color2);
|
||||
if (wcag2.Level == WCAG2Level.AA)
|
||||
{
|
||||
if (wcag2.Size == WCAG2Size.Large) return MathUtils.GreaterThanOrClose(readabilityLevel, 3);
|
||||
if (wcag2.Size == WCAG2Size.Large)
|
||||
{
|
||||
return MathUtils.GreaterThanOrClose(readabilityLevel, 3);
|
||||
}
|
||||
|
||||
return MathUtils.GreaterThanOrClose(readabilityLevel, 4.5);
|
||||
}
|
||||
|
||||
if (wcag2.Level == WCAG2Level.AAA)
|
||||
{
|
||||
if (wcag2.Size == WCAG2Size.Large) return MathUtils.GreaterThanOrClose(readabilityLevel, 4.5);
|
||||
if (wcag2.Size == WCAG2Size.Large)
|
||||
{
|
||||
return MathUtils.GreaterThanOrClose(readabilityLevel, 4.5);
|
||||
}
|
||||
|
||||
return MathUtils.GreaterThanOrClose(readabilityLevel, 7);
|
||||
}
|
||||
@ -272,7 +306,9 @@ public static class ColorUtils
|
||||
|
||||
if (IsReadable(baseColor, bestColor!.Value, new WCAG2Parms { Level = args.Level, Size = args.Size }) ||
|
||||
!args.IncludeFallbackColors)
|
||||
{
|
||||
return bestColor;
|
||||
}
|
||||
|
||||
args.IncludeFallbackColors = false;
|
||||
return MostReadable(baseColor, new List<Color>
|
||||
|
@ -13,8 +13,8 @@ public static class CommonShapeBuilder
|
||||
using var context = checkMark.Open();
|
||||
|
||||
var startPoint = new Point(size.Width * 0.25, size.Height * 0.5);
|
||||
var midPoint = new Point(size.Width * 0.4, size.Height * 0.7);
|
||||
var endPoint = new Point(size.Width * 0.7, size.Height * 0.3);
|
||||
var midPoint = new Point(size.Width * 0.4, size.Height * 0.7);
|
||||
var endPoint = new Point(size.Width * 0.7, size.Height * 0.3);
|
||||
|
||||
context.BeginFigure(startPoint, true);
|
||||
context.LineTo(midPoint);
|
||||
@ -25,7 +25,7 @@ public static class CommonShapeBuilder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个以矩形中点为圆心,以宽和高最小的一半为半径的且指定角度的圆弧
|
||||
/// 生成一个以矩形中点为圆心,以宽和高最小的一半为半径的且指定角度的圆弧
|
||||
/// </summary>
|
||||
/// <param name="rect"></param>
|
||||
/// <param name="startAngle"></param>
|
||||
@ -33,7 +33,10 @@ public static class CommonShapeBuilder
|
||||
/// <returns></returns>
|
||||
public static Geometry BuildArc(Rect rect, double startAngle, double sweepAngle)
|
||||
{
|
||||
if (MathUtils.IsZero(sweepAngle)) return new StreamGeometry();
|
||||
if (MathUtils.IsZero(sweepAngle))
|
||||
{
|
||||
return new StreamGeometry();
|
||||
}
|
||||
|
||||
var angle1 = MathUtilities.Deg2Rad(startAngle);
|
||||
var angle2 = angle1 + MathUtilities.Deg2Rad(sweepAngle);
|
||||
@ -47,7 +50,9 @@ public static class CommonShapeBuilder
|
||||
if (MathUtils.AreClose(normStart, normEnd) && !MathUtils.AreClose(startAngle, sweepAngle))
|
||||
|
||||
// Complete ring.
|
||||
{
|
||||
return new EllipseGeometry(rect);
|
||||
}
|
||||
|
||||
// Partial arc.
|
||||
var deflatedRect = rect;
|
||||
@ -55,7 +60,7 @@ public static class CommonShapeBuilder
|
||||
var centerX = rect.Center.X;
|
||||
var centerY = rect.Center.Y;
|
||||
|
||||
var radiusX = deflatedRect.Width / 2;
|
||||
var radiusX = deflatedRect.Width / 2;
|
||||
var radiusY = deflatedRect.Height / 2;
|
||||
|
||||
var angleGap = RadToNormRad(sweepAngle - startAngle);
|
||||
|
@ -7,19 +7,24 @@ namespace AtomUI.Media;
|
||||
public static class DrawingContextExtensions
|
||||
{
|
||||
public static void DrawPilledRect(this DrawingContext context, IBrush? brush, IPen? pen, Rect rect,
|
||||
Orientation orientation = Orientation.Horizontal,
|
||||
BoxShadows boxShadows = default)
|
||||
Orientation orientation = Orientation.Horizontal,
|
||||
BoxShadows boxShadows = default)
|
||||
{
|
||||
double pillRadius;
|
||||
if (orientation == Orientation.Horizontal)
|
||||
{
|
||||
pillRadius = rect.Height / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
pillRadius = rect.Width / 2;
|
||||
}
|
||||
|
||||
context.DrawRectangle(brush, pen, rect, pillRadius, pillRadius, boxShadows);
|
||||
}
|
||||
|
||||
public static void DrawArc(this DrawingContext context, IPen? pen,
|
||||
Rect rect, double startAngle, double sweepAngle)
|
||||
Rect rect, double startAngle, double sweepAngle)
|
||||
{
|
||||
var geometry = CommonShapeBuilder.BuildArc(rect, startAngle, sweepAngle);
|
||||
context.DrawGeometry(null, pen, geometry);
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
public static class FontUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 将 value 的值转换为像素
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="fontSize"></param>
|
||||
/// <param name="renderScaling"></param>
|
||||
/// <returns></returns>
|
||||
public static double ConvertEmToPixel(double value, double fontSize, double renderScaling = 1.0)
|
||||
/// <summary>
|
||||
/// 将 value 的值转换为像素
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="fontSize"></param>
|
||||
/// <param name="renderScaling"></param>
|
||||
/// <returns></returns>
|
||||
public static double ConvertEmToPixel(double value, double fontSize, double renderScaling = 1.0)
|
||||
{
|
||||
var fontSizePx = fontSize * value * renderScaling;
|
||||
return fontSizePx * value;
|
||||
|
@ -23,7 +23,7 @@ public static class GeometryUtils
|
||||
var newY = rect.Y - offsetYChange;
|
||||
|
||||
// 计算新的矩形宽度和高度
|
||||
var newWidth = rect.Width * scaleFactorX;
|
||||
var newWidth = rect.Width * scaleFactorX;
|
||||
var newHeight = rect.Height * scaleFactorY;
|
||||
|
||||
return new Rect(newX, newY, newWidth, newHeight);
|
||||
@ -31,7 +31,10 @@ public static class GeometryUtils
|
||||
|
||||
public static double CornerRadiusScalarValue(CornerRadius cornerRadius, bool maxWhenNotUniform = true)
|
||||
{
|
||||
if (cornerRadius.IsUniform) return cornerRadius.TopLeft;
|
||||
if (cornerRadius.IsUniform)
|
||||
{
|
||||
return cornerRadius.TopLeft;
|
||||
}
|
||||
|
||||
return maxWhenNotUniform
|
||||
? Math.Max(cornerRadius.TopLeft,
|
||||
|
@ -10,7 +10,6 @@ public class TransitionCompletedEventArgs : EventArgs
|
||||
public bool Status { get; }
|
||||
}
|
||||
|
||||
|
||||
internal interface INotifyTransitionCompleted
|
||||
{
|
||||
public IObservable<bool> CompletedObservable { get; }
|
||||
|
@ -7,14 +7,14 @@ public static class InterpolateUtils
|
||||
public static Color ColorInterpolate(Color fromColor, Color toColor, double progress)
|
||||
{
|
||||
var a1 = fromColor.GetAlphaF();
|
||||
var r1 = fromColor.GetRedF() * a1;
|
||||
var r1 = fromColor.GetRedF() * a1;
|
||||
var g1 = fromColor.GetGreenF() * a1;
|
||||
var b1 = fromColor.GetBlueF() * a1;
|
||||
var b1 = fromColor.GetBlueF() * a1;
|
||||
|
||||
var a2 = toColor.GetAlphaF();
|
||||
var r2 = toColor.GetRedF() * a2;
|
||||
var r2 = toColor.GetRedF() * a2;
|
||||
var g2 = toColor.GetGreenF() * a2;
|
||||
var b2 = toColor.GetBlueF() * a2;
|
||||
var b2 = toColor.GetBlueF() * a2;
|
||||
|
||||
var r = DoubleInterpolate(r1, r2, progress);
|
||||
var g = DoubleInterpolate(g1, g2, progress);
|
||||
|
@ -6,26 +6,26 @@ namespace AtomUI.Media;
|
||||
|
||||
public static class PenUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Smart reuse and update pen properties.
|
||||
/// </summary>
|
||||
/// <param name="pen">Old pen to modify.</param>
|
||||
/// <param name="brush">The brush used to draw.</param>
|
||||
/// <param name="thickness">The stroke thickness.</param>
|
||||
/// <param name="strokeDashArray">The stroke dask array.</param>
|
||||
/// <param name="strokeDaskOffset">The stroke dask offset.</param>
|
||||
/// <param name="lineCap">The line cap.</param>
|
||||
/// <param name="lineJoin">The line join.</param>
|
||||
/// <param name="miterLimit">The miter limit.</param>
|
||||
/// <returns>If a new instance was created and visual invalidation required.</returns>
|
||||
internal static bool TryModifyOrCreate(ref IPen? pen,
|
||||
IBrush? brush,
|
||||
double thickness,
|
||||
IList<double>? strokeDashArray = null,
|
||||
double strokeDaskOffset = default,
|
||||
PenLineCap lineCap = PenLineCap.Flat,
|
||||
PenLineJoin lineJoin = PenLineJoin.Miter,
|
||||
double miterLimit = 10.0)
|
||||
/// <summary>
|
||||
/// Smart reuse and update pen properties.
|
||||
/// </summary>
|
||||
/// <param name="pen">Old pen to modify.</param>
|
||||
/// <param name="brush">The brush used to draw.</param>
|
||||
/// <param name="thickness">The stroke thickness.</param>
|
||||
/// <param name="strokeDashArray">The stroke dask array.</param>
|
||||
/// <param name="strokeDaskOffset">The stroke dask offset.</param>
|
||||
/// <param name="lineCap">The line cap.</param>
|
||||
/// <param name="lineJoin">The line join.</param>
|
||||
/// <param name="miterLimit">The miter limit.</param>
|
||||
/// <returns>If a new instance was created and visual invalidation required.</returns>
|
||||
internal static bool TryModifyOrCreate(ref IPen? pen,
|
||||
IBrush? brush,
|
||||
double thickness,
|
||||
IList<double>? strokeDashArray = null,
|
||||
double strokeDaskOffset = default,
|
||||
PenLineCap lineCap = PenLineCap.Flat,
|
||||
PenLineJoin lineJoin = PenLineJoin.Miter,
|
||||
double miterLimit = 10.0)
|
||||
{
|
||||
var previousPen = pen;
|
||||
if (brush is null)
|
||||
@ -39,9 +39,11 @@ public static class PenUtils
|
||||
|
||||
// strokeDashArray can be IList (instead of AvaloniaList) in future
|
||||
// So, if it supports notification - create a mutable DashStyle
|
||||
{
|
||||
dashStyle = strokeDashArray is INotifyCollectionChanged
|
||||
? new DashStyle(strokeDashArray, strokeDaskOffset)
|
||||
: new ImmutableDashStyle(strokeDashArray, strokeDaskOffset);
|
||||
}
|
||||
|
||||
if (brush is IImmutableBrush immutableBrush && dashStyle is null or ImmutableDashStyle)
|
||||
{
|
||||
|
@ -9,6 +9,6 @@ public class PixelPointTransition : InterpolatingTransitionBase<PixelPoint>
|
||||
{
|
||||
var delta = newValue - oldValue;
|
||||
return new PixelPoint((int)Math.Round(delta.X * progress + oldValue.X),
|
||||
(int)Math.Round(delta.Y * progress + oldValue.Y));
|
||||
(int)Math.Round(delta.Y * progress + oldValue.Y));
|
||||
}
|
||||
}
|
@ -8,12 +8,17 @@ public class SolidColorBrushTransition : InterpolatingTransitionBase<IBrush?>
|
||||
{
|
||||
protected override IBrush? Interpolate(double progress, IBrush? from, IBrush? to)
|
||||
{
|
||||
if (from is null || to is null) return progress >= 0.5 ? to : from;
|
||||
if (from is null || to is null)
|
||||
{
|
||||
return progress >= 0.5 ? to : from;
|
||||
}
|
||||
|
||||
if (from is ISolidColorBrush fromBrush && to is ISolidColorBrush toBrush)
|
||||
{
|
||||
return new ImmutableSolidColorBrush(
|
||||
InterpolateUtils.ColorInterpolate(fromBrush.Color, toBrush.Color, progress),
|
||||
DoubleInterpolate(progress, from.Opacity, to.Opacity));
|
||||
}
|
||||
|
||||
// TODO 不知道这样返回是否合适
|
||||
return from;
|
||||
|
@ -7,10 +7,10 @@ namespace AtomUI.Media;
|
||||
public static class TextUtils
|
||||
{
|
||||
public static Size CalculateTextSize(string text,
|
||||
double fontSize,
|
||||
FontFamily fontFamily,
|
||||
FontStyle fontStyle = FontStyle.Normal,
|
||||
FontWeight fontWeight = FontWeight.Normal)
|
||||
double fontSize,
|
||||
FontFamily fontFamily,
|
||||
FontStyle fontStyle = FontStyle.Normal,
|
||||
FontWeight fontWeight = FontWeight.Normal)
|
||||
{
|
||||
var typeface = new Typeface(fontFamily, fontStyle, fontWeight);
|
||||
using var textLayout = new TextLayout(text, typeface, fontSize, null);
|
||||
|
@ -17,7 +17,6 @@ public enum TransitionKind
|
||||
TransformOperations
|
||||
}
|
||||
|
||||
|
||||
public record class MotionConfig
|
||||
{
|
||||
public MotionConfig(AvaloniaProperty targetProperty, object? startValue = null, object? endValue = null)
|
||||
@ -35,7 +34,6 @@ public record class MotionConfig
|
||||
public TransitionKind TransitionKind { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
{
|
||||
// 定义我们目前支持的动效属性
|
||||
@ -108,7 +106,7 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建动效动画对象
|
||||
/// 创建动效动画对象
|
||||
/// </summary>
|
||||
/// <param name="motionTarget"></param>
|
||||
/// <returns></returns>
|
||||
@ -127,7 +125,9 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
{
|
||||
var transition = _transitions[i];
|
||||
if (transition is INotifyTransitionCompleted notifyTransitionCompleted)
|
||||
{
|
||||
completedObservables[i] = notifyTransitionCompleted.CompletedObservable;
|
||||
}
|
||||
}
|
||||
|
||||
CompletedObservable =
|
||||
@ -164,9 +164,13 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
{
|
||||
TransitionBase transition = default!;
|
||||
if (config.TransitionKind == TransitionKind.Double)
|
||||
{
|
||||
transition = new NotifiableDoubleTransition();
|
||||
}
|
||||
else if (config.TransitionKind == TransitionKind.TransformOperations)
|
||||
{
|
||||
transition = new NotifiableTransformOperationsTransition();
|
||||
}
|
||||
|
||||
transition.Property = config.Property;
|
||||
transition.Duration = config.MotionDuration;
|
||||
@ -176,7 +180,11 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
|
||||
protected MotionConfig? GetMotionConfig(AvaloniaProperty property)
|
||||
{
|
||||
if (_motionConfigs.TryGetValue(property, out var motionConfig)) return motionConfig;
|
||||
if (_motionConfigs.TryGetValue(property, out var motionConfig))
|
||||
{
|
||||
return motionConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -187,12 +195,12 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算顶层动画渲染层的大小
|
||||
/// 计算顶层动画渲染层的大小
|
||||
/// </summary>
|
||||
/// <param name="motionTargetSize">
|
||||
/// 动画目标控件的大小,如果动画直接调度到控件本身,则是控件本身的大小,如果是顶层动画渲染,那么就是 ghost
|
||||
/// 的大小,如果有阴影这个大小包含阴影的 thickness
|
||||
/// 目前的实现没有加一个固定的 Padding
|
||||
/// 动画目标控件的大小,如果动画直接调度到控件本身,则是控件本身的大小,如果是顶层动画渲染,那么就是 ghost
|
||||
/// 的大小,如果有阴影这个大小包含阴影的 thickness
|
||||
/// 目前的实现没有加一个固定的 Padding
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
internal virtual Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -201,7 +209,7 @@ public abstract class AbstractMotion : AvaloniaObject, IMotion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算动画层的全局坐标
|
||||
/// 计算动画层的全局坐标
|
||||
/// </summary>
|
||||
/// <param name="motionTargetSize">动画目标控件的大小,包含阴影</param>
|
||||
/// <param name="motionTargetPosition">动画目标控件的最终全局坐标位置</param>
|
||||
|
@ -10,7 +10,11 @@ internal class AnimationTargetPanel : Panel
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (InAnimation && _cacheMeasureSize != default) return _cacheMeasureSize;
|
||||
if (InAnimation && _cacheMeasureSize != default)
|
||||
{
|
||||
return _cacheMeasureSize;
|
||||
}
|
||||
|
||||
_cacheMeasureSize = base.MeasureOverride(availableSize);
|
||||
return _cacheMeasureSize;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class CollapseMotion : AbstractMotion
|
||||
public MotionConfig? HeightConfig => GetMotionConfig(MotionHeightProperty);
|
||||
|
||||
/// <summary>
|
||||
/// 收起的方向,垂直还是水平方向
|
||||
/// 收起的方向,垂直还是水平方向
|
||||
/// </summary>
|
||||
public Orientation Orientation { get; set; } = Orientation.Vertical;
|
||||
|
||||
@ -49,28 +49,35 @@ public class CollapseMotion : AbstractMotion
|
||||
if (config.Property == MotionHeightProperty)
|
||||
{
|
||||
if (!double.IsNaN(motionTarget.Height))
|
||||
{
|
||||
config.StartValue = Math.Ceiling(motionTarget.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.StartValue = Math.Ceiling(motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
else if (config.Property == MotionWidthProperty)
|
||||
{
|
||||
if (!double.IsNaN(motionTarget.Width))
|
||||
{
|
||||
config.StartValue = Math.Ceiling(motionTarget.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.StartValue = Math.Ceiling(motionTarget.DesiredSize.Width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ExpandMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
public MotionConfig? HeightConfig => GetMotionConfig(MotionHeightProperty);
|
||||
|
||||
/// <summary>
|
||||
/// 展开的方向,垂直还是水平方向
|
||||
/// 展开的方向,垂直还是水平方向
|
||||
/// </summary>
|
||||
public Orientation Orientation { get; set; } = Orientation.Vertical;
|
||||
|
||||
@ -109,16 +116,24 @@ public class ExpandMotion : AbstractMotion
|
||||
if (config.Property == MotionHeightProperty)
|
||||
{
|
||||
if (!double.IsNaN(motionTarget.Height))
|
||||
{
|
||||
config.EndValue = Math.Ceiling(motionTarget.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.EndValue = Math.Ceiling(motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
else if (config.Property == MotionWidthProperty)
|
||||
{
|
||||
if (!double.IsNaN(motionTarget.Width))
|
||||
{
|
||||
config.EndValue = Math.Ceiling(motionTarget.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.EndValue = Math.Ceiling(motionTarget.DesiredSize.Width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ namespace AtomUI.Controls.MotionScene;
|
||||
|
||||
public class Director : IDirector
|
||||
{
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
private readonly Dictionary<IMotionActor, MotionActorState> _states;
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
|
||||
public Director()
|
||||
{
|
||||
@ -18,25 +18,35 @@ public class Director : IDirector
|
||||
public static IDirector? Instance => AvaloniaLocator.Current.GetService<IDirector>();
|
||||
|
||||
/// <summary>
|
||||
/// 目前的实现暂时一个 Actor 只能投递一次,后面可以实现一个等待队列
|
||||
/// 目前的实现暂时一个 Actor 只能投递一次,后面可以实现一个等待队列
|
||||
/// </summary>
|
||||
/// <param name="actor"></param>
|
||||
public void Schedule(MotionActor actor)
|
||||
{
|
||||
if (_states.ContainsKey(actor)) return;
|
||||
if (_states.ContainsKey(actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
actor.NotifyPostedToDirector();
|
||||
SceneLayer? sceneLayer = default;
|
||||
if (actor.DispatchInSceneLayer) sceneLayer = PrepareSceneLayer(actor);
|
||||
SceneLayer? sceneLayer = default;
|
||||
if (actor.DispatchInSceneLayer)
|
||||
{
|
||||
sceneLayer = PrepareSceneLayer(actor);
|
||||
}
|
||||
|
||||
_compositeDisposable = new CompositeDisposable();
|
||||
_compositeDisposable.Add(Disposable.Create(sceneLayer, state =>
|
||||
{
|
||||
if (sceneLayer is not null)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await Task.Delay(300);
|
||||
sceneLayer.Hide();
|
||||
sceneLayer.Dispose();
|
||||
});
|
||||
}
|
||||
}));
|
||||
var state = new MotionActorState(actor, _compositeDisposable);
|
||||
_states.Add(actor, state);
|
||||
@ -64,8 +74,10 @@ public class Director : IDirector
|
||||
private SceneLayer PrepareSceneLayer(MotionActor actor)
|
||||
{
|
||||
if (actor.SceneParent is null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"When the DispatchInSceneLayer property is true, the SceneParent property cannot be null.");
|
||||
}
|
||||
|
||||
// TODO 这里除了 Popup 这种顶层元素以外,还会不会有其他的顶层元素种类
|
||||
// 暂时先处理 Popup 这种情况
|
||||
@ -91,7 +103,9 @@ public class Director : IDirector
|
||||
actor.NotifyMotionPreStart();
|
||||
MotionPreStart?.Invoke(this, new MotionEventArgs(actor));
|
||||
if (actor.Motion.CompletedObservable is null)
|
||||
{
|
||||
throw new InvalidOperationException("The CompletedObservable property of the Motion is empty.");
|
||||
}
|
||||
|
||||
// 设置相关的完成检测
|
||||
_compositeDisposable?.Add(actor.Motion.CompletedObservable.Subscribe(
|
||||
@ -116,7 +130,11 @@ public class Director : IDirector
|
||||
{
|
||||
if (_states.TryGetValue(actor, out var state))
|
||||
{
|
||||
if (state.SceneLayer is not null) state.SceneLayer.Opacity = 0;
|
||||
if (state.SceneLayer is not null)
|
||||
{
|
||||
state.SceneLayer.Opacity = 0;
|
||||
}
|
||||
|
||||
actor.NotifyMotionCompleted();
|
||||
MotionCompleted?.Invoke(this, new MotionEventArgs(actor));
|
||||
state.Dispose();
|
||||
@ -124,7 +142,6 @@ public class Director : IDirector
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MotionActorState : IDisposable
|
||||
{
|
||||
private readonly IDisposable _cleanup;
|
||||
@ -145,7 +162,6 @@ public class Director : IDirector
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MotionEventArgs : EventArgs
|
||||
{
|
||||
public MotionActor MotionActor;
|
||||
|
@ -22,7 +22,6 @@ public class FadeInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class FadeOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
|
@ -8,7 +8,7 @@ public interface IMotion
|
||||
public IObservable<bool>? CompletedObservable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前动效激活的动画属性列表
|
||||
/// 获取当前动效激活的动画属性列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IList<AvaloniaProperty> GetActivatedProperties();
|
||||
|
@ -12,8 +12,8 @@ using Avalonia.VisualTree;
|
||||
namespace AtomUI.MotionScene;
|
||||
|
||||
/// <summary>
|
||||
/// 动效配置类,只要给 Director 提供动效相关信息
|
||||
/// 动效驱动 Actor 的属性,然后由 Actor 驱动动画控件,防止污染动画控件的 Transitions 配置
|
||||
/// 动效配置类,只要给 Director 提供动效相关信息
|
||||
/// 动效驱动 Actor 的属性,然后由 Actor 驱动动画控件,防止污染动画控件的 Transitions 配置
|
||||
/// </summary>
|
||||
public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
@ -31,6 +31,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
|
||||
private static readonly MethodInfo EnableTransitionsMethodInfo;
|
||||
private static readonly MethodInfo DisableTransitionsMethodInfo;
|
||||
private readonly Dictionary<AvaloniaProperty, AnimationState> _transitionsMap;
|
||||
|
||||
protected Control? _ghost;
|
||||
protected AbstractMotion _motion;
|
||||
@ -40,7 +41,6 @@ public class MotionActor : Animatable, IMotionActor
|
||||
private ITransform? _originRenderTransform;
|
||||
private RelativePoint _originRenderTransformOrigin;
|
||||
private double _originWidth;
|
||||
private readonly Dictionary<AvaloniaProperty, AnimationState> _transitionsMap;
|
||||
|
||||
static MotionActor()
|
||||
{
|
||||
@ -86,7 +86,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当 DispatchInSceneLayer 为 true 的时候,必须指定一个动画 SceneLayer 的父窗口,最好不要是 Popup
|
||||
/// 当 DispatchInSceneLayer 为 true 的时候,必须指定一个动画 SceneLayer 的父窗口,最好不要是 Popup
|
||||
/// </summary>
|
||||
public TopLevel? SceneParent { get; set; }
|
||||
|
||||
@ -97,7 +97,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
public bool CompletedStatus { get; internal set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 动画实体
|
||||
/// 动画实体
|
||||
/// </summary>
|
||||
public Control MotionTarget { get; set; }
|
||||
|
||||
@ -111,7 +111,11 @@ public class MotionActor : Animatable, IMotionActor
|
||||
var oldValue = args.OldValue;
|
||||
var newValue = args.NewValue;
|
||||
var priority = args.Priority;
|
||||
if (!actor._transitionsMap.ContainsKey(property)) return;
|
||||
if (!actor._transitionsMap.ContainsKey(property))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var state = actor._transitionsMap[property];
|
||||
if (actor.IsAnimating(property) && priority == BindingPriority.Animation)
|
||||
{
|
||||
@ -124,7 +128,9 @@ public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
var transition = state.Transition;
|
||||
if (transition is INotifyTransitionCompleted notifyTransitionCompleted)
|
||||
{
|
||||
notifyTransitionCompleted.NotifyTransitionCompleted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (property.PropertyType.IsAssignableTo(typeof(ITransform)))
|
||||
@ -135,7 +141,9 @@ public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
var transition = state.Transition;
|
||||
if (transition is INotifyTransitionCompleted notifyTransitionCompleted)
|
||||
{
|
||||
notifyTransitionCompleted.NotifyTransitionCompleted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,10 +152,13 @@ public class MotionActor : Animatable, IMotionActor
|
||||
public bool IsSupportMotionProperty(AvaloniaProperty property)
|
||||
{
|
||||
if (property == AbstractMotion.MotionOpacityProperty ||
|
||||
property == AbstractMotion.MotionWidthProperty ||
|
||||
property == AbstractMotion.MotionHeightProperty ||
|
||||
property == AbstractMotion.MotionWidthProperty ||
|
||||
property == AbstractMotion.MotionHeightProperty ||
|
||||
property == AbstractMotion.MotionRenderTransformProperty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -161,7 +172,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当在 DispatchInSceneLayer 渲染的时候,Ghost 的全局坐标
|
||||
/// 当在 DispatchInSceneLayer 渲染的时候,Ghost 的全局坐标
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Point CalculateGhostPosition()
|
||||
@ -174,9 +185,13 @@ public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
var parentPoint = MotionTarget.TranslatePoint(new Point(0, 0), visualParent);
|
||||
if (parentPoint.HasValue)
|
||||
{
|
||||
point = parentPoint.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
point = MotionTarget.Bounds.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -193,12 +208,15 @@ public class MotionActor : Animatable, IMotionActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在这个接口中,Actor 根据自己的需求对 sceneLayer 进行设置,主要就是位置和大小
|
||||
/// 在这个接口中,Actor 根据自己的需求对 sceneLayer 进行设置,主要就是位置和大小
|
||||
/// </summary>
|
||||
/// <param name="sceneLayer"></param>
|
||||
public virtual void NotifySceneLayerCreated(SceneLayer sceneLayer)
|
||||
{
|
||||
if (!DispatchInSceneLayer) return;
|
||||
if (!DispatchInSceneLayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ghost = GetAnimatableGhost();
|
||||
|
||||
@ -208,9 +226,13 @@ public class MotionActor : Animatable, IMotionActor
|
||||
if (ghost.DesiredSize == default)
|
||||
|
||||
// Popup may not have been shown yet. Measure content
|
||||
{
|
||||
motionTargetSize = LayoutHelper.MeasureChild(ghost, Size.Infinity, new Thickness());
|
||||
}
|
||||
else
|
||||
{
|
||||
motionTargetSize = ghost.DesiredSize;
|
||||
}
|
||||
|
||||
var sceneSize = _motion.CalculateSceneSize(motionTargetSize);
|
||||
var scenePosition = _motion.CalculateScenePosition(motionTargetSize, CalculateGhostPosition());
|
||||
@ -223,7 +245,11 @@ public class MotionActor : Animatable, IMotionActor
|
||||
BuildGhost();
|
||||
RelayMotionProperties();
|
||||
var transitions = new Transitions();
|
||||
foreach (var transition in _motion.BuildTransitions(GetAnimatableGhost())) transitions.Add(transition);
|
||||
foreach (var transition in _motion.BuildTransitions(GetAnimatableGhost()))
|
||||
{
|
||||
transitions.Add(transition);
|
||||
}
|
||||
|
||||
Transitions = transitions;
|
||||
}
|
||||
|
||||
@ -239,7 +265,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当动画目标控件被添加到动画场景中之后调用,这里需要根据 Motion 的种类设置初始位置和大小
|
||||
/// 当动画目标控件被添加到动画场景中之后调用,这里需要根据 Motion 的种类设置初始位置和大小
|
||||
/// </summary>
|
||||
/// <param name="motionTarget"></param>
|
||||
public virtual void NotifyMotionTargetAddedToScene(Control motionTarget)
|
||||
@ -266,11 +292,15 @@ public class MotionActor : Animatable, IMotionActor
|
||||
internal virtual void NotifyMotionPreStart()
|
||||
{
|
||||
if (Transitions is not null)
|
||||
{
|
||||
foreach (var transition in Transitions)
|
||||
{
|
||||
_transitionsMap.Add(transition.Property, new AnimationState
|
||||
{
|
||||
Transition = transition
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var motionConfig in _motion.GetMotionConfigs())
|
||||
{
|
||||
@ -305,6 +335,7 @@ public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
var target = GetAnimatableGhost();
|
||||
foreach (var motionConfig in _motion.GetMotionConfigs())
|
||||
{
|
||||
if (motionConfig.Property == MotionHeightProperty)
|
||||
{
|
||||
_originHeight = target.Height;
|
||||
@ -322,13 +353,14 @@ public class MotionActor : Animatable, IMotionActor
|
||||
_originRenderTransform = target.RenderTransform;
|
||||
_originRenderTransformOrigin = target.RenderTransformOrigin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (change.Property == MotionWidthProperty ||
|
||||
change.Property == MotionHeightProperty ||
|
||||
if (change.Property == MotionWidthProperty ||
|
||||
change.Property == MotionHeightProperty ||
|
||||
change.Property == MotionOpacityProperty ||
|
||||
change.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
@ -341,7 +373,9 @@ public class MotionActor : Animatable, IMotionActor
|
||||
{
|
||||
var target = GetAnimatableGhost();
|
||||
if (target == MotionTarget)
|
||||
{
|
||||
foreach (var motionConfig in _motion.GetMotionConfigs())
|
||||
{
|
||||
if (motionConfig.Property == MotionHeightProperty)
|
||||
{
|
||||
target.SetValue(MotionHeightProperty, _originHeight);
|
||||
@ -360,9 +394,10 @@ public class MotionActor : Animatable, IMotionActor
|
||||
target.SetValue(MotionRenderTransformProperty, _originRenderTransform);
|
||||
target.SetValue(Visual.RenderTransformOriginProperty, _originRenderTransformOrigin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class AnimationState
|
||||
{
|
||||
public ITransition? Transition { get; set; }
|
||||
|
@ -42,7 +42,9 @@ public class MoveDownInMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.StartValue = BuildTranslateTransform(0, -motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -56,7 +58,6 @@ public class MoveDownInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveDownOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -94,7 +95,9 @@ public class MoveDownOutMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.EndValue = BuildTranslateTransform(0, -motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -108,7 +111,6 @@ public class MoveDownOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveLeftInMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -145,7 +147,9 @@ public class MoveLeftInMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.StartValue = BuildTranslateTransform(-motionTarget.DesiredSize.Width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -159,7 +163,6 @@ public class MoveLeftInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveLeftOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -197,7 +200,9 @@ public class MoveLeftOutMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.EndValue = BuildTranslateTransform(-motionTarget.DesiredSize.Width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -211,7 +216,6 @@ public class MoveLeftOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveRightInMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -248,7 +252,9 @@ public class MoveRightInMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.StartValue = BuildTranslateTransform(motionTarget.DesiredSize.Width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -257,7 +263,6 @@ public class MoveRightInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveRightOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -294,7 +299,9 @@ public class MoveRightOutMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.EndValue = BuildTranslateTransform(motionTarget.DesiredSize.Width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -303,7 +310,6 @@ public class MoveRightOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveUpInMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -341,7 +347,9 @@ public class MoveUpInMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.StartValue = BuildTranslateTransform(0, motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
@ -350,7 +358,6 @@ public class MoveUpInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MoveUpOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -388,7 +395,9 @@ public class MoveUpOutMotion : AbstractMotion
|
||||
{
|
||||
base.NotifyPreBuildTransition(config, motionTarget);
|
||||
if (config.Property == MotionRenderTransformProperty)
|
||||
{
|
||||
config.EndValue = BuildTranslateTransform(0, motionTarget.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Size CalculateSceneSize(Size motionTargetSize)
|
||||
|
@ -28,7 +28,7 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的动画顶层窗口
|
||||
/// 初始化一个新的动画顶层窗口
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="impl"></param>
|
||||
@ -38,11 +38,16 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
{
|
||||
ParentTopLevel = parent;
|
||||
impl.SetWindowManagerAddShadowHint(false);
|
||||
if (this is WindowBase window) window.SetTransparentForMouseEvents(true);
|
||||
if (this is WindowBase window)
|
||||
{
|
||||
window.SetTransparentForMouseEvents(true);
|
||||
}
|
||||
|
||||
if (PlatformImpl?.PopupPositioner is ManagedPopupPositioner managedPopupPositioner)
|
||||
{
|
||||
_managedPopupPositionerPopup =
|
||||
ManagedPopupPositionerPopupInfo.GetValue(managedPopupPositioner) as IManagedPopupPositionerPopup;
|
||||
}
|
||||
|
||||
_layout = new Canvas();
|
||||
Content = _layout;
|
||||
@ -50,7 +55,7 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the platform-specific window implementation.
|
||||
/// Gets the platform-specific window implementation.
|
||||
/// </summary>
|
||||
public new IPopupImpl? PlatformImpl => (IPopupImpl?)base.PlatformImpl;
|
||||
|
||||
@ -62,7 +67,7 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that is hosting the popup root.
|
||||
/// Gets the control that is hosting the popup root.
|
||||
/// </summary>
|
||||
Visual? IHostedVisualTreeRoot.Host
|
||||
{
|
||||
@ -74,7 +79,11 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
// if set. This helps to allow the focus manager to restore the focus to the outer
|
||||
// scope when the popup is closed.
|
||||
var parentVisual = Parent as Visual;
|
||||
if (parentVisual?.GetVisualRoot() != null) return parentVisual;
|
||||
if (parentVisual?.GetVisualRoot() != null)
|
||||
{
|
||||
return parentVisual;
|
||||
}
|
||||
|
||||
return ParentTopLevel ?? parentVisual;
|
||||
}
|
||||
}
|
||||
@ -90,9 +99,15 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity;
|
||||
var constraint = availableSize;
|
||||
|
||||
if (double.IsInfinity(constraint.Width)) constraint = constraint.WithWidth(maxAutoSize.Width);
|
||||
if (double.IsInfinity(constraint.Width))
|
||||
{
|
||||
constraint = constraint.WithWidth(maxAutoSize.Width);
|
||||
}
|
||||
|
||||
if (double.IsInfinity(constraint.Height)) constraint = constraint.WithHeight(maxAutoSize.Height);
|
||||
if (double.IsInfinity(constraint.Height))
|
||||
{
|
||||
constraint = constraint.WithHeight(maxAutoSize.Height);
|
||||
}
|
||||
|
||||
var measured = base.MeasureOverride(constraint);
|
||||
var width = measured.Width;
|
||||
@ -100,12 +115,18 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
var widthCache = Width;
|
||||
var heightCache = Height;
|
||||
|
||||
if (!double.IsNaN(widthCache)) width = widthCache;
|
||||
if (!double.IsNaN(widthCache))
|
||||
{
|
||||
width = widthCache;
|
||||
}
|
||||
|
||||
width = Math.Min(width, MaxWidth);
|
||||
width = Math.Max(width, MinWidth);
|
||||
|
||||
if (!double.IsNaN(heightCache)) height = heightCache;
|
||||
if (!double.IsNaN(heightCache))
|
||||
{
|
||||
height = heightCache;
|
||||
}
|
||||
|
||||
height = Math.Min(height, MaxHeight);
|
||||
height = Math.Max(height, MinHeight);
|
||||
@ -129,7 +150,11 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
{
|
||||
base.OnOpened(e);
|
||||
foreach (var child in _layout.Children)
|
||||
{
|
||||
if (child is INotifyCaptureGhostBitmap captureGhost)
|
||||
{
|
||||
captureGhost.NotifyCaptureGhostBitmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,6 @@ public class SlideUpInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideUpOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -75,7 +74,6 @@ public class SlideUpOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideDownInMotion : AbstractMotion
|
||||
{
|
||||
public SlideDownInMotion()
|
||||
@ -122,7 +120,6 @@ public class SlideDownInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideDownOutMotion : AbstractMotion
|
||||
{
|
||||
public SlideDownOutMotion()
|
||||
@ -169,7 +166,6 @@ public class SlideDownOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideLeftInMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -205,7 +201,6 @@ public class SlideLeftInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideLeftOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -241,7 +236,6 @@ public class SlideLeftOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideRightInMotion : AbstractMotion
|
||||
{
|
||||
public SlideRightInMotion()
|
||||
@ -288,7 +282,6 @@ public class SlideRightInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SlideRightOutMotion : AbstractMotion
|
||||
{
|
||||
public SlideRightOutMotion()
|
||||
|
@ -39,7 +39,6 @@ public class ZoomInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -75,7 +74,6 @@ public class ZoomOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomBigInMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -112,7 +110,6 @@ public class ZoomBigInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomBigOutMotion : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -148,7 +145,6 @@ public class ZoomBigOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomUpInMotion : AbstractMotion
|
||||
{
|
||||
public ZoomUpInMotion()
|
||||
@ -195,7 +191,6 @@ public class ZoomUpInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomUpOutMotion : AbstractMotion
|
||||
{
|
||||
public ZoomUpOutMotion()
|
||||
@ -242,7 +237,6 @@ public class ZoomUpOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomLeftInMotion : AbstractMotion
|
||||
{
|
||||
public ZoomLeftInMotion()
|
||||
@ -289,7 +283,6 @@ public class ZoomLeftInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomLeftOutMotion : AbstractMotion
|
||||
{
|
||||
public ZoomLeftOutMotion()
|
||||
@ -336,7 +329,6 @@ public class ZoomLeftOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomRightInMotion : AbstractMotion
|
||||
{
|
||||
public ZoomRightInMotion()
|
||||
@ -383,7 +375,6 @@ public class ZoomRightInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomRightOutMotion : AbstractMotion
|
||||
{
|
||||
public ZoomRightOutMotion()
|
||||
@ -430,7 +421,6 @@ public class ZoomRightOutMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomDownInMotion : AbstractMotion
|
||||
{
|
||||
public ZoomDownInMotion()
|
||||
@ -477,7 +467,6 @@ public class ZoomDownInMotion : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ZoomDownOutMotion : AbstractMotion
|
||||
{
|
||||
public ZoomDownOutMotion()
|
||||
|
@ -29,9 +29,14 @@ internal static class WindowExt
|
||||
|
||||
// 不是确定这样处理是否合适
|
||||
if (flag)
|
||||
{
|
||||
currentStyles |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentStyles &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
|
||||
}
|
||||
|
||||
SetExtendedStyle(impl, currentStyles, false);
|
||||
}
|
||||
|
||||
@ -42,9 +47,14 @@ internal static class WindowExt
|
||||
|
||||
// 不是确定这样处理是否合适
|
||||
if (flag)
|
||||
{
|
||||
currentStyles |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentStyles &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
|
||||
}
|
||||
|
||||
SetExtendedStyle(impl, currentStyles, false);
|
||||
}
|
||||
|
||||
|
@ -3,29 +3,32 @@
|
||||
namespace AtomUI.Reactive;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods associated with the IDisposable interface.
|
||||
/// Extension methods associated with the IDisposable interface.
|
||||
/// </summary>
|
||||
internal static class DisposableMixin
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures the provided disposable is disposed with the specified <see cref="CompositeDisposable" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the disposable.
|
||||
/// </typeparam>
|
||||
/// <param name="item">
|
||||
/// The disposable we are going to want to be disposed by the CompositeDisposable.
|
||||
/// </param>
|
||||
/// <param name="compositeDisposable">
|
||||
/// The <see cref="CompositeDisposable" /> to which <paramref name="item" /> will be added.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The disposable.
|
||||
/// </returns>
|
||||
public static T DisposeWith<T>(this T item, CompositeDisposable compositeDisposable)
|
||||
/// <summary>
|
||||
/// Ensures the provided disposable is disposed with the specified <see cref="CompositeDisposable" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the disposable.
|
||||
/// </typeparam>
|
||||
/// <param name="item">
|
||||
/// The disposable we are going to want to be disposed by the CompositeDisposable.
|
||||
/// </param>
|
||||
/// <param name="compositeDisposable">
|
||||
/// The <see cref="CompositeDisposable" /> to which <paramref name="item" /> will be added.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The disposable.
|
||||
/// </returns>
|
||||
public static T DisposeWith<T>(this T item, CompositeDisposable compositeDisposable)
|
||||
where T : IDisposable
|
||||
{
|
||||
if (compositeDisposable is null) throw new ArgumentNullException(nameof(compositeDisposable));
|
||||
if (compositeDisposable is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(compositeDisposable));
|
||||
}
|
||||
|
||||
compositeDisposable.Add(item);
|
||||
return item;
|
||||
|
@ -54,14 +54,22 @@ public static class ObjectExtension
|
||||
}
|
||||
|
||||
public static T? GetPropertyOrThrow<T>(this object source, string name,
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
BindingFlags flags =
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
{
|
||||
var obj = source.GetType().GetPropertyInfoOrThrow(name, flags).GetValue(source);
|
||||
if (obj is T propertyOrThrow) return propertyOrThrow;
|
||||
if (obj is T propertyOrThrow)
|
||||
{
|
||||
return propertyOrThrow;
|
||||
}
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
if (typeof(T).IsValueType) throw new Exception(name + " is a value type but the value is null.");
|
||||
if (typeof(T).IsValueType)
|
||||
{
|
||||
throw new Exception(name + " is a value type but the value is null.");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
@ -83,11 +91,17 @@ public static class ObjectExtension
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
{
|
||||
var obj = declareType.GetPropertyInfoOrThrow(name, flags).GetValue(source);
|
||||
if (obj is T propertyOrThrow) return propertyOrThrow;
|
||||
if (obj is T propertyOrThrow)
|
||||
{
|
||||
return propertyOrThrow;
|
||||
}
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
if (typeof(T).IsValueType) throw new Exception(name + " is a value type but the value is null.");
|
||||
if (typeof(T).IsValueType)
|
||||
{
|
||||
throw new Exception(name + " is a value type but the value is null.");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
@ -110,7 +124,10 @@ public static class ObjectExtension
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
{
|
||||
var property = declareType.GetProperty(name, flags);
|
||||
if (property is null) return false;
|
||||
if (property is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
property.SetValue(source, value);
|
||||
return true;
|
||||
@ -165,15 +182,22 @@ public static class ObjectExtension
|
||||
}
|
||||
|
||||
public static T? GetFieldOrThrow<T>(this object source, string name,
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
BindingFlags flags =
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
{
|
||||
var obj = source.GetType().GetFieldInfoOrThrow(name, flags).GetValue(source);
|
||||
if (obj is T fieldOrThrow) return fieldOrThrow;
|
||||
if (obj is T fieldOrThrow)
|
||||
{
|
||||
return fieldOrThrow;
|
||||
}
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
if (typeof(T).IsValueType) throw new Exception(name + " is a value type but the value is null.");
|
||||
if (typeof(T).IsValueType)
|
||||
{
|
||||
throw new Exception(name + " is a value type but the value is null.");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
@ -195,10 +219,17 @@ public static class ObjectExtension
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
{
|
||||
var obj = declareType.GetFieldInfoOrThrow(name, flags).GetValue(source);
|
||||
if (obj is T fieldOrThrow) return fieldOrThrow;
|
||||
if (obj is T fieldOrThrow)
|
||||
{
|
||||
return fieldOrThrow;
|
||||
}
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
if (typeof(T).IsValueType) throw new Exception(name + " is a value type but the value is null.");
|
||||
if (typeof(T).IsValueType)
|
||||
{
|
||||
throw new Exception(name + " is a value type but the value is null.");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
@ -221,7 +252,10 @@ public static class ObjectExtension
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
{
|
||||
var field = declareType.GetField(name, flags);
|
||||
if (field is null) return false;
|
||||
if (field is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
field.SetValue(source, value);
|
||||
return true;
|
||||
|
@ -45,29 +45,39 @@ public static class TypeExtension
|
||||
PropertyInfo? info;
|
||||
|
||||
if (!type.TryGetPropertyInfo(name, out info, flags))
|
||||
{
|
||||
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static FieldInfo GetFieldInfoOrThrow(this Type type, string name,
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
BindingFlags flags =
|
||||
BindingFlags.Instance | BindingFlags.NonPublic |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
{
|
||||
FieldInfo? info;
|
||||
if (!type.TryGetFieldInfo(name, out info, flags))
|
||||
{
|
||||
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static MethodInfo GetMethodInfoOrThrow(this Type type, string name,
|
||||
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
BindingFlags flags =
|
||||
BindingFlags.Instance | BindingFlags.NonPublic |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.FlattenHierarchy)
|
||||
{
|
||||
MethodInfo? info;
|
||||
if (!type.TryGetMethodInfo(name, out info, flags))
|
||||
{
|
||||
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace AtomUI.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for enums.
|
||||
/// Provides extension methods for enums.
|
||||
/// </summary>
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
|
@ -16,23 +16,27 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
public static bool AreClose(double value1, double value2)
|
||||
{
|
||||
//in case they are Infinities (then epsilon check does not work)
|
||||
if (value1 == value2) return true;
|
||||
if (value1 == value2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DoubleEpsilon;
|
||||
var delta = value1 - value2;
|
||||
return -eps < delta && eps > delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
@ -40,30 +44,38 @@ public static class MathUtils
|
||||
public static bool AreClose(double value1, double value2, double eps)
|
||||
{
|
||||
//in case they are Infinities (then epsilon check does not work)
|
||||
if (value1 == value2) return true;
|
||||
if (value1 == value2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var delta = value1 - value2;
|
||||
return -eps < delta && eps > delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AreClose - Returns whether or not two floats are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// AreClose - Returns whether or not two floats are "close". That is, whether or
|
||||
/// not they are within epsilon of each other.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first float to compare. </param>
|
||||
/// <param name="value2"> The second float to compare. </param>
|
||||
public static bool AreClose(float value1, float value2)
|
||||
{
|
||||
//in case they are Infinities (then epsilon check does not work)
|
||||
if (value1 == value2) return true;
|
||||
if (value1 == value2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0f) * FloatEpsilon;
|
||||
var delta = value1 - value2;
|
||||
return -eps < delta && eps > delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThan - Returns whether or not the first double is less than the second double.
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// LessThan - Returns whether or not the first double is less than the second double.
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
@ -73,9 +85,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThan - Returns whether or not the first float is less than the second float.
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// LessThan - Returns whether or not the first float is less than the second float.
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first single float to compare. </param>
|
||||
/// <param name="value2"> The second single float to compare. </param>
|
||||
@ -85,9 +97,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThan - Returns whether or not the first double is greater than the second double.
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// GreaterThan - Returns whether or not the first double is greater than the second double.
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
@ -97,9 +109,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThan - Returns whether or not the first float is greater than the second float.
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// GreaterThan - Returns whether or not the first float is greater than the second float.
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
||||
/// the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first float to compare. </param>
|
||||
/// <param name="value2"> The second float to compare. </param>
|
||||
@ -109,9 +121,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThanOrClose - Returns whether or not the first double is less than or close to
|
||||
/// the second double. That is, whether or not the first is strictly less than or within
|
||||
/// epsilon of the other number.
|
||||
/// LessThanOrClose - Returns whether or not the first double is less than or close to
|
||||
/// the second double. That is, whether or not the first is strictly less than or within
|
||||
/// epsilon of the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
@ -121,9 +133,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThanOrClose - Returns whether or not the first float is less than or close to
|
||||
/// the second float. That is, whether or not the first is strictly less than or within
|
||||
/// epsilon of the other number.
|
||||
/// LessThanOrClose - Returns whether or not the first float is less than or close to
|
||||
/// the second float. That is, whether or not the first is strictly less than or within
|
||||
/// epsilon of the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first float to compare. </param>
|
||||
/// <param name="value2"> The second float to compare. </param>
|
||||
@ -133,9 +145,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
|
||||
/// the second double. That is, whether or not the first is strictly greater than or within
|
||||
/// epsilon of the other number.
|
||||
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
|
||||
/// the second double. That is, whether or not the first is strictly greater than or within
|
||||
/// epsilon of the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first double to compare. </param>
|
||||
/// <param name="value2"> The second double to compare. </param>
|
||||
@ -145,9 +157,9 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThanOrClose - Returns whether or not the first float is greater than or close to
|
||||
/// the second float. That is, whether or not the first is strictly greater than or within
|
||||
/// epsilon of the other number.
|
||||
/// GreaterThanOrClose - Returns whether or not the first float is greater than or close to
|
||||
/// the second float. That is, whether or not the first is strictly greater than or within
|
||||
/// epsilon of the other number.
|
||||
/// </summary>
|
||||
/// <param name="value1"> The first float to compare. </param>
|
||||
/// <param name="value2"> The second float to compare. </param>
|
||||
@ -157,8 +169,8 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
|
||||
/// but this is faster.
|
||||
/// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
|
||||
/// but this is faster.
|
||||
/// </summary>
|
||||
/// <param name="value"> The double to compare to 1. </param>
|
||||
public static bool IsOne(double value)
|
||||
@ -167,8 +179,8 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsOne - Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
|
||||
/// but this is faster.
|
||||
/// IsOne - Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
|
||||
/// but this is faster.
|
||||
/// </summary>
|
||||
/// <param name="value"> The float to compare to 1. </param>
|
||||
public static bool IsOne(float value)
|
||||
@ -177,8 +189,8 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
|
||||
/// but this is faster.
|
||||
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
|
||||
/// but this is faster.
|
||||
/// </summary>
|
||||
/// <param name="value"> The double to compare to 0. </param>
|
||||
public static bool IsZero(double value)
|
||||
@ -187,8 +199,8 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
|
||||
/// but this is faster.
|
||||
/// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
|
||||
/// but this is faster.
|
||||
/// </summary>
|
||||
/// <param name="value"> The float to compare to 0. </param>
|
||||
public static bool IsZero(float value)
|
||||
@ -197,7 +209,7 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an angle in degrees to radians.
|
||||
/// Converts an angle in degrees to radians.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle in degrees.</param>
|
||||
/// <returns>The angle in radians.</returns>
|
||||
@ -207,7 +219,7 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an angle in gradians to radians.
|
||||
/// Converts an angle in gradians to radians.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle in gradians.</param>
|
||||
/// <returns>The angle in radians.</returns>
|
||||
@ -217,7 +229,7 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an angle in turns to radians.
|
||||
/// Converts an angle in turns to radians.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle in turns.</param>
|
||||
/// <returns>The angle in radians.</returns>
|
||||
@ -227,7 +239,7 @@ public static class MathUtils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the point of an angle on an ellipse.
|
||||
/// Calculates the point of an angle on an ellipse.
|
||||
/// </summary>
|
||||
/// <param name="centre">The centre point of the ellipse.</param>
|
||||
/// <param name="radiusX">The x radius of the ellipse.</param>
|
||||
|
@ -17,7 +17,6 @@ public enum AddOnDecoratedVariant
|
||||
Borderless
|
||||
}
|
||||
|
||||
|
||||
public enum AddOnDecoratedStatus
|
||||
{
|
||||
Default,
|
||||
@ -25,7 +24,6 @@ public enum AddOnDecoratedStatus
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
[TemplatePart(AddOnDecoratedBoxTheme.LeftAddOnPart, typeof(ContentPresenter))]
|
||||
[TemplatePart(AddOnDecoratedBoxTheme.RightAddOnPart, typeof(ContentPresenter))]
|
||||
[TemplatePart(AddOnDecoratedBoxTheme.InnerBoxContentPart, typeof(ContentPresenter), IsRequired = true)]
|
||||
@ -48,17 +46,29 @@ public class AddOnDecoratedBox : ContentControl
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (change.Property == LeftAddOnProperty || change.Property == RightAddOnProperty)
|
||||
{
|
||||
SetupInnerBoxCornerRadius();
|
||||
}
|
||||
}
|
||||
|
||||
if (change.Property == CornerRadiusProperty || change.Property == BorderThicknessProperty)
|
||||
{
|
||||
SetupAddOnBorderInfo();
|
||||
}
|
||||
|
||||
if (change.Property == StatusProperty) UpdatePseudoClasses();
|
||||
if (change.Property == StatusProperty)
|
||||
{
|
||||
UpdatePseudoClasses();
|
||||
}
|
||||
|
||||
if (change.Property == LeftAddOnProperty || change.Property == RightAddOnProperty)
|
||||
{
|
||||
if (change.NewValue is PathIcon icon) SetupIconTypeAddOnSize(icon);
|
||||
if (change.NewValue is PathIcon icon)
|
||||
{
|
||||
SetupIconTypeAddOnSize(icon);
|
||||
}
|
||||
}
|
||||
else if (change.Property == ContentProperty)
|
||||
{
|
||||
@ -71,9 +81,15 @@ public class AddOnDecoratedBox : ContentControl
|
||||
|
||||
if (change.Property == SizeTypeProperty)
|
||||
{
|
||||
if (LeftAddOn is PathIcon leftIconAddOn) SetupIconTypeAddOnSize(leftIconAddOn);
|
||||
if (LeftAddOn is PathIcon leftIconAddOn)
|
||||
{
|
||||
SetupIconTypeAddOnSize(leftIconAddOn);
|
||||
}
|
||||
|
||||
if (RightAddOn is PathIcon rightIconAddOn) SetupIconTypeAddOnSize(rightIconAddOn);
|
||||
if (RightAddOn is PathIcon rightIconAddOn)
|
||||
{
|
||||
SetupIconTypeAddOnSize(rightIconAddOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,12 +189,10 @@ public class AddOnDecoratedBox : ContentControl
|
||||
|
||||
protected virtual void UpdatePseudoClasses()
|
||||
{
|
||||
PseudoClasses.Set(ErrorPC, Status == AddOnDecoratedStatus.Error);
|
||||
PseudoClasses.Set(ErrorPC, Status == AddOnDecoratedStatus.Error);
|
||||
PseudoClasses.Set(WarningPC, Status == AddOnDecoratedStatus.Warning);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<object?> LeftAddOnProperty =
|
||||
@ -229,8 +243,6 @@ public class AddOnDecoratedBox : ContentControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<AddOnDecoratedBox, CornerRadius> InnerBoxCornerRadiusProperty =
|
||||
|
@ -15,97 +15,97 @@ internal class AddOnDecoratedBoxToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输入框内边距
|
||||
/// 输入框内边距
|
||||
/// </summary>
|
||||
public Thickness Padding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号输入框内边距
|
||||
/// 小号输入框内边距
|
||||
/// </summary>
|
||||
public Thickness PaddingSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 大号输入框内边距
|
||||
/// 大号输入框内边距
|
||||
/// </summary>
|
||||
public Thickness PaddingLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 前/后置标签背景色
|
||||
/// 前/后置标签背景色
|
||||
/// </summary>
|
||||
public Color AddonBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 悬浮态边框色
|
||||
/// 悬浮态边框色
|
||||
/// </summary>
|
||||
public Color HoverBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 激活态边框色
|
||||
/// 激活态边框色
|
||||
/// </summary>
|
||||
public Color ActiveBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 激活态阴影
|
||||
/// 激活态阴影
|
||||
/// </summary>
|
||||
public BoxShadow ActiveShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误状态时激活态阴影
|
||||
/// 错误状态时激活态阴影
|
||||
/// </summary>
|
||||
public BoxShadow ErrorActiveShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 警告状态时激活态阴影
|
||||
/// 警告状态时激活态阴影
|
||||
/// </summary>
|
||||
public BoxShadow WarningActiveShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// hover 状态时背景颜色
|
||||
/// hover 状态时背景颜色
|
||||
/// </summary>
|
||||
public Color HoverBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 激活状态时背景颜色
|
||||
/// 激活状态时背景颜色
|
||||
/// </summary>
|
||||
public Color ActiveBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 字体大小
|
||||
/// 字体大小
|
||||
/// </summary>
|
||||
public double FontSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 大号字体大小
|
||||
/// 大号字体大小
|
||||
/// </summary>
|
||||
public double FontSizeLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号字体大小
|
||||
/// 小号字体大小
|
||||
/// </summary>
|
||||
public double FontSizeSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AddOn 内边距
|
||||
/// AddOn 内边距
|
||||
/// </summary>
|
||||
public Thickness AddOnPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AddOn 小号内边距
|
||||
/// AddOn 小号内边距
|
||||
/// </summary>
|
||||
public Thickness AddOnPaddingSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AddOn 大号内边距
|
||||
/// AddOn 大号内边距
|
||||
/// </summary>
|
||||
public Thickness AddOnPaddingLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 左边内部小组件的边距
|
||||
/// 左边内部小组件的边距
|
||||
/// </summary>
|
||||
public Thickness LeftInnerAddOnMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 右边内部小组件的边距
|
||||
/// 右边内部小组件的边距
|
||||
/// </summary>
|
||||
public Thickness RightInnerAddOnMargin { get; set; }
|
||||
|
||||
@ -117,9 +117,9 @@ internal class AddOnDecoratedBoxToken : AbstractControlDesignToken
|
||||
var lineHeight = _globalToken.FontToken.LineHeight;
|
||||
var lineHeightLG = _globalToken.FontToken.LineHeightLG;
|
||||
var lineWidth = _globalToken.SeedToken.LineWidth;
|
||||
Padding = new Thickness(_globalToken.PaddingSM - lineWidth,
|
||||
Padding = new Thickness(_globalToken.PaddingSM - lineWidth,
|
||||
Math.Round((_globalToken.SeedToken.ControlHeight - fontSize * lineHeight) / 2 * 10) / 10 - lineWidth);
|
||||
PaddingSM = new Thickness(_globalToken.ControlPaddingSM - lineWidth,
|
||||
PaddingSM = new Thickness(_globalToken.ControlPaddingSM - lineWidth,
|
||||
Math.Round((_globalToken.HeightToken.ControlHeightSM - fontSize * lineHeight) / 2 * 10) / 10 - lineWidth);
|
||||
PaddingLG = new Thickness(_globalToken.ControlPadding - lineWidth,
|
||||
Math.Ceiling((_globalToken.HeightToken.ControlHeightLG - fontSizeLG * lineHeightLG) / 2 * 10) / 10 -
|
||||
|
@ -33,9 +33,15 @@ public class AddOnDecoratedInnerBox : ContentControl
|
||||
|
||||
if (change.Property == LeftAddOnContentProperty || change.Property == RightAddOnContentProperty)
|
||||
{
|
||||
if (change.OldValue is Control oldControl) UIStructureUtils.SetTemplateParent(oldControl, null);
|
||||
if (change.OldValue is Control oldControl)
|
||||
{
|
||||
UIStructureUtils.SetTemplateParent(oldControl, null);
|
||||
}
|
||||
|
||||
if (change.NewValue is Control newControl) UIStructureUtils.SetTemplateParent(newControl, this);
|
||||
if (change.NewValue is Control newControl)
|
||||
{
|
||||
UIStructureUtils.SetTemplateParent(newControl, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,11 +53,20 @@ public class AddOnDecoratedInnerBox : ContentControl
|
||||
_rightAddOnLayout = e.NameScope.Find<StackPanel>(AddOnDecoratedInnerBoxTheme.RightAddOnLayoutPart);
|
||||
_clearButton = e.NameScope.Find<IconButton>(AddOnDecoratedInnerBoxTheme.ClearButtonPart);
|
||||
|
||||
if (_leftAddOnLayout is not null) _leftAddOnLayout.SizeChanged += HandleLayoutSizeChanged;
|
||||
if (_rightAddOnLayout is not null) _rightAddOnLayout.SizeChanged += HandleLayoutSizeChanged;
|
||||
if (_leftAddOnLayout is not null)
|
||||
{
|
||||
_leftAddOnLayout.SizeChanged += HandleLayoutSizeChanged;
|
||||
}
|
||||
|
||||
if (_rightAddOnLayout is not null)
|
||||
{
|
||||
_rightAddOnLayout.SizeChanged += HandleLayoutSizeChanged;
|
||||
}
|
||||
|
||||
if (_clearButton is not null)
|
||||
{
|
||||
_clearButton.Click += (sender, args) => { NotifyClearButtonClicked(); };
|
||||
}
|
||||
|
||||
SetupContentPresenterMargin();
|
||||
BuildEffectiveInnerBoxPadding();
|
||||
@ -67,17 +82,24 @@ public class AddOnDecoratedInnerBox : ContentControl
|
||||
var marginLeft = 0d;
|
||||
var marginRight = 0d;
|
||||
if (_leftAddOnLayout is not null)
|
||||
{
|
||||
if (_leftAddOnLayout.DesiredSize.Width > 0 && _leftAddOnLayout.DesiredSize.Height > 0)
|
||||
{
|
||||
marginLeft = _marginXSToken;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rightAddOnLayout is not null)
|
||||
{
|
||||
if (_rightAddOnLayout.DesiredSize.Width > 0 && _rightAddOnLayout.DesiredSize.Height > 0)
|
||||
{
|
||||
marginRight = _marginXSToken;
|
||||
}
|
||||
}
|
||||
|
||||
ContentPresenterMargin = new Thickness(marginLeft, 0, marginRight, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
||||
@ -136,8 +158,6 @@ public class AddOnDecoratedInnerBox : ContentControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly StyledProperty<Thickness> InnerBoxPaddingProperty =
|
||||
|
@ -291,7 +291,8 @@ internal class AddOnDecoratedInnerBoxTheme : BaseControlTheme
|
||||
{
|
||||
var outlineStyle =
|
||||
new Style(selector => selector.Nesting()
|
||||
.PropertyEquals(AddOnDecoratedBox.StyleVariantProperty, AddOnDecoratedVariant.Outline));
|
||||
.PropertyEquals(AddOnDecoratedBox.StyleVariantProperty,
|
||||
AddOnDecoratedVariant.Outline));
|
||||
|
||||
{
|
||||
{
|
||||
@ -393,7 +394,7 @@ internal class AddOnDecoratedInnerBoxTheme : BaseControlTheme
|
||||
var filledStyle =
|
||||
new Style(selector =>
|
||||
selector.Nesting()
|
||||
.PropertyEquals(AddOnDecoratedBox.StyleVariantProperty, AddOnDecoratedVariant.Filled));
|
||||
.PropertyEquals(AddOnDecoratedBox.StyleVariantProperty, AddOnDecoratedVariant.Filled));
|
||||
|
||||
{
|
||||
{
|
||||
|
@ -16,7 +16,6 @@ public enum AlertType
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
public class Alert : TemplatedControl, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<AlertType> TypeProperty =
|
||||
@ -125,8 +124,6 @@ public class Alert : TemplatedControl, IControlCustomStyle
|
||||
_customStyle.HandleTemplateApplied(e.NameScope);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
void IControlCustomStyle.HandleTemplateApplied(INameScope scope)
|
||||
@ -140,8 +137,12 @@ public class Alert : TemplatedControl, IControlCustomStyle
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (e.Property == IsClosableProperty)
|
||||
{
|
||||
SetupCloseButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupCloseButton()
|
||||
|
@ -379,7 +379,10 @@ internal class AlertTheme : BaseControlTheme
|
||||
GlobalTokenResourceKey.MarginXS, BindingPriority.Template,
|
||||
o =>
|
||||
{
|
||||
if (o is double value) return new Thickness(0, value, 0, 0);
|
||||
if (o is double value)
|
||||
{
|
||||
return new Thickness(0, value, 0, 0);
|
||||
}
|
||||
|
||||
return o;
|
||||
});
|
||||
|
@ -14,47 +14,47 @@ internal class AlertToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 默认内间距
|
||||
/// 默认内间距
|
||||
/// </summary>
|
||||
public Thickness DefaultPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 带有描述的内间距
|
||||
/// 带有描述的内间距
|
||||
/// </summary>
|
||||
public Thickness WithDescriptionPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 带有描述的 Message 外间距
|
||||
/// 带有描述的 Message 外间距
|
||||
/// </summary>
|
||||
public Thickness MessageWithDescriptionMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标默认外间距
|
||||
/// 图标默认外间距
|
||||
/// </summary>
|
||||
public Thickness IconDefaultMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标带描述信息外间距
|
||||
/// 图标带描述信息外间距
|
||||
/// </summary>
|
||||
public Thickness IconWithDescriptionMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 没有描述时的图标尺寸
|
||||
/// 没有描述时的图标尺寸
|
||||
/// </summary>
|
||||
public double IconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 带有描述时的图标尺寸
|
||||
/// 带有描述时的图标尺寸
|
||||
/// </summary>
|
||||
public double WithDescriptionIconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮的大小
|
||||
/// 关闭按钮的大小
|
||||
/// </summary>
|
||||
public double CloseIconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外元素的外间距
|
||||
/// 额外元素的外间距
|
||||
/// </summary>
|
||||
public Thickness ExtraElementMargin { get; set; }
|
||||
|
||||
|
@ -11,90 +11,91 @@ namespace AtomUI.Controls;
|
||||
|
||||
public enum ArrowPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element.
|
||||
/// </summary>
|
||||
Bottom,
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element.
|
||||
/// </summary>
|
||||
Bottom,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element.
|
||||
/// </summary>
|
||||
Right,
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element.
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element.
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element.
|
||||
/// </summary>
|
||||
Top,
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element.
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element, with the left edge of the popup
|
||||
/// aligned with the left edge of the target element.
|
||||
/// </summary>
|
||||
TopEdgeAlignedLeft,
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element, with the left edge of the popup
|
||||
/// aligned with the left edge of the target element.
|
||||
/// </summary>
|
||||
TopEdgeAlignedLeft,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element, with the right edge of popup aligned with right edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
TopEdgeAlignedRight,
|
||||
/// <summary>
|
||||
/// Preferred location is above the target element, with the right edge of popup aligned with right edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
TopEdgeAlignedRight,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element, with the left edge of popup aligned with left edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
BottomEdgeAlignedLeft,
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element, with the left edge of popup aligned with left edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
BottomEdgeAlignedLeft,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element, with the right edge of popup aligned with right edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
BottomEdgeAlignedRight,
|
||||
/// <summary>
|
||||
/// Preferred location is below the target element, with the right edge of popup aligned with right edge of the target
|
||||
/// element.
|
||||
/// </summary>
|
||||
BottomEdgeAlignedRight,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element, with the top edge of popup aligned with top edge of the
|
||||
/// target element.
|
||||
/// </summary>
|
||||
LeftEdgeAlignedTop,
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element, with the top edge of popup aligned with top edge of the
|
||||
/// target element.
|
||||
/// </summary>
|
||||
LeftEdgeAlignedTop,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element, with the bottom edge of popup aligned with bottom edge of
|
||||
/// the target element.
|
||||
/// </summary>
|
||||
LeftEdgeAlignedBottom,
|
||||
/// <summary>
|
||||
/// Preferred location is to the left of the target element, with the bottom edge of popup aligned with bottom edge of
|
||||
/// the target element.
|
||||
/// </summary>
|
||||
LeftEdgeAlignedBottom,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element, with the top edge of popup aligned with top edge of the
|
||||
/// target element.
|
||||
/// </summary>
|
||||
RightEdgeAlignedTop,
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element, with the top edge of popup aligned with top edge of the
|
||||
/// target element.
|
||||
/// </summary>
|
||||
RightEdgeAlignedTop,
|
||||
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element, with the bottom edge of popup aligned with bottom edge of
|
||||
/// the target element.
|
||||
/// </summary>
|
||||
RightEdgeAlignedBottom
|
||||
/// <summary>
|
||||
/// Preferred location is to the right of the target element, with the bottom edge of popup aligned with bottom edge of
|
||||
/// the target element.
|
||||
/// </summary>
|
||||
RightEdgeAlignedBottom
|
||||
}
|
||||
|
||||
|
||||
public class ArrowDecoratedBox : ContentControl,
|
||||
IShadowMaskInfoProvider,
|
||||
IControlCustomStyle
|
||||
IShadowMaskInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<bool> IsShowArrowProperty =
|
||||
AvaloniaProperty.Register<ArrowDecoratedBox, bool>(nameof(IsShowArrow), true);
|
||||
|
||||
public static readonly StyledProperty<ArrowPosition> ArrowPositionProperty =
|
||||
AvaloniaProperty.Register<ArrowDecoratedBox, ArrowPosition>(
|
||||
nameof(ArrowPosition), ArrowPosition.Bottom);
|
||||
nameof(ArrowPosition));
|
||||
|
||||
internal static readonly StyledProperty<double> ArrowSizeProperty
|
||||
= AvaloniaProperty.Register<ArrowDecoratedBox, double>(nameof(ArrowSize));
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
|
||||
private Geometry? _arrowGeometry;
|
||||
private Rect _arrowRect;
|
||||
|
||||
@ -102,8 +103,6 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
// 相对坐标
|
||||
private (double, double) _arrowVertexPoint;
|
||||
private Rect _contentRect;
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
private bool _needGenerateArrowVertexPoint = true;
|
||||
|
||||
static ArrowDecoratedBox()
|
||||
@ -119,7 +118,7 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
internal (double, double) ArrowVertexPoint => GetArrowVertexPoint();
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示指示箭头
|
||||
/// 是否显示指示箭头
|
||||
/// </summary>
|
||||
public bool IsShowArrow
|
||||
{
|
||||
@ -128,7 +127,7 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 箭头渲染的位置
|
||||
/// 箭头渲染的位置
|
||||
/// </summary>
|
||||
public ArrowPosition ArrowPosition
|
||||
{
|
||||
@ -137,7 +136,7 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 箭头的大小
|
||||
/// 箭头的大小
|
||||
/// </summary>
|
||||
internal double ArrowSize
|
||||
{
|
||||
@ -147,7 +146,10 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
|
||||
void IControlCustomStyle.HandleTemplateApplied(INameScope scope)
|
||||
{
|
||||
if (IsShowArrow) BuildGeometry(true);
|
||||
if (IsShowArrow)
|
||||
{
|
||||
BuildGeometry(true);
|
||||
}
|
||||
}
|
||||
|
||||
public CornerRadius GetMaskCornerRadius()
|
||||
@ -164,20 +166,20 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
{
|
||||
return arrowPosition switch
|
||||
{
|
||||
ArrowPosition.Left => Direction.Left,
|
||||
ArrowPosition.Left => Direction.Left,
|
||||
ArrowPosition.LeftEdgeAlignedBottom => Direction.Left,
|
||||
ArrowPosition.LeftEdgeAlignedTop => Direction.Left,
|
||||
ArrowPosition.LeftEdgeAlignedTop => Direction.Left,
|
||||
|
||||
ArrowPosition.Top => Direction.Top,
|
||||
ArrowPosition.TopEdgeAlignedLeft => Direction.Top,
|
||||
ArrowPosition.Top => Direction.Top,
|
||||
ArrowPosition.TopEdgeAlignedLeft => Direction.Top,
|
||||
ArrowPosition.TopEdgeAlignedRight => Direction.Top,
|
||||
|
||||
ArrowPosition.Right => Direction.Right,
|
||||
ArrowPosition.Right => Direction.Right,
|
||||
ArrowPosition.RightEdgeAlignedBottom => Direction.Right,
|
||||
ArrowPosition.RightEdgeAlignedTop => Direction.Right,
|
||||
ArrowPosition.RightEdgeAlignedTop => Direction.Right,
|
||||
|
||||
ArrowPosition.Bottom => Direction.Bottom,
|
||||
ArrowPosition.BottomEdgeAlignedLeft => Direction.Bottom,
|
||||
ArrowPosition.Bottom => Direction.Bottom,
|
||||
ArrowPosition.BottomEdgeAlignedLeft => Direction.Bottom,
|
||||
ArrowPosition.BottomEdgeAlignedRight => Direction.Bottom,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(arrowPosition), arrowPosition,
|
||||
"Invalid value for ArrowPosition")
|
||||
@ -196,8 +198,6 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
_customStyle.HandleTemplateApplied(e.NameScope);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
private (double, double) GetArrowVertexPoint()
|
||||
@ -214,15 +214,17 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsShowArrowProperty ||
|
||||
if (e.Property == IsShowArrowProperty ||
|
||||
e.Property == ArrowPositionProperty ||
|
||||
e.Property == ArrowSizeProperty ||
|
||||
e.Property == ArrowSizeProperty ||
|
||||
e.Property == VisualParentProperty)
|
||||
{
|
||||
if (e.Property == IsShowArrowProperty && VisualRoot is null)
|
||||
|
||||
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
|
||||
{
|
||||
_needGenerateArrowVertexPoint = true;
|
||||
}
|
||||
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
@ -234,7 +236,10 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
|
||||
private void BuildGeometry(bool force = false)
|
||||
{
|
||||
if (_arrowGeometry is null || force) _arrowGeometry = CommonShapeBuilder.BuildArrow(ArrowSize, 1.5);
|
||||
if (_arrowGeometry is null || force)
|
||||
{
|
||||
_arrowGeometry = CommonShapeBuilder.BuildArrow(ArrowSize, 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void Render(DrawingContext context)
|
||||
@ -283,9 +288,13 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
if (direction == Direction.Left || direction == Direction.Right)
|
||||
{
|
||||
targetWidth += realArrowSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetHeight += realArrowSize;
|
||||
}
|
||||
}
|
||||
|
||||
var targetSize = new Size(targetWidth, targetHeight);
|
||||
@ -301,7 +310,10 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
for (var i = 0; i < visualCount; ++i)
|
||||
{
|
||||
var child = visualChildren[i];
|
||||
if (child is Layoutable layoutable) layoutable.Arrange(_contentRect);
|
||||
if (child is Layoutable layoutable)
|
||||
{
|
||||
layoutable.Arrange(_contentRect);
|
||||
}
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
@ -318,18 +330,30 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
var arrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width) + 0.5;
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
if (direction == Direction.Left || direction == Direction.Right)
|
||||
{
|
||||
targetWidth -= arrowSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetHeight -= arrowSize;
|
||||
}
|
||||
|
||||
if (direction == Direction.Right)
|
||||
{
|
||||
offsetX = 0.5;
|
||||
}
|
||||
else if (direction == Direction.Bottom)
|
||||
{
|
||||
offsetY = 0.5;
|
||||
}
|
||||
else if (direction == Direction.Top)
|
||||
{
|
||||
offsetY = arrowSize - 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetX = arrowSize - 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
return new Rect(offsetX, offsetY, targetWidth, targetHeight);
|
||||
@ -348,7 +372,7 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
|
||||
var minValue = Math.Min(size.Width, size.Height);
|
||||
var maxValue = Math.Max(size.Width, size.Height);
|
||||
if (position == ArrowPosition.Left ||
|
||||
if (position == ArrowPosition.Left ||
|
||||
position == ArrowPosition.LeftEdgeAlignedTop ||
|
||||
position == ArrowPosition.LeftEdgeAlignedBottom)
|
||||
{
|
||||
@ -361,33 +385,47 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
else if (position == ArrowPosition.LeftEdgeAlignedTop)
|
||||
{
|
||||
if (maxValue * 2 > finalSize.Height / 2)
|
||||
{
|
||||
offsetY = minValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetY = maxValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxValue * 2 > finalSize.Height / 2)
|
||||
{
|
||||
offsetY = finalSize.Height - minValue - maxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetY = finalSize.Height - maxValue * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (position == ArrowPosition.Top ||
|
||||
else if (position == ArrowPosition.Top ||
|
||||
position == ArrowPosition.TopEdgeAlignedLeft ||
|
||||
position == ArrowPosition.TopEdgeAlignedRight)
|
||||
{
|
||||
if (position == ArrowPosition.TopEdgeAlignedLeft)
|
||||
{
|
||||
offsetX = maxValue;
|
||||
}
|
||||
else if (position == ArrowPosition.Top)
|
||||
{
|
||||
offsetX = (finalSize.Width - maxValue) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetX = finalSize.Width - maxValue * 2;
|
||||
}
|
||||
|
||||
targetWidth = maxValue;
|
||||
targetHeight = minValue;
|
||||
}
|
||||
else if (position == ArrowPosition.Right ||
|
||||
else if (position == ArrowPosition.Right ||
|
||||
position == ArrowPosition.RightEdgeAlignedTop ||
|
||||
position == ArrowPosition.RightEdgeAlignedBottom)
|
||||
{
|
||||
@ -399,16 +437,24 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
else if (position == ArrowPosition.RightEdgeAlignedTop)
|
||||
{
|
||||
if (maxValue * 2 > finalSize.Height / 2)
|
||||
{
|
||||
offsetY = minValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetY = maxValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxValue * 2 > finalSize.Height / 2)
|
||||
{
|
||||
offsetY = finalSize.Height - minValue - maxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetY = finalSize.Height - maxValue * 2;
|
||||
}
|
||||
}
|
||||
|
||||
targetWidth = minValue;
|
||||
@ -420,11 +466,17 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
targetWidth = maxValue;
|
||||
targetHeight = minValue;
|
||||
if (position == ArrowPosition.BottomEdgeAlignedLeft)
|
||||
{
|
||||
offsetX = maxValue;
|
||||
}
|
||||
else if (position == ArrowPosition.Bottom)
|
||||
{
|
||||
offsetX = (finalSize.Width - maxValue) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetX = finalSize.Width - maxValue * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,9 +486,13 @@ public class ArrowDecoratedBox : ContentControl,
|
||||
// 计算中点
|
||||
var direction = GetDirection(position);
|
||||
if (direction == Direction.Left || direction == Direction.Right)
|
||||
{
|
||||
_arrowVertexPoint = (center.Y, finalSize.Height - center.Y);
|
||||
}
|
||||
else if (direction == Direction.Top || direction == Direction.Bottom)
|
||||
{
|
||||
_arrowVertexPoint = (center.X, finalSize.Width - center.X);
|
||||
}
|
||||
|
||||
return targetRect;
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ public class ArrowDecoratedBoxToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 箭头三角形大小
|
||||
/// 箭头三角形大小
|
||||
/// </summary>
|
||||
public double ArrowSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认的内边距
|
||||
/// 默认的内边距
|
||||
/// </summary>
|
||||
public Thickness Padding { get; set; }
|
||||
|
||||
|
@ -1,50 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=addondecoratedbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=alert/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=arrowdecoratedbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=badge/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=buttons/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=buttonspinner/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=checkbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=collapse/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=combobox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=datepicker/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=drawer/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=emptyindicator/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=expander/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=flyouts/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=groupbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=input/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=listbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=loading/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=localization/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=marqueelabel/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=menu/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=message/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=notifications/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=numericupdown/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=optionbuttonbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pathicon/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=popup/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=popupconfirm/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=popupconfirm_005Clocalization/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=primitives/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=primitives_005Catomlayer/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=progressbar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=radiobutton/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=segmented/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=separator/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=slider/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=switch/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tabcontrol/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tabcontrol_005Ctabstrip/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tag/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=timepicker/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=timepicker_005Clocalization/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tooltip/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=treeview/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utils/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utils_005Cmotion/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=watermark/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=watermark_005Cglyphs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=window/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
@ -15,37 +15,37 @@ internal class BadgeToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 徽标高度
|
||||
/// 徽标高度
|
||||
/// </summary>
|
||||
public double IndicatorHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号徽标高度
|
||||
/// 小号徽标高度
|
||||
/// </summary>
|
||||
public double IndicatorHeightSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 点状徽标尺寸
|
||||
/// 点状徽标尺寸
|
||||
/// </summary>
|
||||
public double DotSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 徽标文本尺寸
|
||||
/// 徽标文本尺寸
|
||||
/// </summary>
|
||||
public double TextFontSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号徽标文本尺寸
|
||||
/// 小号徽标文本尺寸
|
||||
/// </summary>
|
||||
public double TextFontSizeSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 徽标文本粗细
|
||||
/// 徽标文本粗细
|
||||
/// </summary>
|
||||
public FontWeight TextFontWeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态徽标尺寸
|
||||
/// 状态徽标尺寸
|
||||
/// </summary>
|
||||
public double StatusSize { get; set; }
|
||||
|
||||
@ -78,8 +78,6 @@ internal class BadgeToken : AbstractControlDesignToken
|
||||
BadgeRibbonTextPadding = new Thickness(_globalToken.PaddingXS, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 内部使用的 Token
|
||||
|
||||
public double BadgeFontHeight { get; set; }
|
||||
|
@ -18,7 +18,6 @@ public enum CountBadgeSize
|
||||
Small
|
||||
}
|
||||
|
||||
|
||||
public class CountBadge : Control, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<string?> BadgeColorProperty
|
||||
@ -52,12 +51,12 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
AvaloniaProperty.Register<CountBadge, TimeSpan>(
|
||||
nameof(MotionDuration));
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
|
||||
private AdornerLayer? _adornerLayer;
|
||||
private bool _animating;
|
||||
private CountBadgeAdorner? _badgeAdorner;
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
|
||||
static CountBadge()
|
||||
{
|
||||
AffectsMeasure<CountBadge>(DecoratedTargetProperty,
|
||||
@ -143,7 +142,10 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
public sealed override void ApplyTemplate()
|
||||
{
|
||||
base.ApplyTemplate();
|
||||
if (DecoratedTarget is null) CreateBadgeAdorner();
|
||||
if (DecoratedTarget is null)
|
||||
{
|
||||
CreateBadgeAdorner();
|
||||
}
|
||||
}
|
||||
|
||||
private CountBadgeAdorner CreateBadgeAdorner()
|
||||
@ -153,7 +155,10 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
_badgeAdorner = new CountBadgeAdorner();
|
||||
_customStyle.SetupTokenBindings();
|
||||
HandleDecoratedTargetChanged();
|
||||
if (BadgeColor is not null) SetupBadgeColor(BadgeColor);
|
||||
if (BadgeColor is not null)
|
||||
{
|
||||
SetupBadgeColor(BadgeColor);
|
||||
}
|
||||
}
|
||||
|
||||
return _badgeAdorner;
|
||||
@ -167,7 +172,11 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer == null) return;
|
||||
if (_adornerLayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdornerLayer.SetAdornedElement(badgeAdorner, this);
|
||||
AdornerLayer.SetIsClipEnabled(badgeAdorner, false);
|
||||
_adornerLayer.Children.Add(badgeAdorner);
|
||||
@ -178,7 +187,11 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
{
|
||||
PrepareAdorner();
|
||||
|
||||
if (VisualRoot is null || _animating) return;
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
|
||||
@ -213,14 +226,22 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
private void HideAdorner()
|
||||
{
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer is null || _badgeAdorner is null) return;
|
||||
if (_adornerLayer is null || _badgeAdorner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_adornerLayer.Children.Remove(_badgeAdorner);
|
||||
_adornerLayer = null;
|
||||
}
|
||||
|
||||
private void HideAdornerWithMotion()
|
||||
{
|
||||
if (VisualRoot is null || _animating) return;
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
AbstractMotion motion;
|
||||
@ -291,7 +312,11 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null) return;
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
@ -304,7 +329,11 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null) return;
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdornerWithMotion();
|
||||
}
|
||||
else
|
||||
@ -315,17 +344,28 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (e.Property == DecoratedTargetProperty) HandleDecoratedTargetChanged();
|
||||
if (e.Property == DecoratedTargetProperty)
|
||||
{
|
||||
HandleDecoratedTargetChanged();
|
||||
}
|
||||
|
||||
if (e.Property == BadgeColorProperty) SetupBadgeColor(e.GetNewValue<string>());
|
||||
if (e.Property == BadgeColorProperty)
|
||||
{
|
||||
SetupBadgeColor(e.GetNewValue<string>());
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == CountProperty)
|
||||
{
|
||||
var newCount = e.GetNewValue<int>();
|
||||
if (newCount == 0 && !ShowZero)
|
||||
BadgeIsVisible = false;
|
||||
else if (newCount > 0) BadgeIsVisible = true;
|
||||
{
|
||||
BadgeIsVisible = false;
|
||||
}
|
||||
else if (newCount > 0)
|
||||
{
|
||||
BadgeIsVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,12 +374,17 @@ public class CountBadge : Control, IControlCustomStyle
|
||||
colorStr = colorStr.Trim().ToLower();
|
||||
|
||||
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
|
||||
{
|
||||
if (presetColor.Type.ToString().ToLower() == colorStr)
|
||||
{
|
||||
_badgeAdorner!.BadgeColor = new SolidColorBrush(presetColor.Color());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Color.TryParse(colorStr, out var color)) _badgeAdorner!.BadgeColor = new SolidColorBrush(color);
|
||||
if (Color.TryParse(colorStr, out var color))
|
||||
{
|
||||
_badgeAdorner!.BadgeColor = new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
}
|
@ -74,11 +74,12 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
o => o.Size,
|
||||
(o, v) => o.Size = v);
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
private readonly List<FormattedText> _formattedTexts;
|
||||
|
||||
private BoxShadows _boxShadows;
|
||||
private string? _countText;
|
||||
private Size _countTextSize;
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
private readonly List<FormattedText> _formattedTexts;
|
||||
|
||||
private bool _initialized;
|
||||
|
||||
@ -217,7 +218,10 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
if (Styles.Count == 0) _customStyle.BuildStyles();
|
||||
if (Styles.Count == 0)
|
||||
{
|
||||
_customStyle.BuildStyles();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyTemplate()
|
||||
@ -236,6 +240,7 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
private void BuildBoxShadow()
|
||||
{
|
||||
if (BadgeShadowColor is not null)
|
||||
{
|
||||
_boxShadows = new BoxShadows(new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
@ -244,6 +249,7 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
Spread = BadgeShadowSize,
|
||||
Color = ((SolidColorBrush)BadgeShadowColor).Color
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
@ -253,7 +259,9 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
if (e.Property == BadgeShadowSizeProperty ||
|
||||
e.Property == BadgeShadowColorProperty)
|
||||
{
|
||||
BuildBoxShadow();
|
||||
}
|
||||
|
||||
if (e.Property == CountProperty || e.Property == OverflowCountProperty)
|
||||
{
|
||||
@ -266,7 +274,10 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (IsAdornerMode) return availableSize;
|
||||
if (IsAdornerMode)
|
||||
{
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
return GetBadgePillSize();
|
||||
}
|
||||
@ -278,7 +289,10 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
if (_countText?.Length > 1)
|
||||
{
|
||||
targetWidth += PaddingInline;
|
||||
if (Count > _overflowCount) targetWidth += PaddingInline;
|
||||
if (Count > _overflowCount)
|
||||
{
|
||||
targetWidth += PaddingInline;
|
||||
}
|
||||
}
|
||||
|
||||
targetWidth = Math.Max(targetWidth, _countTextSize.Width);
|
||||
@ -305,9 +319,13 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
private void BuildCountText()
|
||||
{
|
||||
if (Count > _overflowCount)
|
||||
{
|
||||
_countText = $"{_overflowCount}+";
|
||||
}
|
||||
else
|
||||
{
|
||||
_countText = $"{Count}";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
@ -329,9 +347,13 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
Point origin;
|
||||
if (AnimationRenderTransformOrigin.HasValue)
|
||||
{
|
||||
origin = AnimationRenderTransformOrigin.Value.ToPixels(badgeRect.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = RenderTransformOrigin.ToPixels(badgeRect.Size);
|
||||
}
|
||||
|
||||
var offset = Matrix.CreateTranslation(new Point(origin.X + offsetX, origin.Y + offsetY));
|
||||
var renderTransform = -offset * RenderTransform.Value * offset;
|
||||
@ -341,7 +363,7 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
context.DrawPilledRect(BadgeColor, null, badgeRect, Orientation.Horizontal, _boxShadows);
|
||||
|
||||
// 计算合适的文字 x 坐标
|
||||
var textOffsetX = offsetX + (badgeSize.Width - _countTextSize.Width) / 2;
|
||||
var textOffsetX = offsetX + (badgeSize.Width - _countTextSize.Width) / 2;
|
||||
var textOffsetY = offsetY + (badgeSize.Height - _countTextSize.Height) / 2;
|
||||
foreach (var formattedText in _formattedTexts)
|
||||
{
|
||||
@ -360,12 +382,18 @@ internal class CountBadgeAdorner : Control, IControlCustomStyle
|
||||
if (Count > _overflowCount)
|
||||
|
||||
// 生成一个即可
|
||||
{
|
||||
_formattedTexts.Add(BuildFormattedText(_countText));
|
||||
}
|
||||
else
|
||||
|
||||
// 没有数字都生成一个
|
||||
{
|
||||
foreach (var c in _countText)
|
||||
{
|
||||
_formattedTexts.Add(BuildFormattedText(c.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ internal class CountBadgeZoomBadgeIn : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class CountBadgeZoomBadgeOut : AbstractMotion
|
||||
{
|
||||
public CountBadgeZoomBadgeOut()
|
||||
@ -98,7 +97,6 @@ internal class CountBadgeZoomBadgeOut : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class CountBadgeNoWrapperZoomBadgeIn : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
@ -134,7 +132,6 @@ internal class CountBadgeNoWrapperZoomBadgeIn : AbstractMotion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class CountBadgeNoWrapperZoomBadgeOut : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
|
@ -21,7 +21,6 @@ public enum DotBadgeStatus
|
||||
Warning
|
||||
}
|
||||
|
||||
|
||||
public class DotBadge : Control, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<string?> DotColorProperty
|
||||
@ -49,13 +48,14 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
AvaloniaProperty.Register<CountBadge, TimeSpan>(
|
||||
nameof(MotionDuration));
|
||||
|
||||
private AdornerLayer? _adornerLayer;
|
||||
private bool _animating;
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
private DotBadgeAdorner? _dotBadgeAdorner;
|
||||
|
||||
private readonly bool _initialized = false;
|
||||
|
||||
private AdornerLayer? _adornerLayer;
|
||||
private bool _animating;
|
||||
private DotBadgeAdorner? _dotBadgeAdorner;
|
||||
|
||||
static DotBadge()
|
||||
{
|
||||
AffectsMeasure<DotBadge>(DecoratedTargetProperty, TextProperty);
|
||||
@ -135,7 +135,10 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
_dotBadgeAdorner = new DotBadgeAdorner();
|
||||
_customStyle.SetupTokenBindings();
|
||||
HandleDecoratedTargetChanged();
|
||||
if (DotColor is not null) SetupDotColor(DotColor);
|
||||
if (DotColor is not null)
|
||||
{
|
||||
SetupDotColor(DotColor);
|
||||
}
|
||||
}
|
||||
|
||||
return _dotBadgeAdorner;
|
||||
@ -149,7 +152,11 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer == null) return;
|
||||
if (_adornerLayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdornerLayer.SetAdornedElement(dotBadgeAdorner, this);
|
||||
AdornerLayer.SetIsClipEnabled(dotBadgeAdorner, false);
|
||||
_adornerLayer.Children.Add(dotBadgeAdorner);
|
||||
@ -160,7 +167,11 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
{
|
||||
PrepareAdorner();
|
||||
|
||||
if (VisualRoot is null || _animating) return;
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
var motion = new CountBadgeZoomBadgeIn();
|
||||
@ -180,7 +191,10 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
private void HideAdorner()
|
||||
{
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer is null || _dotBadgeAdorner is null) return;
|
||||
if (_adornerLayer is null || _dotBadgeAdorner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_adornerLayer.Children.Remove(_dotBadgeAdorner);
|
||||
_adornerLayer = null;
|
||||
@ -188,7 +202,11 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
|
||||
private void HideAdornerWithMotion()
|
||||
{
|
||||
if (VisualRoot is null || _animating) return;
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
var motion = new CountBadgeZoomBadgeOut();
|
||||
@ -234,7 +252,10 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
public sealed override void ApplyTemplate()
|
||||
{
|
||||
base.ApplyTemplate();
|
||||
if (DecoratedTarget is null) CreateDotBadgeAdorner();
|
||||
if (DecoratedTarget is null)
|
||||
{
|
||||
CreateDotBadgeAdorner();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
@ -245,7 +266,11 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null) return;
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
@ -258,27 +283,44 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null) return;
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DecoratedTarget is not null)
|
||||
{
|
||||
PrepareAdornerWithMotion();
|
||||
}
|
||||
else
|
||||
{
|
||||
PrepareAdorner();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DecoratedTarget is not null)
|
||||
{
|
||||
HideAdornerWithMotion();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideAdorner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
if (e.Property == DecoratedTargetProperty) HandleDecoratedTargetChanged();
|
||||
if (e.Property == DecoratedTargetProperty)
|
||||
{
|
||||
HandleDecoratedTargetChanged();
|
||||
}
|
||||
|
||||
if (e.Property == DotColorProperty) SetupDotColor(e.GetNewValue<string>());
|
||||
if (e.Property == DotColorProperty)
|
||||
{
|
||||
SetupDotColor(e.GetNewValue<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,12 +329,17 @@ public class DotBadge : Control, IControlCustomStyle
|
||||
colorStr = colorStr.Trim().ToLower();
|
||||
|
||||
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
|
||||
{
|
||||
if (presetColor.Type.ToString().ToLower() == colorStr)
|
||||
{
|
||||
_dotBadgeAdorner!.BadgeDotColor = new SolidColorBrush(presetColor.Color());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Color.TryParse(colorStr, out var color)) _dotBadgeAdorner!.BadgeDotColor = new SolidColorBrush(color);
|
||||
if (Color.TryParse(colorStr, out var color))
|
||||
{
|
||||
_dotBadgeAdorner!.BadgeDotColor = new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
}
|
@ -56,9 +56,10 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
AvaloniaProperty.Register<DotBadgeAdorner, Point>(
|
||||
nameof(Offset));
|
||||
|
||||
private BoxShadows _boxShadows;
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
|
||||
private BoxShadows _boxShadows;
|
||||
|
||||
private bool _initialized;
|
||||
|
||||
private bool _isAdornerMode;
|
||||
@ -146,7 +147,10 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
|
||||
void IControlCustomStyle.BuildStyles()
|
||||
{
|
||||
if (Styles.Count == 0) BuildBadgeColorStyle();
|
||||
if (Styles.Count == 0)
|
||||
{
|
||||
BuildBadgeColorStyle();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void ApplyTemplate()
|
||||
@ -230,7 +234,10 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
targetWidth += StatusSize;
|
||||
targetWidth += textSize.Width;
|
||||
targetHeight += Math.Max(textSize.Height, StatusSize);
|
||||
if (textSize.Width > 0) targetWidth += BadgeTextMarginInline;
|
||||
if (textSize.Width > 0)
|
||||
{
|
||||
targetWidth += BadgeTextMarginInline;
|
||||
}
|
||||
}
|
||||
|
||||
return new Size(targetWidth, targetHeight);
|
||||
@ -242,9 +249,13 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
double textOffsetX = 0;
|
||||
if (IsAdornerMode)
|
||||
{
|
||||
textOffsetX += DotSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
textOffsetX += StatusSize;
|
||||
}
|
||||
|
||||
textOffsetX += BadgeTextMarginInline;
|
||||
var textRect = new Rect(new Point(textOffsetX, 0), _textLabel!.DesiredSize);
|
||||
@ -259,8 +270,11 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
base.OnPropertyChanged(e);
|
||||
if (e.Property == IsAdornerModeProperty)
|
||||
{
|
||||
var newValue = e.GetNewValue<bool>();
|
||||
if (_textLabel is not null) _textLabel.IsVisible = !newValue;
|
||||
var newValue = e.GetNewValue<bool>();
|
||||
if (_textLabel is not null)
|
||||
{
|
||||
_textLabel.IsVisible = !newValue;
|
||||
}
|
||||
}
|
||||
else if (e.Property == BadgeShadowSizeProperty ||
|
||||
e.Property == BadgeShadowColorProperty)
|
||||
@ -273,9 +287,13 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
var dotSize = 0d;
|
||||
if (IsAdornerMode)
|
||||
{
|
||||
dotSize = DotSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
dotSize = StatusSize;
|
||||
}
|
||||
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
@ -297,9 +315,13 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
Point origin;
|
||||
if (AnimationRenderTransformOrigin.HasValue)
|
||||
{
|
||||
origin = AnimationRenderTransformOrigin.Value.ToPixels(dotRect.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = RenderTransformOrigin.ToPixels(dotRect.Size);
|
||||
}
|
||||
|
||||
var offset = Matrix.CreateTranslation(new Point(origin.X + offsetX, origin.Y + offsetY));
|
||||
var renderTransform = -offset * RenderTransform.Value * offset;
|
||||
@ -312,6 +334,7 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
private void BuildBoxShadow()
|
||||
{
|
||||
if (BadgeShadowColor is not null)
|
||||
{
|
||||
_boxShadows = new BoxShadows(new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
@ -320,5 +343,6 @@ internal class DotBadgeAdorner : Control, IControlCustomStyle
|
||||
Spread = BadgeShadowSize,
|
||||
Color = ((SolidColorBrush)BadgeShadowColor).Color
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ public enum RibbonBadgePlacement
|
||||
End
|
||||
}
|
||||
|
||||
|
||||
public class RibbonBadge : Control, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<string?> RibbonColorProperty
|
||||
@ -38,9 +37,9 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
public static readonly StyledProperty<bool> BadgeIsVisibleProperty =
|
||||
AvaloniaProperty.Register<RibbonBadge, bool>(nameof(BadgeIsVisible));
|
||||
|
||||
private AdornerLayer? _adornerLayer;
|
||||
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
|
||||
private AdornerLayer? _adornerLayer;
|
||||
private RibbonBadgeAdorner? _ribbonBadgeAdorner;
|
||||
|
||||
static RibbonBadge()
|
||||
@ -126,19 +125,27 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
colorStr = colorStr.Trim().ToLower();
|
||||
|
||||
foreach (var presetColor in PresetPrimaryColor.AllColorTypes())
|
||||
{
|
||||
if (presetColor.Type.ToString().ToLower() == colorStr)
|
||||
{
|
||||
_ribbonBadgeAdorner!.RibbonColor = new SolidColorBrush(presetColor.Color());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Color.TryParse(colorStr, out var color)) _ribbonBadgeAdorner!.RibbonColor = new SolidColorBrush(color);
|
||||
if (Color.TryParse(colorStr, out var color))
|
||||
{
|
||||
_ribbonBadgeAdorner!.RibbonColor = new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void ApplyTemplate()
|
||||
{
|
||||
base.ApplyTemplate();
|
||||
if (DecoratedTarget is null) CreateBadgeAdorner();
|
||||
if (DecoratedTarget is null)
|
||||
{
|
||||
CreateBadgeAdorner();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
@ -150,7 +157,11 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null) return;
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
@ -161,9 +172,15 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (e.Property == DecoratedTargetProperty) HandleDecoratedTargetChanged();
|
||||
if (e.Property == DecoratedTargetProperty)
|
||||
{
|
||||
HandleDecoratedTargetChanged();
|
||||
}
|
||||
|
||||
if (e.Property == RibbonColorProperty) SetupRibbonColor(e.GetNewValue<string>());
|
||||
if (e.Property == RibbonColorProperty)
|
||||
{
|
||||
SetupRibbonColor(e.GetNewValue<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +191,10 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
_ribbonBadgeAdorner = new RibbonBadgeAdorner();
|
||||
_customStyle.SetupTokenBindings();
|
||||
HandleDecoratedTargetChanged();
|
||||
if (RibbonColor is not null) SetupRibbonColor(RibbonColor);
|
||||
if (RibbonColor is not null)
|
||||
{
|
||||
SetupRibbonColor(RibbonColor);
|
||||
}
|
||||
}
|
||||
|
||||
return _ribbonBadgeAdorner;
|
||||
@ -188,7 +208,11 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer == null) return;
|
||||
if (_adornerLayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdornerLayer.SetAdornedElement(ribbonBadgeAdorner, this);
|
||||
AdornerLayer.SetIsClipEnabled(ribbonBadgeAdorner, false);
|
||||
_adornerLayer.Children.Add(ribbonBadgeAdorner);
|
||||
@ -198,7 +222,10 @@ public class RibbonBadge : Control, IControlCustomStyle
|
||||
private void HideAdorner()
|
||||
{
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer is null || _ribbonBadgeAdorner is null) return;
|
||||
if (_adornerLayer is null || _ribbonBadgeAdorner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_adornerLayer.Children.Remove(_ribbonBadgeAdorner);
|
||||
_adornerLayer = null;
|
||||
|
@ -50,8 +50,8 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
(o, v) => o.IsAdornerMode = v);
|
||||
|
||||
private readonly BorderRenderHelper _borderRenderHelper;
|
||||
private Geometry? _cornerGeometry;
|
||||
private readonly IControlCustomStyle _customStyle;
|
||||
private Geometry? _cornerGeometry;
|
||||
private bool _initialized;
|
||||
|
||||
private bool _isAdornerMode;
|
||||
@ -143,7 +143,10 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
if (Styles.Count == 0) _customStyle.BuildStyles();
|
||||
if (Styles.Count == 0)
|
||||
{
|
||||
_customStyle.BuildStyles();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void ApplyTemplate()
|
||||
@ -182,7 +185,10 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (_textBlock is not null) _textBlock.Arrange(GetTextRect());
|
||||
if (_textBlock is not null)
|
||||
{
|
||||
_textBlock.Arrange(GetTextRect());
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
@ -191,8 +197,12 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (e.Property == PlacementProperty)
|
||||
{
|
||||
BuildCornerGeometry(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
@ -231,7 +241,10 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
|
||||
private Rect GetTextRect()
|
||||
{
|
||||
if (_textBlock is null) return default;
|
||||
if (_textBlock is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
@ -239,9 +252,13 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
{
|
||||
offsetY += BadgeRibbonOffset.Y;
|
||||
if (Placement == RibbonBadgePlacement.End)
|
||||
{
|
||||
offsetX = DesiredSize.Width - _textBlock.DesiredSize.Width + BadgeRibbonOffset.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetX = -BadgeRibbonOffset.X;
|
||||
}
|
||||
}
|
||||
|
||||
return new Rect(new Point(offsetX, offsetY), _textBlock.DesiredSize);
|
||||
@ -249,7 +266,10 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
|
||||
private Rect GetCornerRect()
|
||||
{
|
||||
if (_cornerGeometry is null) return default;
|
||||
if (_cornerGeometry is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var targetWidth = _cornerGeometry.Bounds.Width;
|
||||
var targetHeight = _cornerGeometry.Bounds.Height;
|
||||
@ -258,12 +278,18 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
if (!IsAdornerMode)
|
||||
{
|
||||
offsetY = DesiredSize.Height - targetHeight;
|
||||
if (Placement == RibbonBadgePlacement.End) offsetX = DesiredSize.Width - targetWidth;
|
||||
if (Placement == RibbonBadgePlacement.End)
|
||||
{
|
||||
offsetX = DesiredSize.Width - targetWidth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var textRect = GetTextRect();
|
||||
if (Placement == RibbonBadgePlacement.End) offsetX = textRect.Right - targetWidth;
|
||||
var textRect = GetTextRect();
|
||||
if (Placement == RibbonBadgePlacement.End)
|
||||
{
|
||||
offsetX = textRect.Right - targetWidth;
|
||||
}
|
||||
|
||||
offsetY = textRect.Bottom;
|
||||
}
|
||||
@ -288,9 +314,15 @@ internal class RibbonBadgeAdorner : Control, IControlCustomStyle
|
||||
context.EndFigure(true);
|
||||
_cornerGeometry = geometryStream;
|
||||
var transforms = new TransformGroup();
|
||||
if (BadgeRibbonCornerTransform is not null) transforms.Children.Add(BadgeRibbonCornerTransform);
|
||||
if (BadgeRibbonCornerTransform is not null)
|
||||
{
|
||||
transforms.Children.Add(BadgeRibbonCornerTransform);
|
||||
}
|
||||
|
||||
if (Placement == RibbonBadgePlacement.Start) transforms.Children.Add(new ScaleTransform(-1, 1));
|
||||
if (Placement == RibbonBadgePlacement.Start)
|
||||
{
|
||||
transforms.Children.Add(new ScaleTransform(-1, 1));
|
||||
}
|
||||
|
||||
_cornerGeometry.Transform = transforms;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ namespace AtomUI.Controls;
|
||||
|
||||
using AvaloniaButtonSpinner = Avalonia.Controls.ButtonSpinner;
|
||||
|
||||
|
||||
public class ButtonSpinner : AvaloniaButtonSpinner
|
||||
{
|
||||
private ButtonSpinnerDecoratedBox? _decoratedBox;
|
||||
@ -17,8 +16,13 @@ public class ButtonSpinner : AvaloniaButtonSpinner
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (change.Property == CornerRadiusProperty)
|
||||
{
|
||||
SetupSpinnerHandleCornerRadius();
|
||||
else if (change.Property == ButtonSpinnerLocationProperty) SetupSpinnerHandleCornerRadius();
|
||||
}
|
||||
else if (change.Property == ButtonSpinnerLocationProperty)
|
||||
{
|
||||
SetupSpinnerHandleCornerRadius();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupSpinnerHandleCornerRadius()
|
||||
@ -26,15 +30,19 @@ public class ButtonSpinner : AvaloniaButtonSpinner
|
||||
if (_spinnerHandleDecorator is not null)
|
||||
{
|
||||
if (ButtonSpinnerLocation == Location.Left)
|
||||
{
|
||||
_spinnerHandleDecorator.CornerRadius = new CornerRadius(CornerRadius.TopLeft,
|
||||
0,
|
||||
0,
|
||||
CornerRadius.BottomLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
_spinnerHandleDecorator.CornerRadius = new CornerRadius(0,
|
||||
CornerRadius.TopRight,
|
||||
CornerRadius.BottomRight,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,8 +60,6 @@ public class ButtonSpinner : AvaloniaButtonSpinner
|
||||
SetupSpinnerHandleCornerRadius();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<object?> LeftAddOnProperty =
|
||||
|
@ -7,8 +7,6 @@ internal class ButtonSpinnerDecoratedBox : AddOnDecoratedBox
|
||||
{
|
||||
protected override Type StyleKeyOverride => typeof(AddOnDecoratedBox);
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<ButtonSpinnerDecoratedBox, Location> ButtonSpinnerLocationProperty =
|
||||
|
@ -27,11 +27,15 @@ internal class ButtonSpinnerInnerBox : AddOnDecoratedInnerBox, ICustomHitTest
|
||||
{
|
||||
var padding = _spinnerHandleWidthToken + InnerBoxPadding.Right;
|
||||
if (ButtonSpinnerLocation == Location.Right)
|
||||
{
|
||||
EffectiveInnerBoxPadding = new Thickness(InnerBoxPadding.Left, InnerBoxPadding.Top, padding,
|
||||
InnerBoxPadding.Bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectiveInnerBoxPadding = new Thickness(padding, InnerBoxPadding.Top, InnerBoxPadding.Right,
|
||||
InnerBoxPadding.Bottom);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -77,8 +81,6 @@ internal class ButtonSpinnerInnerBox : AddOnDecoratedInnerBox, ICustomHitTest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<object?> SpinnerContentProperty =
|
||||
@ -92,8 +94,6 @@ internal class ButtonSpinnerInnerBox : AddOnDecoratedInnerBox, ICustomHitTest
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<ButtonSpinnerInnerBox, Location> ButtonSpinnerLocationProperty =
|
||||
|
@ -20,42 +20,42 @@ internal class ButtonSpinnerToken : LineEditToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输入框宽度
|
||||
/// 输入框宽度
|
||||
/// </summary>
|
||||
public double ControlWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮宽度
|
||||
/// 操作按钮宽度
|
||||
/// </summary>
|
||||
public double HandleWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮图标大小
|
||||
/// 操作按钮图标大小
|
||||
/// </summary>
|
||||
public double HandleIconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮背景色
|
||||
/// 操作按钮背景色
|
||||
/// </summary>
|
||||
public Color HandleBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮激活背景色
|
||||
/// 操作按钮激活背景色
|
||||
/// </summary>
|
||||
public Color HandleActiveBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮悬浮颜色
|
||||
/// 操作按钮悬浮颜色
|
||||
/// </summary>
|
||||
public Color HandleHoverColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作按钮边框颜色
|
||||
/// 操作按钮边框颜色
|
||||
/// </summary>
|
||||
public Color HandleBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 面性变体操作按钮背景色
|
||||
/// 面性变体操作按钮背景色
|
||||
/// </summary>
|
||||
public Color FilledHandleBg { get; set; }
|
||||
|
||||
|
@ -21,7 +21,6 @@ namespace AtomUI.Controls;
|
||||
using AvaloniaButton = Avalonia.Controls.Button;
|
||||
using ButtonSizeType = SizeType;
|
||||
|
||||
|
||||
public enum ButtonType
|
||||
{
|
||||
Default,
|
||||
@ -30,7 +29,6 @@ public enum ButtonType
|
||||
Text
|
||||
}
|
||||
|
||||
|
||||
public enum ButtonShape
|
||||
{
|
||||
Default,
|
||||
@ -38,15 +36,13 @@ public enum ButtonShape
|
||||
Round
|
||||
}
|
||||
|
||||
|
||||
// TODO 目前不能动态切换 ButtonType
|
||||
|
||||
|
||||
[PseudoClasses(IconOnlyPC, LoadingPC)]
|
||||
public class Button : AvaloniaButton,
|
||||
ISizeTypeAware,
|
||||
IControlCustomStyle,
|
||||
IWaveAdornerInfoProvider
|
||||
ISizeTypeAware,
|
||||
IControlCustomStyle,
|
||||
IWaveAdornerInfoProvider
|
||||
{
|
||||
public const string IconOnlyPC = ":icononly";
|
||||
public const string LoadingPC = ":loading";
|
||||
@ -91,6 +87,7 @@ public class Button : AvaloniaButton,
|
||||
if (ButtonType == ButtonType.Default)
|
||||
{
|
||||
if (IsDanger)
|
||||
{
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = DangerShadow.OffsetX,
|
||||
@ -98,7 +95,9 @@ public class Button : AvaloniaButton,
|
||||
Color = DangerShadow.Color,
|
||||
BlurRadius = DangerShadow.Blur
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = DefaultShadow.OffsetX,
|
||||
@ -106,10 +105,12 @@ public class Button : AvaloniaButton,
|
||||
Color = DefaultShadow.Color,
|
||||
BlurRadius = DefaultShadow.Blur
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (ButtonType == ButtonType.Primary)
|
||||
{
|
||||
if (IsDanger)
|
||||
{
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = DangerShadow.OffsetX,
|
||||
@ -117,7 +118,9 @@ public class Button : AvaloniaButton,
|
||||
Color = DangerShadow.Color,
|
||||
BlurRadius = DangerShadow.Blur
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = PrimaryShadow.OffsetX,
|
||||
@ -125,6 +128,7 @@ public class Button : AvaloniaButton,
|
||||
Color = PrimaryShadow.Color,
|
||||
BlurRadius = PrimaryShadow.Blur
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,9 +136,13 @@ public class Button : AvaloniaButton,
|
||||
{
|
||||
ControlStateUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed)
|
||||
{
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
}
|
||||
else
|
||||
{
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
@ -182,56 +190,79 @@ public class Button : AvaloniaButton,
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsPressedProperty ||
|
||||
e.Property == IsPressedProperty ||
|
||||
e.Property == IsEnabledProperty)
|
||||
{
|
||||
_customStyle.CollectStyleState();
|
||||
ApplyIconModeStyleConfig();
|
||||
if (e.Property == IsPressedProperty)
|
||||
{
|
||||
if (!IsLoading && _styleState.HasFlag(ControlStyleState.Raised) && (ButtonType == ButtonType.Primary ||
|
||||
ButtonType == ButtonType.Default))
|
||||
ButtonType == ButtonType.Default))
|
||||
{
|
||||
WaveType waveType = default;
|
||||
if (Shape == ButtonShape.Default)
|
||||
{
|
||||
waveType = WaveType.RoundRectWave;
|
||||
}
|
||||
else if (Shape == ButtonShape.Round)
|
||||
waveType = WaveType.PillWave;
|
||||
else if (Shape == ButtonShape.Circle) waveType = WaveType.CircleWave;
|
||||
{
|
||||
waveType = WaveType.PillWave;
|
||||
}
|
||||
else if (Shape == ButtonShape.Circle)
|
||||
{
|
||||
waveType = WaveType.CircleWave;
|
||||
}
|
||||
|
||||
Color? waveColor = null;
|
||||
if (IsDanger)
|
||||
{
|
||||
if (ButtonType == ButtonType.Primary && !IsGhost)
|
||||
{
|
||||
waveColor = Color.Parse(Background?.ToString()!);
|
||||
}
|
||||
else
|
||||
{
|
||||
waveColor = Color.Parse(Foreground?.ToString()!);
|
||||
}
|
||||
}
|
||||
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, waveType, waveColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == ButtonTypeProperty)
|
||||
{
|
||||
if (VisualRoot is not null) SetupControlTheme();
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
SetupControlTheme();
|
||||
}
|
||||
}
|
||||
else if (e.Property == ContentProperty ||
|
||||
e.Property == TextProperty ||
|
||||
e.Property == TextProperty ||
|
||||
e.Property == IsLoadingProperty)
|
||||
{
|
||||
UpdatePseudoClasses();
|
||||
}
|
||||
|
||||
if (e.Property == IconProperty) SetupIcon();
|
||||
if (e.Property == IconProperty)
|
||||
{
|
||||
SetupIcon();
|
||||
}
|
||||
|
||||
if (e.Property == IsDangerProperty ||
|
||||
e.Property == IsGhostProperty ||
|
||||
e.Property == IsGhostProperty ||
|
||||
e.Property == ButtonTypeProperty)
|
||||
{
|
||||
SetupIconBrush();
|
||||
}
|
||||
|
||||
if (e.Property == BorderBrushProperty ||
|
||||
e.Property == ButtonTypeProperty)
|
||||
{
|
||||
SetupEffectiveBorderThickness();
|
||||
}
|
||||
}
|
||||
|
||||
public Rect WaveGeometry()
|
||||
@ -287,19 +318,29 @@ public class Button : AvaloniaButton,
|
||||
private void SetupControlTheme()
|
||||
{
|
||||
if (ButtonType == ButtonType.Default)
|
||||
{
|
||||
TokenResourceBinder.CreateTokenBinding(this, ThemeProperty, DefaultButtonTheme.ID);
|
||||
}
|
||||
else if (ButtonType == ButtonType.Primary)
|
||||
{
|
||||
TokenResourceBinder.CreateTokenBinding(this, ThemeProperty, PrimaryButtonTheme.ID);
|
||||
}
|
||||
else if (ButtonType == ButtonType.Text)
|
||||
{
|
||||
TokenResourceBinder.CreateTokenBinding(this, ThemeProperty, TextButtonTheme.ID);
|
||||
}
|
||||
else if (ButtonType == ButtonType.Link)
|
||||
{
|
||||
TokenResourceBinder.CreateTokenBinding(this, ThemeProperty, LinkButtonTheme.ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyShapeStyleConfig()
|
||||
{
|
||||
if (Shape == ButtonShape.Circle)
|
||||
{
|
||||
TokenResourceBinder.CreateTokenBinding(this, PaddingProperty, ButtonTokenResourceKey.CirclePadding);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
@ -312,16 +353,25 @@ public class Button : AvaloniaButton,
|
||||
|
||||
protected virtual void ApplyIconModeStyleConfig()
|
||||
{
|
||||
if (Icon is null) return;
|
||||
if (Icon is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled))
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken))
|
||||
{
|
||||
Icon.IconMode = IconMode.Selected;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver))
|
||||
{
|
||||
Icon.IconMode = IconMode.Active;
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon.IconMode = IconMode.Normal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -348,9 +398,13 @@ public class Button : AvaloniaButton,
|
||||
else if (ButtonType == ButtonType.Primary)
|
||||
{
|
||||
if (IsGhost)
|
||||
{
|
||||
EffectiveBorderThickness = BorderThickness;
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectiveBorderThickness = new Thickness(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -469,9 +523,9 @@ public class Button : AvaloniaButton,
|
||||
}
|
||||
|
||||
protected virtual void NotifyIconBrushCalculated(in TokenResourceKey normalFilledBrushKey,
|
||||
in TokenResourceKey selectedFilledBrushKey,
|
||||
in TokenResourceKey activeFilledBrushKey,
|
||||
in TokenResourceKey disabledFilledBrushKey)
|
||||
in TokenResourceKey selectedFilledBrushKey,
|
||||
in TokenResourceKey activeFilledBrushKey,
|
||||
in TokenResourceKey disabledFilledBrushKey)
|
||||
{
|
||||
}
|
||||
|
||||
@ -481,8 +535,6 @@ public class Button : AvaloniaButton,
|
||||
PseudoClasses.Set(LoadingPC, IsLoading);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<ButtonType> ButtonTypeProperty =
|
||||
@ -568,8 +620,6 @@ public class Button : AvaloniaButton,
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly StyledProperty<double> ControlHeightTokenProperty =
|
||||
|
@ -16,217 +16,217 @@ internal class ButtonToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文字字重
|
||||
/// 文字字重
|
||||
/// </summary>
|
||||
public int FontWeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮阴影
|
||||
/// 默认按钮阴影
|
||||
/// </summary>
|
||||
public BoxShadow DefaultShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主要按钮阴影
|
||||
/// 主要按钮阴影
|
||||
/// </summary>
|
||||
public BoxShadow PrimaryShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 危险按钮阴影
|
||||
/// 危险按钮阴影
|
||||
/// </summary>
|
||||
public BoxShadow DangerShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主要按钮文本颜色
|
||||
/// 主要按钮文本颜色
|
||||
/// </summary>
|
||||
public Color PrimaryColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮文本颜色
|
||||
/// 默认按钮文本颜色
|
||||
/// </summary>
|
||||
public Color DefaultColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮背景色
|
||||
/// 默认按钮背景色
|
||||
/// </summary>
|
||||
public Color DefaultBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮边框颜色
|
||||
/// 默认按钮边框颜色
|
||||
/// </summary>
|
||||
public Color DefaultBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认的禁用边框颜色
|
||||
/// 默认的禁用边框颜色
|
||||
/// </summary>
|
||||
public Color DefaultBorderColorDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 危险按钮文本颜色
|
||||
/// 危险按钮文本颜色
|
||||
/// </summary>
|
||||
public Color DangerColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮悬浮态背景色
|
||||
/// 默认按钮悬浮态背景色
|
||||
/// </summary>
|
||||
public Color DefaultHoverBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮悬浮态文本颜色
|
||||
/// 默认按钮悬浮态文本颜色
|
||||
/// </summary>
|
||||
public Color DefaultHoverColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮悬浮态边框颜色
|
||||
/// 默认按钮悬浮态边框颜色
|
||||
/// </summary>
|
||||
public Color DefaultHoverBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮激活态背景色
|
||||
/// 默认按钮激活态背景色
|
||||
/// </summary>
|
||||
public Color DefaultActiveBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮激活态文字颜色
|
||||
/// 默认按钮激活态文字颜色
|
||||
/// </summary>
|
||||
public Color DefaultActiveColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认按钮激活态边框颜色
|
||||
/// 默认按钮激活态边框颜色
|
||||
/// </summary>
|
||||
public Color DefaultActiveBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 禁用状态边框颜色
|
||||
/// 禁用状态边框颜色
|
||||
/// </summary>
|
||||
public Color BorderColorDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认幽灵按钮文本颜色
|
||||
/// 默认幽灵按钮文本颜色
|
||||
/// </summary>
|
||||
public Color DefaultGhostColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 幽灵按钮背景色
|
||||
/// 幽灵按钮背景色
|
||||
/// </summary>
|
||||
public Color GhostBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认幽灵按钮边框颜色
|
||||
/// 默认幽灵按钮边框颜色
|
||||
/// </summary>
|
||||
public Color DefaultGhostBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮内间距
|
||||
/// 按钮内间距
|
||||
/// </summary>
|
||||
public Thickness Padding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 大号按钮内间距
|
||||
/// 大号按钮内间距
|
||||
/// </summary>
|
||||
public Thickness PaddingLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号按钮内间距
|
||||
/// 小号按钮内间距
|
||||
/// </summary>
|
||||
public Thickness PaddingSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮右边一个额外的区域对右侧的小号外边距
|
||||
/// 按钮右边一个额外的区域对右侧的小号外边距
|
||||
/// </summary>
|
||||
public Thickness ExtraContentMarginSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮右边一个额外的区域对右侧的外边距
|
||||
/// 按钮右边一个额外的区域对右侧的外边距
|
||||
/// </summary>
|
||||
public Thickness ExtraContentMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮右边一个额外的区域对右侧的大号外边距
|
||||
/// 按钮右边一个额外的区域对右侧的大号外边距
|
||||
/// </summary>
|
||||
public Thickness ExtraContentMarginLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 圆形按钮内间距
|
||||
/// 圆形按钮内间距
|
||||
/// </summary>
|
||||
public Thickness CirclePadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double OnlyIconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 大号只有图标的按钮图标尺寸
|
||||
/// 大号只有图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double OnlyIconSizeLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小号只有图标的按钮图标尺寸
|
||||
/// 小号只有图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double OnlyIconSizeSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标的按钮图标尺寸
|
||||
/// 图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double IconSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double IconSizeLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// 只有图标的按钮图标尺寸
|
||||
/// </summary>
|
||||
public double IconSizeSM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 完成 Icon 外边距
|
||||
/// 完成 Icon 外边距
|
||||
/// </summary>
|
||||
public Thickness IconMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮组边框颜色
|
||||
/// 按钮组边框颜色
|
||||
/// </summary>
|
||||
public Color GroupBorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 链接按钮悬浮态背景色
|
||||
/// 链接按钮悬浮态背景色
|
||||
/// </summary>
|
||||
public Color LinkHoverBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文本按钮悬浮态背景色
|
||||
/// 文本按钮悬浮态背景色
|
||||
/// </summary>
|
||||
public Color TextHoverBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按钮内容字体大小
|
||||
/// 按钮内容字体大小
|
||||
/// </summary>
|
||||
public double ContentFontSize { get; set; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// 大号按钮内容字体大小
|
||||
/// 大号按钮内容字体大小
|
||||
/// </summary>
|
||||
public double ContentFontSizeLG { get; set; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// 小号按钮内容字体大小
|
||||
/// 小号按钮内容字体大小
|
||||
/// </summary>
|
||||
public double ContentFontSizeSM { get; set; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// 按钮内容字体行高
|
||||
/// 按钮内容字体行高
|
||||
/// </summary>
|
||||
public double ContentLineHeight { get; set; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// 大号按钮内容字体行高
|
||||
/// 大号按钮内容字体行高
|
||||
/// </summary>
|
||||
public double ContentLineHeightLG { get; set; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// 小号按钮内容字体行高
|
||||
/// 小号按钮内容字体行高
|
||||
/// </summary>
|
||||
public double ContentLineHeightSM { get; set; } = double.NaN;
|
||||
|
||||
@ -316,7 +316,7 @@ internal class ButtonToken : AbstractControlDesignToken
|
||||
Math.Max((controlHeightLG - ContentLineHeightLG) / 2 - lineWidth, 0));
|
||||
|
||||
ExtraContentMarginSM = new Thickness(PaddingSM.Left / 2, 0, 0, 0);
|
||||
ExtraContentMargin = new Thickness(Padding.Left / 2, 0, 0, 0);
|
||||
ExtraContentMargin = new Thickness(Padding.Left / 2, 0, 0, 0);
|
||||
ExtraContentMarginLG = new Thickness(PaddingLG.Left / 2, 0, 0, 0);
|
||||
|
||||
CirclePadding = new Thickness(PaddingSM.Left / 2);
|
||||
@ -330,27 +330,25 @@ internal class ButtonToken : AbstractControlDesignToken
|
||||
|
||||
IconMargin = new Thickness(0, 0, _globalToken.PaddingXXS, 0);
|
||||
|
||||
IconOnyPadding = new Thickness(Math.Max((controlHeight - ContentLineHeight) / 2 - lineWidth, 0));
|
||||
IconOnyPadding = new Thickness(Math.Max((controlHeight - ContentLineHeight) / 2 - lineWidth, 0));
|
||||
IconOnyPaddingLG = new Thickness(Math.Max((controlHeightLG - ContentLineHeightLG) / 2 - lineWidth, 0));
|
||||
IconOnyPaddingSM = new Thickness(Math.Max((controlHeightSM - ContentLineHeightSM) / 2 - lineWidth, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 内部 Token 定义
|
||||
|
||||
/// <summary>
|
||||
/// IconOnly 按钮内间距
|
||||
/// IconOnly 按钮内间距
|
||||
/// </summary>
|
||||
public Thickness IconOnyPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IconOnly 大号按钮内间距
|
||||
/// IconOnly 大号按钮内间距
|
||||
/// </summary>
|
||||
public Thickness IconOnyPaddingLG { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IconOnly 小号按钮内间距
|
||||
/// IconOnly 小号按钮内间距
|
||||
/// </summary>
|
||||
public Thickness IconOnyPaddingSM { get; set; }
|
||||
|
||||
|
@ -57,20 +57,29 @@ public class DropdownButton : Button
|
||||
base.OnApplyTemplate(e);
|
||||
TokenResourceBinder.CreateGlobalTokenBinding(this, MarginToAnchorProperty, GlobalTokenResourceKey.MarginXXS);
|
||||
SetupFlyoutProperties();
|
||||
if (IsShowIndicator) RightExtraContent = _openIndicatorIcon;
|
||||
if (IsShowIndicator)
|
||||
{
|
||||
RightExtraContent = _openIndicatorIcon;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (e.Property == IsShowIndicatorProperty)
|
||||
{
|
||||
if (IsShowIndicator)
|
||||
{
|
||||
RightExtraContent = _openIndicatorIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
RightExtraContent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
@ -107,12 +116,16 @@ public class DropdownButton : Button
|
||||
{
|
||||
var host = popupHostProvider.PopupHost;
|
||||
if (host is PopupRoot popupRoot)
|
||||
{
|
||||
if (popupRoot.Parent is Popup popup)
|
||||
{
|
||||
if (popup.Child is MenuFlyoutPresenter menuFlyoutPresenter)
|
||||
{
|
||||
_menuFlyoutPresenter = menuFlyoutPresenter;
|
||||
menuFlyoutPresenter.MenuItemClicked += HandleMenuItemClicked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,9 +145,9 @@ public class DropdownButton : Button
|
||||
}
|
||||
|
||||
protected override void NotifyIconBrushCalculated(in TokenResourceKey normalFilledBrushKey,
|
||||
in TokenResourceKey selectedFilledBrushKey,
|
||||
in TokenResourceKey activeFilledBrushKey,
|
||||
in TokenResourceKey disabledFilledBrushKey)
|
||||
in TokenResourceKey selectedFilledBrushKey,
|
||||
in TokenResourceKey activeFilledBrushKey,
|
||||
in TokenResourceKey disabledFilledBrushKey)
|
||||
{
|
||||
if (_openIndicatorIcon is not null)
|
||||
{
|
||||
@ -156,11 +169,17 @@ public class DropdownButton : Button
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled))
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken))
|
||||
{
|
||||
_openIndicatorIcon.IconMode = IconMode.Selected;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver))
|
||||
{
|
||||
_openIndicatorIcon.IconMode = IconMode.Active;
|
||||
}
|
||||
else
|
||||
{
|
||||
_openIndicatorIcon.IconMode = IconMode.Normal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -171,8 +190,6 @@ public class DropdownButton : Button
|
||||
base.ApplyIconModeStyleConfig();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<MenuFlyout?> DropdownFlyoutProperty =
|
||||
|
@ -11,7 +11,6 @@ namespace AtomUI.Controls;
|
||||
|
||||
using AvaloniaButton = Avalonia.Controls.Button;
|
||||
|
||||
|
||||
public class IconButton : AvaloniaButton, ICustomHitTest
|
||||
{
|
||||
private ControlStyleState _styleState;
|
||||
@ -46,11 +45,14 @@ public class IconButton : AvaloniaButton, ICustomHitTest
|
||||
if (e.Property == IconProperty)
|
||||
{
|
||||
var oldIcon = e.GetOldValue<PathIcon?>();
|
||||
if (oldIcon is not null) ((ISetLogicalParent)oldIcon).SetParent(null);
|
||||
if (oldIcon is not null)
|
||||
{
|
||||
((ISetLogicalParent)oldIcon).SetParent(null);
|
||||
}
|
||||
|
||||
SetupIcon();
|
||||
}
|
||||
else if (e.Property == IsPressedProperty ||
|
||||
else if (e.Property == IsPressedProperty ||
|
||||
e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsEnabledProperty)
|
||||
{
|
||||
@ -80,8 +82,13 @@ public class IconButton : AvaloniaButton, ICustomHitTest
|
||||
{
|
||||
Icon.IconMode = IconMode.Normal;
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken))
|
||||
Icon.IconMode = IconMode.Selected;
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver)) Icon.IconMode = IconMode.Active;
|
||||
{
|
||||
Icon.IconMode = IconMode.Selected;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver))
|
||||
{
|
||||
Icon.IconMode = IconMode.Active;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -94,13 +101,15 @@ public class IconButton : AvaloniaButton, ICustomHitTest
|
||||
{
|
||||
ControlStateUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed)
|
||||
{
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
}
|
||||
else
|
||||
{
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<PathIcon?> IconProperty
|
||||
|
@ -53,7 +53,7 @@ internal class IconButtonTheme : BaseControlTheme
|
||||
}
|
||||
var enableHoverBgStyle = new Style(selector =>
|
||||
selector.Nesting().PropertyEquals(IconButton.IsEnableHoverEffectProperty, true)
|
||||
.Class(StdPseudoClass.PointerOver));
|
||||
.Class(StdPseudoClass.PointerOver));
|
||||
{
|
||||
var contentStyle = new Style(selector => selector.Nesting().Template().Name(IconContentPart));
|
||||
contentStyle.Add(ContentPresenter.BackgroundProperty, GlobalTokenResourceKey.ColorBgTextHover);
|
||||
|
@ -25,11 +25,11 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
internal const string pcChecked = ":checked";
|
||||
internal const string pcPressed = ":pressed";
|
||||
internal const string pcFlyoutOpen = ":flyout-open";
|
||||
private readonly FlyoutStateHelper _flyoutStateHelper;
|
||||
|
||||
private bool _commandCanExecute = true;
|
||||
|
||||
private IDisposable? _flyoutPropertyChangedDisposable;
|
||||
private readonly FlyoutStateHelper _flyoutStateHelper;
|
||||
private KeyGesture? _hotkey;
|
||||
private bool _isAttachedToLogicalTree;
|
||||
private bool _isFlyoutOpen;
|
||||
@ -74,7 +74,10 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
|
||||
private void CanExecuteChanged(ICommand? command, object? parameter)
|
||||
{
|
||||
if (!((ILogical)this).IsAttachedToLogicalTree) return;
|
||||
if (!((ILogical)this).IsAttachedToLogicalTree)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var canExecute = command is null || command.CanExecute(parameter);
|
||||
|
||||
@ -86,7 +89,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visual state of the control by applying latest PseudoClasses.
|
||||
/// Updates the visual state of the control by applying latest PseudoClasses.
|
||||
/// </summary>
|
||||
protected void UpdatePseudoClasses()
|
||||
{
|
||||
@ -101,7 +104,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the secondary button's flyout.
|
||||
/// Closes the secondary button's flyout.
|
||||
/// </summary>
|
||||
protected void CloseFlyout()
|
||||
{
|
||||
@ -109,7 +112,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers all flyout events.
|
||||
/// Registers all flyout events.
|
||||
/// </summary>
|
||||
/// <param name="flyout">The flyout to connect events to.</param>
|
||||
private void RegisterFlyoutEvents(Flyout? flyout)
|
||||
@ -120,13 +123,14 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
flyout.Closed += HandleFlyoutClosed;
|
||||
|
||||
_flyoutPropertyChangedDisposable = flyout
|
||||
.GetPropertyChangedObservable(Avalonia.Controls.Primitives.Popup.PlacementProperty)
|
||||
.Subscribe(HandleFlyoutPlacementPropertyChanged);
|
||||
.GetPropertyChangedObservable(Avalonia.Controls.Primitives.Popup
|
||||
.PlacementProperty)
|
||||
.Subscribe(HandleFlyoutPlacementPropertyChanged);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly unregisters all flyout events.
|
||||
/// Explicitly unregisters all flyout events.
|
||||
/// </summary>
|
||||
/// <param name="flyout">The flyout to disconnect events from.</param>
|
||||
private void UnregisterFlyoutEvents(Flyout? flyout)
|
||||
@ -142,11 +146,14 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly unregisters all events related to the two buttons in OnApplyTemplate().
|
||||
/// Explicitly unregisters all events related to the two buttons in OnApplyTemplate().
|
||||
/// </summary>
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
if (_primaryButton != null) _primaryButton.Click -= HandlePrimaryButtonClick;
|
||||
if (_primaryButton != null)
|
||||
{
|
||||
_primaryButton.Click -= HandlePrimaryButtonClick;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
@ -180,13 +187,19 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
_secondaryButton = e.NameScope.Find<Button>("PART_SecondaryButton");
|
||||
_flyoutStateHelper.AnchorTarget = _secondaryButton;
|
||||
|
||||
if (_primaryButton != null) _primaryButton.Click += HandlePrimaryButtonClick;
|
||||
if (_primaryButton != null)
|
||||
{
|
||||
_primaryButton.Click += HandlePrimaryButtonClick;
|
||||
}
|
||||
|
||||
if (FlyoutButtonIcon is null)
|
||||
{
|
||||
FlyoutButtonIcon = new PathIcon
|
||||
{
|
||||
Kind = "EllipsisOutlined"
|
||||
};
|
||||
}
|
||||
|
||||
SetupEffectiveButtonType();
|
||||
SetupFlyoutProperties();
|
||||
RegisterFlyoutEvents(Flyout);
|
||||
@ -239,7 +252,10 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
_hotkey = HotKey;
|
||||
SetCurrentValue(HotKeyProperty, null);
|
||||
|
||||
if (Command != null) Command.CanExecuteChanged -= CanExecuteChanged;
|
||||
if (Command != null)
|
||||
{
|
||||
Command.CanExecuteChanged -= CanExecuteChanged;
|
||||
}
|
||||
|
||||
_isAttachedToLogicalTree = false;
|
||||
}
|
||||
@ -254,9 +270,15 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
|
||||
if (_isAttachedToLogicalTree)
|
||||
{
|
||||
if (oldValue is ICommand oldCommand) oldCommand.CanExecuteChanged -= CanExecuteChanged;
|
||||
if (oldValue is ICommand oldCommand)
|
||||
{
|
||||
oldCommand.CanExecuteChanged -= CanExecuteChanged;
|
||||
}
|
||||
|
||||
if (newValue is ICommand newCommand) newCommand.CanExecuteChanged += CanExecuteChanged;
|
||||
if (newValue is ICommand newCommand)
|
||||
{
|
||||
newCommand.CanExecuteChanged += CanExecuteChanged;
|
||||
}
|
||||
}
|
||||
|
||||
CanExecuteChanged(newValue, CommandParameter);
|
||||
@ -272,7 +294,10 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
// If flyout is changed while one is already open, make sure we
|
||||
// close the old one first
|
||||
// This is the same behavior as Button
|
||||
if (oldFlyout != null && oldFlyout.IsOpen) oldFlyout.Hide();
|
||||
if (oldFlyout != null && oldFlyout.IsOpen)
|
||||
{
|
||||
oldFlyout.Hide();
|
||||
}
|
||||
|
||||
// Must unregister events here while a reference to the old flyout still exists
|
||||
UnregisterFlyoutEvents(oldFlyout);
|
||||
@ -295,9 +320,13 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
private void SetupEffectiveButtonType()
|
||||
{
|
||||
if (IsPrimaryButtonType)
|
||||
{
|
||||
EffectiveButtonType = ButtonType.Primary;
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectiveButtonType = ButtonType.Default;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupButtonCornerRadius()
|
||||
@ -366,7 +395,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="Click" /> event when the primary button part is clicked.
|
||||
/// Invokes the <see cref="Click" /> event when the primary button part is clicked.
|
||||
/// </summary>
|
||||
/// <param name="e">The event args from the internal Click event.</param>
|
||||
protected virtual void OnClickPrimary(RoutedEventArgs? e)
|
||||
@ -388,7 +417,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the split button's flyout is opened.
|
||||
/// Invoked when the split button's flyout is opened.
|
||||
/// </summary>
|
||||
protected virtual void OnFlyoutOpened()
|
||||
{
|
||||
@ -396,7 +425,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the split button's flyout is closed.
|
||||
/// Invoked when the split button's flyout is closed.
|
||||
/// </summary>
|
||||
protected virtual void OnFlyoutClosed()
|
||||
{
|
||||
@ -404,7 +433,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when the internal primary button part is clicked.
|
||||
/// Event handler for when the internal primary button part is clicked.
|
||||
/// </summary>
|
||||
private void HandlePrimaryButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
@ -414,7 +443,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="PopupFlyoutBase.Placement" /> property changes.
|
||||
/// Called when the <see cref="PopupFlyoutBase.Placement" /> property changes.
|
||||
/// </summary>
|
||||
private void HandleFlyoutPlacementPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
@ -422,13 +451,12 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when the split button's flyout is opened.
|
||||
/// Event handler for when the split button's flyout is opened.
|
||||
/// </summary>
|
||||
private void HandleFlyoutOpened(object? sender, EventArgs e)
|
||||
{
|
||||
var flyout = sender as Flyout;
|
||||
|
||||
|
||||
// It is possible to share flyouts among multiple controls including SplitButton.
|
||||
// This can cause a problem here since all controls that share a flyout receive
|
||||
// the same Opened/Closed events at the same time.
|
||||
@ -445,7 +473,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when the split button's flyout is closed.
|
||||
/// Event handler for when the split button's flyout is closed.
|
||||
/// </summary>
|
||||
private void HandleFlyoutClosed(object? sender, EventArgs e)
|
||||
{
|
||||
@ -463,20 +491,27 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
var size = base.ArrangeOverride(finalSize);
|
||||
if (_originRect is null) _originRect = _secondaryButton?.Bounds;
|
||||
var size = base.ArrangeOverride(finalSize);
|
||||
if (_originRect is null)
|
||||
{
|
||||
_originRect = _secondaryButton?.Bounds;
|
||||
}
|
||||
|
||||
if (!IsPrimaryButtonType)
|
||||
{
|
||||
if (_secondaryButton is not null && _originRect.HasValue)
|
||||
{
|
||||
_secondaryButton.Arrange(
|
||||
_originRect.Value.Inflate(new Thickness(_secondaryButton.BorderThickness.Left, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_secondaryButton is not null && _originRect.HasValue)
|
||||
{
|
||||
_secondaryButton.Arrange(
|
||||
_originRect.Value.Deflate(new Thickness(_secondaryButton.BorderThickness.Left, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
@ -485,10 +520,15 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
if (IsPrimaryButtonType)
|
||||
{
|
||||
if (_secondaryButton is not null)
|
||||
{
|
||||
var offset = _secondaryButton.TranslatePoint(new Point(0, 0), this);
|
||||
if (!offset.HasValue) return;
|
||||
if (!offset.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var optionState = context.PushRenderOptions(new RenderOptions
|
||||
{
|
||||
EdgeMode = EdgeMode.Aliased
|
||||
@ -497,10 +537,9 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
var endPoint = new Point(offset.Value.X, Bounds.Height);
|
||||
context.DrawLine(new Pen(BorderBrush, BorderThickness.Left), startPoint, endPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
|
||||
@ -566,7 +605,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
AvaloniaProperty.Register<SplitButton, bool>(nameof(IsPrimaryButtonType));
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the user presses the primary part of the <see cref="SplitButton" />.
|
||||
/// Raised when the user presses the primary part of the <see cref="SplitButton" />.
|
||||
/// </summary>
|
||||
public event EventHandler<RoutedEventArgs>? Click
|
||||
{
|
||||
@ -581,7 +620,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a parameter to be passed to the <see cref="Command" />.
|
||||
/// Gets or sets a parameter to be passed to the <see cref="Command" />.
|
||||
/// </summary>
|
||||
public object? CommandParameter
|
||||
{
|
||||
@ -590,7 +629,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Flyout" /> that is shown when the secondary part is pressed.
|
||||
/// Gets or sets the <see cref="Flyout" /> that is shown when the secondary part is pressed.
|
||||
/// </summary>
|
||||
public Flyout? Flyout
|
||||
{
|
||||
@ -599,7 +638,7 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an <see cref="KeyGesture" /> associated with this control
|
||||
/// Gets or sets an <see cref="KeyGesture" /> associated with this control
|
||||
/// </summary>
|
||||
public KeyGesture? HotKey
|
||||
{
|
||||
@ -699,8 +738,6 @@ public class SplitButton : ContentControl, ICommandSource, ISizeTypeAware
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<SplitButton, CornerRadius> PrimaryButtonCornerRadiusProperty =
|
||||
|
@ -103,7 +103,7 @@ internal class SplitButtonTheme : BaseControlTheme
|
||||
Add(buttonStyle);
|
||||
var buttonHoverStyle = new Style(selector =>
|
||||
selector.Nesting().Template().Name(MainLayoutPart).Child().OfType<Button>()
|
||||
.Class(StdPseudoClass.PointerOver));
|
||||
.Class(StdPseudoClass.PointerOver));
|
||||
buttonHoverStyle.Add(Visual.ZIndexProperty, ActivatedZIndex);
|
||||
Add(buttonHoverStyle);
|
||||
}
|
||||
|
@ -28,9 +28,16 @@ public class ToggleIconButton : ToggleButton
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
if (CheckedIcon is not null) ConfigureIcon(CheckedIcon);
|
||||
if (CheckedIcon is not null)
|
||||
{
|
||||
ConfigureIcon(CheckedIcon);
|
||||
}
|
||||
|
||||
if (UnCheckedIcon is not null)
|
||||
{
|
||||
ConfigureIcon(UnCheckedIcon);
|
||||
}
|
||||
|
||||
if (UnCheckedIcon is not null) ConfigureIcon(UnCheckedIcon);
|
||||
ApplyIconToContent();
|
||||
}
|
||||
|
||||
@ -63,8 +70,13 @@ public class ToggleIconButton : ToggleButton
|
||||
{
|
||||
pathIcon.IconMode = IconMode.Normal;
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken))
|
||||
{
|
||||
pathIcon.IconMode = IconMode.Selected;
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver)) pathIcon.IconMode = IconMode.Active;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.MouseOver))
|
||||
{
|
||||
pathIcon.IconMode = IconMode.Active;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -76,28 +88,38 @@ public class ToggleIconButton : ToggleButton
|
||||
|
||||
if (change.Property == CheckedIconProperty ||
|
||||
change.Property == UnCheckedIconProperty)
|
||||
{
|
||||
if (change.NewValue is PathIcon newIcon)
|
||||
{
|
||||
ConfigureIcon(newIcon);
|
||||
ApplyIconToContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void ApplyIconToContent()
|
||||
{
|
||||
if (IsChecked.HasValue && IsChecked.Value)
|
||||
{
|
||||
Content = CheckedIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
Content = UnCheckedIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectStyleState()
|
||||
{
|
||||
ControlStateUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed)
|
||||
{
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
}
|
||||
else
|
||||
{
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HitTest(Point point)
|
||||
@ -110,8 +132,6 @@ public class ToggleIconButton : ToggleButton
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<PathIcon?> CheckedIconProperty
|
||||
|
@ -18,11 +18,10 @@ namespace AtomUI.Controls;
|
||||
|
||||
using AvaloniaCheckBox = Avalonia.Controls.CheckBox;
|
||||
|
||||
|
||||
public class CheckBox : AvaloniaCheckBox,
|
||||
ICustomHitTest,
|
||||
IWaveAdornerInfoProvider,
|
||||
IControlCustomStyle
|
||||
ICustomHitTest,
|
||||
IWaveAdornerInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
internal static readonly StyledProperty<double> CheckIndicatorSizeProperty =
|
||||
AvaloniaProperty.Register<CheckBox, double>(nameof(CheckIndicatorSize));
|
||||
@ -168,7 +167,10 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
for (var i = 0; i < visualCount; i++)
|
||||
{
|
||||
var visual = visualChildren[i];
|
||||
if (visual is Layoutable layoutable) layoutable.Arrange(arrangeRect);
|
||||
if (visual is Layoutable layoutable)
|
||||
{
|
||||
layoutable.Arrange(arrangeRect);
|
||||
}
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
@ -207,8 +209,6 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
@ -246,9 +246,13 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
}
|
||||
|
||||
if (IsPressed)
|
||||
{
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
}
|
||||
else
|
||||
{
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
// Measure 之后才有值
|
||||
@ -259,7 +263,7 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
|
||||
private Rect TextRect()
|
||||
{
|
||||
var offsetX = CheckIndicatorSize + PaddingInline;
|
||||
var offsetX = CheckIndicatorSize + PaddingInline;
|
||||
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
|
||||
}
|
||||
|
||||
@ -268,15 +272,24 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled))
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.On))
|
||||
{
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.Off))
|
||||
{
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
|
||||
}
|
||||
else if (_styleState.HasFlag(ControlStyleState.Indeterminate))
|
||||
{
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.On)) IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
|
||||
if (_styleState.HasFlag(ControlStyleState.On))
|
||||
{
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,15 +308,17 @@ public class CheckBox : AvaloniaCheckBox,
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsCheckedProperty ||
|
||||
e.Property == IsCheckedProperty ||
|
||||
e.Property == IsEnabledProperty)
|
||||
{
|
||||
_customStyle.CollectStyleState();
|
||||
SetupIndicatorCheckedMarkEffectSize();
|
||||
if (e.Property == IsCheckedProperty &&
|
||||
if (e.Property == IsCheckedProperty &&
|
||||
_styleState.HasFlag(ControlStyleState.Enabled) &&
|
||||
_styleState.HasFlag(ControlStyleState.On))
|
||||
{
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ internal class CheckBoxToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复选框标志的大小
|
||||
/// 复选框标志的大小
|
||||
/// </summary>
|
||||
public double CheckIndicatorSize { get; set; }
|
||||
|
||||
|
@ -20,14 +20,12 @@ public enum CollapseTriggerType
|
||||
Icon
|
||||
}
|
||||
|
||||
|
||||
public enum CollapseExpandIconPosition
|
||||
{
|
||||
Start,
|
||||
End
|
||||
}
|
||||
|
||||
|
||||
[TemplatePart(CollapseTheme.ItemsPresenterPart, typeof(ItemsPresenter))]
|
||||
public class Collapse : SelectingItemsControl
|
||||
{
|
||||
@ -50,9 +48,15 @@ public class Collapse : SelectingItemsControl
|
||||
private void SetupItemsBorderThickness()
|
||||
{
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
for (var i = 0; i < ItemCount; ++i)
|
||||
{
|
||||
if (Items[i] is CollapseItem collapseItem)
|
||||
{
|
||||
SetupCollapseBorderThickness(collapseItem, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
@ -100,17 +104,24 @@ public class Collapse : SelectingItemsControl
|
||||
{
|
||||
if (!IsBorderless)
|
||||
{
|
||||
if (index == ItemCount - 1 && !collapseItem.IsSelected) headerBorderBottom = 0d;
|
||||
if (index == ItemCount - 1 && !collapseItem.IsSelected)
|
||||
{
|
||||
headerBorderBottom = 0d;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (collapseItem.IsSelected || (index == ItemCount - 1 && !collapseItem.IsSelected))
|
||||
{
|
||||
headerBorderBottom = 0d;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == ItemCount - 1 &&
|
||||
(collapseItem.IsSelected || (!collapseItem.IsSelected && collapseItem.InAnimating)))
|
||||
{
|
||||
contentBorderBottom = 0d;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -130,8 +141,12 @@ public class Collapse : SelectingItemsControl
|
||||
{
|
||||
var containerFromEventSource = GetContainerFromEventSource(e.Source);
|
||||
if (containerFromEventSource is CollapseItem collapseItem)
|
||||
{
|
||||
if (!collapseItem.InAnimating)
|
||||
{
|
||||
e.Handled = UpdateSelectionFromEventSource(e.Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,8 +157,12 @@ public class Collapse : SelectingItemsControl
|
||||
{
|
||||
var containerFromEventSource = GetContainerFromEventSource(e.Source);
|
||||
if (containerFromEventSource is CollapseItem collapseItem)
|
||||
{
|
||||
if (!collapseItem.InAnimating && collapseItem.IsPointInHeaderBounds(e.GetPosition(collapseItem)))
|
||||
{
|
||||
e.Handled = UpdateSelectionFromEventSource(e.Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,12 +173,16 @@ public class Collapse : SelectingItemsControl
|
||||
var container = GetContainerFromEventSource(e.Source);
|
||||
if (container != null
|
||||
&& container.GetVisualsAt(e.GetPosition(container))
|
||||
.Any(c => container == c || container.IsVisualAncestorOf(c)))
|
||||
.Any(c => container == c || container.IsVisualAncestorOf(c)))
|
||||
{
|
||||
var containerFromEventSource = GetContainerFromEventSource(e.Source);
|
||||
if (containerFromEventSource is CollapseItem collapseItem)
|
||||
{
|
||||
if (!collapseItem.InAnimating && collapseItem.IsPointInHeaderBounds(e.GetPosition(collapseItem)))
|
||||
{
|
||||
e.Handled = UpdateSelectionFromEventSource(e.Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,34 +191,48 @@ public class Collapse : SelectingItemsControl
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (change.Property == IsBorderlessProperty)
|
||||
{
|
||||
SetupEffectiveBorderThickness();
|
||||
}
|
||||
}
|
||||
|
||||
if (change.Property == IsAccordionProperty)
|
||||
{
|
||||
SetupSelectionMode();
|
||||
}
|
||||
else if (change.Property == IsBorderlessProperty ||
|
||||
change.Property == IsGhostStyleProperty)
|
||||
{
|
||||
SetupItemsBorderThickness();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupEffectiveBorderThickness()
|
||||
{
|
||||
if (IsBorderless || IsGhostStyle)
|
||||
{
|
||||
EffectiveBorderThickness = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectiveBorderThickness = BorderThickness;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupSelectionMode()
|
||||
{
|
||||
if (IsAccordion)
|
||||
{
|
||||
SelectionMode = SelectionMode.Single | SelectionMode.Toggle;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionMode = SelectionMode.Multiple | SelectionMode.Toggle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
||||
@ -254,8 +291,6 @@ public class Collapse : SelectingItemsControl
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<Collapse, Thickness> EffectiveBorderThicknessProperty =
|
||||
|
@ -43,16 +43,25 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
{
|
||||
if (obj.NewValue is IHeadered headered)
|
||||
{
|
||||
if (Header != headered.Header) SetCurrentValue(HeaderProperty, headered.Header);
|
||||
if (Header != headered.Header)
|
||||
{
|
||||
SetCurrentValue(HeaderProperty, headered.Header);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(obj.NewValue is Control)) SetCurrentValue(HeaderProperty, obj.NewValue);
|
||||
if (!(obj.NewValue is Control))
|
||||
{
|
||||
SetCurrentValue(HeaderProperty, obj.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Header == obj.OldValue) SetCurrentValue(HeaderProperty, obj.NewValue);
|
||||
if (Header == obj.OldValue)
|
||||
{
|
||||
SetCurrentValue(HeaderProperty, obj.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,28 +77,47 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
HandleSelectedChanged();
|
||||
_enableAnimation = true;
|
||||
if (_expandButton is not null)
|
||||
{
|
||||
_expandButton.Click += (sender, args) => { IsSelected = !IsSelected; };
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (change.Property == ExpandIconProperty) SetupIconButton();
|
||||
if (change.Property == ExpandIconProperty)
|
||||
{
|
||||
SetupIconButton();
|
||||
}
|
||||
|
||||
if (VisualRoot is not null)
|
||||
{
|
||||
if (change.Property == IsSelectedProperty)
|
||||
{
|
||||
HandleSelectedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (change.Property == AddOnContentProperty)
|
||||
{
|
||||
if (change.OldValue is Control oldControl) UIStructureUtils.SetTemplateParent(oldControl, null);
|
||||
if (change.OldValue is Control oldControl)
|
||||
{
|
||||
UIStructureUtils.SetTemplateParent(oldControl, null);
|
||||
}
|
||||
|
||||
if (change.NewValue is Control newControl) UIStructureUtils.SetTemplateParent(newControl, this);
|
||||
if (change.NewValue is Control newControl)
|
||||
{
|
||||
UIStructureUtils.SetTemplateParent(newControl, this);
|
||||
}
|
||||
}
|
||||
else if (change.Property == ExpandIconProperty)
|
||||
{
|
||||
var oldExpandIcon = change.GetOldValue<PathIcon?>();
|
||||
if (oldExpandIcon is not null) UIStructureUtils.SetTemplateParent(oldExpandIcon, null);
|
||||
if (oldExpandIcon is not null)
|
||||
{
|
||||
UIStructureUtils.SetTemplateParent(oldExpandIcon, null);
|
||||
}
|
||||
|
||||
SetupIconButton();
|
||||
}
|
||||
}
|
||||
@ -99,15 +127,22 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
if (Presenter is not null)
|
||||
{
|
||||
if (IsSelected)
|
||||
{
|
||||
ExpandItemContent();
|
||||
}
|
||||
else
|
||||
{
|
||||
CollapseItemContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandItemContent()
|
||||
{
|
||||
if (_animationTarget is null || InAnimating) return;
|
||||
if (_animationTarget is null || InAnimating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_enableAnimation)
|
||||
{
|
||||
@ -135,7 +170,10 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
|
||||
private void CollapseItemContent()
|
||||
{
|
||||
if (_animationTarget is null || InAnimating) return;
|
||||
if (_animationTarget is null || InAnimating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_enableAnimation)
|
||||
{
|
||||
@ -180,12 +218,13 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
internal bool IsPointInHeaderBounds(Point position)
|
||||
{
|
||||
if (_headerDecorator is not null && TriggerType != CollapseTriggerType.Icon)
|
||||
{
|
||||
return _headerDecorator.Bounds.Contains(position);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 公共属性定义
|
||||
|
||||
public static readonly StyledProperty<bool> IsSelectedProperty =
|
||||
@ -235,8 +274,6 @@ public class CollapseItem : HeaderedContentControl, ISelectable
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<CollapseItem, SizeType> SizeTypeProperty =
|
||||
|
@ -257,9 +257,9 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
private void BuildTriggerStyle()
|
||||
{
|
||||
var headerTriggerHandleStyle = new Style(selector => selector.Nesting()
|
||||
.PropertyEquals(
|
||||
CollapseItem.TriggerTypeProperty,
|
||||
CollapseTriggerType.Header));
|
||||
.PropertyEquals(
|
||||
CollapseItem.TriggerTypeProperty,
|
||||
CollapseTriggerType.Header));
|
||||
var headerDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
|
||||
headerDecoratorStyle.Add(InputElement.CursorProperty, new Cursor(StandardCursorType.Hand));
|
||||
headerTriggerHandleStyle.Add(headerDecoratorStyle);
|
||||
@ -267,7 +267,7 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
|
||||
var iconTriggerHandleStyle =
|
||||
new Style(selector => selector.Nesting()
|
||||
.PropertyEquals(CollapseItem.TriggerTypeProperty, CollapseTriggerType.Icon));
|
||||
.PropertyEquals(CollapseItem.TriggerTypeProperty, CollapseTriggerType.Icon));
|
||||
var expandIconStyle = new Style(selector => selector.Nesting().Template().Name(ExpandButtonPart));
|
||||
expandIconStyle.Add(InputElement.CursorProperty, new Cursor(StandardCursorType.Hand));
|
||||
iconTriggerHandleStyle.Add(expandIconStyle);
|
||||
@ -277,9 +277,9 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
private void BuildTriggerPositionStyle()
|
||||
{
|
||||
var startPositionStyle = new Style(selector => selector.Nesting()
|
||||
.PropertyEquals(
|
||||
CollapseItem.ExpandIconPositionProperty,
|
||||
CollapseExpandIconPosition.Start));
|
||||
.PropertyEquals(
|
||||
CollapseItem.ExpandIconPositionProperty,
|
||||
CollapseExpandIconPosition.Start));
|
||||
{
|
||||
var expandButtonStyle = new Style(selector => selector.Nesting().Template().Name(ExpandButtonPart));
|
||||
expandButtonStyle.Add(Grid.ColumnProperty, 0);
|
||||
@ -288,9 +288,9 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
}
|
||||
Add(startPositionStyle);
|
||||
var endPositionStyle = new Style(selector => selector.Nesting()
|
||||
.PropertyEquals(
|
||||
CollapseItem.ExpandIconPositionProperty,
|
||||
CollapseExpandIconPosition.End));
|
||||
.PropertyEquals(
|
||||
CollapseItem.ExpandIconPositionProperty,
|
||||
CollapseExpandIconPosition.End));
|
||||
{
|
||||
var expandButtonStyle = new Style(selector => selector.Nesting().Template().Name(ExpandButtonPart));
|
||||
expandButtonStyle.Add(Grid.ColumnProperty, 3);
|
||||
@ -326,7 +326,7 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled));
|
||||
disabledStyle.Add(TemplatedControl.ForegroundProperty, GlobalTokenResourceKey.ColorTextDisabled);
|
||||
var headerContentPresenterStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart)
|
||||
.Descendant().OfType<ContentPresenter>());
|
||||
.Descendant().OfType<ContentPresenter>());
|
||||
headerContentPresenterStyle.Add(ContentPresenter.ForegroundProperty, GlobalTokenResourceKey.ColorTextDisabled);
|
||||
disabledStyle.Add(headerContentPresenterStyle);
|
||||
|
||||
@ -336,7 +336,7 @@ internal class CollapseItemTheme : BaseControlTheme
|
||||
private void BuildAddOnContentStyle()
|
||||
{
|
||||
var addOnContentStyle = new Style(selector => selector.Nesting().Template().Name(AddOnContentPresenterPart)
|
||||
.Descendant().OfType<PathIcon>());
|
||||
.Descendant().OfType<PathIcon>());
|
||||
addOnContentStyle.Add(Layoutable.WidthProperty, GlobalTokenResourceKey.IconSize);
|
||||
addOnContentStyle.Add(Layoutable.HeightProperty, GlobalTokenResourceKey.IconSize);
|
||||
Add(addOnContentStyle);
|
||||
|
@ -15,22 +15,22 @@ internal class CollapseToken : AbstractControlDesignToken
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 折叠面板头部内边距
|
||||
/// 折叠面板头部内边距
|
||||
/// </summary>
|
||||
public Thickness HeaderPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 折叠面板头部背景
|
||||
/// 折叠面板头部背景
|
||||
/// </summary>
|
||||
public Color HeaderBg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 折叠面板内容内边距
|
||||
/// 折叠面板内容内边距
|
||||
/// </summary>
|
||||
public Thickness ContentPadding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 折叠面板内容背景
|
||||
/// 折叠面板内容背景
|
||||
/// </summary>
|
||||
public Color ContentBg { get; set; }
|
||||
|
||||
@ -50,8 +50,6 @@ internal class CollapseToken : AbstractControlDesignToken
|
||||
RightExpandButtonMargin = new Thickness(_globalToken.MarginSM, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 内部 Token 定义
|
||||
|
||||
public Thickness CollapseHeaderPaddingSM { get; set; }
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user