Skip to content

Commit ded6d9f

Browse files
Merge pull request #73 from OpenAstroTech/remove-metro
Remove MahApps.Metro, add custom theme engine (V1.2.0)
2 parents 17821b3 + a6acd6b commit ded6d9f

105 files changed

Lines changed: 20190 additions & 8914 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(powershell -ExecutionPolicy Bypass -File \"c:\\\\Users\\\\Lutz.KRETZSCHMAR\\\\Source\\\\OpenAstroTracker-Desktop\\\\fix_novas.ps1\")",
5+
"Bash(powershell -ExecutionPolicy Bypass -File \"c:\\\\Users\\\\Lutz.KRETZSCHMAR\\\\Source\\\\OpenAstroTracker-Desktop\\\\fix_connect2.ps1\")"
6+
]
7+
}
8+
}

CLAUDE.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Requires .NET Framework 4.0 targeting pack and ASCOM Platform 6.5+. Output: `ASC
2222
nuget restore OATControl/OATControl.sln
2323
msbuild OATControl/OATControl.sln /p:Configuration=Debug /p:Platform="Any CPU"
2424
```
25-
Requires .NET Framework 4.7.2. Includes OATCommunications, OATCommunications.WPF, OATCommunications.ASCOM, and OATTest projects.
25+
Requires .NET Framework 4.7.2. Includes OATCommunications, OATCommunications.WPF, OATCommunications.ASCOM, and OATTest projects. Current version is 1.2.0.0. Must be built in Visual Studio on Windows (not available in WSL).
2626

