4 Commits

4 changed files with 362 additions and 219 deletions

View File

@@ -8,9 +8,44 @@
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
<!-- ClipForge custom palette (less Windows, more app identity) -->
<SolidColorBrush x:Key="ClipForgeSidebarBrush" Color="#0A0A0E"/>
<SolidColorBrush x:Key="ClipForgeMainBrush" Color="#0F0F14"/>
<SolidColorBrush x:Key="ClipForgeCardBrush" Color="#16161D"/>
<SolidColorBrush x:Key="ClipForgeCardBorderBrush" Color="#252532"/>
<SolidColorBrush x:Key="ClipForgeAccentBrush" Color="#E8FF47"/>
<SolidColorBrush x:Key="ClipForgeTextPrimaryBrush" Color="#F0F0F5"/>
<SolidColorBrush x:Key="ClipForgeTextSecondaryBrush" Color="#8E8E9A"/>
<SolidColorBrush x:Key="ClipForgeNavHoverBrush" Color="#1E1E26"/>
<!-- Sidebar nav: default (unselected) -->
<Style x:Key="ClipForgeNavButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource ClipForgeTextPrimaryBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="12,10"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
<!-- Sidebar nav: selected / accent -->
<Style x:Key="ClipForgeNavButtonSelectedStyle" TargetType="Button" BasedOn="{StaticResource ClipForgeNavButtonStyle}">
<Setter Property="Background" Value="{StaticResource ClipForgeAccentBrush}"/>
<Setter Property="Foreground" Value="#0A0A0E"/>
</Style>
<!-- Accent button (Save Settings, etc.) -->
<Style x:Key="ClipForgeAccentButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource ClipForgeAccentBrush}"/>
<Setter Property="Foreground" Value="#0A0A0E"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="20,10"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@@ -13,136 +13,144 @@ namespace ClipForge
public const uint MOD_WIN = 0x0008; public const uint MOD_WIN = 0x0008;
public const uint MOD_NOREPEAT = 0x4000; public const uint MOD_NOREPEAT = 0x4000;
private static readonly Dictionary<VirtualKey, string> KeyNames = new() // Built in static ctor with TryAdd so duplicate VirtualKey enum values (e.g. Kana/Hangul = 0x15) don't throw.
private static readonly Dictionary<VirtualKey, string> KeyNames = new();
static HotkeyHelper()
{ {
{ VirtualKey.LeftButton, "LMB" }, TryAdd(VirtualKey.LeftButton, "LMB");
{ VirtualKey.RightButton, "RMB" }, TryAdd(VirtualKey.RightButton, "RMB");
{ VirtualKey.Cancel, "Cancel" }, TryAdd(VirtualKey.Cancel, "Cancel");
{ VirtualKey.Back, "Backspace" }, TryAdd(VirtualKey.Back, "Backspace");
{ VirtualKey.Tab, "Tab" }, TryAdd(VirtualKey.Tab, "Tab");
{ VirtualKey.Clear, "Clear" }, TryAdd(VirtualKey.Clear, "Clear");
{ VirtualKey.Enter, "Enter" }, TryAdd(VirtualKey.Enter, "Enter");
{ VirtualKey.Shift, "Shift" }, TryAdd(VirtualKey.Shift, "Shift");
{ VirtualKey.Control, "Ctrl" }, TryAdd(VirtualKey.Control, "Ctrl");
{ VirtualKey.Menu, "Alt" }, TryAdd(VirtualKey.Menu, "Alt");
{ VirtualKey.Pause, "Pause" }, TryAdd(VirtualKey.Pause, "Pause");
{ VirtualKey.CapitalLock, "Caps Lock" }, TryAdd(VirtualKey.CapitalLock, "Caps Lock");
{ VirtualKey.Kana, "Kana" }, TryAdd(VirtualKey.Kana, "Kana");
{ VirtualKey.Hangul, "Hangul" }, TryAdd(VirtualKey.Hangul, "Hangul"); // same value as Kana on some SDKs; TryAdd skips duplicate
{ VirtualKey.Junja, "Junja" }, TryAdd(VirtualKey.Junja, "Junja");
{ VirtualKey.Final, "Final" }, TryAdd(VirtualKey.Final, "Final");
{ VirtualKey.Hanja, "Hanja" }, TryAdd(VirtualKey.Hanja, "Hanja");
{ VirtualKey.Kanji, "Kanji" }, TryAdd(VirtualKey.Kanji, "Kanji");
{ VirtualKey.Escape, "Escape" }, TryAdd(VirtualKey.Escape, "Escape");
{ VirtualKey.Convert, "Convert" }, TryAdd(VirtualKey.Convert, "Convert");
{ VirtualKey.NonConvert, "NonConvert" }, TryAdd(VirtualKey.NonConvert, "NonConvert");
{ VirtualKey.Accept, "Accept" }, TryAdd(VirtualKey.Accept, "Accept");
{ VirtualKey.ModeChange, "ModeChange" }, TryAdd(VirtualKey.ModeChange, "ModeChange");
{ VirtualKey.Space, "Space" }, TryAdd(VirtualKey.Space, "Space");
{ VirtualKey.PageUp, "PgUp" }, TryAdd(VirtualKey.PageUp, "PgUp");
{ VirtualKey.PageDown, "PgDn" }, TryAdd(VirtualKey.PageDown, "PgDn");
{ VirtualKey.End, "End" }, TryAdd(VirtualKey.End, "End");
{ VirtualKey.Home, "Home" }, TryAdd(VirtualKey.Home, "Home");
{ VirtualKey.Left, "Left" }, TryAdd(VirtualKey.Left, "Left");
{ VirtualKey.Up, "Up" }, TryAdd(VirtualKey.Up, "Up");
{ VirtualKey.Right, "Right" }, TryAdd(VirtualKey.Right, "Right");
{ VirtualKey.Down, "Down" }, TryAdd(VirtualKey.Down, "Down");
{ VirtualKey.Select, "Select" }, TryAdd(VirtualKey.Select, "Select");
{ VirtualKey.Print, "Print" }, TryAdd(VirtualKey.Print, "Print");
{ VirtualKey.Execute, "Execute" }, TryAdd(VirtualKey.Execute, "Execute");
{ VirtualKey.Snapshot, "Print Screen" }, TryAdd(VirtualKey.Snapshot, "Print Screen");
{ VirtualKey.Insert, "Insert" }, TryAdd(VirtualKey.Insert, "Insert");
{ VirtualKey.Delete, "Delete" }, TryAdd(VirtualKey.Delete, "Delete");
{ VirtualKey.Help, "Help" }, TryAdd(VirtualKey.Help, "Help");
{ VirtualKey.Number0, "0" }, TryAdd(VirtualKey.Number0, "0");
{ VirtualKey.Number1, "1" }, TryAdd(VirtualKey.Number1, "1");
{ VirtualKey.Number2, "2" }, TryAdd(VirtualKey.Number2, "2");
{ VirtualKey.Number3, "3" }, TryAdd(VirtualKey.Number3, "3");
{ VirtualKey.Number4, "4" }, TryAdd(VirtualKey.Number4, "4");
{ VirtualKey.Number5, "5" }, TryAdd(VirtualKey.Number5, "5");
{ VirtualKey.Number6, "6" }, TryAdd(VirtualKey.Number6, "6");
{ VirtualKey.Number7, "7" }, TryAdd(VirtualKey.Number7, "7");
{ VirtualKey.Number8, "8" }, TryAdd(VirtualKey.Number8, "8");
{ VirtualKey.Number9, "9" }, TryAdd(VirtualKey.Number9, "9");
{ VirtualKey.A, "A" }, TryAdd(VirtualKey.A, "A");
{ VirtualKey.B, "B" }, TryAdd(VirtualKey.B, "B");
{ VirtualKey.C, "C" }, TryAdd(VirtualKey.C, "C");
{ VirtualKey.D, "D" }, TryAdd(VirtualKey.D, "D");
{ VirtualKey.E, "E" }, TryAdd(VirtualKey.E, "E");
{ VirtualKey.F, "F" }, TryAdd(VirtualKey.F, "F");
{ VirtualKey.G, "G" }, TryAdd(VirtualKey.G, "G");
{ VirtualKey.H, "H" }, TryAdd(VirtualKey.H, "H");
{ VirtualKey.I, "I" }, TryAdd(VirtualKey.I, "I");
{ VirtualKey.J, "J" }, TryAdd(VirtualKey.J, "J");
{ VirtualKey.K, "K" }, TryAdd(VirtualKey.K, "K");
{ VirtualKey.L, "L" }, TryAdd(VirtualKey.L, "L");
{ VirtualKey.M, "M" }, TryAdd(VirtualKey.M, "M");
{ VirtualKey.N, "N" }, TryAdd(VirtualKey.N, "N");
{ VirtualKey.O, "O" }, TryAdd(VirtualKey.O, "O");
{ VirtualKey.P, "P" }, TryAdd(VirtualKey.P, "P");
{ VirtualKey.Q, "Q" }, TryAdd(VirtualKey.Q, "Q");
{ VirtualKey.R, "R" }, TryAdd(VirtualKey.R, "R");
{ VirtualKey.S, "S" }, TryAdd(VirtualKey.S, "S");
{ VirtualKey.T, "T" }, TryAdd(VirtualKey.T, "T");
{ VirtualKey.U, "U" }, TryAdd(VirtualKey.U, "U");
{ VirtualKey.V, "V" }, TryAdd(VirtualKey.V, "V");
{ VirtualKey.W, "W" }, TryAdd(VirtualKey.W, "W");
{ VirtualKey.X, "X" }, TryAdd(VirtualKey.X, "X");
{ VirtualKey.Y, "Y" }, TryAdd(VirtualKey.Y, "Y");
{ VirtualKey.Z, "Z" }, TryAdd(VirtualKey.Z, "Z");
{ VirtualKey.LeftWindows, "Win" }, TryAdd(VirtualKey.LeftWindows, "Win");
{ VirtualKey.RightWindows, "Win" }, TryAdd(VirtualKey.RightWindows, "Win");
{ VirtualKey.Application, "App" }, TryAdd(VirtualKey.Application, "App");
{ VirtualKey.Sleep, "Sleep" }, TryAdd(VirtualKey.Sleep, "Sleep");
{ VirtualKey.NumberPad0, "Num 0" }, TryAdd(VirtualKey.NumberPad0, "Num 0");
{ VirtualKey.NumberPad1, "Num 1" }, TryAdd(VirtualKey.NumberPad1, "Num 1");
{ VirtualKey.NumberPad2, "Num 2" }, TryAdd(VirtualKey.NumberPad2, "Num 2");
{ VirtualKey.NumberPad3, "Num 3" }, TryAdd(VirtualKey.NumberPad3, "Num 3");
{ VirtualKey.NumberPad4, "Num 4" }, TryAdd(VirtualKey.NumberPad4, "Num 4");
{ VirtualKey.NumberPad5, "Num 5" }, TryAdd(VirtualKey.NumberPad5, "Num 5");
{ VirtualKey.NumberPad6, "Num 6" }, TryAdd(VirtualKey.NumberPad6, "Num 6");
{ VirtualKey.NumberPad7, "Num 7" }, TryAdd(VirtualKey.NumberPad7, "Num 7");
{ VirtualKey.NumberPad8, "Num 8" }, TryAdd(VirtualKey.NumberPad8, "Num 8");
{ VirtualKey.NumberPad9, "Num 9" }, TryAdd(VirtualKey.NumberPad9, "Num 9");
{ VirtualKey.Multiply, "Num *" }, TryAdd(VirtualKey.Multiply, "Num *");
{ VirtualKey.Add, "Num +" }, TryAdd(VirtualKey.Add, "Num +");
{ VirtualKey.Separator, "Num ," }, TryAdd(VirtualKey.Separator, "Num ,");
{ VirtualKey.Subtract, "Num -" }, TryAdd(VirtualKey.Subtract, "Num -");
{ VirtualKey.Decimal, "Num ." }, TryAdd(VirtualKey.Decimal, "Num .");
{ VirtualKey.Divide, "Num /" }, TryAdd(VirtualKey.Divide, "Num /");
{ VirtualKey.F1, "F1" }, TryAdd(VirtualKey.F1, "F1");
{ VirtualKey.F2, "F2" }, TryAdd(VirtualKey.F2, "F2");
{ VirtualKey.F3, "F3" }, TryAdd(VirtualKey.F3, "F3");
{ VirtualKey.F4, "F4" }, TryAdd(VirtualKey.F4, "F4");
{ VirtualKey.F5, "F5" }, TryAdd(VirtualKey.F5, "F5");
{ VirtualKey.F6, "F6" }, TryAdd(VirtualKey.F6, "F6");
{ VirtualKey.F7, "F7" }, TryAdd(VirtualKey.F7, "F7");
{ VirtualKey.F8, "F8" }, TryAdd(VirtualKey.F8, "F8");
{ VirtualKey.F9, "F9" }, TryAdd(VirtualKey.F9, "F9");
{ VirtualKey.F10, "F10" }, TryAdd(VirtualKey.F10, "F10");
{ VirtualKey.F11, "F11" }, TryAdd(VirtualKey.F11, "F11");
{ VirtualKey.F12, "F12" }, TryAdd(VirtualKey.F12, "F12");
{ VirtualKey.F13, "F13" }, TryAdd(VirtualKey.F13, "F13");
{ VirtualKey.F14, "F14" }, TryAdd(VirtualKey.F14, "F14");
{ VirtualKey.F15, "F15" }, TryAdd(VirtualKey.F15, "F15");
{ VirtualKey.F16, "F16" }, TryAdd(VirtualKey.F16, "F16");
{ VirtualKey.F17, "F17" }, TryAdd(VirtualKey.F17, "F17");
{ VirtualKey.F18, "F18" }, TryAdd(VirtualKey.F18, "F18");
{ VirtualKey.F19, "F19" }, TryAdd(VirtualKey.F19, "F19");
{ VirtualKey.F20, "F20" }, TryAdd(VirtualKey.F20, "F20");
{ VirtualKey.F21, "F21" }, TryAdd(VirtualKey.F21, "F21");
{ VirtualKey.F22, "F22" }, TryAdd(VirtualKey.F22, "F22");
{ VirtualKey.F23, "F23" }, TryAdd(VirtualKey.F23, "F23");
{ VirtualKey.F24, "F24" }, TryAdd(VirtualKey.F24, "F24");
{ VirtualKey.NumberKeyLock, "NumLock" }, TryAdd(VirtualKey.NumberKeyLock, "NumLock");
{ VirtualKey.Scroll, "Scroll Lock" }, TryAdd(VirtualKey.Scroll, "Scroll Lock");
{ VirtualKey.LeftShift, "Shift" }, TryAdd(VirtualKey.LeftShift, "Shift");
{ VirtualKey.RightShift, "Shift" }, TryAdd(VirtualKey.RightShift, "Shift");
{ VirtualKey.LeftControl, "Ctrl" }, TryAdd(VirtualKey.LeftControl, "Ctrl");
{ VirtualKey.RightControl, "Ctrl" }, TryAdd(VirtualKey.RightControl, "Ctrl");
{ VirtualKey.LeftMenu, "Alt" }, TryAdd(VirtualKey.LeftMenu, "Alt");
{ VirtualKey.RightMenu, "Alt" }, TryAdd(VirtualKey.RightMenu, "Alt");
}; }
private static void TryAdd(VirtualKey key, string name)
{
KeyNames.TryAdd(key, name);
}
/// <summary>Format modifier flags + virtual key as display string (e.g. "Alt + PgUp").</summary> /// <summary>Format modifier flags + virtual key as display string (e.g. "Alt + PgUp").</summary>
public static string ToDisplayString(uint modifiers, uint vk) public static string ToDisplayString(uint modifiers, uint vk)

