Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/ManagedDrive.App/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,19 @@
Click="MinimizeButton_Click"
ToolTip="{DynamicResource Tip.Minimize}"
AutomationProperties.Name="{DynamicResource Tip.Minimize}">
<TextBlock Style="{StaticResource IconText}" Text="&#xE921;" FontSize="12"/>
<TextBlock Style="{StaticResource MinimizeIconText}"/>
</Button>
<Button x:Name="MaximizeBtn" Style="{StaticResource IconButton}"
Click="MaximizeButton_Click"
ToolTip="{DynamicResource Tip.MaximizeRestore}"
AutomationProperties.Name="{DynamicResource Tip.MaximizeRestore}">
<TextBlock x:Name="MaximizeIcon" Style="{StaticResource IconText}" Text="&#xE922;" FontSize="12"/>
<TextBlock Style="{StaticResource MaximizeIconText}"/>
</Button>
<Button Style="{StaticResource CloseIconButton}"
Click="CloseButton_Click"
ToolTip="{DynamicResource Tip.HideToTray}"
AutomationProperties.Name="{DynamicResource Tip.HideToTray}">
<TextBlock Style="{StaticResource IconText}" Text="&#xE8BB;"/>
<TextBlock Style="{StaticResource CloseIconText}"/>
</Button>
</StackPanel>

Expand Down
10 changes: 0 additions & 10 deletions src/ManagedDrive.App/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ public MainWindow(MainViewModel viewModel)
InitializeComponent();
DataContext = viewModel;
WindowMaximizeHelper.HookMaximizeBehavior(this);
StateChanged += (_, _) => UpdateMaximizeIcon();
UpdateMaximizeIcon();
}

private void CloseButton_Click(object sender, RoutedEventArgs e) => Close();
Expand All @@ -27,14 +25,6 @@ public MainWindow(MainViewModel viewModel)
private void MaximizeButton_Click(object sender, RoutedEventArgs e) =>
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;

/// <summary>
/// Swaps the caption button's glyph between "maximize" and "restore" to match the current
/// <see cref="Window.WindowState"/>, since it toggles between the two rather than having
/// separate buttons for each.
/// </summary>
private void UpdateMaximizeIcon() =>
MaximizeIcon.Text = WindowState == WindowState.Maximized ? "" : "";

/// <summary>
/// Keeps the selected disk in view: a clicked notification can select one that is scrolled away.
/// </summary>
Expand Down
36 changes: 34 additions & 2 deletions src/ManagedDrive.App/Themes/AppTheme.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,38 @@
<SolidColorBrush x:Key="AppCloseHover" Color="#C42B1C"/>
<SolidColorBrush x:Key="AppClosePressed" Color="#A02317"/>

<!-- Window button icons. Glyph and colour come from the current style (WindowGlyph* in AppStyle.{X}.xaml,
WindowIcon* in its palette), so each style draws its own set. -->
<Style x:Key="MinimizeIconText" TargetType="TextBlock" BasedOn="{StaticResource IconText}">
<Setter Property="Text" Value="{DynamicResource WindowGlyphMinimize}"/>
<Setter Property="Foreground" Value="{DynamicResource WindowIconMinimize}"/>
</Style>

<!-- The button toggles between maximize and restore, so the glyph follows the window state. -->
<Style x:Key="MaximizeIconText" TargetType="TextBlock" BasedOn="{StaticResource IconText}">
<Setter Property="Text" Value="{DynamicResource WindowGlyphMaximize}"/>
<Setter Property="Foreground" Value="{DynamicResource WindowIconMaximize}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType=Window}}" Value="Maximized">
<Setter Property="Text" Value="{DynamicResource WindowGlyphRestore}"/>
</DataTrigger>
</Style.Triggers>
</Style>

<!-- An explicit Foreground beats the button's own, so the white glyph of CloseIconButton's red hover is set here. -->
<Style x:Key="CloseIconText" TargetType="TextBlock" BasedOn="{StaticResource IconText}">
<Setter Property="Text" Value="{DynamicResource WindowGlyphClose}"/>
<Setter Property="Foreground" Value="{DynamicResource WindowIconClose}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Button}}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource AncestorType=Button}}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>

