├── .config └── dotnet-tools.json ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── renovate.json └── workflows │ ├── _build.yml │ ├── _unit_test.yml │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .husky └── commit-msg ├── .releaserc.cjs ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── Directory.Build.props ├── GroupBox.Avalonia.256.png ├── GroupBox.Avalonia.Sample ├── App.axaml ├── App.axaml.cs ├── Assets │ └── avalonia-logo.ico ├── GroupBox.Avalonia.Sample.csproj ├── Program.cs ├── Views │ ├── FluentWindow.axaml │ ├── FluentWindow.axaml.cs │ ├── MainView.axaml │ ├── MainView.axaml.cs │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── SimpleWindow.axaml │ └── SimpleWindow.axaml.cs └── app.manifest ├── GroupBox.Avalonia.Sample_Dark.png ├── GroupBox.Avalonia.Sample_Light.png ├── GroupBox.Avalonia.ico ├── GroupBox.Avalonia.sln ├── GroupBox.Avalonia.xcf ├── GroupBox.Avalonia ├── Controls │ ├── GroupBox.axaml │ └── GroupBox.axaml.cs ├── Converters │ └── StaticConverters.cs ├── GroupBox.Avalonia.csproj └── Themes │ ├── GroupBoxBasic.axaml │ └── GroupBoxClassic.axaml ├── LICENSE ├── README.md ├── Settings.XamlStyler ├── Settings.XamlStyler.schema.json ├── commitlint.config.ts ├── package-lock.json ├── package.json └── tools ├── formatXamlFiles.mjs └── updateReadMe.mjs /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "xamlstyler.console": { 6 | "version": "3.2404.2", 7 | "commands": [ 8 | "xstyler" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | charset="utf-8-bom" 2 | 3 | [*.{axaml,xaml}] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | xml_wrap_tags_and_pi = true 8 | xml_space_after_last_pi_attribute = true 9 | xml_space_before_self_closing = true 10 | xml_attribute_style = first_attribute_on_single_line 11 | xml_attribute_indent = align_by_first_attribute 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates 5 | 6 | version: 2 7 | enable-beta-ecosystems: true 8 | updates: 9 | - package-ecosystem: "gitsubmodule" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | target-branch: "develop" 14 | groups: 15 | "Git SubModules": 16 | patterns: 17 | - "*" 18 | 19 | - package-ecosystem: "github-actions" 20 | directory: "/" 21 | schedule: 22 | interval: "daily" 23 | target-branch: "develop" 24 | groups: 25 | "GitHub Actions Dependencies": 26 | patterns: 27 | - "*" 28 | 29 | - package-ecosystem: "npm" 30 | directory: "/" 31 | schedule: 32 | interval: "daily" 33 | target-branch: "develop" 34 | groups: 35 | "NPM Dependencies": 36 | patterns: 37 | - "*" 38 | 39 | - package-ecosystem: "nuget" 40 | directory: "/" 41 | schedule: 42 | interval: "daily" 43 | target-branch: "develop" 44 | groups: 45 | "NuGet Dependencies": 46 | patterns: 47 | - "*" 48 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>BinToss/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/_build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | 4 | env: 5 | DOTNET_ROLL_FORWARD: "Major" 6 | 7 | jobs: 8 | build: 9 | name: Build (${{matrix.os}}) 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [windows-latest, ubuntu-latest, macos-latest] 14 | 15 | runs-on: ${{matrix.os}} 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | 21 | - uses: actions/setup-node@v4 22 | with: 23 | cache: "npm" 24 | check-latest: true 25 | node-version-file: package.json 26 | - run: npm i -g npm@latest 27 | 28 | - uses: actions/setup-dotnet@v4 29 | with: 30 | dotnet-version: "8.x" 31 | 32 | - name: NPM - Clean Install 33 | run: npm ci 34 | 35 | - name: NPM - Audit Signatures 36 | run: npm audit signatures 37 | 38 | - name: Pack Solution 39 | run: dotnet pack 40 | -------------------------------------------------------------------------------- /.github/workflows/_unit_test.yml: -------------------------------------------------------------------------------- 1 | # inspired by https://github.com/GitTools/GitVersion/blob/main/.github/workflows/_unit_tests.yml 2 | on: 3 | workflow_call: 4 | env: 5 | DOTNET_ROLL_FORWARD: "Major" 6 | 7 | jobs: 8 | unit_test: 9 | name: Unit Tests (${{ matrix.os }}) 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [windows-latest, ubuntu-latest, macos-latest] 14 | 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | 21 | - uses: actions/setup-node@v4 22 | with: 23 | cache: "npm" 24 | check-latest: true 25 | node-version-file: package.json 26 | - run: npm i -g npm@latest 27 | 28 | - run: npm ci 29 | 30 | - uses: actions/setup-dotnet@v4.0.0 31 | with: 32 | dotnet-version: "8.x" 33 | 34 | - run: dotnet test 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Inspired by https://github.com/GitTools/GitVersion/blob/main/.github/workflows/ci.yml 2 | 3 | name: CI 4 | 5 | on: 6 | push: 7 | branches-ignore: # if CI must run on these branches, Release will call CI 8 | - main 9 | - develop 10 | pull_request: 11 | workflow_call: 12 | 13 | env: 14 | DOTNET_ROLL_FORWARD: "Major" 15 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 16 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 17 | DOTNET_NOLOGO: 1 18 | 19 | jobs: 20 | build: 21 | name: Build 22 | uses: ./.github/workflows/_build.yml 23 | unit_test: 24 | name: Unit Test 25 | uses: ./.github/workflows/_unit_test.yml 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Test changes locally with https://github.com/nektos/act 2 | # Edit grahically with https://marketplace.visualstudio.com/items?itemName=actionforge.actionforge 3 | name: Release 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | - develop 10 | env: 11 | DOTNET_ROLL_FORWARD: "Major" 12 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 13 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 14 | DOTNET_NOLOGO: 1 15 | 16 | jobs: 17 | ci: 18 | name: CI 19 | uses: ./.github/workflows/ci.yml 20 | # based on https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/ci-configurations/github-actions.md 21 | release: 22 | needs: [ci] 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: write # to be able to publish a GitHub release 26 | issues: write # to be able to comment on released issues 27 | pull-requests: write # to be able to comment on released pull requests 28 | id-token: write # to enable use of OIDC for npm provenance 29 | packages: write # for pushing GitHub Nuget packages 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 # necessary for semantic release 35 | 36 | - uses: actions/setup-node@v4 37 | with: 38 | cache: "npm" 39 | check-latest: true 40 | node-version-file: package.json 41 | node-version: lts/* 42 | 43 | - uses: actions/setup-dotnet@v4 44 | with: 45 | dotnet-version: "8.x" 46 | 47 | - name: NPM - Clean Install 48 | run: npm ci 49 | 50 | - name: NPM - Audit Signatures 51 | run: npm audit signatures 52 | 53 | - name: Update version in README's Avalonia badge 54 | run: npm run update-readme 55 | 56 | # [release# 57 | # steps](https://github.com/semantic-release/semantic-release#release-steps) 58 | # Plugins add sub-steps e.g. @semantic-release/git's Prepare will create a 59 | # release commit, including configurable file assets." 60 | # After the new version Git tagged, @semantic-release/exec runs 61 | # `dotnet publish`. @semantic-release/github adds the artifacts to the 62 | # GitHub Release. 63 | - name: Semantic Release 64 | id: semantic_release 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} 68 | shell: pwsh 69 | run: | 70 | $output=npx semantic-release 71 | if ($output.EndsWith('There are no relevant changes, so no new version is released.')) { 72 | echo "NoNewVersion=true" >> "$env:GITHUB_OUTPUT" 73 | } 74 | 75 | # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry 76 | # https://github.com/actions/setup-dotnet/tree/v3/#setting-up-authentication-for-nuget-feeds 77 | - name: .NET - Pack n' Push NuPkg 78 | if: ${{!steps.semantic_release.outputs.NO_NEW_VERSION}} 79 | env: 80 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 81 | NUGET_TOKEN: ${{secrets.NUGET_TOKEN}} 82 | shell: pwsh 83 | run: | 84 | dotnet pack ./GroupBox.Avalonia/GroupBox.Avalonia.csproj -o ./publish/ 85 | dotnet nuget push ./publish/*.nupkg --source https://api.nuget.org/v3/index.json --api-key $env:NUGET_TOKEN 86 | dotnet nuget push ./publish/*.nupkg --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key $env:GITHUB_TOKEN 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,dotnetcore,csharp 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,dotnetcore,csharp 3 | 4 | ### Csharp ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.rsuser 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Ww][Ii][Nn]32/ 31 | [Aa][Rr][Mm]/ 32 | [Aa][Rr][Mm]64/ 33 | bld/ 34 | [Bb]in/ 35 | [Oo]bj/ 36 | [Ll]og/ 37 | [Ll]ogs/ 38 | 39 | # Visual Studio 2015/2017 cache/options directory 40 | .vs/ 41 | # Uncomment if you have tasks that create the project's static files in wwwroot 42 | #wwwroot/ 43 | 44 | # Visual Studio 2017 auto generated files 45 | Generated\ Files/ 46 | 47 | # MSTest test Results 48 | [Tt]est[Rr]esult*/ 49 | [Bb]uild[Ll]og.* 50 | 51 | # NUnit 52 | *.VisualState.xml 53 | TestResult.xml 54 | nunit-*.xml 55 | 56 | # Build Results of an ATL Project 57 | [Dd]ebugPS/ 58 | [Rr]eleasePS/ 59 | dlldata.c 60 | 61 | # Benchmark Results 62 | BenchmarkDotNet.Artifacts/ 63 | 64 | # .NET Core 65 | project.lock.json 66 | project.fragment.lock.json 67 | artifacts/ 68 | 69 | # ASP.NET Scaffolding 70 | ScaffoldingReadMe.txt 71 | 72 | # StyleCop 73 | StyleCopReport.xml 74 | 75 | # Files built by Visual Studio 76 | *_i.c 77 | *_p.c 78 | *_h.h 79 | *.ilk 80 | *.meta 81 | *.obj 82 | *.iobj 83 | *.pch 84 | *.pdb 85 | *.ipdb 86 | *.pgc 87 | *.pgd 88 | *.rsp 89 | *.sbr 90 | *.tlb 91 | *.tli 92 | *.tlh 93 | *.tmp 94 | *.tmp_proj 95 | *_wpftmp.csproj 96 | *.log 97 | *.tlog 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opendb 113 | *.opensdf 114 | *.sdf 115 | *.cachefile 116 | *.VC.db 117 | *.VC.VC.opendb 118 | 119 | # Visual Studio profiler 120 | *.psess 121 | *.vsp 122 | *.vspx 123 | *.sap 124 | 125 | # Visual Studio Trace Files 126 | *.e2e 127 | 128 | # TFS 2012 Local Workspace 129 | $tf/ 130 | 131 | # Guidance Automation Toolkit 132 | *.gpState 133 | 134 | # ReSharper is a .NET coding add-in 135 | _ReSharper*/ 136 | *.[Rr]e[Ss]harper 137 | *.DotSettings.user 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # DotCover is a Code Coverage Tool 143 | *.dotCover 144 | 145 | # AxoCover is a Code Coverage Tool 146 | .axoCover/* 147 | !.axoCover/settings.json 148 | 149 | # Coverlet is a free, cross platform Code Coverage Tool 150 | coverage*.json 151 | coverage*.xml 152 | coverage*.info 153 | 154 | # Visual Studio code coverage results 155 | *.coverage 156 | *.coveragexml 157 | 158 | # NCrunch 159 | _NCrunch_* 160 | .*crunch*.local.xml 161 | nCrunchTemp_* 162 | 163 | # MightyMoose 164 | *.mm.* 165 | AutoTest.Net/ 166 | 167 | # Web workbench (sass) 168 | .sass-cache/ 169 | 170 | # Installshield output folder 171 | [Ee]xpress/ 172 | 173 | # DocProject is a documentation generator add-in 174 | DocProject/buildhelp/ 175 | DocProject/Help/*.HxT 176 | DocProject/Help/*.HxC 177 | DocProject/Help/*.hhc 178 | DocProject/Help/*.hhk 179 | DocProject/Help/*.hhp 180 | DocProject/Help/Html2 181 | DocProject/Help/html 182 | 183 | # Click-Once directory 184 | publish/ 185 | 186 | # Publish Web Output 187 | *.[Pp]ublish.xml 188 | *.azurePubxml 189 | # Note: Comment the next line if you want to checkin your web deploy settings, 190 | # but database connection strings (with potential passwords) will be unencrypted 191 | *.pubxml 192 | *.publishproj 193 | 194 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 195 | # checkin your Azure Web App publish settings, but sensitive information contained 196 | # in these scripts will be unencrypted 197 | PublishScripts/ 198 | 199 | # NuGet Packages 200 | *.nupkg 201 | # NuGet Symbol Packages 202 | *.snupkg 203 | # The packages folder can be ignored because of Package Restore 204 | **/[Pp]ackages/* 205 | # except build/, which is used as an MSBuild target. 206 | !**/[Pp]ackages/build/ 207 | # Uncomment if necessary however generally it will be regenerated when needed 208 | #!**/[Pp]ackages/repositories.config 209 | # NuGet v3's project.json files produces more ignorable files 210 | *.nuget.props 211 | *.nuget.targets 212 | 213 | # Microsoft Azure Build Output 214 | csx/ 215 | *.build.csdef 216 | 217 | # Microsoft Azure Emulator 218 | ecf/ 219 | rcf/ 220 | 221 | # Windows Store app package directories and files 222 | AppPackages/ 223 | BundleArtifacts/ 224 | Package.StoreAssociation.xml 225 | _pkginfo.txt 226 | *.appx 227 | *.appxbundle 228 | *.appxupload 229 | 230 | # Visual Studio cache files 231 | # files ending in .cache can be ignored 232 | *.[Cc]ache 233 | # but keep track of directories ending in .cache 234 | !?*.[Cc]ache/ 235 | 236 | # Others 237 | ClientBin/ 238 | ~$* 239 | *~ 240 | *.dbmdl 241 | *.dbproj.schemaview 242 | *.jfm 243 | *.pfx 244 | *.publishsettings 245 | orleans.codegen.cs 246 | 247 | # Including strong name files can present a security risk 248 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 249 | #*.snk 250 | 251 | # Since there are multiple workflows, uncomment next line to ignore bower_components 252 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 253 | #bower_components/ 254 | 255 | # RIA/Silverlight projects 256 | Generated_Code/ 257 | 258 | # Backup & report files from converting an old project file 259 | # to a newer Visual Studio version. Backup files are not needed, 260 | # because we have git ;-) 261 | _UpgradeReport_Files/ 262 | Backup*/ 263 | UpgradeLog*.XML 264 | UpgradeLog*.htm 265 | ServiceFabricBackup/ 266 | *.rptproj.bak 267 | 268 | # SQL Server files 269 | *.mdf 270 | *.ldf 271 | *.ndf 272 | 273 | # Business Intelligence projects 274 | *.rdl.data 275 | *.bim.layout 276 | *.bim_*.settings 277 | *.rptproj.rsuser 278 | *- [Bb]ackup.rdl 279 | *- [Bb]ackup ([0-9]).rdl 280 | *- [Bb]ackup ([0-9][0-9]).rdl 281 | 282 | # Microsoft Fakes 283 | FakesAssemblies/ 284 | 285 | # GhostDoc plugin setting file 286 | *.GhostDoc.xml 287 | 288 | # Node.js Tools for Visual Studio 289 | .ntvs_analysis.dat 290 | node_modules/ 291 | 292 | # Visual Studio 6 build log 293 | *.plg 294 | 295 | # Visual Studio 6 workspace options file 296 | *.opt 297 | 298 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 299 | *.vbw 300 | 301 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 302 | *.vbp 303 | 304 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 305 | *.dsw 306 | *.dsp 307 | 308 | # Visual Studio 6 technical files 309 | 310 | # Visual Studio LightSwitch build output 311 | **/*.HTMLClient/GeneratedArtifacts 312 | **/*.DesktopClient/GeneratedArtifacts 313 | **/*.DesktopClient/ModelManifest.xml 314 | **/*.Server/GeneratedArtifacts 315 | **/*.Server/ModelManifest.xml 316 | _Pvt_Extensions 317 | 318 | # Paket dependency manager 319 | .paket/paket.exe 320 | paket-files/ 321 | 322 | # FAKE - F# Make 323 | .fake/ 324 | 325 | # CodeRush personal settings 326 | .cr/personal 327 | 328 | # Python Tools for Visual Studio (PTVS) 329 | __pycache__/ 330 | *.pyc 331 | 332 | # Cake - Uncomment if you are using it 333 | # tools/** 334 | # !tools/packages.config 335 | 336 | # Tabs Studio 337 | *.tss 338 | 339 | # Telerik's JustMock configuration file 340 | *.jmconfig 341 | 342 | # BizTalk build output 343 | *.btp.cs 344 | *.btm.cs 345 | *.odx.cs 346 | *.xsd.cs 347 | 348 | # OpenCover UI analysis results 349 | OpenCover/ 350 | 351 | # Azure Stream Analytics local run output 352 | ASALocalRun/ 353 | 354 | # MSBuild Binary and Structured Log 355 | *.binlog 356 | 357 | # NVidia Nsight GPU debugger configuration file 358 | *.nvuser 359 | 360 | # MFractors (Xamarin productivity tool) working folder 361 | .mfractor/ 362 | 363 | # Local History for Visual Studio 364 | .localhistory/ 365 | 366 | # Visual Studio History (VSHistory) files 367 | .vshistory/ 368 | 369 | # BeatPulse healthcheck temp database 370 | healthchecksdb 371 | 372 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 373 | MigrationBackup/ 374 | 375 | # Ionide (cross platform F# VS Code tools) working folder 376 | .ionide/ 377 | 378 | # Fody - auto-generated XML schema 379 | FodyWeavers.xsd 380 | 381 | # VS Code files for those working on multiple tools 382 | .vscode/* 383 | !.vscode/settings.json 384 | !.vscode/tasks.json 385 | !.vscode/launch.json 386 | !.vscode/extensions.json 387 | *.code-workspace 388 | 389 | # Local History for Visual Studio Code 390 | .history/ 391 | 392 | # Windows Installer files from build outputs 393 | *.cab 394 | *.msi 395 | *.msix 396 | *.msm 397 | *.msp 398 | 399 | # JetBrains Rider 400 | *.sln.iml 401 | 402 | ### DotnetCore ### 403 | # .NET Core build folders 404 | bin/ 405 | obj/ 406 | 407 | # Common node modules locations 408 | /node_modules 409 | /wwwroot/node_modules 410 | 411 | ### Linux ### 412 | 413 | # temporary files which can be created if a process still has a handle open of a deleted file 414 | .fuse_hidden* 415 | 416 | # KDE directory preferences 417 | .directory 418 | 419 | # Linux trash folder which might appear on any partition or disk 420 | .Trash-* 421 | 422 | # .nfs files are created when an open file is removed but is still being accessed 423 | .nfs* 424 | 425 | ### macOS ### 426 | # General 427 | .DS_Store 428 | .AppleDouble 429 | .LSOverride 430 | 431 | # Icon must end with two \r 432 | Icon 433 | 434 | 435 | # Thumbnails 436 | ._* 437 | 438 | # Files that might appear in the root of a volume 439 | .DocumentRevisions-V100 440 | .fseventsd 441 | .Spotlight-V100 442 | .TemporaryItems 443 | .Trashes 444 | .VolumeIcon.icns 445 | .com.apple.timemachine.donotpresent 446 | 447 | # Directories potentially created on remote AFP share 448 | .AppleDB 449 | .AppleDesktop 450 | Network Trash Folder 451 | Temporary Items 452 | .apdisk 453 | 454 | ### macOS Patch ### 455 | # iCloud generated files 456 | *.icloud 457 | 458 | ### Windows ### 459 | # Windows thumbnail cache files 460 | Thumbs.db 461 | Thumbs.db:encryptable 462 | ehthumbs.db 463 | ehthumbs_vista.db 464 | 465 | # Dump file 466 | *.stackdump 467 | 468 | # Folder config file 469 | [Dd]esktop.ini 470 | 471 | # Recycle Bin used on file shares 472 | $RECYCLE.BIN/ 473 | 474 | # Windows Installer files 475 | 476 | # Windows shortcuts 477 | *.lnk 478 | 479 | # End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,dotnetcore,csharp 480 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /.releaserc.cjs: -------------------------------------------------------------------------------- 1 | /** Semantic-Release configuration for GroupBox.Avalonia 2 | * @extends { hceSharedConfig } 3 | * 4 | * <-- TABLE OF CONTENTS --> 5 | * - Insert-Edit Plugins 6 | * - Append Plugins 7 | */ 8 | 9 | /** 10 | * @typedef { object } PluginOptionsGit 11 | * @prop { string } message 12 | * @prop {[ string | [string] | {path:string} ]} assets 13 | */ 14 | 15 | /** 16 | * @type {import("semantic-release").Options} 17 | */ 18 | const hceSharedConfig = require('@halospv3/hce.shared-config') 19 | if (process.argv.includes("--debug") || process.argv.includes("--verbose")) { 20 | console.info("hce.shared-config:\n" + JSON.stringify(hceSharedConfig, null, 2)) 21 | } 22 | /** 23 | * @type { typeof hceSharedConfig.plugins } 24 | */ 25 | const newPlugins = hceSharedConfig.plugins; 26 | const projs = [ 27 | './GroupBox.Avalonia/GroupBox.Avalonia.csproj', 28 | './GroupBox.Avalonia.Sample/GroupBox.Avalonia.Sample.csproj']; 29 | const dotnetPublish = projs.map(v => `dotnet publish ${v}`).join(" && "); 30 | 31 | //#region Insert-Edit Plugins 32 | /* Insert and/or configure plugins. Can be used to load plugin A before plugin B 33 | or edit a plugin's existing configuration */ 34 | for (var i = 0; i < newPlugins.length; i++) { 35 | /** e.g. 36 | //#region Git Options | https://github.com/semantic-release/git#options 37 | // if defined without plugin options, replace with tuple-like array with assets option defined. 38 | if (plugins[i] === "@semantic-release/git") { 39 | plugins[i] = [plugins[i], { assets: ["README.md", "CHANGELOG.md"] }] 40 | } 41 | if (plugins[i][0] === "@semantic-release/git") { 42 | // if assets array undefined, define it 43 | if (!plugins[i][1].assets) { 44 | plugins[i][1].assets = []; 45 | } 46 | // ensure README.md is in assets array 47 | if (!plugins[i][1].assets.some(a => a === "README.md" || a.path === "README.md")) { 48 | plugins[i][1].assets.push("README.md"); 49 | } 50 | // ensure CHANGELOG.md is in assets array 51 | if (!plugins[i][1].assets.some(a => a === "CHANGELOG.md" || a.path === "CHANGELOG.md")) { 52 | plugins[i][1].assets.push("CHANGELOG.md"); 53 | } 54 | } 55 | //#endregion Git Options 56 | */ 57 | } 58 | //#endregion Insert-Edit Plugins 59 | 60 | //#region Append Plugins 61 | newPlugins.push( 62 | // APPEND this array of [pluginName, pluginConfig] to plugins 63 | // https://github.com/semantic-release/exec#usage 64 | ["@semantic-release/exec", { 65 | // 'ZipPublishDir' zips each publish folder to ./publish/*.zip 66 | prepareCmd: dotnetPublish 67 | }] 68 | ) 69 | //#endregion Append Plugins 70 | 71 | if (process.argv.includes("--debug") || process.argv.includes("--verbose")) { 72 | console.info("modified plugins array:\n" + JSON.stringify(newPlugins, null, 2)) 73 | } 74 | 75 | /** 76 | * @type {import("semantic-release").Options} 77 | */ 78 | module.exports = { 79 | extends: "@halospv3/hce.shared-config", 80 | plugins: newPlugins 81 | }; 82 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "appulate.filewatcher", 4 | "actionforge.actionforge", 5 | "avaloniateam.vscode-avalonia" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/GroupBox.Avalonia.Sample/bin/Debug/net8.0/GroupBox.Avalonia.Sample.dll", 13 | "args": [], 14 | "cwd": "${workspaceFolder}/GroupBox.Avalonia.Sample", 15 | "console": "internalConsole", 16 | "stopAtEntry": false, 17 | "justMyCode": false 18 | }, 19 | { 20 | "name": ".NET Core Attach", 21 | "type": "coreclr", 22 | "request": "attach" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "GroupBox.Avalonia.sln", 3 | "xml.codeLens.enabled": true, 4 | "xml.format.maxLineWidth": 100, 5 | "xml.format.preserveAttributeLineBreaks": false, 6 | "xml.format.splitAttributes": "alignWithFirstAttr", 7 | "[AXAML]": { 8 | "editor.defaultFormatter": "redhat.vscode-xml", 9 | }, 10 | "filewatcher.commands": [ 11 | { 12 | "vscodeTask": [ 13 | "workbench.action.tasks.runTask", 14 | "npm: xstyler" 15 | ], 16 | "event": "onFileChange", 17 | "match": ".*/.axaml" 18 | } 19 | ], 20 | "csharp.debug.symbolOptions.searchMicrosoftSymbolServer": true, 21 | "csharp.debug.symbolOptions.searchNuGetOrgSymbolServer": true 22 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/GroupBox.Avalonia.sln", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary;ForceNoAlign" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/GroupBox.Avalonia.sln", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary;ForceNoAlign" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/GroupBox.Avalonia.Sample", 37 | "/consoleloggerparameters:NoSummary;ForceNoAlign" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | }, 41 | { 42 | "type": "npm", 43 | "script": "xstyler", 44 | "problemMatcher": [], 45 | "label": "npm: xstyler", 46 | "detail": "node ./tools/formatXamlFiles.mjs", 47 | "presentation": { 48 | "reveal": "never", 49 | "panel": "dedicated", 50 | "showReuseMessage": false, 51 | "focus": false 52 | } 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.0](https://github.com/BinToss/GroupBox.Avalonia/compare/v0.1.0...v1.0.0) (2024-03-18) 2 | 3 | 4 | ### ⚠ BREAKING CHANGES 5 | 6 | * move FluentTheme, SimpleTheme deps to Sample 7 | * convert ClassicTheme, ModernTheme styles to ControlThemes 8 | * move themes to Themes 9 | 10 | ### Features 11 | 12 | * **GroupBox.Avalonia.Sample:** add ComboBox to select Theme Variant (Light/Dark) ([45f1326](https://github.com/BinToss/GroupBox.Avalonia/commit/45f1326f39d5dc854cb97fce9ed4179ab91e810d)) 13 | * **GroupBox.Avalonia:** assign Names to ModernTheme elements ([f1b2994](https://github.com/BinToss/GroupBox.Avalonia/commit/f1b2994d04cb3159ee0a8c57e8c1ad131708ae71)) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * **GroupBox.Avalonia.Sample:** add background tint to MainWindow ([1784c55](https://github.com/BinToss/GroupBox.Avalonia/commit/1784c5583b9418f914e12c7f0218c001d472deff)) 19 | * **GroupBox.Avalonia.Sample:** adjust MainView Grid to evenly distribute space ([20df3bc](https://github.com/BinToss/GroupBox.Avalonia/commit/20df3bc3930b362431b72ef0cea7a4e4292f2c8f)) 20 | * **GroupBox.Avalonia:** add 1px margin to GBHeaderText ([6157178](https://github.com/BinToss/GroupBox.Avalonia/commit/615717869393db2cb1b2430173c3eeb1fb35c96d)) 21 | * **GroupBox.Avalonia:** define ThemeBorderMidBrush ourselves for ClassicTheme ([18bbcfd](https://github.com/BinToss/GroupBox.Avalonia/commit/18bbcfdd652ad54b848e10e75fc29c31486ed9b2)) 22 | * **GroupBox.Avalonia:** disable anti-aliasing for classic Border ([24dd908](https://github.com/BinToss/GroupBox.Avalonia/commit/24dd90824148f3eb092be5ebcc34f8938b75d1f8)) 23 | * **GroupBox.Avalonia:** dynamically adjust spacing ([4cb5c64](https://github.com/BinToss/GroupBox.Avalonia/commit/4cb5c648dda84c8b99cb518ff69ed1453e8c2408)) 24 | * **GroupBox.Avalonia:** dynamically update Header's border clip ([5bdd9d3](https://github.com/BinToss/GroupBox.Avalonia/commit/5bdd9d31659776ce0cd62907ae2e00f8ae54968d)) 25 | * **GroupBox.Avalonia:** move ThemeBorderMidBrush resources ([2baffe3](https://github.com/BinToss/GroupBox.Avalonia/commit/2baffe3fb459df4c7eba75db9fd9a67b6449973a)) 26 | 27 | 28 | ### Performance Improvements 29 | 30 | * **GroupBox.Avalonia:** remove redundant Border ([ce0c8d3](https://github.com/BinToss/GroupBox.Avalonia/commit/ce0c8d3bc55835464bd0c2355fee7b4db288daea)) 31 | 32 | 33 | ### Code Refactoring 34 | 35 | * convert ClassicTheme, ModernTheme styles to ControlThemes ([312bad4](https://github.com/BinToss/GroupBox.Avalonia/commit/312bad4989b43b83f2abdb5e86c0461579a7014e)) 36 | * move themes to Themes ([4d28d30](https://github.com/BinToss/GroupBox.Avalonia/commit/4d28d304d50156c4aa3f6cbb954a15c3e0232b0c)) 37 | 38 | 39 | ### Build System 40 | 41 | * move FluentTheme, SimpleTheme deps to Sample ([321e0bc](https://github.com/BinToss/GroupBox.Avalonia/commit/321e0bc1d5d5ccd987421de498d5df3b4df6ef7a)) 42 | 43 | ## [1.0.0-develop.1](https://github.com/BinToss/GroupBox.Avalonia/compare/v0.1.0...v1.0.0-develop.1) (2024-03-18) 44 | 45 | 46 | ### ⚠ BREAKING CHANGES 47 | 48 | * move FluentTheme, SimpleTheme deps to Sample 49 | * convert ClassicTheme, ModernTheme styles to ControlThemes 50 | * move themes to Themes 51 | 52 | ### Features 53 | 54 | * **GroupBox.Avalonia.Sample:** add ComboBox to select Theme Variant (Light/Dark) ([45f1326](https://github.com/BinToss/GroupBox.Avalonia/commit/45f1326f39d5dc854cb97fce9ed4179ab91e810d)) 55 | * **GroupBox.Avalonia:** assign Names to ModernTheme elements ([f1b2994](https://github.com/BinToss/GroupBox.Avalonia/commit/f1b2994d04cb3159ee0a8c57e8c1ad131708ae71)) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * **GroupBox.Avalonia.Sample:** add background tint to MainWindow ([1784c55](https://github.com/BinToss/GroupBox.Avalonia/commit/1784c5583b9418f914e12c7f0218c001d472deff)) 61 | * **GroupBox.Avalonia.Sample:** adjust MainView Grid to evenly distribute space ([20df3bc](https://github.com/BinToss/GroupBox.Avalonia/commit/20df3bc3930b362431b72ef0cea7a4e4292f2c8f)) 62 | * **GroupBox.Avalonia:** add 1px margin to GBHeaderText ([6157178](https://github.com/BinToss/GroupBox.Avalonia/commit/615717869393db2cb1b2430173c3eeb1fb35c96d)) 63 | * **GroupBox.Avalonia:** define ThemeBorderMidBrush ourselves for ClassicTheme ([18bbcfd](https://github.com/BinToss/GroupBox.Avalonia/commit/18bbcfdd652ad54b848e10e75fc29c31486ed9b2)) 64 | * **GroupBox.Avalonia:** disable anti-aliasing for classic Border ([24dd908](https://github.com/BinToss/GroupBox.Avalonia/commit/24dd90824148f3eb092be5ebcc34f8938b75d1f8)) 65 | * **GroupBox.Avalonia:** dynamically adjust spacing ([4cb5c64](https://github.com/BinToss/GroupBox.Avalonia/commit/4cb5c648dda84c8b99cb518ff69ed1453e8c2408)) 66 | * **GroupBox.Avalonia:** dynamically update Header's border clip ([5bdd9d3](https://github.com/BinToss/GroupBox.Avalonia/commit/5bdd9d31659776ce0cd62907ae2e00f8ae54968d)) 67 | * **GroupBox.Avalonia:** move ThemeBorderMidBrush resources ([2baffe3](https://github.com/BinToss/GroupBox.Avalonia/commit/2baffe3fb459df4c7eba75db9fd9a67b6449973a)) 68 | 69 | 70 | ### Performance Improvements 71 | 72 | * **GroupBox.Avalonia:** remove redundant Border ([ce0c8d3](https://github.com/BinToss/GroupBox.Avalonia/commit/ce0c8d3bc55835464bd0c2355fee7b4db288daea)) 73 | 74 | 75 | ### Code Refactoring 76 | 77 | * convert ClassicTheme, ModernTheme styles to ControlThemes ([312bad4](https://github.com/BinToss/GroupBox.Avalonia/commit/312bad4989b43b83f2abdb5e86c0461579a7014e)) 78 | * move themes to Themes ([4d28d30](https://github.com/BinToss/GroupBox.Avalonia/commit/4d28d304d50156c4aa3f6cbb954a15c3e0232b0c)) 79 | 80 | 81 | ### Build System 82 | 83 | * move FluentTheme, SimpleTheme deps to Sample ([321e0bc](https://github.com/BinToss/GroupBox.Avalonia/commit/321e0bc1d5d5ccd987421de498d5df3b4df6ef7a)) 84 | 85 | ## [1.0.0-develop.1](https://github.com/BinToss/GroupBox.Avalonia/compare/v0.1.0...v1.0.0-develop.1) (2024-03-17) 86 | 87 | 88 | ### ⚠ BREAKING CHANGES 89 | 90 | * move FluentTheme, SimpleTheme deps to Sample 91 | * convert ClassicTheme, ModernTheme styles to ControlThemes 92 | * move themes to Themes 93 | 94 | ### Features 95 | 96 | * **GroupBox.Avalonia.Sample:** add ComboBox to select Theme Variant (Light/Dark) ([45f1326](https://github.com/BinToss/GroupBox.Avalonia/commit/45f1326f39d5dc854cb97fce9ed4179ab91e810d)) 97 | * **GroupBox.Avalonia:** assign Names to ModernTheme elements ([f1b2994](https://github.com/BinToss/GroupBox.Avalonia/commit/f1b2994d04cb3159ee0a8c57e8c1ad131708ae71)) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * **GroupBox.Avalonia.Sample:** add background tint to MainWindow ([1784c55](https://github.com/BinToss/GroupBox.Avalonia/commit/1784c5583b9418f914e12c7f0218c001d472deff)) 103 | * **GroupBox.Avalonia.Sample:** adjust MainView Grid to evenly distribute space ([20df3bc](https://github.com/BinToss/GroupBox.Avalonia/commit/20df3bc3930b362431b72ef0cea7a4e4292f2c8f)) 104 | * **GroupBox.Avalonia:** add 1px margin to GBHeaderText ([6157178](https://github.com/BinToss/GroupBox.Avalonia/commit/615717869393db2cb1b2430173c3eeb1fb35c96d)) 105 | * **GroupBox.Avalonia:** define ThemeBorderMidBrush ourselves for ClassicTheme ([18bbcfd](https://github.com/BinToss/GroupBox.Avalonia/commit/18bbcfdd652ad54b848e10e75fc29c31486ed9b2)) 106 | * **GroupBox.Avalonia:** disable anti-aliasing for classic Border ([24dd908](https://github.com/BinToss/GroupBox.Avalonia/commit/24dd90824148f3eb092be5ebcc34f8938b75d1f8)) 107 | * **GroupBox.Avalonia:** dynamically adjust spacing ([4cb5c64](https://github.com/BinToss/GroupBox.Avalonia/commit/4cb5c648dda84c8b99cb518ff69ed1453e8c2408)) 108 | * **GroupBox.Avalonia:** dynamically update Header's border clip ([5bdd9d3](https://github.com/BinToss/GroupBox.Avalonia/commit/5bdd9d31659776ce0cd62907ae2e00f8ae54968d)) 109 | * **GroupBox.Avalonia:** move ThemeBorderMidBrush resources ([2baffe3](https://github.com/BinToss/GroupBox.Avalonia/commit/2baffe3fb459df4c7eba75db9fd9a67b6449973a)) 110 | 111 | 112 | ### Performance Improvements 113 | 114 | * **GroupBox.Avalonia:** remove redundant Border ([ce0c8d3](https://github.com/BinToss/GroupBox.Avalonia/commit/ce0c8d3bc55835464bd0c2355fee7b4db288daea)) 115 | 116 | 117 | ### Code Refactoring 118 | 119 | * convert ClassicTheme, ModernTheme styles to ControlThemes ([312bad4](https://github.com/BinToss/GroupBox.Avalonia/commit/312bad4989b43b83f2abdb5e86c0461579a7014e)) 120 | * move themes to Themes ([4d28d30](https://github.com/BinToss/GroupBox.Avalonia/commit/4d28d304d50156c4aa3f6cbb954a15c3e0232b0c)) 121 | 122 | 123 | ### Build System 124 | 125 | * move FluentTheme, SimpleTheme deps to Sample ([321e0bc](https://github.com/BinToss/GroupBox.Avalonia/commit/321e0bc1d5d5ccd987421de498d5df3b4df6ef7a)) 126 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), '.git/index')) 4 | $(RepoRoot)node_modules/@halospv3/hce.shared-config/ 5 | $(HCESharedDir)GitVersion.yml 6 | 11.0.10 7 | 1.0.1 8 | 9 | 10 | 11 | Release 12 | true 13 | true 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.256.png -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/App.axaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using GroupBox.Avalonia.Sample.Views; 5 | using HotAvalonia; 6 | 7 | namespace GroupBox.Avalonia.Sample; 8 | 9 | public partial class App : Application 10 | { 11 | public override void Initialize() 12 | { 13 | this.EnableHotReload(); 14 | AvaloniaXamlLoader.Load(this); 15 | } 16 | 17 | public FluentWindow? FluentWindow { get; private set; } 18 | public SimpleWindow? SimpleWindow { get; private set; } 19 | 20 | public override void OnFrameworkInitializationCompleted() 21 | { 22 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 23 | { 24 | desktop.MainWindow = new MainWindow(); 25 | FluentWindow = new(); 26 | SimpleWindow = new(); 27 | } 28 | 29 | base.OnFrameworkInitializationCompleted(); 30 | } 31 | } -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.Sample/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/GroupBox.Avalonia.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0 5 | enable 6 | true 7 | app.manifest 8 | true 9 | enable 10 | true 11 | 12 | 13 | 14 | $(DefineConstants);ENABLE_XAML_HOT_RELOAD 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | 3 | namespace GroupBox.Avalonia.Sample; 4 | 5 | class Program 6 | { 7 | // Initialization code. Don't use any Avalonia, third-party APIs or any 8 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 9 | // yet and stuff might break. 10 | [STAThread] 11 | public static void Main(string[] args) => BuildAvaloniaApp() 12 | .StartWithClassicDesktopLifetime(args); 13 | 14 | // Avalonia configuration, don't remove; also used by visual designer. 15 | public static AppBuilder BuildAvaloniaApp() 16 | => AppBuilder.Configure() 17 | .UsePlatformDetect() 18 | .WithInterFont() 19 | .LogToTrace(); 20 | } 21 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/FluentWindow.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/FluentWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace GroupBox.Avalonia.Sample.Views; 4 | 5 | public partial class FluentWindow : Window 6 | { 7 | public FluentWindow() => InitializeComponent(); 8 | } -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/MainView.axaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | CheckBox0 21 | CheckBox1 22 | 23 | 24 | 25 | 27 | 28 | CheckBox0 29 | CheckBox1 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace GroupBox.Avalonia.Sample.Views; 4 | 5 | public partial class MainView : UserControl 6 | { 7 | public MainView() => InitializeComponent(); 8 | } 9 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 37 | 38 | 39 | Show FluentWindow 40 | 41 | 42 | 43 | Show SimpleWindow 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Interactivity; 5 | using Avalonia.Styling; 6 | 7 | namespace GroupBox.Avalonia.Sample.Views; 8 | 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | ThemeVariantComboBox.ItemsSource = ThemeVariants.Keys as IEnumerable; 15 | ThemeVariantComboBox.SelectedIndex = 0; 16 | } 17 | 18 | public ReadOnlyDictionary ThemeVariants { get; } = new Dictionary(){ 19 | {"Default", ThemeVariant.Default}, 20 | {"Light", ThemeVariant.Light}, 21 | {"Dark",ThemeVariant.Dark} 22 | }.AsReadOnly(); 23 | 24 | internal static App? App => Application.Current as App; 25 | public static FluentWindow? FluentWindow => App?.FluentWindow; 26 | public static SimpleWindow? SimpleWindow => App?.SimpleWindow; 27 | 28 | public static void FluentWindowButton_OnChecked(object sender, RoutedEventArgs args) 29 | { 30 | if (FluentWindow?.IsVisible is false) 31 | FluentWindow.Show(); 32 | } 33 | 34 | public static void FluentWindowButton_OnUnchecked(object sender, RoutedEventArgs args) 35 | { 36 | if (FluentWindow?.IsVisible is true) 37 | FluentWindow.Hide(); 38 | } 39 | 40 | public static void SimpleWindowButton_OnChecked(object sender, RoutedEventArgs args) 41 | { 42 | if (SimpleWindow?.IsVisible is false) 43 | SimpleWindow.Show(); 44 | } 45 | 46 | public static void SimpleWindowButton_OnUnchecked(object sender, RoutedEventArgs args) 47 | { 48 | if (SimpleWindow?.IsVisible is true) 49 | SimpleWindow.Hide(); 50 | } 51 | 52 | public void ThemeVariantComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs args) 53 | { 54 | string selection = args.AddedItems[0]?.ToString() ?? ""; 55 | try 56 | { 57 | if (App is not null) 58 | App.RequestedThemeVariant = ThemeVariants[selection]; 59 | } 60 | catch (KeyNotFoundException) { } 61 | } 62 | } -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/SimpleWindow.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/Views/SimpleWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace GroupBox.Avalonia.Sample.Views; 4 | 5 | public partial class SimpleWindow : Window 6 | { 7 | public SimpleWindow() => InitializeComponent(); 8 | 9 | } -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample_Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.Sample_Dark.png -------------------------------------------------------------------------------- /GroupBox.Avalonia.Sample_Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.Sample_Light.png -------------------------------------------------------------------------------- /GroupBox.Avalonia.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.ico -------------------------------------------------------------------------------- /GroupBox.Avalonia.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.5.001.0 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GroupBox.Avalonia.Sample", "GroupBox.Avalonia.Sample\GroupBox.Avalonia.Sample.csproj", "{B45E08EF-6D48-4E2F-BE6A-A19BC580F008}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GroupBox.Avalonia", "GroupBox.Avalonia\GroupBox.Avalonia.csproj", "{4AC2D0D6-BAE0-46C6-8105-50C0B7C79D52}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{0E930A03-068F-48C7-8843-39B8CCADD7C0}" 10 | ProjectSection(SolutionItems) = preProject 11 | Directory.Build.props = Directory.Build.props 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {B45E08EF-6D48-4E2F-BE6A-A19BC580F008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {B45E08EF-6D48-4E2F-BE6A-A19BC580F008}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {B45E08EF-6D48-4E2F-BE6A-A19BC580F008}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {B45E08EF-6D48-4E2F-BE6A-A19BC580F008}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {4AC2D0D6-BAE0-46C6-8105-50C0B7C79D52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {4AC2D0D6-BAE0-46C6-8105-50C0B7C79D52}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {4AC2D0D6-BAE0-46C6-8105-50C0B7C79D52}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {4AC2D0D6-BAE0-46C6-8105-50C0B7C79D52}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {89900FFF-1F81-44A9-B0E6-15AB96162EF9} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /GroupBox.Avalonia.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinToss/GroupBox.Avalonia/b32498a5684ec833c96d0ea7548524a591ca5d75/GroupBox.Avalonia.xcf -------------------------------------------------------------------------------- /GroupBox.Avalonia/Controls/GroupBox.axaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /GroupBox.Avalonia/Controls/GroupBox.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls.Primitives; 2 | 3 | namespace GroupBox.Avalonia.Controls; 4 | /// 5 | /// A thin wrapper for intended for recreations of GroupBox and GroupBox-like Controls. 6 | /// From Microsoft's documentation: This Control displays a frame around a group of controls with an optional caption.
7 | ///
8 | /// 9 | /// If you're looking for that win32/WinForms/Aero look, set the GroupBox's Theme to {StaticResource GroupBoxClassic}.
10 | /// If you haven't already, you may want to consider reading the Windows 7 design guidelines. A GroupBox may be incorrect for the intended content or layout. 11 | ///
12 | public partial class GroupBox : HeaderedContentControl 13 | { 14 | public GroupBox() => InitializeComponent(); 15 | } 16 | -------------------------------------------------------------------------------- /GroupBox.Avalonia/Converters/StaticConverters.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Data; 3 | using Avalonia.Data.Converters; 4 | using Avalonia.Media; 5 | 6 | namespace GroupBox.Avalonia.Converters 7 | { 8 | public static class StaticConverters 9 | { 10 | public static FuncMultiValueConverter ConvertBoundsToOuterBorder { get; } = 11 | new(rects => 12 | { 13 | List arr = rects.Cast().ToList() ?? throw new NullReferenceException(); 14 | return arr.Count < 3 15 | ? throw new ArgumentOutOfRangeException(nameof(rects), "Amount of given Bounds was less than 3") 16 | : new CombinedGeometry( 17 | GeometryCombineMode.Exclude, 18 | new RectangleGeometry(arr[0]), 19 | new RectangleGeometry(arr[1]), 20 | new TranslateTransform(-arr[2].Left, -arr[2].Top) 21 | ); 22 | }); 23 | 24 | /// 25 | /// Disguise a Thickness (e.g. Margin) as a Rect. The Rect's Left, Right, Top, and Bottom are equal to the Thickness's properties. 26 | /// 27 | public static FuncValueConverter ThicknessRectConverter { get; } = 28 | new(thickness => 29 | { 30 | if (thickness is Thickness thick) 31 | return new Rect(thick.Left, thick.Top, thick.Right - thick.Left, thick.Top - thick.Bottom); 32 | 33 | throw new InvalidCastException(); 34 | }); 35 | public static FuncValueConverter OSBorderCornerRadius { get; } = new((OperatingSystem? osVersion) => 36 | new(osVersion?.Platform switch 37 | { 38 | PlatformID.Win32NT => osVersion.Version.Major switch 39 | { 40 | /* check radius via Windows' presentationsettings.exe */ 41 | 42 | // XP and older. XP may have used 3, but older ones were probably 0. 43 | 5 => 0, 44 | 6 => osVersion.Version.Minor switch 45 | { 46 | // Vista, 7 are 3 47 | 0 or 1 => 3, 48 | // 8, 8.1 are...unknown 49 | _ => 0, 50 | }, 51 | // 10 and later. My copies of Win11 use 0. 52 | 10 => osVersion.Version.Build switch 53 | { 54 | var win11 when win11 >= 22000 => 0, 55 | _ => 0, 56 | }, 57 | _ => 0, 58 | }, 59 | _ => 0 60 | })); 61 | 62 | public static FuncMultiValueConverter ConvertDoublesToThickness { get; } = new(src => 63 | { 64 | List doubles = new(); 65 | foreach (var nd in src) 66 | { 67 | if (nd is double d) 68 | doubles.Add(d); 69 | else throw new InvalidCastException(); 70 | } 71 | return doubles.Count switch 72 | { 73 | 1 => new Thickness(doubles[0]), 74 | 2 => new Thickness(doubles[0], doubles[1]), 75 | 4 => new Thickness(doubles[0], doubles[1], doubles[2], doubles[3]), 76 | _ => throw new IndexOutOfRangeException("Source data must be doubles. Thickness' constructors only allow for 1, 2, or 4 arguments"), 77 | }; 78 | }); 79 | 80 | public static FuncValueConverter BoundsToHeaderHeightDividedByTwo { get; } = new(rect => 81 | rect.HasValue ? rect.Value.Height / 2 : throw new InvalidCastException()); 82 | } 83 | } -------------------------------------------------------------------------------- /GroupBox.Avalonia/GroupBox.Avalonia.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Library 4 | net6.0 5 | enable 6 | true 7 | true 8 | enable 9 | 10 | 11 | 12 | $(DefineConstants);ENABLE_XAML_HOT_RELOAD 13 | 14 | 15 | 16 | BinToss.GroupBox.Avalonia 17 | 18 | 19 | BinToss 20 | GroupBox control for AvaloniaUI 21 | Another attempt to recreate the GroupBox control for AvaloniaUI. 22 | © 2023 Noah Sherwin 23 | MIT 24 | README.md 25 | Avalonia AvaloniaUI GroupBox 26 | https://github.com/BinToss/GroupBox.Avalonia 27 | $(RepositoryUrl)#groupboxavalonia 28 | git 29 | true 30 | true 31 | true 32 | snupkg 33 | true 34 | GroupBox.Avalonia.256.png 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /GroupBox.Avalonia/Themes/GroupBoxBasic.axaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /GroupBox.Avalonia/Themes/GroupBoxClassic.axaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 11 | 12 | 13 | 15 | 16 | 17 | 3 18 | 0 19 | 20 | 21 | 22 | #FF888888 23 | 24 | 25 | 26 | #FF888888 27 | 28 | 29 | 30 | #FF808080 31 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Noah Sherwin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | [![semantic-release: conventionalcommits](https://img.shields.io/badge/semantic--release-conventionalcommits-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) 7 | [![avalonia](https://img.shields.io/badge/avalonia-v11.0.10-8b44ac?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjM1IiB2aWV3Qm94PSIwIDAgMzUgMzUiIHdpZHRoPSIzNSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMzAuNDY2MSAzNC45MjhoLjIwOTNjMi4xODQyIDAgMy45Nzg2LTEuNjM2MiA0LjIyOTktMy43NTI4bC4wMzAzLTE0LjE3OTdjLS4yNDg0LTkuNDI4NTMtNy45Njk0LTE2Ljk5NTUtMTcuNDU3OS0xNi45OTU1LTkuNjQ1MDcgMC0xNy40NjM5NjcxIDcuODE4OS0xNy40NjM5NjcxIDE3LjQ2NCAwIDkuNTQxOSA3LjY1MjQ0NzEgMTcuMjk5MSAxNy4xNTQ5NjcxIDE3LjQ2NHoiIGZpbGw9IiNmZmYiLz48ZyBmaWxsPSIjOGI0NGFjIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xNy41MjM5IDUuOTQ4Yy01LjQ5NzEgMC0xMC4wOTQyMyAzLjg1MzE3LTExLjIzNzkgOS4wMDYgMS4wOTQ5Mi4zMDY5IDEuODk3ODUgMS4zMTI0IDEuODk3ODUgMi41MDUzIDAgMS4xOTMtLjgwMjkzIDIuMTk4NC0xLjg5Nzg1IDIuNTA1NCAxLjE0MzY2IDUuMTUyOCA1Ljc0MDggOS4wMDU5IDExLjIzNzkgOS4wMDU5IDIuMDAxMSAwIDMuODgyOS0uNTEwNSA1LjUyMjMtMS40MDg2djEuMzMwN2g1Ljk4OXYtMTAuOTU2MmMuMDA1NS0uMTQ1NyAwLS4zMzAyIDAtLjQ3NzIgMC02LjM1NzUtNS4xNTM4LTExLjUxMTMtMTEuNTExMy0xMS41MTEzem0tNS41MTQxIDExLjUxMTNjMC0zLjA0NTMgMi40Njg4LTUuNTE0MSA1LjUxNDEtNS41MTQxIDMuMDQ1NCAwIDUuNTE0MSAyLjQ2ODggNS41MTQxIDUuNTE0MSAwIDMuMDQ1NC0yLjQ2ODcgNS41MTQxLTUuNTE0MSA1LjUxNDEtMy4wNDUzIDAtNS41MTQxLTIuNDY4Ny01LjUxNDEtNS41MTQxeiIgZmlsbC1ydWxlPSJldmVub2RkIi8%2BPHBhdGggZD0ibTcuMzY4NDEgMTcuNDUxN2MwIDEuMDE3NC0uODI0NzMgMS44NDIxLTEuODQyMSAxLjg0MjFzLTEuODQyMTEtLjgyNDctMS44NDIxMS0xLjg0MjEuODI0NzQtMS44NDIxIDEuODQyMTEtMS44NDIxIDEuODQyMS44MjQ3IDEuODQyMSAxLjg0MjF6Ii8%2BPC9nPjwvc3ZnPg%3D%3D)](https://github.com/AvaloniaUI/Avalonia) 8 | 9 | [![CI](https://github.com/BinToss/GroupBox.Avalonia/actions/workflows/ci.yml/badge.svg)](https://github.com/BinToss/GroupBox.Avalonia/actions/workflows/ci.yml) 10 | [![Release](https://github.com/BinToss/GroupBox.Avalonia/actions/workflows/release.yml/badge.svg)](https://github.com/BinToss/GroupBox.Avalonia/actions/workflows/release.yml) 11 | [![latest](https://buildstats.info/nuget/BinToss.GroupBox.Avalonia)](https://www.nuget.org/packages/BinToss.GroupBox.Avalonia/) 12 | 13 |
14 | 15 | 16 | 17 | # GroupBox.Avalonia 18 | 19 |

20 | 21 | Another attempt to recreate the GroupBox control for AvaloniaUI. 22 | 23 | Based on @derekantrican's Classic and Modern GroupBox examples at [[GroupBox] Missing · Issue #823 · AvaloniaUI/Avalonia](https://github.com/AvaloniaUI/Avalonia/issues/823#issuecomment-692270581). 24 | 25 | When this library is production-ready, its themes/styles _may_ be pull-requested to teast/Avalonia.GroupBox. Otherwise, someone else will need to maintain this project. 26 | 27 | ![GroupBox.Avalonia.Sample.png](./GroupBox.Avalonia.Sample_Dark.png) 28 | ![GroupBox.Avalonia.Sample.png](./GroupBox.Avalonia.Sample_Light.png) 29 | 30 | # Known Issues 31 | 32 | - Sample app re-distributes the checkbox labels...weirdly. 33 | 34 | # Usage 35 | 36 | 1. Add the package: https://www.nuget.org/packages/BinToss.GroupBox.Avalonia/ 37 | 38 | ```sh 39 | dotnet add package BinToss.GroupBox.Avalonia 40 | ``` 41 | 42 | 2. In an AXAML/XAML file, add the following xmlns statement: `xmlns:gb="using:GroupBox.Avalonia.Controls"`. I used `ctrl` in the sample project, but other projects should use `gb`. 43 | https://github.com/BinToss/GroupBox.Avalonia/blob/5515704496a0103200733b788bcfcad1c1ce5ea1/GroupBox.Avalonia.Sample/Views/MainView.axaml#L1-L5 44 | 45 | 3. Add a basic GroupBox via `` 46 | https://github.com/BinToss/GroupBox.Avalonia/blob/5515704496a0103200733b788bcfcad1c1ce5ea1/GroupBox.Avalonia.Sample/Views/MainView.axaml#L25-L31 47 | 48 | For its contents, you should use a `Panel`-based `Control` such as `Grid` or `StackPanel`. It's a *Group*Box, after all. 49 | 50 | 4. (Optional) To use the "classic" theme replicating Windows' classic GroupBox, use `Theme="{StaticResource GroupBoxClassic}"` 51 | https://github.com/BinToss/GroupBox.Avalonia/blob/5515704496a0103200733b788bcfcad1c1ce5ea1/GroupBox.Avalonia.Sample/Views/MainView.axaml#L16-L18 52 | 53 | --- 54 | 55 | For more info, see [GroupBox.Avalonia.Sample](https://github.com/BinToss/GroupBox.Avalonia/tree/main/GroupBox.Avalonia.Sample). 56 | -------------------------------------------------------------------------------- /Settings.XamlStyler: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./Settings.XamlStyler.schema.json", 3 | "IndentSize": 2, 4 | "KeepFirstAttributeOnSameLine": true, 5 | "AttributesTolerance": 2, 6 | "MaxAttributesPerLine": 2, 7 | "SeparateByGroups": true, 8 | "AttributeOrderingRuleGroups": [ 9 | "x:Class", 10 | "xmlns", 11 | "xmlns:x", 12 | "x:Key, Key, x:Name, Name, x:Uid, Uid, Title, Header", 13 | "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom", 14 | "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight", 15 | "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex", 16 | "mc:Ignorable, d:DesignWidth, d:DesignHeight, d:IsDataSource, d:LayoutOverrides, d:IsStaticText", 17 | "*:*, *", 18 | "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint", 19 | "Storyboard.*, From, To, Duration" 20 | ] 21 | } -------------------------------------------------------------------------------- /Settings.XamlStyler.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/Xavalon/XamlStyler/main/Settings.XamlStyler.schema.json", 4 | "type": "object", 5 | "properties": { 6 | "IndentSize": { 7 | "description": "XAML Styler (plugin only) detects and uses Visual Studio indent options by default. However, since xstyler.exe runs independently of Visual Studio, we are unable to detect those options. If you want to override the default behavior for either the plugin or xstyler.exe, you can set an extra property \"IndentSize\" in an external configuration.", 8 | "type": "integer", 9 | "default": 4 10 | }, 11 | "IndentWithTabs": { 12 | "description": "XAML Styler (plugin only) detects and uses Visual Studio insertTabs setting by default. However, since xstyler.exe runs independently of Visual Studio, we are unable to detect those options. If you want to override the default behavior for either the plugin or xstyler.exe, you can set an extra property \"IndentWithTabs\" in an external configuration.", 13 | "type": "boolean", 14 | "default": false 15 | }, 16 | "AttributesTolerance": { 17 | "type": "integer", 18 | "default": 2 19 | }, 20 | "KeepFirstAttributeOnSameLine": { 21 | "type": "boolean", 22 | "default": false 23 | }, 24 | "MaxAttributeCharactersPerLine": { 25 | "type": "integer", 26 | "default": 0 27 | }, 28 | "MaxAttributesPerLine": { 29 | "type": "integer", 30 | "default": 1 31 | }, 32 | "NewlineExemptionElements": { 33 | "type": "string", 34 | "default": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransform, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter" 35 | }, 36 | "SeparateByGroups": { 37 | "type": "boolean", 38 | "default": false 39 | }, 40 | "AttributeIndentation": { 41 | "type": "integer", 42 | "description": "Defines the number of spaces that attributes are indented on elements with more than one line of attributes. A value of 0 will align indentation with the first attribute.\nSee https://github.com/Xavalon/XamlStyler/wiki/Attribute-Formatting#attribute-indentation", 43 | "default": 0 44 | }, 45 | "AttributeIndentationStyle": { 46 | "type": "integer", 47 | "enum": [ 48 | { 49 | "code": 0, 50 | "description": "Mixed" 51 | }, 52 | { 53 | "code": 1, 54 | "description": "Spaces" 55 | } 56 | ], 57 | "default": 1 58 | }, 59 | "RemoveDesignTimeReferences": { 60 | "type": "boolean", 61 | "default": false 62 | }, 63 | "IgnoreDesignTimeReferencePrefix": { 64 | "type": "boolean", 65 | "default": false 66 | }, 67 | "EnableAttributeReordering": { 68 | "type": "boolean", 69 | "default": true 70 | }, 71 | "AttributeOrderingRuleGroups": { 72 | "type": "array", 73 | "items": { 74 | "type": "string" 75 | }, 76 | "default": [ 77 | "x:Class", 78 | "xmlns, xmlns:x", 79 | "xmlns:*", 80 | "x:Key, Key, x:Name, Name, x:Uid, Uid, Title", 81 | "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom", 82 | "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight", 83 | "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex", 84 | "*:*, *", 85 | "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint", 86 | "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText", 87 | "Storyboard.*, From, To, Duration" 88 | ] 89 | }, 90 | "FirstLineAttributes": { 91 | "type": "string", 92 | "default": "" 93 | }, 94 | "OrderAttributesByName": { 95 | "type": "boolean", 96 | "default": true 97 | }, 98 | "PutEndingBracketOnNewLine": { 99 | "type": "boolean", 100 | "default": false 101 | }, 102 | "RemoveEndingTagOfEmptyElement": { 103 | "type": "boolean", 104 | "default": true 105 | }, 106 | "SpaceBeforeClosingSlash": { 107 | "type": "boolean", 108 | "default": true 109 | }, 110 | "RootElementLineBreakRule": { 111 | "type": "integer", 112 | "enum": [ 113 | { 114 | "code": 0, 115 | "description": "Default" 116 | }, 117 | { 118 | "code": 1, 119 | "description": "Always" 120 | }, 121 | { 122 | "code": 2, 123 | "description": "Never" 124 | } 125 | ], 126 | "default": 0 127 | }, 128 | "ReorderVSM": { 129 | "type": "integer", 130 | "enum": [ 131 | { 132 | "code": 0, 133 | "description": "None" 134 | }, 135 | { 136 | "code": 1, 137 | "description": "First" 138 | }, 139 | { 140 | "code": 2, 141 | "description": "Last" 142 | } 143 | ], 144 | "default": 2 145 | }, 146 | "ReorderGridChildren": { 147 | "type": "boolean", 148 | "default": false 149 | }, 150 | "ReorderCanvasChildren": { 151 | "type": "boolean", 152 | "default": false 153 | }, 154 | "ReorderSetters": { 155 | "type": "integer", 156 | "enum": [ 157 | { 158 | "code": 0, 159 | "description": "None" 160 | }, 161 | { 162 | "code": 1, 163 | "description": "Property" 164 | }, 165 | { 166 | "code": 2, 167 | "description": "TargetName" 168 | }, 169 | { 170 | "code": 3, 171 | "description": "TargetNameThenProperty" 172 | } 173 | ], 174 | "default": 0 175 | }, 176 | "FormatMarkupExtension": { 177 | "type": "boolean", 178 | "default": true 179 | }, 180 | "NoNewLineMarkupExtensions": { 181 | "type": "string", 182 | "default": "x:Bind, Binding" 183 | }, 184 | "ThicknessSeparator": { 185 | "type": "integer", 186 | "enum": [ 187 | { 188 | "code": 0, 189 | "description": "None" 190 | }, 191 | { 192 | "code": 1, 193 | "description": "Space" 194 | }, 195 | { 196 | "code": 2, 197 | "description": "Comma" 198 | } 199 | ], 200 | "default": 2 201 | }, 202 | "ThicknessAttributes": { 203 | "type": "string", 204 | "default": "Margin, Padding, BorderThickness, ThumbnailClipMargin" 205 | }, 206 | "FormatOnSave": { 207 | "type": "boolean", 208 | "default": true 209 | }, 210 | "CommentPadding": { 211 | "type": "integer", 212 | "default": 2 213 | } 214 | } 215 | } -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | import hceShared from "./node_modules/@halospv3/hce.shared-config/commitlint.config" 2 | export default hceShared; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "dev-environment utilities", 3 | "scripts": { 4 | "prepare": "husky install && dotnet tool restore", 5 | "format": "dotnet format && npm run xstyler", 6 | "release": "npx semantic-release", 7 | "semantic-release": "npx semantic-release", 8 | "test": "dotnet test", 9 | "update-readme": "node ./tools/updateReadMe.mjs", 10 | "xstyler": "node -- ./tools/formatXamlFiles.mjs --ignore" 11 | }, 12 | "devDependencies": { 13 | "@halospv3/hce.shared-config": "^2.1.0", 14 | "@semantic-release/exec": "^6.0.3" 15 | }, 16 | "engines": { 17 | "node": ">=20.12.0", 18 | "npm": ">=10.5.0" 19 | }, 20 | "engineStrict": true, 21 | "private": true 22 | } 23 | -------------------------------------------------------------------------------- /tools/formatXamlFiles.mjs: -------------------------------------------------------------------------------- 1 | import { exec } from "node:child_process"; 2 | import * as fs from "node:fs/promises" 3 | import path, { dirname } from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | 6 | process.chdir(dirname(fileURLToPath(import.meta.url)) + '/..'); 7 | 8 | const projNames = ["GroupBox.Avalonia", "GroupBox.Avalonia.Sample"]; 9 | const pwd = process.cwd(); 10 | Promise.all(projNames 11 | .map(async (projName) => 12 | (await fs.readdir(projName, { recursive: true })) 13 | .map(relativeFilePath => 14 | path.normalize(path.join(pwd, projName, relativeFilePath)) 15 | ) 16 | )).then( 17 | function (v) { 18 | // must be comma-separated! 19 | const axamlFiles = v.flat().filter(file => file.endsWith('.axaml')).join(','); 20 | const args = ['xstyler', '-c', './Settings.XamlStyler', '-f', `"${axamlFiles}"`, ...process.argv.slice(2)]; 21 | const xstyler = exec(`dotnet ${args.join(' ')}`); 22 | xstyler.stdout.on('data', chunk => process.stdout.write(chunk)); 23 | xstyler.stderr.on('data', chunk => process.stderr.write(chunk)); 24 | } 25 | ) 26 | -------------------------------------------------------------------------------- /tools/updateReadMe.mjs: -------------------------------------------------------------------------------- 1 | import { execFileSync } from "child_process"; 2 | import { readFileSync, writeFileSync } from 'fs' 3 | import { dirname, join } from 'path'; 4 | import { fileURLToPath } from 'url'; 5 | 6 | // this file's directory 7 | const __dirname = dirname(fileURLToPath(import.meta.url)); 8 | 9 | function updateVersionInAvaloniaBadge() { 10 | 11 | const dotnet = "dotnet"; 12 | const args = ['msbuild', './GroupBox.Avalonia/GroupBox.Avalonia.csproj', '-getItem:PackageReference']; 13 | const subprocess = execFileSync(dotnet, args, { encoding: 'utf8', stdio: "pipe", }); 14 | 15 | const out = subprocess; 16 | // console.log('dotnet output: ' + out); 17 | // console.log(JSON.parse(out)); 18 | const avaloniaVersion = JSON.parse(out).Items.PackageReference.find(v => v.Identity === 'Avalonia').Version.toString(); 19 | const pattern = RegExp('(?<=\\[!\\[avalonia]\\(https:\\/\\/img\\.shields\\.io\\/badge\\/avalonia-v)\\d+\\.\\d+\\.\\d+'); 20 | const readmePath = join(dirname(__dirname), "README.md"); 21 | const readMeBuffer = readFileSync(readmePath, { encoding: 'utf8' }); 22 | //console.log(readMeBuffer); 23 | //console.log(readMeBuffer.replace(pattern, avaloniaVersion)); 24 | writeFileSync(readmePath, readMeBuffer.replace(pattern, avaloniaVersion), { encoding: "utf8" }); 25 | } 26 | 27 | updateVersionInAvaloniaBadge(); --------------------------------------------------------------------------------