View File

@@ -13,56 +13,71 @@
<MicaBackdrop /> <MicaBackdrop />
</Window.SystemBackdrop> </Window.SystemBackdrop>
<Grid x:Name="RootGrid"> <Grid x:Name="RootGrid" Background="{StaticResource ClipForgeMainBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/> <ColumnDefinition Width="220"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- SIDEBAR --> <!-- SIDEBAR -->
<Border Grid.Column="0" <Border Grid.Column="0"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" Background="{StaticResource ClipForgeSidebarBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="0,0,1,0"> BorderThickness="0,0,1,0">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="48"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- App title --> <!-- Logo + wordmark -->
<Border Grid.Row="0" Padding="16,0"> <Border Grid.Row="0" Padding="20,24,20,20" Margin="0,0,0,8">
<TextBlock Text="CLIPFORGE" <StackPanel Spacing="14" HorizontalAlignment="Left">
FontSize="13" <Border Width="44" Height="44"
FontWeight="Bold" Background="{StaticResource ClipForgeAccentBrush}"
CharacterSpacing="80" CornerRadius="10"
Foreground="#E8FF47" HorizontalAlignment="Left">
VerticalAlignment="Center"/> <TextBlock Text="&#xE8F1;"
FontFamily="Segoe MDL2 Assets"
FontSize="22"
Foreground="#0A0A0E"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="ClipForge"
FontSize="20"
FontWeight="Bold"
Foreground="{StaticResource ClipForgeAccentBrush}"
CharacterSpacing="60"/>
<TextBlock Text="Capture. Trim. Share."
FontSize="11"
Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
Opacity="0.9"/>
</StackPanel>
</Border> </Border>
<!-- Nav items --> <!-- Nav items -->
<StackPanel Grid.Row="1" Spacing="2" Padding="8,8"> <StackPanel Grid.Row="1" Spacing="4" Padding="12,8" VerticalAlignment="Top">
<Button x:Name="NavClips" <Button x:Name="NavClips"
HorizontalAlignment="Stretch"
Click="NavClips_Click" Click="NavClips_Click"
Style="{StaticResource AccentButtonStyle}"> Style="{StaticResource ClipForgeNavButtonSelectedStyle}">
<StackPanel Orientation="Horizontal" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock Text="&#xE8F1;" <TextBlock Text="&#xE8F1;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="14"/> FontSize="16"/>
<TextBlock Text="Clips" <TextBlock Text="Clips"
FontSize="13" FontSize="13"
FontWeight="SemiBold"/> FontWeight="SemiBold"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="NavSettings" <Button x:Name="NavSettings"
HorizontalAlignment="Stretch" Click="NavSettings_Click"
Click="NavSettings_Click"> Style="{StaticResource ClipForgeNavButtonStyle}">
<StackPanel Orientation="Horizontal" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock Text="&#xE713;" <TextBlock Text="&#xE713;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="14"/> FontSize="16"/>
<TextBlock Text="Settings" <TextBlock Text="Settings"
FontSize="13" FontSize="13"
FontWeight="SemiBold"/> FontWeight="SemiBold"/>
@@ -73,10 +88,15 @@
<!-- Record button --> <!-- Record button -->
<Button x:Name="RecordButton" <Button x:Name="RecordButton"
Grid.Row="2" Grid.Row="2"
Margin="8" Margin="12,12,12,20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="12,10"
Click="RecordButton_Click"> Click="RecordButton_Click">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="10">
<Ellipse x:Name="RecordDot" <Ellipse x:Name="RecordDot"
Width="8" Height="8" Width="8" Height="8"
Fill="#FF4757"/> Fill="#FF4757"/>
@@ -84,6 +104,7 @@
Text="CAPTURING" Text="CAPTURING"
FontSize="11" FontSize="11"
FontWeight="Bold" FontWeight="Bold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
CharacterSpacing="40"/> CharacterSpacing="40"/>
</StackPanel> </StackPanel>
</Button> </Button>
@@ -91,7 +112,7 @@
</Border> </Border>
<!-- MAIN CONTENT --> <!-- MAIN CONTENT -->
<Grid Grid.Column="1"> <Grid Grid.Column="1" Background="{StaticResource ClipForgeMainBrush}">
<!-- CLIPS PAGE --> <!-- CLIPS PAGE -->
<Grid x:Name="ClipsPage" Visibility="Visible"> <Grid x:Name="ClipsPage" Visibility="Visible">
@@ -102,37 +123,44 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Header --> <!-- Header -->
<Border Grid.Row="0" Padding="24,20,24,0"> <Border Grid.Row="0" Padding="28,24,28,0">
<Grid> <Grid>
<StackPanel Orientation="Horizontal" Spacing="12" <StackPanel Orientation="Horizontal" Spacing="12"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
<TextBlock Text="My Clips" <TextBlock Text="My Clips"
FontSize="24" FontSize="26"
FontWeight="Bold"/> FontWeight="Bold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"/>
<TextBlock x:Name="ClipCountText" <TextBlock x:Name="ClipCountText"
Text="0 clips" Text="0 clips"
FontSize="12" FontSize="13"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Margin="0,0,0,3"/> Margin="0,0,0,4"/>
</StackPanel> </StackPanel>
<Button Content="Open Folder" <Button Content="Open Folder"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Background="{StaticResource ClipForgeCardBrush}"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,8"
Click="OpenFolder_Click"/> Click="OpenFolder_Click"/>
</Grid> </Grid>
</Border> </Border>
<!-- Toolbar --> <!-- Toolbar -->
<Border Grid.Row="1" Padding="24,12"> <Border Grid.Row="1" Padding="28,16">
<AutoSuggestBox x:Name="SearchBox" <AutoSuggestBox x:Name="SearchBox"
PlaceholderText="Search clips..." PlaceholderText="Search clips..."
Width="260" Width="280"
HorizontalAlignment="Left" HorizontalAlignment="Left"
TextChanged="SearchBox_TextChanged"/> TextChanged="SearchBox_TextChanged"/>
</Border> </Border>
<!-- Clip Grid --> <!-- Clip Grid -->
<ScrollViewer Grid.Row="2" Padding="24,0,24,24"> <ScrollViewer Grid.Row="2" Padding="28,0,28,28">
<ItemsControl x:Name="ClipGrid"> <ItemsControl x:Name="ClipGrid">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@@ -143,11 +171,11 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:ClipFile"> <DataTemplate x:DataType="local:ClipFile">
<Border Width="220" <Border Width="220"
Margin="0,0,12,12" Margin="0,0,14,14"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8"> CornerRadius="10">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="124"/> <RowDefinition Height="124"/>
@@ -156,8 +184,8 @@
<!-- Thumbnail --> <!-- Thumbnail -->
<Border Grid.Row="0" <Border Grid.Row="0"
Background="#1a1a2e" Background="{StaticResource ClipForgeSidebarBrush}"
CornerRadius="8,8,0,0"> CornerRadius="10,10,0,0">
<Grid> <Grid>
<!-- Placeholder icon --> <!-- Placeholder icon -->
<TextBlock Text="&#xE786;" <TextBlock Text="&#xE786;"
@@ -198,19 +226,23 @@
<TextBlock Text="{x:Bind Title}" <TextBlock Text="{x:Bind Title}"
FontSize="12" FontSize="12"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
TextTrimming="CharacterEllipsis"/> TextTrimming="CharacterEllipsis"/>
<Grid> <Grid>
<TextBlock Text="{x:Bind CreatedAtDisplay}" <TextBlock Text="{x:Bind CreatedAtDisplay}"
FontSize="10" FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<TextBlock Text="{x:Bind FileSize}" <TextBlock Text="{x:Bind FileSize}"
FontSize="10" FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
HorizontalAlignment="Right"/> HorizontalAlignment="Right"/>
</Grid> </Grid>
<StackPanel Orientation="Horizontal" Spacing="6"> <StackPanel Orientation="Horizontal" Spacing="6">
<Button Tag="{x:Bind Path}" <Button Tag="{x:Bind Path}"
Click="TrimClip_Click" Click="TrimClip_Click"
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
FontSize="11" Padding="8,4"> FontSize="11" Padding="8,4">
<TextBlock Text="&#xE8C6;" <TextBlock Text="&#xE8C6;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
@@ -218,6 +250,9 @@
</Button> </Button>
<Button Tag="{x:Bind Path}" <Button Tag="{x:Bind Path}"
Click="RenameClip_Click" Click="RenameClip_Click"
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
FontSize="11" Padding="8,4"> FontSize="11" Padding="8,4">
<TextBlock Text="&#xE8AC;" <TextBlock Text="&#xE8AC;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
@@ -225,6 +260,9 @@
</Button> </Button>
<Button Tag="{x:Bind Path}" <Button Tag="{x:Bind Path}"
Click="DeleteClip_Click" Click="DeleteClip_Click"
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource ClipForgeTextSecondaryBrush}"
FontSize="11" Padding="8,4"> FontSize="11" Padding="8,4">
<TextBlock Text="&#xE74D;" <TextBlock Text="&#xE74D;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
@@ -243,33 +281,36 @@
<!-- SETTINGS PAGE --> <!-- SETTINGS PAGE -->
<Grid x:Name="SettingsPage" Visibility="Collapsed"> <Grid x:Name="SettingsPage" Visibility="Collapsed">
<ScrollViewer Padding="24"> <ScrollViewer Padding="28">
<StackPanel Spacing="24" MaxWidth="520" HorizontalAlignment="Left"> <StackPanel Spacing="24" MaxWidth="540" HorizontalAlignment="Left">
<TextBlock Text="Settings" <TextBlock Text="Settings"
FontSize="24" FontSize="26"
FontWeight="Bold"/> FontWeight="Bold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"/>
<!-- Clip Length --> <!-- Clip Length -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" <Border Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="10"
Padding="20"> Padding="20">
<StackPanel Spacing="12"> <StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="&#xE714;" <TextBlock Text="&#xE714;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{StaticResource ClipForgeAccentBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="Clip Length" <TextBlock Text="Clip Length"
FontSize="14" FontSize="14"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<TextBlock Text="How many seconds to save when you press the hotkey." <TextBlock Text="How many seconds to save when you press the hotkey."
FontSize="12" FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<Grid> <Grid>
<Slider x:Name="ClipLengthSlider" <Slider x:Name="ClipLengthSlider"
Minimum="10" Minimum="10"
@@ -281,7 +322,7 @@
Text="30 seconds" Text="30 seconds"
FontSize="12" FontSize="12"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="#E8FF47" Foreground="{StaticResource ClipForgeAccentBrush}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</Grid> </Grid>
@@ -289,25 +330,27 @@
</Border> </Border>
<!-- Video Quality --> <!-- Video Quality -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" <Border Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="10"
Padding="20"> Padding="20">
<StackPanel Spacing="12"> <StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="&#xE7F4;" <TextBlock Text="&#xE7F4;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{StaticResource ClipForgeAccentBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="Video Quality" <TextBlock Text="Video Quality"
FontSize="14" FontSize="14"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<TextBlock Text="Higher quality means larger file sizes." <TextBlock Text="Higher quality means larger file sizes."
FontSize="12" FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<Grid> <Grid>
<Slider x:Name="QualitySlider" <Slider x:Name="QualitySlider"
Minimum="10" Minimum="10"
@@ -319,7 +362,7 @@
Text="70%" Text="70%"
FontSize="12" FontSize="12"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="#E8FF47" Foreground="{StaticResource ClipForgeAccentBrush}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</Grid> </Grid>
@@ -327,25 +370,27 @@
</Border> </Border>
<!-- Framerate --> <!-- Framerate -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" <Border Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="10"
Padding="20"> Padding="20">
<StackPanel Spacing="12"> <StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="&#xE7F8;" <TextBlock Text="&#xE7F8;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{StaticResource ClipForgeAccentBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="Framerate" <TextBlock Text="Framerate"
FontSize="14" FontSize="14"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<TextBlock Text="Higher framerates are smoother but use more storage." <TextBlock Text="Higher framerates are smoother but use more storage."
FontSize="12" FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<ComboBox x:Name="FramerateCombo" <ComboBox x:Name="FramerateCombo"
SelectionChanged="FramerateCombo_SelectionChanged"> SelectionChanged="FramerateCombo_SelectionChanged">
<ComboBoxItem Content="30 FPS"/> <ComboBoxItem Content="30 FPS"/>
@@ -355,61 +400,66 @@
</Border> </Border>
<!-- Hotkey --> <!-- Hotkey -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" <Border Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="10"
Padding="20"> Padding="20">
<StackPanel Spacing="12"> <StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="&#xE92E;" <TextBlock Text="&#xE92E;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{StaticResource ClipForgeAccentBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="Clip Hotkey" <TextBlock Text="Clip Hotkey"
FontSize="14" FontSize="14"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<TextBlock Text="Click the key below, then press your desired combination." <TextBlock Text="Click the key below, then press your desired combination."
FontSize="12" FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<Button x:Name="HotkeyRecorderButton" <Button x:Name="HotkeyRecorderButton"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Click="HotkeyRecorderButton_Click" Click="HotkeyRecorderButton_Click"
Background="#1a1a2e" Background="{StaticResource ClipForgeSidebarBrush}"
BorderThickness="0" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
CornerRadius="6" BorderThickness="1"
CornerRadius="8"
Padding="16,10" Padding="16,10"
MinWidth="140" MinWidth="140"
FontFamily="Consolas" FontFamily="Consolas"
FontSize="14" FontSize="14"
FontWeight="Bold" FontWeight="Bold"
Foreground="#E8FF47" Foreground="{StaticResource ClipForgeAccentBrush}"
Content="Alt + F9"/> Content="Alt + F9"/>
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Startup with Windows --> <!-- Startup with Windows -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" <Border Background="{StaticResource ClipForgeCardBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderBrush="{StaticResource ClipForgeCardBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="10"
Padding="20"> Padding="20">
<StackPanel Spacing="12"> <StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="&#xE7E8;" <TextBlock Text="&#xE7E8;"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{StaticResource ClipForgeAccentBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="Startup" <TextBlock Text="Startup"
FontSize="14" FontSize="14"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{StaticResource ClipForgeTextPrimaryBrush}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<TextBlock Text="Launch ClipForge automatically when Windows starts." <TextBlock Text="Launch ClipForge automatically when Windows starts."
FontSize="12" FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> Foreground="{StaticResource ClipForgeTextSecondaryBrush}"/>
<ToggleSwitch x:Name="StartupToggle" <ToggleSwitch x:Name="StartupToggle"
OnContent="Enabled" OnContent="Enabled"
OffContent="Disabled" OffContent="Disabled"
@@ -420,7 +470,7 @@
<!-- Save button --> <!-- Save button -->
<Button x:Name="SaveSettingsButton" <Button x:Name="SaveSettingsButton"
Content="Save Settings" Content="Save Settings"
Style="{StaticResource AccentButtonStyle}" Style="{StaticResource ClipForgeAccentButtonStyle}"
Click="SaveSettings_Click" Click="SaveSettings_Click"
HorizontalAlignment="Left"/> HorizontalAlignment="Left"/>

View File

@@ -255,7 +255,7 @@ namespace ClipForge
{ {
if (_isRecordingHotkey) return; if (_isRecordingHotkey) return;
_isRecordingHotkey = true; _isRecordingHotkey = true;
HotkeyRecorderButton.Content = "Press any key..."; HotkeyRecorderButton.Content = "Press any key... (Esc to cancel)";
HotkeyRecorderButton.Focus(FocusState.Programmatic); HotkeyRecorderButton.Focus(FocusState.Programmatic);
HotkeyRecorderButton.KeyDown += OnHotkeyCaptureKeyDown; HotkeyRecorderButton.KeyDown += OnHotkeyCaptureKeyDown;
} }
@@ -265,26 +265,72 @@ namespace ClipForge
if (!_isRecordingHotkey) return; if (!_isRecordingHotkey) return;
e.Handled = true; e.Handled = true;
var key = e.Key; try
if (HotkeyHelper.IsModifierKey(key)) {
return; // wait for a non-modifier key var key = e.Key;
// Escape cancels without changing the hotkey
if (key == VirtualKey.Escape)
{
StopRecordingHotkey(restoreDisplay: true);
return;
}
if (HotkeyHelper.IsModifierKey(key))
return; // wait for a non-modifier key
if (key == VirtualKey.None)
return;
uint mod = 0; uint mod = 0;
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) mod |= HotkeyHelper.MOD_CONTROL; if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) mod |= HotkeyHelper.MOD_CONTROL;
if ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0) mod |= HotkeyHelper.MOD_ALT; if ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0) mod |= HotkeyHelper.MOD_ALT;
if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) mod |= HotkeyHelper.MOD_SHIFT; if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) mod |= HotkeyHelper.MOD_SHIFT;
if ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0) mod |= HotkeyHelper.MOD_WIN; if ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0) mod |= HotkeyHelper.MOD_WIN;
uint vk = (uint)key; uint vk = (uint)key;
_settingsService.Settings.HotkeyModifiers = (int)mod; _settingsService.Settings.HotkeyModifiers = (int)mod;
_settingsService.Settings.HotkeyVirtualKey = (int)vk; _settingsService.Settings.HotkeyVirtualKey = (int)vk;
var ok = _hotkeyService.UpdateHotkey(mod, vk); var display = HotkeyHelper.ToDisplayString(mod, vk);
var display = HotkeyHelper.ToDisplayString(mod, vk); StopRecordingHotkey(restoreDisplay: false);
HotkeyRecorderButton.Content = ok ? display : display + " (in use?)"; HotkeyRecorderButton.Content = display;
// Defer registration so the key is released first; otherwise the new hotkey can fire
// immediately and re-enter (e.g. OnClipRequested) and crash.
_ = DelayedHotkeyRegister(mod, vk, display);
}
catch (Exception ex)
{
StopRecordingHotkey(restoreDisplay: true);
_ = ShowToastAsync("⚠ " + ex.Message);
}
}
private void StopRecordingHotkey(bool restoreDisplay)
{
HotkeyRecorderButton.KeyDown -= OnHotkeyCaptureKeyDown; HotkeyRecorderButton.KeyDown -= OnHotkeyCaptureKeyDown;
_isRecordingHotkey = false; _isRecordingHotkey = false;
if (restoreDisplay)
{
var s = _settingsService.Settings;
HotkeyRecorderButton.Content = HotkeyHelper.ToDisplayString((uint)s.HotkeyModifiers, (uint)s.HotkeyVirtualKey);
}
}
private async System.Threading.Tasks.Task DelayedHotkeyRegister(uint mod, uint vk, string display)
{
await System.Threading.Tasks.Task.Delay(300);
App.MainQueue?.TryEnqueue(() =>
{
try
{
var ok = _hotkeyService.UpdateHotkey(mod, vk);
HotkeyRecorderButton.Content = ok ? display : display + " (in use?)";
}
catch (Exception ex)
{
HotkeyRecorderButton.Content = display;
_ = ShowToastAsync("⚠ " + ex.Message);
}
});
} }
// --- NAV --- // --- NAV ---
@@ -292,12 +338,16 @@ namespace ClipForge
{ {
ClipsPage.Visibility = Visibility.Visible; ClipsPage.Visibility = Visibility.Visible;
SettingsPage.Visibility = Visibility.Collapsed; SettingsPage.Visibility = Visibility.Collapsed;
NavClips.Style = (Microsoft.UI.Xaml.Style)Application.Current.Resources["ClipForgeNavButtonSelectedStyle"];
NavSettings.Style = (Microsoft.UI.Xaml.Style)Application.Current.Resources["ClipForgeNavButtonStyle"];
} }
private void NavSettings_Click(object sender, RoutedEventArgs e) private void NavSettings_Click(object sender, RoutedEventArgs e)
{ {
ClipsPage.Visibility = Visibility.Collapsed; ClipsPage.Visibility = Visibility.Collapsed;
SettingsPage.Visibility = Visibility.Visible; SettingsPage.Visibility = Visibility.Visible;
NavSettings.Style = (Microsoft.UI.Xaml.Style)Application.Current.Resources["ClipForgeNavButtonSelectedStyle"];
NavClips.Style = (Microsoft.UI.Xaml.Style)Application.Current.Resources["ClipForgeNavButtonStyle"];
} }
private void RecordButton_Click(object sender, RoutedEventArgs e) private void RecordButton_Click(object sender, RoutedEventArgs e)