Add theme import and export function

This commit is contained in:
Michael Bucari-Tovo 2025-03-19 16:59:58 -06:00 committed by MBucari
parent f183b587b8
commit aab4f1d9d6
5 changed files with 116 additions and 39 deletions

View File

@ -8,7 +8,7 @@
<ContentControl.Styles> <ContentControl.Styles>
<Style Selector="controls|GroupBox"> <Style Selector="controls|GroupBox">
<Setter Property="BorderBrush" Value="{DynamicResource SystemBaseMediumLowColor}" /> <Setter Property="BorderBrush" Value="{DynamicResource SystemBaseMediumColor}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" /> <Setter Property="CornerRadius" Value="3" />
<Setter Property="Template"> <Setter Property="Template">

View File

@ -19,13 +19,13 @@
<TextBox Margin="5,0" Text="This is an editable text box" /> <TextBox Margin="5,0" Text="This is an editable text box" />
<TextBox Margin="5,0" Text="This is a read-only text box" IsReadOnly="True" /> <TextBox Margin="5,0" Text="This is a read-only text box" IsReadOnly="True" />
<NumericUpDown Margin="5,0" Value="100" /> <NumericUpDown Margin="5,0" Value="100" />
<controls:LinkLabel VerticalAlignment="Center" Margin="5,0" Text="This is an unvisited link" /> <controls:LinkLabel VerticalAlignment="Center" Margin="5,5" Text="This is an unvisited link" />
<controls:LinkLabel VerticalAlignment="Center" Margin="5,0" Text="This is a visited link" Foreground="{DynamicResource HyperlinkVisited}" /> <controls:LinkLabel VerticalAlignment="Center" Margin="5,5" Text="This is a visited link" Foreground="{DynamicResource HyperlinkVisited}" />
<StackPanel Margin="5,0" Height="15" Orientation="Horizontal"> <StackPanel Margin="5,0" Height="25" Orientation="Horizontal">
<StackPanel.Styles> <StackPanel.Styles>
<Style Selector="Path"> <Style Selector="Path">
<Setter Property="Stretch" Value="Uniform" /> <Setter Property="Stretch" Value="Uniform" />
<Setter Property="Margin" Value="3,0" /> <Setter Property="Margin" Value="3,5" />
<Setter Property="Fill" Value="{DynamicResource IconFill}" /> <Setter Property="Fill" Value="{DynamicResource IconFill}" />
</Style> </Style>
</StackPanel.Styles> </StackPanel.Styles>

View File

