Add config import and export

This commit is contained in:
Ray Berezowski
2026-06-02 21:28:59 -04:00
parent 835ce2159b
commit b0fc1b4679
3 changed files with 153 additions and 8 deletions

View File

@@ -19,15 +19,17 @@
<Border DockPanel.Dock="Bottom" BorderThickness="1,0,0,0" Padding="0,12,0,0" <Border DockPanel.Dock="Bottom" BorderThickness="1,0,0,0" Padding="0,12,0,0"
BorderBrush="{DynamicResource AppBorderBrush}"> BorderBrush="{DynamicResource AppBorderBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <WrapPanel HorizontalAlignment="Right">
<Button Content="Save" Click="Save_Click" Margin="0,0,8,0"/> <Button Content="Save" Click="Save_Click" Margin="0,0,8,0"/>
<Button Content="Backup Config" Click="BackupConfig_Click" Margin="0,0,8,0"/> <Button Content="Backup Config" Click="BackupConfig_Click" Margin="0,0,8,0"/>
<Button Content="Restore Config" Click="RestoreConfig_Click" Margin="0,0,8,0"/> <Button Content="Restore Config" Click="RestoreConfig_Click" Margin="0,0,8,0"/>
<Button Content="Export Config" Click="ExportConfig_Click" Margin="0,0,8,0"/>
<Button Content="Import Config" Click="ImportConfig_Click" Margin="0,0,8,0"/>
<Button Content="Reload Config" Click="ReloadConfig_Click" Margin="0,0,8,0"/> <Button Content="Reload Config" Click="ReloadConfig_Click" Margin="0,0,8,0"/>
<Button Content="Open Config Folder" Click="OpenConfigFolder_Click" Margin="0,0,8,0"/> <Button Content="Open Config Folder" Click="OpenConfigFolder_Click" Margin="0,0,8,0"/>
<Button Content="About" Click="About_Click" Margin="0,0,8,0"/> <Button Content="About" Click="About_Click" Margin="0,0,8,0"/>
<Button Content="Close" Click="Close_Click"/> <Button Content="Close" Click="Close_Click"/>
</StackPanel> </WrapPanel>
</Border> </Border>
<Grid> <Grid>

View File

@@ -636,6 +636,77 @@ public partial class MainWindow : Window
} }
} }
private void ExportConfig_Click(object sender, RoutedEventArgs e)
{
if (controller is null)
{
return;
}
ApplyConfigFields();
using SaveFileDialog dialog = new()
{
Title = "Export launcher config",
Filter = "JSON config files|*.json|All files|*.*",
FileName = $"TaskbarLauncher-config-{DateTime.Now:yyyyMMdd-HHmmss}.json",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
};
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
return;
}
try
{
string exportPath = ConfigService.ExportTo(dialog.FileName, controller.Config);
StatusText.Text = $"Exported config to {exportPath}";
}
catch (Exception ex)
{
MessageBox.Show($"Could not export config.\n\n{ex.Message}", "Taskbar Launcher");
}
}
private void ImportConfig_Click(object sender, RoutedEventArgs e)
{
using OpenFileDialog dialog = new()
{
Title = "Import launcher config",
Filter = "JSON config files|*.json|All files|*.*",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
};
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
return;
}
MessageBoxResult result = MessageBox.Show(
"Import this config file and replace the current launcher config?\n\nA local backup will be created first.",
"Taskbar Launcher",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
{
return;
}
try
{
string backupPath = ConfigService.Backup();
ConfigService.ImportFrom(dialog.FileName);
controller?.ReloadConfig();
RefreshView();
StatusText.Text = $"Imported config from {dialog.FileName}. Previous config backed up to {backupPath}";
}
catch (Exception ex)
{
MessageBox.Show($"Could not import config.\n\n{ex.Message}", "Taskbar Launcher");
}
}
private List<int>? FindItemPath(LauncherItem item) private List<int>? FindItemPath(LauncherItem item)
{ {
if (controller is null) if (controller is null)

View File

@@ -61,18 +61,35 @@ public static class ConfigService
return backupPath; return backupPath;
} }
public static LauncherConfig Restore(string sourcePath) public static string ExportTo(string destinationPath, LauncherConfig config)
{ {
if (!File.Exists(sourcePath)) if (string.IsNullOrWhiteSpace(destinationPath))
{ {
throw new FileNotFoundException("The selected config file was not found.", sourcePath); throw new ArgumentException("Choose a destination path for the exported config.", nameof(destinationPath));
} }
string json = File.ReadAllText(sourcePath); string? destinationFolder = Path.GetDirectoryName(destinationPath);
LauncherConfig config = JsonSerializer.Deserialize<LauncherConfig>(json, JsonOptions) if (!string.IsNullOrWhiteSpace(destinationFolder))
?? throw new InvalidDataException("The selected config file could not be read."); {
Directory.CreateDirectory(destinationFolder);
}
NormalizeDisplayModes(config); NormalizeDisplayModes(config);
string json = JsonSerializer.Serialize(config, JsonOptions);
File.WriteAllText(destinationPath, json);
return destinationPath;
}
public static LauncherConfig ImportFrom(string sourcePath)
{
LauncherConfig config = ReadConfigFile(sourcePath);
Save(config);
return config;
}
public static LauncherConfig Restore(string sourcePath)
{
LauncherConfig config = ReadConfigFile(sourcePath);
Save(config); Save(config);
return config; return config;
} }
@@ -135,6 +152,61 @@ public static class ConfigService
return config; return config;
} }
private static LauncherConfig ReadConfigFile(string sourcePath)
{
if (!File.Exists(sourcePath))
{
throw new FileNotFoundException("The selected config file was not found.", sourcePath);
}
string json = File.ReadAllText(sourcePath);
LauncherConfig config;
try
{
config = JsonSerializer.Deserialize<LauncherConfig>(json, JsonOptions)
?? throw new InvalidDataException("The selected config file could not be read.");
}
catch (JsonException ex)
{
throw new InvalidDataException("The selected file is not a valid Taskbar Launcher config.", ex);
}
ValidateConfig(config);
NormalizeDisplayModes(config);
return config;
}
private static void ValidateConfig(LauncherConfig config)
{
if (config.Items is null)
{
throw new InvalidDataException("The selected config is missing launcher items.");
}
foreach (LauncherItem item in config.Items)
{
ValidateItem(item);
}
}
private static void ValidateItem(LauncherItem item)
{
if (item.Children is null)
{
throw new InvalidDataException($"The item '{item.Title}' has an invalid children list.");
}
if (!Enum.IsDefined(item.Type))
{
throw new InvalidDataException($"The item '{item.Title}' has an invalid type.");
}
foreach (LauncherItem child in item.Children)
{
ValidateItem(child);
}
}
private static void NormalizeDisplayModes(LauncherConfig config) private static void NormalizeDisplayModes(LauncherConfig config)
{ {
foreach (LauncherItem item in config.Items) foreach (LauncherItem item in config.Items)