feat: developer tools page, auto-test orchestrator, BIP display, tests redesign
Bundles several feature streams that have been iterating on the working tree: - Developer Tools page (Debug-only via DEVELOPER_TOOLS symbol): hosts the identification card, manual KWP write + transaction log, ROM/EEPROM dump card with progress banner and completion message, persisted custom-commands library, persisted EEPROM passwords library. New service primitives: IKwpService.SendRawCustomAsync / ReadEepromAsync / ReadRomEepromAsync. Persistence mirrors the Clients XML pattern in two new files (custom_commands.xml, eeprom_passwords.xml). - Auto-test orchestrator (IAutoTestOrchestrator + AutoTestState): linear K-Line read -> unlock -> bench-on -> test sequence with snackbar UI and progress dialog VM, gated on dashboard alarms. - BIP-STATUS display: BipDisplayViewModel + BipDisplayView, RAM read at 0x0106 via IKwpService.ReadBipStatusAsync; status definitions in BipStatusDefinition. - Tests page redesign: TestSectionCard + PhaseTileView replacing the old TestPlanView/TestRunningView/TestDoneView/TestPreconditionsView/ TestSectionView controls and their VMs. - Pump command sliders: Fluent thick-track style with overhang thumb, click-anywhere-and-drag, mouse-wheel adjustment. - Window startup: app.manifest declares PerMonitorV2 DPI awareness, MainWindow installs a WM_GETMINMAXINFO hook in OnSourceInitialized and maximizes there (after the hook is in place) so the app fits the work area exactly on any display configuration. - Misc: PercentToPixelsConverter, seed_aliases.py one-shot pump-alias importer, tools/Import-BipStatus.ps1, kline_eeprom_spec.md and dump-functions reference docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,57 +1,11 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:conv="clr-namespace:HC_APTBS.Converters">
|
||||
|
||||
<!-- Shared converter: enum ↔ int for TabControl.SelectedIndex -->
|
||||
<conv:EnumToIntConverter x:Key="EnumToInt"/>
|
||||
|
||||
<!-- Nav rail ListBoxItem: 56px tall, left accent bar on selection -->
|
||||
<Style x:Key="NavItem" TargetType="ListBoxItem">
|
||||
<Setter Property="Height" Value="56"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListBoxItem">
|
||||
<Border x:Name="Root" Background="Transparent">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border x:Name="Accent" Grid.Column="0" Background="Transparent"/>
|
||||
<ContentPresenter Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="16,0,0,0"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Root" Property="Background" Value="#FF3A4050"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Root" Property="Background" Value="#FF404860"/>
|
||||
<Setter TargetName="Accent" Property="Background" Value="#FF2196F3"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Nav rail ListBox -->
|
||||
<Style x:Key="NavRail" TargetType="ListBox">
|
||||
<Setter Property="Background" Value="#FF2F3440"/>
|
||||
<Setter Property="Foreground" Value="#FFE6E6E6"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource NavItem}"/>
|
||||
</Style>
|
||||
|
||||
<!-- TabControl that hides its tab strip so the nav rail is the only selector -->
|
||||
<Style x:Key="HiddenTabsTabControl" TargetType="TabControl">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
@@ -72,4 +26,122 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--
|
||||
Boxy nav item template: vertical icon-over-label tile inside a rounded
|
||||
card. Replaces WPF-UI's default horizontal row layout
|
||||
(LeftCompactNavigationViewItemTemplate) when assigned to
|
||||
NavigationView.ItemTemplate.
|
||||
|
||||
Mechanism: WPF-UI's NavigationView assigns each NavigationViewItem's
|
||||
Template programmatically via UpdateMenuItemsTemplate (in
|
||||
NavigationView.Base.cs). A direct property assignment beats any
|
||||
Style.Setter, so the only reliable way to override is to provide a
|
||||
keyed ControlTemplate and assign it to NavigationView.ItemTemplate
|
||||
from MainWindow.xaml.
|
||||
|
||||
Active = current page. Per user request, the active state is a muted
|
||||
grey instead of a bright accent fill, signalling "you are here / not a
|
||||
clickable destination right now". Hover and pressed states still
|
||||
layer a transparent darkening overlay on top.
|
||||
|
||||
Triggers on IsActive (WPF-UI's selected-state DP on
|
||||
NavigationViewItemBase), not IsSelected.
|
||||
-->
|
||||
<ControlTemplate x:Key="BoxyNavItemTemplate"
|
||||
TargetType="{x:Type ui:NavigationViewItem}">
|
||||
<!-- Asymmetric horizontal margin compensates for the 4px scrollbar
|
||||
gutter that WPF-UI's NavigationView reserves on the right
|
||||
(DynamicScrollViewer Padding="0,0,4,0"); this lands the visible
|
||||
item edges at 10px from both the left and right of the pane. -->
|
||||
<Grid Margin="6,3" MinHeight="80">
|
||||
<!-- Background lives in Border.Style (level-8 style setter), NOT
|
||||
as a local-value attribute (level 3), so the IsActive
|
||||
template trigger below (level 7) can actually override it.
|
||||
Setting Background="{DynamicResource ...}" as an attribute
|
||||
here would silently block the trigger — same precedence
|
||||
trap as Background="Transparent" on HoverOverlay. -->
|
||||
<Border x:Name="Root"
|
||||
CornerRadius="8"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource ControlFillColorDefaultBrush}"/>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
</Border>
|
||||
<Grid Margin="6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Icon: hosts the ui:SymbolIcon assigned via
|
||||
NavigationViewItem.Icon. TextElement inheritance
|
||||
propagates FontSize/Foreground into the SymbolIcon. -->
|
||||
<ContentControl x:Name="IconHost"
|
||||
Grid.Row="0"
|
||||
Content="{TemplateBinding Icon}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Focusable="False"
|
||||
IsTabStop="False"
|
||||
TextElement.FontSize="32"
|
||||
TextElement.Foreground="{DynamicResource TextFillColorPrimaryBrush}"/>
|
||||
<ContentPresenter x:Name="LabelHost"
|
||||
Grid.Row="1"
|
||||
Content="{TemplateBinding Content}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,4,0,0"
|
||||
TextBlock.FontFamily="{DynamicResource ContentControlThemeFontFamily}"
|
||||
TextBlock.FontSize="12"
|
||||
TextBlock.FontWeight="SemiBold"
|
||||
TextBlock.Foreground="{DynamicResource TextFillColorPrimaryBrush}"/>
|
||||
</Grid>
|
||||
<!-- Overlay darkens on hover/press/active without disturbing the
|
||||
Root background colour. Background is intentionally LEFT
|
||||
UNSET — assigning Background="Transparent" here would be a
|
||||
local value, which beats template-trigger setters in WPF
|
||||
property precedence and would silently block the
|
||||
IsMouseOver/IsPressed/IsActive triggers below from changing
|
||||
the colour. With Background unset, those triggers drive the
|
||||
overlay. -->
|
||||
<Border x:Name="HoverOverlay"
|
||||
CornerRadius="8"
|
||||
IsHitTestVisible="False"/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="HoverOverlay" Property="Background" Value="#18000000"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="HoverOverlay" Property="Background" Value="#30000000"/>
|
||||
</Trigger>
|
||||
<!-- Active = current page: pressed-looking dark fill + muted
|
||||
foreground, signalling "you are here / not a destination"
|
||||
with a held-down feel.
|
||||
|
||||
IMPORTANT: we paint the dark fill onto Root directly with a
|
||||
translucent-BLACK brush (#30000000 ≈ 19% black) instead of
|
||||
going through HoverOverlay or a WPF-UI ControlFillColor*
|
||||
brush. WPF-UI's ControlFillColorSecondaryBrush is
|
||||
#80F9F9F9 (translucent WHITE) — over the white pane it
|
||||
doesn't actually darken anything. A translucent-black brush
|
||||
darkens reliably regardless of the parent colour and
|
||||
matches the IsPressed overlay strength. -->
|
||||
<Trigger Property="IsActive" Value="True">
|
||||
<Setter TargetName="Root" Property="Background" Value="#30000000"/>
|
||||
<Setter TargetName="IconHost" Property="TextElement.Foreground"
|
||||
Value="{DynamicResource TextFillColorSecondaryBrush}"/>
|
||||
<Setter TargetName="LabelHost" Property="TextBlock.Foreground"
|
||||
Value="{DynamicResource TextFillColorSecondaryBrush}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.4"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
Reference in New Issue
Block a user