2727
### OATSimulation (`OATSimulation/OATSimulation.sln`)
2828
```bash
@@ -42,7 +42,7 @@ No unit test projects. Validation uses:
4242

4343
```
4444
Client Applications
45-
├── OATControl (WPF) — Main mount control UI, MahApps.Metro
45+
├── OATControl (WPF) — Main mount control UI, custom theming
4646
├── OATTest (WPF) — Protocol test harness
4747
├── OATSimulation (WPF) — 3D mount visualization (SharpDX)
4848
└── ASCOM.Driver (WinForms) — ASCOM telescope/focuser driver
@@ -87,6 +87,36 @@ Response types: `NoResponse`, `DigitResponse` (single char), `FullResponse` (`#`
8787
- `ASCOM.Driver/OpenAstroTracker/SharedResources.cs` — Profile management, logging, connection init
8888
- `OATControl/ViewModels/MountVM.cs` — Main OATControl application logic
8989

90+
## Theming System
91+
92+
Custom theme engine (MahApps.Metro fully removed). Supports runtime switching, user themes, hot-reload, and a built-in theme editor. The `AppPrimaryColor`/`AppPrimaryBrush` keys have been removed — all references migrated to semantic keys (`AppForegroundBrush`, `AppButtonBorderBrush`, `AppButtonHoverBrush`, etc.).
93+
94+
### Theme file structure
95+
- Theme XAML files contain **only `Color` resources** (no brushes)
96+
- `SolidColorBrush` resources are generated at runtime by `ThemeManager.GenerateBrushes`
97+
- `ThemeColorDefinitions.cs` defines all color keys, display names, groups, and defaults
98+
- Brush key naming: `AppXxxColor``AppXxxBrush` (via `BrushKeyFromColorKey`)
99+
100+
### Key files
101+
- `OATControl/Theming/ThemeManager.cs` — Runtime theme loading, brush generation, user theme CRUD
102+
- `OATControl/Theming/ThemeColorDefinitions.cs` — Color key registry (used by editor, validation, import/export)
103+
- `OATControl/Resources/Themes/Base.xaml` — Implicit control styles (templates, triggers using DynamicResource)
104+
- `OATControl/Resources/Themes/DarkAstronomy.xaml` — Dark theme (colors only)
105+
- `OATControl/Resources/Themes/Daylight.xaml` — Light theme (colors only)
106+
- `OATControl/DlgThemeEditor.xaml/.cs` — Theme editor dialog (color picker, live preview, save/export)
107+
108+
### User themes
109+
- Stored as XAML in `%AppData%\OpenAstroTracker\Themes\`
110+
- Filename is file-safe (non-alphanumeric → `_`), metadata `ThemeName` stores display name
111+
- ThemeManager scans user folder on startup; validates files contain at least one known color key
112+
113+
### DlgThemeEditor notes
114+
- Editor chrome pinned to Daylight via local ResourceDictionary + GenerateBrushes in Window_Loaded
115+
- `_editingTheme` = file-safe identifier for ThemeManager; `EditingThemeName` = display name only
116+
- `_suppressSelectionChanged` prevents SelectionChanged handler during programmatic selection (e.g., "New Theme")
117+
- ListBox uses `ThemeListEntry` objects with `DisplayMemberPath="DisplayName"` / `SelectedValuePath="Id"`
118+
- `_updatingPicker` guards against re-entrant picker updates when color changes originate from the picker itself. `OnSelectedColorChanged` must still update `HexColorBox.Text` even when `_updatingPicker` is true, since the hex display is not bound to the model
119+
90120
## CI/CD
91121

92122
Azure Pipelines (`ASCOM.Driver/azure-pipelines.yml`) triggers on `master`. Builds ASCOM driver in Release, generates InnoSetup installer, publishes artifacts.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(git diff *)",
5+
"Bash(grep -v \"Designer\\\\.cs$\")"
6+
]
7+
}
8+
}

OATControl/.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ root = true
88

99
# Indentation and spacing
1010
indent_size = 4
11-
indent_style = tab
11+
indent_style = space
1212
tab_width = 4
1313

1414
# New line preferences

OATControl/App.xaml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@
66
<Application.Resources>
77
<ResourceDictionary>
88
<ResourceDictionary.MergedDictionaries>
9-
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
10-
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
11-
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
12-
<!-- Accent and AppTheme setting -->
13-
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Red.xaml" />
14-
<ResourceDictionary Source="pack://application:,,,/;component/Resources/RedControls.xaml" />
9+
<ResourceDictionary Source="pack://application:,,,/OATControl;component/Resources/Themes/Base.xaml" />
10+
<ResourceDictionary Source="pack://application:,,,/OATControl;component/Controls/IconButton.xaml" />
1511
</ResourceDictionary.MergedDictionaries>
1612
<DrawingImage x:Key="WaitingIcon">
1713
<DrawingImage.Drawing>
18-
<GeometryDrawing Brush="Red">
14+
<GeometryDrawing Brush="{DynamicResource AppForegroundBrush}">
1915
<GeometryDrawing.Geometry>
2016
<Geometry>M0.60101273,29.959071C0.60101268,29.959071,0.60726115,29.959071,0.6150718,29.959071L0.6297169,29.959071 0.63850358,29.959071 0.64318994,29.959071 0.64494708,29.959071 17.834004,29.959071C18.334981,29.959071,18.536,29.959071,18.536,30.259074L18.536,31.562087C18.536,31.962081,18.334981,31.962081,17.834004,31.962081L0.70098832,31.962081C0.20001191,32.063081,-1.7566754E-07,31.962081,1.1368684E-13,31.662078L1.1368684E-13,30.359081C-1.7566754E-07,29.959071,0.20001191,29.959071,0.60101273,29.959071z M9.2179812,17.634044L8.5159864,17.935056C8.4160108,17.935056,6.4119834,18.837051,6.3120078,21.642049L9.2179812,21.642049 12.122978,21.642049C12.023003,18.837051,10.219995,18.035047,10.019007,17.935056z M3.5060097,5.8110187C3.4060036,6.9140179 3.5060097,8.3160298 4.006986,9.6190264 4.6090058,11.322033 6.0109824,12.62503 8.0150098,13.526034L9.4179935,14.12804 10.821008,13.526034C12.825004,12.62503 14.127005,11.322033 14.829001,9.6190264 15.329977,8.3160298 15.329977,6.9140179 15.329977,5.8110187L9.5179996,5.8110187z M1.302001,3.407006L9.2179812,3.407006 17.132985,3.407006C17.132985,3.4070058,20.138995,11.422039,11.42199,14.428044L11.321984,17.434047C11.321984,17.434047,18.134999,19.438051,17.43398,26.652072L17.232991,28.055074 9.1169986,28.055074 1.0020132,28.055074 0.80099442,26.652072C0.10000587,19.438051,6.91299,17.434047,6.91299,17.434047L6.8129839,14.428044C-1.7040091,11.422039,1.302001,3.4070058,1.302001,3.407006z M0.70098832,0L17.834004,0C18.334981,0,18.536,0.10000634,18.536,0.40100195L18.536,1.8040053C18.536,2.2040152,18.334981,2.1040089,17.834004,2.1040087L0.70098832,2.1040087C0.20001191,2.1040089,-1.7566754E-07,2.2040152,1.1368684E-13,1.8040053L1.1368684E-13,0.40100195C-1.7566754E-07,0.10000634,0.20001191,0,0.70098832,0z</Geometry>
2117
</GeometryDrawing.Geometry>
@@ -24,7 +20,7 @@
2420
</DrawingImage>
2521
<DrawingImage x:Key="CompleteIcon">
2622
<DrawingImage.Drawing>
27-
<GeometryDrawing Brush="Red">
23+
<GeometryDrawing Brush="{DynamicResource AppForegroundBrush}">
2824
<GeometryDrawing.Geometry>
2925
<Geometry>M29.403992,0L32,3.5860286 8.3720093,21.479001 5.7740173,17.895017 5.776001,17.893002 0,9.9110087 3.5079956,7.2570179 9.2829895,15.23602z</Geometry>
3026
</GeometryDrawing.Geometry>
@@ -33,7 +29,7 @@
3329
</DrawingImage>
3430
<DrawingImage x:Key="InProgressIcon">
3531
<DrawingImage.Drawing>
36-
<GeometryDrawing Brush="Red">
32+
<GeometryDrawing Brush="{DynamicResource AppForegroundBrush}">
3733
<GeometryDrawing.Geometry>
3834
<Geometry>M2,3.8790137L2,26.424095 17.708,15.066054z M12.441894,1.1261289L31.999938,15.054149 12.447936,29.192131 11.275939,27.571153 28.569945,15.066112 11.281921,2.75504z M0,0L21.138,15.054054 0,30.33811z</Geometry>
3935
</GeometryDrawing.Geometry>

OATControl/App.xaml.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MahApps.Metro;
1+
using OATControl.Theming;
22
using System;
33
using System.Windows;
44
using System.Threading;
@@ -47,18 +47,13 @@ protected override void OnStartup(StartupEventArgs e)
4747
Log.EnableLogging();
4848
}
4949

50-
ThemeManager.AddAccent("RedAccent", new Uri("pack://application:,,,/OATControl;component/Resources/RedAccent.xaml"));
51-
ThemeManager.AddAppTheme("RedTheme", new Uri("pack://application:,,,/OATControl;component/Resources/RedTheme.xaml"));
52-
ThemeManager.AddAccent("RedControls", new Uri("pack://application:,,,/OATControl;component/Resources/RedControls.xaml"));
53-
54-
// get the current app style (theme and accent) from the application
55-
// you can then use the current theme and custom accent instead set a new theme
56-
Tuple<AppTheme, Accent> appStyle = ThemeManager.DetectAppStyle(Application.Current);
57-
58-
// now set the Green accent and dark theme
59-
ThemeManager.ChangeAppStyle(Application.Current,
60-
ThemeManager.GetAccent("RedAccent"),
61-
ThemeManager.GetAppTheme("RedTheme"));
50+
AppSettings.Instance.Load();
51+
var savedTheme = AppSettings.Instance.ThemeName;
52+
#if DEBUG
53+
ThemeManager.Instance.HotReloadEnabled = true;
54+
#endif
55+
ThemeManager.Instance.ScanUserThemes();
56+
ThemeManager.Instance.ApplyTheme(savedTheme);
6257

6358
base.OnStartup(e);
6459

OATControl/CHANGELOG.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Changelog
2+
3+
## V1.2.0.0
4+
5+
### MahApps.Metro Removal
6+
7+
- Removed MahApps.Metro (v1.6.5) and ControlzEx (v3.0.2.4) NuGet dependencies
8+
- Removed MahApps.Metro, ControlzEx, and System.Windows.Interactivity assembly references from .csproj
9+
- Removed MahApps.Metro DLLs from InnoSetup installer (`OATControl Setup.iss`)
10+
- Deleted legacy theme resource files: `RedAccent.xaml`, `RedControls.xaml`, `RedTheme.xaml`, `GreyControls.xaml`
11+
12+
### Custom Theme Engine
13+
14+
- Created `ThemeManager` singleton (`Theming/ThemeManager.cs`, +377 lines) for runtime theme loading, brush generation, and theme switching
15+
- Created `ThemeColorDefinitions.cs` — centralized registry of all color keys with display names, groups, and default values
16+
- `ThemeManager.GenerateBrushes` creates `SolidColorBrush` resources at runtime from theme `Color` resources; also generates `SystemColors` override brushes (`HighlightTextBrushKey`, `ControlTextBrushKey`, `InactiveSelectionHighlightBrushKey`)
17+
- Brush key naming convention: `AppXxxColor` (Color resource) → `AppXxxBrush` (generated SolidColorBrush)
18+
- User themes stored as XAML in `%AppData%\OpenAstroTracker\Themes\`; scanned and validated on startup
19+
- Theme selection persisted in `AppSettings.ThemeName` (defaults to DarkAstronomy)
20+
- Theme picker added to `DlgAppSettings` General tab with live switching
21+
- Added `ThemeManager.ImportTheme`/`ExportTheme`/`DeleteTheme` for user theme file management
22+
23+
### Theme Files
24+
25+
- `Resources/Themes/Base.xaml` (+652 lines) — implicit styles for all standard WPF controls: TextBlock, TextBox, Button, CheckBox, RadioButton, ComboBox, TabControl, TabItem, ScrollBar, ProgressBar, ListViewItem, ToggleButton. All use `DynamicResource` for theme-aware binding.
26+
- `Resources/Themes/DarkAstronomy.xaml` — dark theme with red accent palette (49 color definitions)
27+
- `Resources/Themes/Daylight.xaml` — light theme with blue accent palette (49 color definitions)
28+
- Bundled user themes: Blue Planet, Dark Observatory, Deep Space, NINA (`Theming/*.xaml`)
29+
- Theme XAML files contain only `Color` resources; brushes generated at runtime
30+
31+
### Custom Controls
32+
33+
- **ThemedWindow** (`Controls/ThemedWindow.cs`) — `Window` subclass with `WindowChrome` (`CaptionHeight=30`, `ResizeBorderThickness=4`, `GlassFrameThickness=0`). Replaces `MetroWindow`. Provides borderless window with custom chrome.
34+
- `ShowTitleBar` property controls title bar visibility
35+
- `TitleBarButtons` collection for custom title bar buttons
36+
- `ControlTemplate` in Base.xaml with `WindowCommands` (minimize, maximize, close)
37+
- **ToggleSwitch** (`Controls/ToggleSwitch.xaml/.cs`) — replaces `MahApps.Metro.Controls.ToggleSwitchButton`. Pill-shaped toggle with sliding thumb. `IsChecked` DP with `ThumbIndicatorBrush` for theme integration.
38+
- **IconButton** (`Controls/IconButton.xaml/.cs`) — icon-only button control using `ResourceDictionary` style pattern. Replaces MahApps icon button usage.
39+
- **LabeledToggleSwitch** (`Controls/LabeledToggleSwitch.xaml/.cs`) — ToggleSwitch with label text, used in settings and theme editor.
40+
- **SlewProgressBar** (`Controls/SlewProgressBar.xaml/.cs`) — reusable progress bar for slew/drift align. Three DPs: `Progress` (double 0-1), `IsActive` (bool), `BarThickness` (double, default 6). Renders as `Border` with `LinearGradientBrush` using theme accent/disabled colors. Replaces 3 inline progress bar implementations in MainWindow.
41+
42+
### Window/Dialog Migration
43+
44+
All 14 windows/dialogs migrated from `MetroWindow` to `ThemedWindow`:
45+
- MainWindow, DlgAppSettings, DlgAxisCalibration, DlgChecklist, DlgChecklistEditor, DlgChooseOat, DlgCustomActionSetup, DlgEditPoint, DlgMessageBox, DlgNinaPolarAlignment, DlgRunPolarAlignment, DlgRunPolarAlignmentStep1, DlgSharpCapPolarAlignment, DlgWaitForGXState, MiniController, SettingsDialog, SlewPointsWindow, TargetChooser
46+
47+
Each dialog:
48+
- Replaced `MetroWindow` base with `ThemedWindow`
49+
- Removed MahApps namespace declarations
50+
- Replaced `ToggleSwitchButton` instances with `ToggleSwitch`
51+
52+
### Color Migration
53+
54+
- Converted all `StaticResource` brush references to `DynamicResource` across 12+ XAML files for runtime theme switching
55+
- Replaced all MahApps brush key references (`AccentBaseColorBrush`, `AccentColorBrush2/3/4`, `TextBrush`, `WhiteBrush`, `WindowBackgroundBrush`, `ControlBackgroundBrush`, `HighlightBrush`) with semantic theme keys (`AppForegroundBrush`, `AppPrimaryBrush`, `AppBackgroundBrush`, etc.)
56+
- Replaced ~287 hardcoded hex color values across 22+ XAML files with `DynamicResource` semantic key bindings
57+
- Removed `AppPrimaryColor`/`AppPrimaryBrush` — all references migrated to specific semantic keys (`AppForegroundBrush`, `AppButtonBorderBrush`, `AppButtonHoverBrush`, etc.)
58+
- `ScopeCircles` and `ScopePointer` — converted `Foreground` from CLR property to `DependencyProperty` for `DynamicResource` binding support
59+
- `MotorIndicator` — refactored to use theme brushes
60+
61+
### Theme Editor (DlgThemeEditor)
62+
63+
- New dialog (`DlgThemeEditor.xaml` 594 lines, `DlgThemeEditor.xaml.cs` 1048 lines) with:
64+
- Live preview as mini app window with real themed controls
65+
- HSL color picker with interactive saturation/lightness square and hue bar
66+
- RGB and HSL slider inputs with bidirectional sync
67+
- Hex color textbox (RRGGBB) with bidirectional sync to RGB/HSL sliders
68+
- Color key selection grouped by category (Text, Background, Buttons, etc.)
69+
- New Theme: clones current theme into editable user theme
70+
- Save/Save As: prompts for theme name + author, saves as user theme XAML
71+
- Import/Export: file dialogs for sharing theme files
72+
- Editor chrome pinned to Daylight theme (doesn't change when editing other themes)
73+
- `_updatingPicker` guard prevents re-entrant picker updates; `HexColorBox.Text` still updated during picker-driven changes
74+
- `_suppressSelectionChanged` prevents handler during programmatic list selection
75+
76+
### Style Consolidation
77+
78+
- Added semantic `TextBlock` styles to Base.xaml: header, subheader, body, caption styles
79+
- Unified `ListViewItem` template with consistent selection/hover behavior
80+
- Consolidated `ToggleButton` and `Button` styles into Base.xaml
81+
- Renamed button styles to semantic names
82+
- Added `MetroListBoxItem` named style for `ListBoxItem`
83+
- Added implicit `ToggleButton` style with themed hover/pressed/checked/disabled states
84+
85+
### Other Changes
86+
87+
- Removed `System.Windows.Forms` dependency; replaced with WPF-native `Microsoft.Win32` file dialogs
88+
- Removed `DlgStepCalibration.xaml/.cs` (789 lines) — unused dialog
89+
- Removed unused style resources
90+
- Fixed `MiniController` layout and styling
91+
- Cleaned up dialog chrome and removed unused files
92+
- Version bumped from 1.1.24.0 to 1.2.0.0 (`AssemblyInfo.cs`, `OATControl Setup.iss`)
93+
- `MountVM.cs` — replaced MahApps `DialogManager.ShowMessageAsync()` with `MessageBox.Show()` for DEC limits dialog
94+
- `PushButton.xaml`/`StopButton.xaml` — removed MahApps converter imports, migrated all brush references to DynamicResource
95+
- `RangeSlider.xaml/.cs` — tick color properties (`MajorTickColor`, `MinorTickColor`, `TickLabelColor`) converted from CLR properties to `DependencyProperty`s for `DynamicResource` binding
96+
- `RangeSlider.xaml` — replaced ~12 hardcoded `SolidColorBrush` resources with theme-derived bindings
97+
- `MotorIndicator.cs` — added `DisabledForeground` DP; control visually dims when mount is disconnected
98+
- `ThemedWindow.cs` — automatic `Background`/`Foreground` binding via `SetResourceReference`; tinted icon support in title bar; `OnShowTitleBarChanged` dynamically toggles `WindowChrome.CaptionHeight`
99+
- `DlgChecklist.xaml` — replaced `BoolToBrushConverter` with `DataTrigger`-based Foreground bindings for correct `DynamicResource` resolution
100+
- `App.xaml` status icons (`WaitingIcon`, `CompleteIcon`, `InProgressIcon`) changed from hardcoded `Brush="Red"` to `DynamicResource AppForegroundBrush`
101+
- `TargetChooser.xaml` — fixed out-of-theme cyan hover border by inheriting implicit `ListViewItem` style
102+
- Dialogs set `TitleBarButtons="Close"` where minimize/maximize are inappropriate (DlgAppSettings, DlgAxisCalibration, DlgChooseOat, DlgChecklistEditor, DlgMessageBox)
103+
- Chromeless windows (`ShowTitleBar=False`): DlgChecklist, MiniController, SlewPointsWindow
104+
- ThemeManager hot-reload: `FileSystemWatcher` monitors theme XAML in DEBUG builds for instant preview during development
105+
- Installer: ships bundled user themes to `%AppData%\OpenAstroTracker\Themes\` with `onlyifdoesntexist` flag

0 commit comments

Comments
 (0)