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: 6 additions & 0 deletions src/CodingWithCalvin.VSToolbox/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>

<!-- Override ToggleSwitch accent color to purple -->
<SolidColorBrush x:Key="ToggleSwitchFillOn" Color="#68217A"/>
<SolidColorBrush x:Key="ToggleSwitchFillOnPointerOver" Color="#7B2A91"/>
<SolidColorBrush x:Key="ToggleSwitchFillOnPressed" Color="#551A66"/>
<SolidColorBrush x:Key="ToggleSwitchFillOnDisabled" Color="#4D68217A"/>

<!-- Converters -->
<converters:NullToBoolConverter x:Key="NullToBoolConverter"/>
<converters:FilePathToImageConverter x:Key="FilePathToImageConverter"/>
Expand Down
26 changes: 21 additions & 5 deletions src/CodingWithCalvin.VSToolbox/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ public partial class App : Application
private Window? _window;
private AppWindow? _appWindow;
private TrayIconService? _trayIconService;
private SettingsService? _settingsService;

public Window? MainWindow => _window;
public SettingsService Settings => _settingsService ??= new SettingsService();

public App()
{
Expand Down Expand Up @@ -122,7 +124,11 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
// Set window size and position to bottom-right
PositionWindowBottomRight(540, 600);

_window.Activate();
// Only show window if not starting minimized
if (!Settings.StartMinimized)
{
_window.Activate();
}
}

private void ConfigureCustomTitleBar()
Expand Down Expand Up @@ -178,11 +184,21 @@ private void PositionWindowBottomRight(int width, int height)

private void OnAppWindowClosing(AppWindow sender, AppWindowClosingEventArgs args)
{
// Prevent window from closing - hide it instead
args.Cancel = true;
if (Settings.CloseToTray)
{
// Prevent window from closing - hide it instead
args.Cancel = true;

// Hide the window
_appWindow?.Hide();
// Hide the window
_appWindow?.Hide();
}
else
{
// Actually close the app
_trayIconService?.Dispose();
_mutex?.ReleaseMutex();
_mutex?.Dispose();
}
}

private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
Expand Down
169 changes: 169 additions & 0 deletions src/CodingWithCalvin.VSToolbox/Services/SettingsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using System.Text.Json;
using Microsoft.Win32;

namespace CodingWithCalvin.VSToolbox.Services;

public class SettingsService
{
private const string StartupRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string AppName = "VSToolbox";
private const string SettingsFileName = "settings.json";

private static readonly string SettingsFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"CodingWithCalvin.VSToolbox",
SettingsFileName);

private static SettingsData? _settings;
private static readonly object _lock = new();

private static SettingsData Settings
{
get
{
if (_settings is null)
{
lock (_lock)
{
_settings ??= LoadSettings();
}
}
return _settings;
}
}

public bool LaunchOnStartup
{
get => Settings.LaunchOnStartup;
set
{
Settings.LaunchOnStartup = value;
SaveSettings();
UpdateStartupRegistration(value);
}
}

public bool StartMinimized
{
get => Settings.StartMinimized;
set
{
Settings.StartMinimized = value;
SaveSettings();
}
}

public bool MinimizeToTray
{
get => Settings.MinimizeToTray;
set
{
Settings.MinimizeToTray = value;
SaveSettings();
}
}

public bool CloseToTray
{
get => Settings.CloseToTray;
set
{
Settings.CloseToTray = value;
SaveSettings();
}
}

private static SettingsData LoadSettings()
{
try
{
if (File.Exists(SettingsFilePath))
{
var json = File.ReadAllText(SettingsFilePath);
return JsonSerializer.Deserialize<SettingsData>(json) ?? new SettingsData();
}
}
catch
{
// If we can't load settings, return defaults
}
return new SettingsData();
}

private static void SaveSettings()
{
try
{
var directory = Path.GetDirectoryName(SettingsFilePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}

var json = JsonSerializer.Serialize(_settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(SettingsFilePath, json);
}
catch
{
// Silently fail if we can't save settings
}
}

private static void UpdateStartupRegistration(bool enable)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupRegistryKey, writable: true);
if (key is null) return;