@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="850" d:DesignHeight="850" mc:Ignorable="d" d:DesignWidth="965" d:DesignHeight="850"
Width="450" Height="450" Width="965" Height="850"
x:Class="LibationAvalonia.Dialogs.ThemePickerDialog" x:Class="LibationAvalonia.Dialogs.ThemePickerDialog"
xmlns:controls="clr-namespace:LibationAvalonia.Controls" xmlns:controls="clr-namespace:LibationAvalonia.Controls"
Title="Theme Editor"> Title="Theme Editor">
@ -11,14 +11,21 @@
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<controls:ThemePreviewControl Name="ThemePickerPreviewControl" Margin="5" Grid.Column="1" /> <controls:ThemePreviewControl Name="ThemePickerPreviewControl" Margin="5" Grid.Column="1" />
<Grid <Grid
RowDefinitions="*,Auto"> RowDefinitions="*,Auto,Auto">
<Grid.Styles>
<Style Selector="Button">
<Setter Property="Height" Value="30" />
<Setter Property="Padding" Value="20,0" />
<Setter Property="Margin" Value="5" />
</Style>
</Grid.Styles>
<DataGrid <DataGrid
Grid.Row="0"
GridLinesVisibility="All" GridLinesVisibility="All"
Margin="5" Margin="5"
IsReadOnly="False" IsReadOnly="False"
ItemsSource="{Binding ThemeColors}"> ItemsSource="{Binding ThemeColors}">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Color"> <DataGridTemplateColumn Width="Auto" Header="Color">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
@ -27,48 +34,44 @@
Color="{Binding ThemeColor, Mode=TwoWay}" /> Color="{Binding ThemeColor, Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTextColumn <DataGridTextColumn
Width="Auto" Width="Auto"
Binding="{Binding ThemeItemName, Mode=TwoWay}" Binding="{Binding ThemeItemName, Mode=TwoWay}"
Header="Theme Item"/> Header="Theme Item"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<Grid <Grid
Grid.Row="1" Grid.Row="1"
ColumnDefinitions="Auto,Auto,Auto,*,Auto"> ColumnDefinitions="Auto,*,Auto">
<Button
<Grid.Styles> Grid.Column="0"
<Style Selector="Button"> Content="Import Theme"
<Setter Property="Height" Value="30" /> Command="{Binding ImportTheme}" />
<Setter Property="Padding" Value="20,0" /> <Button
<Setter Property="Margin" Value="5" /> Grid.Column="2"
</Style> Content="Export Theme"
</Grid.Styles> Command="{Binding ExportTheme}" />
</Grid>
<Grid
Grid.Row="2"
ColumnDefinitions="Auto,Auto,Auto,*,Auto">
<Button <Button
Grid.Column="0" Grid.Column="0"
Content="Cancel" Content="Cancel"
Command="{Binding CancelAndClose}" /> Command="{Binding CancelAndClose}" />
<Button <Button
Grid.Column="1" Grid.Column="1"
Content="Reset" Content="Reset"
Command="{Binding ResetColors}" /> Command="{Binding ResetColors}" />
<Button <Button
Grid.Column="2" Grid.Column="2"
Content="Defaults" Content="Defaults"
Command="{Binding LoadDefaultColors}" /> Command="{Binding LoadDefaultColors}" />
<Button <Button
Grid.Column="4" Grid.Column="4"
Content="Save" Content="Save"
Command="{Binding SaveAndCloseAsync}" /> Command="{Binding SaveAndCloseAsync}" />
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -7,6 +7,7 @@ using LibationAvalonia.Themes;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Platform.Storage;
#nullable enable #nullable enable
namespace LibationAvalonia.Dialogs; namespace LibationAvalonia.Dialogs;
@ -15,13 +16,14 @@ public partial class ThemePickerDialog : DialogWindow
{ {
protected DataGridCollectionView ThemeColors { get; } protected DataGridCollectionView ThemeColors { get; }
private ChardonnayTheme ExistingTheme { get; } = ChardonnayTheme.GetLiveTheme(); private ChardonnayTheme ExistingTheme { get; } = ChardonnayTheme.GetLiveTheme();
private ChardonnayTheme WorkingTheme { get; }
public ThemePickerDialog() public ThemePickerDialog()
{ {
InitializeComponent(); InitializeComponent();
CancelOnEscape = false; CancelOnEscape = false;
var workingTheme = (ChardonnayTheme)ExistingTheme.Clone(); WorkingTheme = (ChardonnayTheme)ExistingTheme.Clone();
ThemeColors = new(EnumerateThemeItemColors(workingTheme, ActualThemeVariant)); ThemeColors = new(EnumerateThemeItemColors());
DataContext = this; DataContext = this;
Closing += ThemePickerDialog_Closing; Closing += ThemePickerDialog_Closing;
@ -36,6 +38,78 @@ public partial class ThemePickerDialog : DialogWindow
} }
} }
protected async Task ImportTheme()
{
try
{
var openFileDialogOptions = new FilePickerOpenOptions
{
Title = $"Select the ChardonnayTheme.json file",
AllowMultiple = false,
FileTypeFilter =
[
new("JSON files (*.json)")
{
Patterns = ["*.json"],
AppleUniformTypeIdentifiers = ["public.json"]
}
]
};
var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
var selectedFile = selectedFiles.SingleOrDefault()?.TryGetLocalPath();
if (selectedFile is null) return;
using (var theme = new ChardonnayThemePersister(selectedFile))
{
theme.Target.ApplyTheme(ActualThemeVariant);
}
await MessageBox.Show(this, "Theme imported and applied", "Theme Imported");
}
catch (Exception ex)
{
await MessageBox.ShowAdminAlert(this, "Error attempting to import your chardonnay theme.", "Error Importing", ex);
}
}
protected async Task ExportTheme()
{
try
{
var options = new FilePickerSaveOptions
{
Title = "Where to export Library",
SuggestedFileName = $"ChardonnayTheme",
DefaultExtension = "json",
ShowOverwritePrompt = true,
FileTypeChoices =
[
new("JSON files (*.json)")
{
Patterns = ["*.json"],
AppleUniformTypeIdentifiers = ["public.json"]
},
new("All files (*.*)") { Patterns = ["*"] }
]
};
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
if (selectedFile is null) return;
using (var theme = new ChardonnayThemePersister(WorkingTheme, selectedFile))
theme.Target.Save();
await MessageBox.Show(this, "Theme exported to:\r\n" + selectedFile, "Theme Exported");
}
catch (Exception ex)
{
await MessageBox.ShowAdminAlert(this, "Error attempting to export your chardonnay theme.", "Error Exporting", ex);
}
}
protected override void CancelAndClose() protected override void CancelAndClose()
{ {
ExistingTheme.ApplyTheme(ActualThemeVariant); ExistingTheme.ApplyTheme(ActualThemeVariant);
@ -83,17 +157,17 @@ public partial class ThemePickerDialog : DialogWindow
} }
} }
private static IEnumerable<ThemeItemColor> EnumerateThemeItemColors(ChardonnayTheme workingTheme, ThemeVariant themeVariant) private IEnumerable<ThemeItemColor> EnumerateThemeItemColors()
=> workingTheme => WorkingTheme
.GetThemeColors(themeVariant) .GetThemeColors(ActualThemeVariant)
.Select(kvp => new ThemeItemColor .Select(kvp => new ThemeItemColor
{ {
ThemeItemName = kvp.Key, ThemeItemName = kvp.Key,
ThemeColor = kvp.Value, ThemeColor = kvp.Value,
ColorSetter = c => ColorSetter = c =>
{ {
workingTheme.SetColor(themeVariant, kvp.Key, c); WorkingTheme.SetColor(ActualThemeVariant, kvp.Key, c);
workingTheme.ApplyTheme(themeVariant); WorkingTheme.ApplyTheme(ActualThemeVariant);
} }
}); });

View File

@ -12,9 +12,9 @@ public class ChardonnayThemePersister : JsonFilePersister<ChardonnayTheme>
{ {
public static string jsonPath = System.IO.Path.Combine(Configuration.Instance.LibationFiles, "ChardonnayTheme.json"); public static string jsonPath = System.IO.Path.Combine(Configuration.Instance.LibationFiles, "ChardonnayTheme.json");
private ChardonnayThemePersister(string path) public ChardonnayThemePersister(string path)
: base(path, null) { } : base(path, null) { }
private ChardonnayThemePersister(ChardonnayTheme target, string path) public ChardonnayThemePersister(ChardonnayTheme target, string path)
: base(target, path, null) { } : base(target, path, null) { }
protected override JsonSerializerSettings GetSerializerSettings() protected override JsonSerializerSettings GetSerializerSettings()