New row between top bar and content showing pill-shaped status indicators for the system dependencies that PROSERVE needs (SteamVR, Vive Business Streaming process, VR headset reachable on the network, etc.). Each pill is colored : 🟢 OK, 🟠 limitation détectée (e.g. ping élevé), 🔴 KO, gris = non configuré. Hover → tooltip with full check kind, target, last RTT/process count and timestamp. Architecture - LocalConfig.HealthChecksConfig : list of HealthCheckEntry (Kind=Ping or Process, Target=IP/host or process name, ping thresholds in ms, refresh interval in s). - ISystemHealthService + SystemHealthService : ping via System.Net.NetworkInformation.Ping (no admin required), process lookup via System.Diagnostics.Process.GetProcessesByName. Returns HealthResult { Severity, Detail }. - HealthIndicatorViewModel : observable wrapper per entry with Severity-driven Brushes (background pill, border, icon colour) and composed Tooltip text. - MainViewModel.InitHealthIndicators + StartHealthLoop : populates ObservableCollection<HealthIndicatorViewModel> from config and runs parallel checks every RefreshIntervalSeconds (default 10s). - MainWindow.xaml : new Row 1 (between top bar and body) hosting an ItemsControl bound to HealthIndicators. Row collapses cleanly if the list is empty (HasHealthIndicators=false). Defaults shipped (all editable in %LocalAppData%\PSLauncher\config.json) - 🎮 SteamVR (Process : vrserver) - 📡 Vive Business Streaming (Process : HtcConnectionUtility) - 🥽 Casque VR (Ping : empty, user fills in the headset IP) The Settings UI editor for managing the list is intentionally deferred to a future iteration — config.json edit is enough to start using it. Versions bumped to 0.21.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
722 lines
46 KiB
XML
722 lines
46 KiB
XML
<Window x:Class="PSLauncher.App.Views.MainWindow"
|
||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
xmlns:vm="clr-namespace:PSLauncher.App.ViewModels"
|
||
xmlns:loc="clr-namespace:PSLauncher.Core.Localization;assembly=PSLauncher.Core"
|
||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
||
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
|
||
Title="PROSERVE Launcher"
|
||
Icon="pack://application:,,,/Resources/favicon.ico"
|
||
Background="Black"
|
||
Width="1280" Height="720"
|
||
MinWidth="980" MinHeight="600"
|
||
WindowStartupLocation="CenterScreen"
|
||
WindowStyle="None"
|
||
ResizeMode="CanResize"
|
||
AllowsTransparency="False"
|
||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
|
||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||
mc:Ignorable="d"
|
||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
|
||
|
||
<shell:WindowChrome.WindowChrome>
|
||
<shell:WindowChrome CaptionHeight="56"
|
||
GlassFrameThickness="0"
|
||
CornerRadius="0"
|
||
ResizeBorderThickness="6"
|
||
UseAeroCaptionButtons="False" />
|
||
</shell:WindowChrome.WindowChrome>
|
||
|
||
<Window.Resources>
|
||
|
||
<!-- =================== TEMPLATE : LIGNE COMPACTE (autres versions) =================== -->
|
||
<DataTemplate x:Key="CompactRowTemplate" DataType="{x:Type vm:VersionRowViewModel}">
|
||
<Border Margin="0,0,0,8" CornerRadius="6" BorderThickness="1">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Bg.Card}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Border}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsRemoteOnly}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.AvailableBg}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding IsBusy}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.BusyBg}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="4" /> <!-- accent strip -->
|
||
<ColumnDefinition Width="*" />
|
||
<ColumnDefinition Width="Auto" />
|
||
<ColumnDefinition Width="Auto" />
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<!-- Strip latérale colorée : Border avec coins arrondis à gauche
|
||
pour suivre la rondeur (CornerRadius=6) de la card parent. -->
|
||
<Border Grid.Column="0" CornerRadius="5,0,0,5">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Installed}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsRemoteOnly}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding IsBusy}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
</Border>
|
||
|
||
<Grid Grid.Column="1" Margin="16,12">
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto" />
|
||
<RowDefinition Height="Auto" />
|
||
</Grid.RowDefinitions>
|
||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||
<TextBlock Text="{Binding Version, StringFormat='v{0}'}"
|
||
FontSize="15" FontWeight="SemiBold"
|
||
VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.Text.Primary}" />
|
||
<Border CornerRadius="10" Padding="8,3" Margin="12,0,0,0"
|
||
VerticalAlignment="Center">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Installed}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsRemoteOnly}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding IsBusy}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
<TextBlock Text="{Binding StateLabel}" FontSize="11" FontWeight="SemiBold"
|
||
Foreground="White" />
|
||
</Border>
|
||
</StackPanel>
|
||
<TextBlock Grid.Row="1" FontSize="11"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
Margin="0,4,0,0">
|
||
<Run Text="{Binding SizeDisplay, Mode=OneWay}" />
|
||
<Run Text=" • " />
|
||
<Run Text="{Binding PrimaryDate, Mode=OneWay}" />
|
||
</TextBlock>
|
||
</Grid>
|
||
|
||
<!-- Action button -->
|
||
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center" Margin="12,0,0,0">
|
||
<Button Style="{StaticResource PrimaryButton}"
|
||
Content="{x:Static loc:Strings.ActionLaunch}" Padding="22,8" FontSize="13"
|
||
Command="{Binding LaunchCommand}"
|
||
Visibility="{Binding IsInstalled, Converter={StaticResource BoolToVisibility}}" />
|
||
<Button Style="{StaticResource AccentButton}"
|
||
Content="{Binding InstallButtonLabel}" Padding="22,8" FontSize="13"
|
||
Command="{Binding InstallCommand}"
|
||
Visibility="{Binding IsRemoteOnly, Converter={StaticResource BoolToVisibility}}" />
|
||
<!-- Bouton rouge « Annuler » : visible uniquement quand on a un partial.
|
||
Permet d'abandonner le partial et repartir de zéro (avec confirmation). -->
|
||
<Button Style="{StaticResource DangerButton}"
|
||
Content="{x:Static loc:Strings.ActionCancel}"
|
||
Padding="14,8" FontSize="13" Margin="6,0,0,0"
|
||
Command="{Binding RestartFromZeroCommand}"
|
||
ToolTip="{x:Static loc:Strings.MenuRestartFromZero}"
|
||
Visibility="{Binding ShowRestartFromZero, Converter={StaticResource BoolToVisibility}}" />
|
||
<StackPanel Orientation="Horizontal"
|
||
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVisibility}}">
|
||
<ProgressBar Width="140" Height="6"
|
||
Value="{Binding ProgressPercent}" Maximum="100"
|
||
Foreground="{StaticResource Brush.Status.Busy}"
|
||
Background="#2A2A30" BorderThickness="0"
|
||
VerticalAlignment="Center" />
|
||
<TextBlock Text="{Binding ProgressPercent, StringFormat={}{0:0}%}"
|
||
FontSize="11" Margin="8,0,0,0" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.Text.Secondary}" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<!-- ... menu -->
|
||
<Button Grid.Column="3"
|
||
Style="{StaticResource IconButton}"
|
||
Content="⋯" FontSize="18" FontWeight="Bold"
|
||
Margin="8,0,8,0" VerticalAlignment="Center"
|
||
ToolTip="Plus d'actions"
|
||
Tag="{Binding}"
|
||
Click="OnMoreMenuClick">
|
||
<Button.ContextMenu>
|
||
<ContextMenu>
|
||
<MenuItem Header="{x:Static loc:Strings.MenuReleaseNotes}"
|
||
Command="{Binding PlacementTarget.Tag.ShowReleaseNotesCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuOpenFolder}"
|
||
Command="{Binding PlacementTarget.Tag.OpenFolderCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuRestartFromZero}"
|
||
Command="{Binding PlacementTarget.Tag.RestartFromZeroCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.HasResumableDownload, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<Separator Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuUninstall}"
|
||
Command="{Binding PlacementTarget.Tag.UninstallCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
</ContextMenu>
|
||
</Button.ContextMenu>
|
||
</Button>
|
||
</Grid>
|
||
</Border>
|
||
</DataTemplate>
|
||
</Window.Resources>
|
||
|
||
<!--
|
||
Layout principal — grille 2 colonnes × 3 rangées :
|
||
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Top bar (ColSpan 2) │ Row 0
|
||
├──────────┬──────────────────────────────────────────────────┤
|
||
│ │ │
|
||
│ Sidebar │ Content area │ Row 1
|
||
│ (Library │ (Library / Report / Doc) │
|
||
│ Report │ │
|
||
│ Doc) ├──────────────────────────────────────────────────┤
|
||
│ RowSpan2 │ Footer (DL/install) │ Row 2
|
||
│ │ visible only on Library │
|
||
└──────────┴──────────────────────────────────────────────────┘
|
||
200px *
|
||
|
||
La sidebar a RowSpan=2 → s'étend du sous-topbar jusqu'au bas
|
||
de fenêtre. Le footer est confiné à la colonne content.
|
||
-->
|
||
<Grid Background="{StaticResource Brush.Bg.Window}">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="200" />
|
||
<ColumnDefinition Width="*" />
|
||
</Grid.ColumnDefinitions>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto" /> <!-- Top bar -->
|
||
<RowDefinition Height="Auto" /> <!-- Health banner -->
|
||
<RowDefinition Height="*" /> <!-- Body (sidebar + content) -->
|
||
<RowDefinition Height="Auto" /> <!-- Footer -->
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Background image : limité à la zone content (Row 2, Col 1) pour ne pas
|
||
passer derrière la sidebar, le banner ou le footer. -->
|
||
<Rectangle Grid.Row="2" Grid.Column="1" IsHitTestVisible="False">
|
||
<Rectangle.Fill>
|
||
<ImageBrush ImageSource="pack://application:,,,/Resources/Background.png"
|
||
Stretch="UniformToFill"
|
||
AlignmentX="Right"
|
||
AlignmentY="Bottom"
|
||
Opacity="0.25" />
|
||
</Rectangle.Fill>
|
||
</Rectangle>
|
||
<Rectangle Grid.Row="2" Grid.Column="1"
|
||
Fill="{StaticResource Brush.Bg.BlueTint}"
|
||
IsHitTestVisible="False" />
|
||
|
||
<!-- Top bar : 3 colonnes (brand / license center / chrome).
|
||
Grid.ColumnSpan=2 → s'étend sur sidebar + content. -->
|
||
<Border Grid.Row="0" Grid.ColumnSpan="2" Background="{StaticResource Brush.Bg.Sidebar}"
|
||
BorderBrush="{StaticResource Brush.Border}" BorderThickness="0,0,0,1"
|
||
Padding="20,12">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*" /> <!-- brand -->
|
||
<ColumnDefinition Width="*" /> <!-- license (center) -->
|
||
<ColumnDefinition Width="*" /> <!-- chrome (right) -->
|
||
</Grid.ColumnDefinitions>
|
||
<!-- Col 1 : brand -->
|
||
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
|
||
<Image Source="pack://application:,,,/Resources/logo-asterion-white.png"
|
||
Height="36"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,14,0"
|
||
SnapsToDevicePixels="True"
|
||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||
<TextBlock Text="PROSERVE"
|
||
FontFamily="{StaticResource Font.Brand}"
|
||
FontSize="22"
|
||
Foreground="{StaticResource Brush.Text.Primary}"
|
||
VerticalAlignment="Center" />
|
||
<TextBlock Text="Launcher"
|
||
FontSize="20"
|
||
FontWeight="Bold"
|
||
Foreground="{StaticResource Brush.Text.Primary}"
|
||
VerticalAlignment="Center"
|
||
Margin="14,0,0,0" />
|
||
</StackPanel>
|
||
|
||
<!-- Col 2 : badge license cliquable, centré -->
|
||
<Border Grid.Column="1"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
Cursor="Hand"
|
||
CornerRadius="16" Padding="14,6"
|
||
shell:WindowChrome.IsHitTestVisibleInChrome="True"
|
||
ToolTip="{x:Static loc:Strings.LicenseTooltip}">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="#2A1414" />
|
||
<Setter Property="BorderBrush" Value="#EF4444" />
|
||
<Setter Property="BorderThickness" Value="1" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Ok">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.InstalledBg}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Installed}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Warning">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.BusyBg}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
<Border.InputBindings>
|
||
<MouseBinding Gesture="LeftClick" Command="{Binding OpenLicenseCommand}" />
|
||
</Border.InputBindings>
|
||
<StackPanel Orientation="Horizontal">
|
||
<TextBlock Text="{Binding LicenseIcon}"
|
||
FontSize="15" FontWeight="Bold"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,8,0">
|
||
<TextBlock.Style>
|
||
<Style TargetType="TextBlock">
|
||
<Setter Property="Foreground" Value="#EF4444" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Ok">
|
||
<Setter Property="Foreground" Value="{StaticResource Brush.Status.Installed}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Warning">
|
||
<Setter Property="Foreground" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</TextBlock.Style>
|
||
</TextBlock>
|
||
<TextBlock Text="{Binding LicenseSummary}"
|
||
FontSize="13" FontWeight="SemiBold"
|
||
VerticalAlignment="Center">
|
||
<TextBlock.Style>
|
||
<Style TargetType="TextBlock">
|
||
<Setter Property="Foreground" Value="#FCA5A5" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Ok">
|
||
<Setter Property="Foreground" Value="#86EFAC" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding LicenseSeverity}" Value="Warning">
|
||
<Setter Property="Foreground" Value="#FCD34D" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</TextBlock.Style>
|
||
</TextBlock>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<!-- Col 3 : Paramètres + window chrome -->
|
||
<StackPanel Grid.Column="2" Orientation="Horizontal"
|
||
HorizontalAlignment="Right" VerticalAlignment="Center">
|
||
<Button Style="{StaticResource SecondaryButton}"
|
||
Content="{x:Static loc:Strings.TopBarSettings}"
|
||
shell:WindowChrome.IsHitTestVisibleInChrome="True"
|
||
Command="{Binding OpenSettingsCommand}"
|
||
Margin="0,0,16,0" />
|
||
<Button Style="{StaticResource WindowControlButton}"
|
||
Content="—" ToolTip="{x:Static loc:Strings.Minimize}"
|
||
shell:WindowChrome.IsHitTestVisibleInChrome="True"
|
||
Click="OnMinimizeClick" />
|
||
<Button Style="{StaticResource WindowControlButton}"
|
||
Content="☐" ToolTip="{x:Static loc:Strings.Maximize}"
|
||
shell:WindowChrome.IsHitTestVisibleInChrome="True"
|
||
Click="OnMaxRestoreClick" />
|
||
<Button Style="{StaticResource WindowCloseButton}"
|
||
Content="✕" ToolTip="{x:Static loc:Strings.CloseTooltip}"
|
||
shell:WindowChrome.IsHitTestVisibleInChrome="True"
|
||
Click="OnCloseClick" />
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- ============== Health banner (Row 1, ColSpan 2) ==============
|
||
Pills colorées indiquant l'état des dépendances système (SteamVR,
|
||
Vive Streaming, ping casque…). Cachée si la config n'a aucun check.
|
||
Tooltip détaillé au survol de chaque pill. -->
|
||
<Border Grid.Row="1" Grid.ColumnSpan="2"
|
||
Background="{StaticResource Brush.Bg.Sidebar}"
|
||
BorderBrush="{StaticResource Brush.Border}"
|
||
BorderThickness="0,0,0,1"
|
||
Padding="16,8"
|
||
Visibility="{Binding HasHealthIndicators, Converter={StaticResource BoolToVisibility}}">
|
||
<ItemsControl ItemsSource="{Binding HealthIndicators}">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<StackPanel Orientation="Horizontal" />
|
||
</ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate>
|
||
<Border Background="{Binding StatusBrush}"
|
||
BorderBrush="{Binding BorderBrush}"
|
||
BorderThickness="1"
|
||
CornerRadius="12"
|
||
Padding="10,4"
|
||
Margin="0,0,8,0"
|
||
ToolTip="{Binding Tooltip}"
|
||
ToolTipService.InitialShowDelay="200"
|
||
ToolTipService.ShowDuration="20000">
|
||
<StackPanel Orientation="Horizontal">
|
||
<TextBlock Text="{Binding Icon}"
|
||
FontSize="14"
|
||
Foreground="{Binding IconForeground}"
|
||
VerticalAlignment="Center" />
|
||
<TextBlock Text="{Binding Name}"
|
||
Margin="6,0,0,0"
|
||
FontSize="12"
|
||
Foreground="{StaticResource Brush.Text.Primary}"
|
||
VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
</Border>
|
||
|
||
<!-- ============== Content area (Row 2, Col 1, swap par CurrentPage) ==============
|
||
Superpose plusieurs blocs (Library / Report / Documentation) gérés par
|
||
leur Visibility liée à CurrentPage. -->
|
||
<Grid Grid.Row="2" Grid.Column="1">
|
||
|
||
<!-- ====================== LIBRARY PAGE ====================== -->
|
||
<Grid Visibility="{Binding IsLibrary, Converter={StaticResource BoolToVisibility}}">
|
||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||
<StackPanel Margin="32,28">
|
||
|
||
<!-- ========= FEATURED (version courante) ========= -->
|
||
<Border CornerRadius="10" BorderThickness="1"
|
||
Padding="0"
|
||
Margin="0,0,0,32"
|
||
Visibility="{Binding HasFeaturedVersion, Converter={StaticResource BoolToVisibility}}">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Bg.Card}" />
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Installed}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsRemoteOnly}" Value="True">
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsBusy}" Value="True">
|
||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="6" />
|
||
<ColumnDefinition Width="*" />
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<!-- Bande latérale large : Border avec coins arrondis seulement à
|
||
gauche pour suivre la rondeur de la card parent (CornerRadius=10).
|
||
Rectangle ne se clippe pas sur le parent ⇒ on utilisait un visuel
|
||
qui dépassait les coins. -->
|
||
<Border Grid.Column="0" CornerRadius="9,0,0,9">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Installed}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsRemoteOnly}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsBusy}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
</Border>
|
||
|
||
<Grid Grid.Column="1" Margin="32,28">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*" />
|
||
<ColumnDefinition Width="Auto" />
|
||
<ColumnDefinition Width="Auto" />
|
||
</Grid.ColumnDefinitions>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto" />
|
||
<RowDefinition Height="Auto" />
|
||
<RowDefinition Height="Auto" />
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Label "Version courante" -->
|
||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||
Text="{x:Static loc:Strings.FeaturedCurrent}"
|
||
FontSize="11" FontWeight="Bold"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
Margin="0,0,0,4" />
|
||
|
||
<!-- Numéro version + badge -->
|
||
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
|
||
<TextBlock Text="{Binding FeaturedVersion.Version, StringFormat='PROSERVE v{0}'}"
|
||
FontSize="32" FontWeight="Bold"
|
||
Foreground="{StaticResource Brush.Text.Primary}" />
|
||
<Border CornerRadius="12" Padding="10,4" Margin="16,8,0,0"
|
||
VerticalAlignment="Center">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Installed}" />
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsRemoteOnly}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Available}" />
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding FeaturedVersion.IsBusy}" Value="True">
|
||
<Setter Property="Background" Value="{StaticResource Brush.Status.Busy}" />
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Border.Style>
|
||
<TextBlock Text="{Binding FeaturedVersion.StateLabel}" FontSize="12" FontWeight="SemiBold"
|
||
Foreground="White" />
|
||
</Border>
|
||
</StackPanel>
|
||
|
||
<!-- Meta -->
|
||
<TextBlock Grid.Row="2" Grid.Column="0"
|
||
FontSize="13"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
Margin="0,8,0,0">
|
||
<Run Text="{x:Static loc:Strings.ReleasedOn}" />
|
||
<Run Text="{Binding FeaturedVersion.PrimaryDate, Mode=OneWay}" />
|
||
<Run Text=" • " />
|
||
<Run Text="{Binding FeaturedVersion.SizeDisplay, Mode=OneWay}" />
|
||
</TextBlock>
|
||
|
||
<!-- Action principale (gros bouton) -->
|
||
<Button Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
|
||
Style="{StaticResource PrimaryButton}"
|
||
Content="{x:Static loc:Strings.ActionLaunchBig}"
|
||
FontSize="20" Padding="56,16"
|
||
VerticalAlignment="Center"
|
||
Command="{Binding FeaturedVersion.LaunchCommand}"
|
||
Visibility="{Binding FeaturedVersion.IsInstalled, Converter={StaticResource BoolToVisibility}}" />
|
||
|
||
<StackPanel Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
|
||
Orientation="Vertical" VerticalAlignment="Center"
|
||
Visibility="{Binding FeaturedVersion.IsRemoteOnly, Converter={StaticResource BoolToVisibility}}">
|
||
<Button Style="{StaticResource AccentButton}"
|
||
Content="{Binding FeaturedVersion.InstallButtonLabel}"
|
||
FontSize="20" Padding="48,16"
|
||
Command="{Binding FeaturedVersion.InstallCommand}" />
|
||
<!-- Bouton rouge « Annuler » : visible uniquement quand un partial existe.
|
||
Permet d'abandonner et repartir de zéro (confirmation requise). -->
|
||
<Button Style="{StaticResource DangerButton}"
|
||
Content="{x:Static loc:Strings.ActionCancel}"
|
||
FontSize="13" Padding="20,8" Margin="0,8,0,0"
|
||
HorizontalAlignment="Center"
|
||
Command="{Binding FeaturedVersion.RestartFromZeroCommand}"
|
||
ToolTip="{x:Static loc:Strings.MenuRestartFromZero}"
|
||
Visibility="{Binding FeaturedVersion.ShowRestartFromZero, Converter={StaticResource BoolToVisibility}}" />
|
||
</StackPanel>
|
||
|
||
<StackPanel Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
|
||
Orientation="Vertical" VerticalAlignment="Center" Width="220"
|
||
Visibility="{Binding FeaturedVersion.IsBusy, Converter={StaticResource BoolToVisibility}}">
|
||
<TextBlock Text="{Binding FeaturedVersion.StateLabel}"
|
||
HorizontalAlignment="Center"
|
||
Foreground="{StaticResource Brush.Status.Busy}"
|
||
FontWeight="SemiBold" Margin="0,0,0,8" />
|
||
<ProgressBar Height="8"
|
||
Value="{Binding FeaturedVersion.ProgressPercent}" Maximum="100"
|
||
Foreground="{StaticResource Brush.Status.Busy}"
|
||
Background="#2A2A30" BorderThickness="0" />
|
||
<TextBlock Text="{Binding FeaturedVersion.ProgressPercent, StringFormat={}{0:0}%}"
|
||
HorizontalAlignment="Center"
|
||
FontSize="12" Margin="0,4,0,0"
|
||
Foreground="{StaticResource Brush.Text.Secondary}" />
|
||
</StackPanel>
|
||
|
||
<!-- ... menu -->
|
||
<Button Grid.Row="0" Grid.RowSpan="3" Grid.Column="2"
|
||
Style="{StaticResource IconButton}"
|
||
Content="⋯" FontSize="22" FontWeight="Bold"
|
||
Margin="12,0,0,0"
|
||
VerticalAlignment="Center"
|
||
ToolTip="Plus d'actions"
|
||
Tag="{Binding FeaturedVersion}"
|
||
Click="OnMoreMenuClick">
|
||
<Button.ContextMenu>
|
||
<ContextMenu>
|
||
<MenuItem Header="{x:Static loc:Strings.MenuReleaseNotes}"
|
||
Command="{Binding PlacementTarget.Tag.ShowReleaseNotesCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuOpenFolder}"
|
||
Command="{Binding PlacementTarget.Tag.OpenFolderCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuRestartFromZero}"
|
||
Command="{Binding PlacementTarget.Tag.RestartFromZeroCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.HasResumableDownload, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<Separator Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
<MenuItem Header="{x:Static loc:Strings.MenuUninstall}"
|
||
Command="{Binding PlacementTarget.Tag.UninstallCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
|
||
</ContextMenu>
|
||
</Button.ContextMenu>
|
||
</Button>
|
||
</Grid>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- ========= AUTRES VERSIONS ========= -->
|
||
<StackPanel Visibility="{Binding HasOtherVersions, Converter={StaticResource BoolToVisibility}}">
|
||
<Grid Margin="0,0,0,12">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto" />
|
||
<ColumnDefinition Width="*" />
|
||
</Grid.ColumnDefinitions>
|
||
<TextBlock Grid.Column="0" Text="{x:Static loc:Strings.SectionOther}"
|
||
FontSize="12" FontWeight="Bold"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
VerticalAlignment="Center" />
|
||
<Border Grid.Column="1" Background="{StaticResource Brush.Border}"
|
||
Height="1" Margin="12,0,0,0" VerticalAlignment="Center" />
|
||
</Grid>
|
||
|
||
<ItemsControl ItemsSource="{Binding OtherVersions}"
|
||
ItemTemplate="{StaticResource CompactRowTemplate}" />
|
||
</StackPanel>
|
||
|
||
<!-- Empty state -->
|
||
<Border Background="{StaticResource Brush.Bg.Card}"
|
||
BorderBrush="{StaticResource Brush.Border}" BorderThickness="1"
|
||
CornerRadius="6"
|
||
Padding="32"
|
||
Visibility="{Binding EmptyHintVisibility}">
|
||
<TextBlock Text="{Binding EmptyHint}"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
TextWrapping="Wrap" />
|
||
</Border>
|
||
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
|
||
<!-- Bouton flottant "Vérifier les MAJ" : ancré bas-gauche du body, au-dessus
|
||
du background bitmap, à l'extérieur du footer. -->
|
||
<Button HorizontalAlignment="Left" VerticalAlignment="Bottom"
|
||
Margin="32,0,0,24"
|
||
Style="{StaticResource SecondaryButton}"
|
||
Content="{x:Static loc:Strings.TopBarCheckUpdates}"
|
||
Command="{Binding CheckForUpdatesCommand}" />
|
||
|
||
<!-- Copyright flottant en bas centré, sur une pill sombre pour rester
|
||
lisible quel que soit l'arrière-plan (image, voile noir, etc.). -->
|
||
<Border HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||
Margin="0,0,0,28"
|
||
Background="#A0000000"
|
||
CornerRadius="10"
|
||
Padding="10,4">
|
||
<TextBlock Text="{x:Static loc:Strings.Copyright}"
|
||
FontSize="11"
|
||
Foreground="White" />
|
||
</Border>
|
||
</Grid>
|
||
<!-- ====================== END LIBRARY PAGE ====================== -->
|
||
|
||
<!-- ====================== REPORT PAGE (WebView2) ====================== -->
|
||
<Grid Background="{StaticResource Brush.Bg.Window}"
|
||
Visibility="{Binding IsReport, Converter={StaticResource BoolToVisibility}}">
|
||
<wv2:WebView2 x:Name="ReportWebView"
|
||
Source="{Binding ReportUri, Mode=OneWay}" />
|
||
</Grid>
|
||
|
||
<!-- ====================== DOCUMENTATION PAGE ====================== -->
|
||
<Grid Background="{StaticResource Brush.Bg.Window}"
|
||
Visibility="{Binding IsDocumentation, Converter={StaticResource BoolToVisibility}}">
|
||
<!-- WebView2 si une URL est configurée, sinon un placeholder explicatif -->
|
||
<wv2:WebView2 x:Name="DocsWebView"
|
||
Source="{Binding DocumentationUri, Mode=OneWay}"
|
||
Visibility="{Binding HasDocumentationUrl, Converter={StaticResource BoolToVisibility}}" />
|
||
<TextBlock Text="{x:Static loc:Strings.DocsPlaceholder}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
FontSize="14" TextAlignment="Center" TextWrapping="Wrap"
|
||
MaxWidth="500"
|
||
Visibility="{Binding HasDocumentationUrl, Converter={StaticResource InverseBoolToVisibility}}" />
|
||
</Grid>
|
||
|
||
</Grid>
|
||
<!-- ============== END Content area ============== -->
|
||
|
||
<!-- ============== Left sidebar : nav buttons ==============
|
||
Row 2 + RowSpan=2 → la sidebar s'étend depuis le bas du health
|
||
banner jusqu'en bas de la fenêtre, couvrant content + footer.
|
||
(Row 0 = top bar, Row 1 = health banner full-width.) -->
|
||
<Border Grid.Row="2" Grid.RowSpan="2" Grid.Column="0"
|
||
Background="#0A0A0E"
|
||
BorderBrush="{StaticResource Brush.Border}"
|
||
BorderThickness="0,0,1,0">
|
||
<StackPanel Orientation="Vertical" Margin="0,16,0,0">
|
||
<!-- Style des boutons définis dans Theme.xaml (NavButton) -->
|
||
<Button Style="{StaticResource NavButton}"
|
||
Command="{Binding NavigateLibraryCommand}"
|
||
Tag="{Binding IsLibrary}">
|
||
<StackPanel Orientation="Horizontal">
|
||
<TextBlock Text="📚" FontSize="16" Margin="0,0,12,0" VerticalAlignment="Center" />
|
||
<TextBlock Text="{x:Static loc:Strings.NavLibrary}" VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</Button>
|
||
<Button Style="{StaticResource NavButton}"
|
||
Command="{Binding NavigateReportCommand}"
|
||
Tag="{Binding IsReport}">
|
||
<StackPanel Orientation="Horizontal">
|
||
<TextBlock Text="📊" FontSize="16" Margin="0,0,12,0" VerticalAlignment="Center" />
|
||
<TextBlock Text="{x:Static loc:Strings.NavReport}" VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</Button>
|
||
<Button Style="{StaticResource NavButton}"
|
||
Command="{Binding NavigateDocumentationCommand}"
|
||
Tag="{Binding IsDocumentation}">
|
||
<StackPanel Orientation="Horizontal">
|
||
<TextBlock Text="📖" FontSize="16" Margin="0,0,12,0" VerticalAlignment="Center" />
|
||
<TextBlock Text="{x:Static loc:Strings.NavDocumentation}" VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</Button>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<!-- Footer : Row 3, Col 1 → cantonné à la colonne content, sous la zone
|
||
Library/Report/Doc. La sidebar (RowSpan=2 sur rows 2-3) le contourne
|
||
par sa colonne. -->
|
||
<Border Grid.Row="3" Grid.Column="1"
|
||
Background="{StaticResource Brush.Bg.Footer}"
|
||
BorderBrush="{StaticResource Brush.Border}" BorderThickness="0,1,0,0"
|
||
Padding="20,10"
|
||
Visibility="{Binding FooterVisibility}">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*" />
|
||
<ColumnDefinition Width="Auto" />
|
||
</Grid.ColumnDefinitions>
|
||
<StackPanel Grid.Column="0">
|
||
<TextBlock Text="{Binding FooterText}"
|
||
Foreground="{StaticResource Brush.Text.Secondary}"
|
||
FontSize="12" />
|
||
<ProgressBar Height="4" Margin="0,4,0,0"
|
||
Value="{Binding ProgressPercent}" Maximum="100"
|
||
Foreground="{StaticResource Brush.Accent}"
|
||
Background="#2A2A30" BorderThickness="0" />
|
||
</StackPanel>
|
||
<Button Grid.Column="1"
|
||
Style="{StaticResource SecondaryButton}"
|
||
Content="{x:Static loc:Strings.ActionCancel}"
|
||
VerticalAlignment="Center" Margin="12,0,0,0"
|
||
Command="{Binding CancelDownloadCommand}"
|
||
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVisibility}}" />
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
</Window>
|