if (enable)
{
var exePath = Environment.ProcessPath;
if (!string.IsNullOrEmpty(exePath))
{
key.SetValue(AppName, $"\"{exePath}\"");
}
}
else
{
key.DeleteValue(AppName, throwOnMissingValue: false);
}
}
catch
{
// Silently fail if we can't access the registry
}
}

/// <summary>
/// Checks if the app is currently registered for startup and syncs the setting.
/// Call this on app startup to ensure settings match actual state.
/// </summary>
public void SyncStartupSetting()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupRegistryKey, writable: false);
var isRegistered = key?.GetValue(AppName) is not null;

// Update setting to match registry state
if (Settings.LaunchOnStartup != isRegistered)
{
Settings.LaunchOnStartup = isRegistered;
SaveSettings();
}
}
catch
{
// Silently fail
}
}

private class SettingsData
{
public bool LaunchOnStartup { get; set; }
public bool StartMinimized { get; set; }
public bool MinimizeToTray { get; set; } = true;
public bool CloseToTray { get; set; } = true;
}
}
102 changes: 92 additions & 10 deletions src/CodingWithCalvin.VSToolbox/Views/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,11 @@
<TextBlock Text="Installed"/>
</StackPanel>
</RadioButton>
<!-- Settings tab hidden for now -->
<RadioButton
x:Name="SettingsTab"
GroupName="MainTabs"
Checked="OnTabChanged"
Style="{StaticResource PillTabStyle}"
Visibility="Collapsed">
Style="{StaticResource PillTabStyle}">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon Glyph="&#xE713;" FontSize="14"/>
<TextBlock Text="Settings"/>
Expand Down Expand Up @@ -241,16 +239,100 @@

<!-- Settings Tab Content -->
<ScrollViewer x:Name="SettingsContent" Grid.Row="1" Visibility="Collapsed">
<StackPanel Spacing="24" Padding="0,0,16,0">
<!-- Placeholder for settings content -->
<StackPanel Spacing="8" Padding="0,0,16,0">
<!-- Startup Section -->
<TextBlock
Text="Settings"
FontSize="20"
Text="Startup"
FontSize="14"
FontWeight="SemiBold"
Style="{StaticResource AppTitleStyle}"/>
Margin="0,0,0,4"/>

<!-- Launch on startup -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" CornerRadius="6" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="Launch on startup" FontWeight="SemiBold"/>
<TextBlock Text="Start Visual Studio Toolbox when Windows starts"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
</StackPanel>
<ToggleSwitch x:Name="LaunchOnStartupToggle"
Grid.Column="1"
Toggled="OnLaunchOnStartupToggled"
OnContent="" OffContent=""/>
</Grid>
</Border>

<!-- Start minimized -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" CornerRadius="6" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="Start minimized" FontWeight="SemiBold"/>
<TextBlock Text="Start the app minimized to the system tray"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
</StackPanel>
<ToggleSwitch x:Name="StartMinimizedToggle"
Grid.Column="1"
Toggled="OnStartMinimizedToggled"
OnContent="" OffContent=""/>
</Grid>
</Border>

<!-- Behavior Section -->
<TextBlock
Text="Settings options will appear here."
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
Text="Behavior"
FontSize="14"
FontWeight="SemiBold"
Margin="0,8,0,4"/>

<!-- Minimize to tray -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" CornerRadius="6" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="Minimize to tray" FontWeight="SemiBold"/>
<TextBlock Text="Hide to system tray when minimizing"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
</StackPanel>
<ToggleSwitch x:Name="MinimizeToTrayToggle"
Grid.Column="1"
Toggled="OnMinimizeToTrayToggled"
OnContent="" OffContent=""/>
</Grid>
</Border>

<!-- Close to tray -->
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" CornerRadius="6" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="Close to tray" FontWeight="SemiBold"/>
<TextBlock Text="Hide to system tray instead of closing the app"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
</StackPanel>
<ToggleSwitch x:Name="CloseToTrayToggle"
Grid.Column="1"
Toggled="OnCloseToTrayToggled"
OnContent="" OffContent=""/>
</Grid>
</Border>
</StackPanel>
</ScrollViewer>

Expand Down
Loading