feat: redesign Pump page with Fluent card layout, bottom snackbar, and RPM chart

- Replace sub-nav + HiddenTabsTabControl with 3-column Fluent card layout:
  PumpCommandsCard (vertical ME/FBKW/PreIn sliders) + DfiCalibrationCard /
  PumpLiveDataCard (KPI tiles + RPM rolling chart + redesigned status bytes) /
  PumpIdentificationCard + DtcCard
- Add PumpTopStripView: pump selector, model badge, CAN + K-Line chips
- Move immobilizer unlock to MainWindow bottom snackbar (UnlockSnackbarView):
  auto-close on success after 3 s, persist on failure with manual Dismiss
- Redesign StatusDisplayView to 2×8 rounded 28px tiles with bit index + tooltip
- Add NullToVisibilityConverter; add SnackbarShell, PumpCard, and related styles
- Delete obsolete views: UnlockProgressDialog, UnlockPanelView,
  PumpIdentificationPanelView, PumpLiveDataView, DfiManageView,
  DtcListView, PumpControlView
- PumpPageViewModel: remove PumpSubPage enum, add RpmChart wired to Root.PumpRpm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 14:03:47 +02:00
parent 197e9d1775
commit 70be693116
37 changed files with 1356 additions and 1317 deletions

View File

@@ -1,138 +0,0 @@
<Window x:Class="HC_APTBS.Views.Dialogs.UnlockProgressDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="{DynamicResource Dialog.Unlock.Title}"
Height="360" Width="340"
WindowStyle="None" ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Topmost="True"
Background="#FF2B2929"
MouseLeftButtonDown="OnMouseDrag"
Closing="OnWindowClosing">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="210"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Unlock type label -->
<TextBlock Grid.Row="0"
Text="{Binding UnlockTypeLabel, Mode=OneWay}"
FontSize="14" Foreground="#AAAAAA"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<!-- Progress ring area -->
<Grid Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- Background ring -->
<Ellipse Width="200" Height="200"
Stroke="#4D4D4D" StrokeThickness="10"
Fill="Transparent"/>
<!-- Content inside ring -->
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{DynamicResource Dialog.Unlock.Progress}"
FontSize="12" Foreground="#888888"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<!-- Percentage -->
<TextBlock FontSize="60" FontFamily="Courier New"
Foreground="White" HorizontalAlignment="Center">
<TextBlock.Text>
<Binding Path="Progress" Mode="OneWay"
StringFormat="{}{0}%"/>
</TextBlock.Text>
</TextBlock>
<!-- Elapsed time -->
<TextBlock Text="{Binding ElapsedTime, Mode=OneWay}"
FontSize="16" FontFamily="Courier New"
Foreground="#CCCCCC" HorizontalAlignment="Center"
Margin="0,2,0,0"/>
</StackPanel>
</Grid>
<!-- Phase text -->
<TextBlock Grid.Row="2"
Text="{Binding PhaseText, Mode=OneWay}"
FontSize="16" Foreground="White"
HorizontalAlignment="Center" Margin="0,4,0,6"/>
<!-- Progress bar -->
<ProgressBar Grid.Row="3"
Value="{Binding Progress, Mode=OneWay}"
Minimum="0" Maximum="100"
Height="12" Margin="8,0"
Foreground="#00EC00" Background="#3D3D3D"/>
<!-- Result text — overlays the spacer row so it never displaces buttons -->
<TextBlock Grid.Row="4"
Text="{Binding ResultText, Mode=OneWay}"
FontSize="22" FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center"
Visibility="{Binding IsComplete, Converter={StaticResource BoolToVis}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#FF5858"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuccess}" Value="True">
<Setter Property="Foreground" Value="#00EC00"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- Buttons -->
<StackPanel Grid.Row="5" Orientation="Horizontal"
HorizontalAlignment="Center" Margin="0,8,0,0">
<!-- Cancel button -->
<Button Content="{DynamicResource Common.Cancel}" Width="90" Height="30"
Margin="0,0,12,0"
Command="{Binding CancelCommand}"
Foreground="White" FontWeight="Bold">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#FF5858"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#4D4D4D"/>
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<!-- Close button -->
<Button Content="{DynamicResource Common.Close}" Width="90" Height="30"
Command="{Binding CloseCommand}"
Foreground="White" FontWeight="Bold">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#4D4D4D"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Background" Value="#337AB7"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
</Window>

View File

@@ -1,49 +0,0 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using HC_APTBS.ViewModels.Dialogs;
namespace HC_APTBS.Views.Dialogs
{
/// <summary>
/// Non-modal window showing immobilizer unlock progress.
/// Prevents user-initiated closing until the unlock sequence completes;
/// programmatic close via <see cref="ForceClose"/> always succeeds.
/// Equivalent to the old <c>WUnlocker</c> window.
/// </summary>
public partial class UnlockProgressDialog : Window
{
private bool _forceClose;
/// <summary>Creates the dialog and wires the ViewModel.</summary>
public UnlockProgressDialog(UnlockProgressViewModel vm)
{
InitializeComponent();
DataContext = vm;
vm.RequestClose += ForceClose;
}
/// <summary>Closes the window unconditionally (bypasses the completion guard).</summary>
public void ForceClose()
{
_forceClose = true;
Close();
}
/// <summary>Allows dragging the borderless window by clicking anywhere.</summary>
private void OnMouseDrag(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
DragMove();
}
/// <summary>Prevents user-initiated closing while the unlock sequence is still running.</summary>
private void OnWindowClosing(object? sender, CancelEventArgs e)
{
if (_forceClose) return;
if (DataContext is UnlockProgressViewModel vm && !vm.IsComplete)
e.Cancel = true;
}
}
}