<Style x:Key="CloseIconButton" TargetType="Button" BasedOn="{StaticResource IconButton}">
<!-- Style triggers beat the template's, so these replace the grey hover of the base style. -->
<Style.Triggers>
Expand Down Expand Up @@ -133,15 +165,15 @@
<DockPanel>
<Border DockPanel.Dock="Top"
Background="{DynamicResource AppBarBackground}"
Height="40"
Height="48"
Padding="12,0,4,0">
<DockPanel>
<Button DockPanel.Dock="Right"
Style="{StaticResource CloseIconButton}"
Command="{x:Static views:DialogWindowBase.CloseDialogCommand}"
WindowChrome.IsHitTestVisibleInChrome="True"
ToolTip="{DynamicResource Tip.CloseDialog}">
<TextBlock Style="{StaticResource IconText}" Text="&#xE8BB;"/>
<TextBlock Style="{StaticResource CloseIconText}"/>
</Button>
<TextBlock VerticalAlignment="Center"
FontSize="13" FontWeight="Medium"
Expand Down
9 changes: 9 additions & 0 deletions src/ManagedDrive.App/Themes/Styles/AppStyle.A.xaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:infra="clr-namespace:ManagedDrive.App.Infrastructure"
xmlns:controls="clr-namespace:ManagedDrive.App.Controls">

Expand All @@ -15,6 +16,14 @@
<Thickness x:Key="DiskCardWarningPadding">15</Thickness>
<Thickness x:Key="DiskCardOverlayMargin">-17</Thickness>

<!-- Window button glyphs (Segoe Fluent Icons), read by the MinimizeIconText/MaximizeIconText/CloseIconText styles in
AppTheme.xaml. Style A and C use the thin Windows caption glyphs; style B overrides them with the rounder ones
that match its tool icons. -->
<sys:String x:Key="WindowGlyphMinimize">&#xE921;</sys:String>
<sys:String x:Key="WindowGlyphMaximize">&#xE922;</sys:String>
<sys:String x:Key="WindowGlyphRestore">&#xE923;</sys:String>
<sys:String x:Key="WindowGlyphClose">&#xE8BB;</sys:String>

<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<infra:PercentToGridLengthConverter x:Key="PercentToGridLengthConverter"/>
<infra:IsFirstItemConverter x:Key="IsFirstItemConverter"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@
<SolidColorBrush x:Key="ToolIconAbout" Color="#FF3CC4DD"/>
<SolidColorBrush x:Key="ToolIconExit" Color="#FFFF6B6B"/>

<!-- Window button icons (minimize, maximize/restore, close). Every style palette must define these. -->
<SolidColorBrush x:Key="WindowIconMinimize" Color="#FFF5B83D"/>
<SolidColorBrush x:Key="WindowIconMaximize" Color="#FF34D399"/>
<SolidColorBrush x:Key="WindowIconClose" Color="#FFFF6B6B"/>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@
<SolidColorBrush x:Key="ToolIconAbout" Color="#FF0C93B0"/>
<SolidColorBrush x:Key="ToolIconExit" Color="#FFD93838"/>

<!-- Window button icons (minimize, maximize/restore, close). Every style palette must define these. -->
<SolidColorBrush x:Key="WindowIconMinimize" Color="#FFD99100"/>
<SolidColorBrush x:Key="WindowIconMaximize" Color="#FF16A34A"/>
<SolidColorBrush x:Key="WindowIconClose" Color="#FFD93838"/>

</ResourceDictionary>
8 changes: 8 additions & 0 deletions src/ManagedDrive.App/Themes/Styles/AppStyle.B.xaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:ManagedDrive.App.Controls">

<!-- Style B (dashboard): starts from style A. The tool buttons move into a left sidebar, cards are raised panels
Expand All @@ -20,6 +21,13 @@
<CornerRadius x:Key="SectionCornerRadius">10</CornerRadius>
<CornerRadius x:Key="PopupCornerRadius">8</CornerRadius>

<!-- Window button glyphs: the rounder Remove / FullScreen / BackToWindow / Cancel icons, the same family as the
coloured tool icons (colours: WindowIcon* in the palette). -->
<sys:String x:Key="WindowGlyphMinimize">&#xE738;</sys:String>
<sys:String x:Key="WindowGlyphMaximize">&#xE740;</sys:String>
<sys:String x:Key="WindowGlyphRestore">&#xE73F;</sys:String>
<sys:String x:Key="WindowGlyphClose">&#xE711;</sys:String>

<Style x:Key="SidebarStyle" TargetType="Border">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@
<SolidColorBrush x:Key="ToolIconAbout" Color="#FFEDEDED"/>
<SolidColorBrush x:Key="ToolIconExit" Color="#FFEDEDED"/>

<!-- Window button icons: monochrome too. -->
<SolidColorBrush x:Key="WindowIconMinimize" Color="#FFEDEDED"/>
<SolidColorBrush x:Key="WindowIconMaximize" Color="#FFEDEDED"/>
<SolidColorBrush x:Key="WindowIconClose" Color="#FFEDEDED"/>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@
<SolidColorBrush x:Key="ToolIconAbout" Color="#FF1C1C1C"/>
<SolidColorBrush x:Key="ToolIconExit" Color="#FF1C1C1C"/>

<!-- Window button icons: monochrome too. -->
<SolidColorBrush x:Key="WindowIconMinimize" Color="#FF1C1C1C"/>
<SolidColorBrush x:Key="WindowIconMaximize" Color="#FF1C1C1C"/>
<SolidColorBrush x:Key="WindowIconClose" Color="#FF1C1C1C"/>

</ResourceDictionary>
2 changes: 1 addition & 1 deletion src/ManagedDrive.App/Views/DialogWindowBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected DialogWindowBase()
{
WindowChrome.SetWindowChrome(this, new()
{
CaptionHeight = 40,
CaptionHeight = 48,
ResizeBorderThickness = new(0),
GlassFrameThickness = new(0),
NonClientFrameEdges = NonClientFrameEdges.None,
Expand Down
2 changes: 1 addition & 1 deletion src/ManagedDrive.App/Views/DiskContentDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
xmlns:helpers="clr-namespace:ManagedDrive.App.Helpers"
Style="{DynamicResource DialogWindowStyle}"
Title="{DynamicResource DiskContent.Title}"
Width="480" Height="480"
Width="480" Height="488"
MinWidth="360" MinHeight="320"
ResizeMode="CanResize"
WindowStartupLocation="CenterOwner">
Expand Down
2 changes: 1 addition & 1 deletion src/ManagedDrive.App/Views/DiskContentDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public DiskContentDialog(DiskViewModel target)
// fixed-size), so this window alone can be resized by dragging its edges.
WindowChrome.SetWindowChrome(this, new()
{
CaptionHeight = 40,
CaptionHeight = 48,
ResizeBorderThickness = new(6),
GlassFrameThickness = new(0),
NonClientFrameEdges = NonClientFrameEdges.None,
Expand Down
2 changes: 1 addition & 1 deletion src/ManagedDrive.App/Views/SpaceUsageDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public SpaceUsageDialog(DiskViewModel target, int listSize, Action<int> listSize
// Same as the disk-content dialog: the one other dialog that can be resized and maximized.
WindowChrome.SetWindowChrome(this, new()
{
CaptionHeight = 40,
CaptionHeight = 48,
ResizeBorderThickness = new(6),
GlassFrameThickness = new(0),
NonClientFrameEdges = NonClientFrameEdges.None,
Expand Down
50 changes: 50 additions & 0 deletions tests/ManagedDrive.Tests/Themes/PaletteKeysTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ public sealed class PaletteKeysTests
private static readonly string[] SidebarIconKeys =
{ "ToolIconNew", "ToolIconImport", "ToolIconRestore", "ToolIconSettings", "ToolIconAbout", "ToolIconExit" };

/// <summary>
/// Brush keys read by the minimize, maximize and close icon styles.
/// </summary>
private static readonly string[] WindowIconKeys =
{ "WindowIconMinimize", "WindowIconMaximize", "WindowIconClose" };

/// <summary>
/// Glyph keys read by the minimize, maximize and close icon styles.
/// </summary>
private static readonly string[] WindowGlyphKeys =
{ "WindowGlyphMinimize", "WindowGlyphMaximize", "WindowGlyphRestore", "WindowGlyphClose" };

/// <summary>
/// Verifies that the keys the cards use exist in the theme palette alone and in the theme palette
/// with a style palette merged over it, for every style and theme.
Expand Down Expand Up @@ -68,6 +80,44 @@ public void StyleB_DefinesEverySidebarIconKey(string theme)
Assert.Empty(missing);
}

/// <summary>
/// Verifies that styles B and C define a colour for each window button icon in both themes.
/// </summary>
[Theory]
[InlineData("B", "Light")]
[InlineData("B", "Dark")]
[InlineData("C", "Light")]
[InlineData("C", "Dark")]
public void StylePalette_DefinesEveryWindowIconKey(string style, string theme)
{
// Arrange
var keys = KeysOf($"Styles/AppStyle.{style}.Colors.{theme}.xaml");

// Act
var missing = WindowIconKeys.Where(k => !keys.Contains(k)).ToList();

// Assert
Assert.Empty(missing);
}

/// <summary>
/// Verifies that the base style (inherited by C) and style B each define every window button glyph.
/// </summary>
[Theory]
[InlineData("A")]
[InlineData("B")]
public void Style_DefinesEveryWindowGlyphKey(string style)
{
// Arrange
var keys = KeysOf($"Styles/AppStyle.{style}.xaml");

// Act
var missing = WindowGlyphKeys.Where(k => !keys.Contains(k)).ToList();

// Assert
Assert.Empty(missing);
}

/// <summary>
/// Verifies that a light palette and its dark counterpart define the same keys, so a theme switch
/// never drops a brush.
Expand Down
Loading