mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-18 00:21:33 -07:00
* add custom bootstrappers * add avalonedit to licenses page * add gif support * add stretch & stretchdirection to images * dont create a bitmapimage for gifs * remove maxheight and maxwidth sets * remove comment * add isenabled * add more textblock properties * add markdowntextblocks * update how transform elements are stored * overhaul textbox content * dont set fontsize if not set * fix warnings * add foreground property to control * add background property to textblock * count descendants and increase element cap * add auto complete * dont display completion window if there is no data * sort schema elements and types * make ! close the completion window * add end tag auto complete * fix pos being wrong * dont treat comments as elements * add imagebrushes * follow same conventions as brushes * fix exception messages * fix them again * update schema * fix crash * now it works * wrong attribute name * add solidcolorbrush * move converters into a separate file * add lineargradientbrushes * unify handlers * update schema * add fake BloxstrapCustomBootstrapper * stop adding an extra end character * add property element auto-complete * add title attribute to custombloxstrapbootstrapper * add shapes * add string translation support * use default wpf size instead of 100x100 * update min height of window * fix verticalalignment not working * uncap height and width * add effects * move transformation handler inside frameworkelement * fix title bar effect & transformation removal * add more frameworkelement properties * add layout transform * add font properties to control * improve window border stuff * make sure file contents are in CRLF * add cornerradius to progress bar * add progressring * Update wpfui * update schema * update function names * add children check to content * make sure only one content is defined * add fontfamily * update schema * only allow file uris for images * disable backdrop * move text setter to textblock handler from base * split up creator into multiple files * turn version into a constant * add grids * cleanup converters * add IgnoreTitleBarInset * add Version to schema * reveal custom bootstrapper stuff on selection * increase listbox height * only set statustext binding in textblock * update ui * rename ZIndex to Panel.ZIndex * add stackpanel * add border * fix being unable to apply transforms on grids * rearrange and add new editor button * use snackbars for saving * add close confirmation message * use viewmodel variable * remove pointless onpropertychanged call * add version string format * start editor window in the centre * update licenses page also resized the about window so everything could fit nicely * fix border not inheriting frameworkelement * add WindowCornerPreference * add the import dialog * add an export theme button * update version number * localise CustomDialog exceptions * localise custom theme editor * localise custom theme add dialog * localise frontend * localise appearance menu page * change customtheme error strings namespace * change icons on appearance page * update button margin on appearance page
316 lines
10 KiB
C#
316 lines
10 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
|
|
using Microsoft.Win32;
|
|
|
|
using Bloxstrap.UI.Elements.Settings;
|
|
using Bloxstrap.UI.Elements.Editor;
|
|
using Bloxstrap.UI.Elements.Dialogs;
|
|
|
|
namespace Bloxstrap.UI.ViewModels.Settings
|
|
{
|
|
public class AppearanceViewModel : NotifyPropertyChangedViewModel
|
|
{
|
|
private readonly Page _page;
|
|
|
|
public ICommand PreviewBootstrapperCommand => new RelayCommand(PreviewBootstrapper);
|
|
public ICommand BrowseCustomIconLocationCommand => new RelayCommand(BrowseCustomIconLocation);
|
|
|
|
public ICommand AddCustomThemeCommand => new RelayCommand(AddCustomTheme);
|
|
public ICommand DeleteCustomThemeCommand => new RelayCommand(DeleteCustomTheme);
|
|
public ICommand RenameCustomThemeCommand => new RelayCommand(RenameCustomTheme);
|
|
public ICommand EditCustomThemeCommand => new RelayCommand(EditCustomTheme);
|
|
public ICommand ExportCustomThemeCommand => new RelayCommand(ExportCustomTheme);
|
|
|
|
private void PreviewBootstrapper()
|
|
{
|
|
IBootstrapperDialog dialog = App.Settings.Prop.BootstrapperStyle.GetNew();
|
|
|
|
if (App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.ByfronDialog)
|
|
dialog.Message = Strings.Bootstrapper_StylePreview_ImageCancel;
|
|
else
|
|
dialog.Message = Strings.Bootstrapper_StylePreview_TextCancel;
|
|
|
|
dialog.CancelEnabled = true;
|
|
dialog.ShowBootstrapper();
|
|
}
|
|
|
|
private void BrowseCustomIconLocation()
|
|
{
|
|
var dialog = new OpenFileDialog
|
|
{
|
|
Filter = $"{Strings.Menu_IconFiles}|*.ico"
|
|
};
|
|
|
|
if (dialog.ShowDialog() != true)
|
|
return;
|
|
|
|
CustomIconLocation = dialog.FileName;
|
|
OnPropertyChanged(nameof(CustomIconLocation));
|
|
}
|
|
|
|
public AppearanceViewModel(Page page)
|
|
{
|
|
_page = page;
|
|
|
|
foreach (var entry in BootstrapperIconEx.Selections)
|
|
Icons.Add(new BootstrapperIconEntry { IconType = entry });
|
|
|
|
PopulateCustomThemes();
|
|
}
|
|
|
|
public IEnumerable<Theme> Themes { get; } = Enum.GetValues(typeof(Theme)).Cast<Theme>();
|
|
|
|
public Theme Theme
|
|
{
|
|
get => App.Settings.Prop.Theme;
|
|
set
|
|
{
|
|
App.Settings.Prop.Theme = value;
|
|
((MainWindow)Window.GetWindow(_page)!).ApplyTheme();
|
|
}
|
|
}
|
|
|
|
public static List<string> Languages => Locale.GetLanguages();
|
|
|
|
public string SelectedLanguage
|
|
{
|
|
get => Locale.SupportedLocales[App.Settings.Prop.Locale];
|
|
set => App.Settings.Prop.Locale = Locale.GetIdentifierFromName(value);
|
|
}
|
|
|
|
public IEnumerable<BootstrapperStyle> Dialogs { get; } = BootstrapperStyleEx.Selections;
|
|
|
|
public BootstrapperStyle Dialog
|
|
{
|
|
get => App.Settings.Prop.BootstrapperStyle;
|
|
set
|
|
{
|
|
App.Settings.Prop.BootstrapperStyle = value;
|
|
OnPropertyChanged(nameof(CustomThemesExpanded)); // TODO: only fire when needed
|
|
}
|
|
}
|
|
|
|
public bool CustomThemesExpanded => App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.CustomDialog;
|
|
|
|
public ObservableCollection<BootstrapperIconEntry> Icons { get; set; } = new();
|
|
|
|
public BootstrapperIcon Icon
|
|
{
|
|
get => App.Settings.Prop.BootstrapperIcon;
|
|
set => App.Settings.Prop.BootstrapperIcon = value;
|
|
}
|
|
|
|
public string Title
|
|
{
|
|
get => App.Settings.Prop.BootstrapperTitle;
|
|
set => App.Settings.Prop.BootstrapperTitle = value;
|
|
}
|
|
|
|
public string CustomIconLocation
|
|
{
|
|
get => App.Settings.Prop.BootstrapperIconCustomLocation;
|
|
set
|
|
{
|
|
if (String.IsNullOrEmpty(value))
|
|
{
|
|
if (App.Settings.Prop.BootstrapperIcon == BootstrapperIcon.IconCustom)
|
|
App.Settings.Prop.BootstrapperIcon = BootstrapperIcon.IconBloxstrap;
|
|
}
|
|
else
|
|
{
|
|
App.Settings.Prop.BootstrapperIcon = BootstrapperIcon.IconCustom;
|
|
}
|
|
|
|
App.Settings.Prop.BootstrapperIconCustomLocation = value;
|
|
|
|
OnPropertyChanged(nameof(Icon));
|
|
OnPropertyChanged(nameof(Icons));
|
|
}
|
|
}
|
|
|
|
private void DeleteCustomThemeStructure(string name)
|
|
{
|
|
string dir = Path.Combine(Paths.CustomThemes, name);
|
|
Directory.Delete(dir, true);
|
|
}
|
|
|
|
private void RenameCustomThemeStructure(string oldName, string newName)
|
|
{
|
|
string oldDir = Path.Combine(Paths.CustomThemes, oldName);
|
|
string newDir = Path.Combine(Paths.CustomThemes, newName);
|
|
Directory.Move(oldDir, newDir);
|
|
}
|
|
|
|
private void AddCustomTheme()
|
|
{
|
|
var dialog = new AddCustomThemeDialog();
|
|
dialog.ShowDialog();
|
|
|
|
if (dialog.Created)
|
|
{
|
|
CustomThemes.Add(dialog.ThemeName);
|
|
SelectedCustomThemeIndex = CustomThemes.Count - 1;
|
|
|
|
OnPropertyChanged(nameof(SelectedCustomThemeIndex));
|
|
OnPropertyChanged(nameof(IsCustomThemeSelected));
|
|
|
|
if (dialog.OpenEditor)
|
|
EditCustomTheme();
|
|
}
|
|
}
|
|
|
|
private void DeleteCustomTheme()
|
|
{
|
|
if (SelectedCustomTheme is null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
DeleteCustomThemeStructure(SelectedCustomTheme);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
App.Logger.WriteException("AppearanceViewModel::DeleteCustomTheme", ex);
|
|
Frontend.ShowMessageBox(string.Format(Strings.Menu_Appearance_CustomThemes_DeleteFailed, SelectedCustomTheme, ex.Message), MessageBoxImage.Error);
|
|
return;
|
|
}
|
|
|
|
CustomThemes.Remove(SelectedCustomTheme);
|
|
|
|
if (CustomThemes.Any())
|
|
{
|
|
SelectedCustomThemeIndex = CustomThemes.Count - 1;
|
|
OnPropertyChanged(nameof(SelectedCustomThemeIndex));
|
|
}
|
|
|
|
OnPropertyChanged(nameof(IsCustomThemeSelected));
|
|
}
|
|
|
|
private void RenameCustomTheme()
|
|
{
|
|
if (SelectedCustomTheme is null)
|
|
return;
|
|
|
|
if (SelectedCustomTheme == SelectedCustomThemeName)
|
|
return;
|
|
|
|
try
|
|
{
|
|
RenameCustomThemeStructure(SelectedCustomTheme, SelectedCustomThemeName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
App.Logger.WriteException("AppearanceViewModel::RenameCustomTheme", ex);
|
|
Frontend.ShowMessageBox(string.Format(Strings.Menu_Appearance_CustomThemes_RenameFailed, SelectedCustomTheme, ex.Message), MessageBoxImage.Error);
|
|
return;
|
|
}
|
|
|
|
int idx = CustomThemes.IndexOf(SelectedCustomTheme);
|
|
CustomThemes[idx] = SelectedCustomThemeName;
|
|
|
|
SelectedCustomThemeIndex = idx;
|
|
OnPropertyChanged(nameof(SelectedCustomThemeIndex));
|
|
}
|
|
|
|
private void EditCustomTheme()
|
|
{
|
|
if (SelectedCustomTheme is null)
|
|
return;
|
|
|
|
new BootstrapperEditorWindow(SelectedCustomTheme).ShowDialog();
|
|
}
|
|
|
|
private void ExportCustomTheme()
|
|
{
|
|
if (SelectedCustomTheme is null)
|
|
return;
|
|
|
|
var dialog = new SaveFileDialog
|
|
{
|
|
FileName = $"{SelectedCustomTheme}.zip",
|
|
Filter = $"{Strings.FileTypes_ZipArchive}|*.zip"
|
|
};
|
|
|
|
if (dialog.ShowDialog() != true)
|
|
return;
|
|
|
|
string themeDir = Path.Combine(Paths.CustomThemes, SelectedCustomTheme);
|
|
|
|
using var memStream = new MemoryStream();
|
|
using var zipStream = new ZipOutputStream(memStream);
|
|
|
|
foreach (var filePath in Directory.EnumerateFiles(themeDir, "*.*", SearchOption.AllDirectories))
|
|
{
|
|
string relativePath = filePath[(themeDir.Length + 1)..];
|
|
|
|
var entry = new ZipEntry(relativePath);
|
|
entry.DateTime = DateTime.Now;
|
|
|
|
zipStream.PutNextEntry(entry);
|
|
|
|
using var fileStream = File.OpenRead(filePath);
|
|
fileStream.CopyTo(zipStream);
|
|
}
|
|
|
|
zipStream.CloseEntry();
|
|
zipStream.Finish();
|
|
memStream.Position = 0;
|
|
|
|
using var outputStream = File.OpenWrite(dialog.FileName);
|
|
memStream.CopyTo(outputStream);
|
|
|
|
Process.Start("explorer.exe", $"/select,\"{dialog.FileName}\"");
|
|
}
|
|
|
|
private void PopulateCustomThemes()
|
|
{
|
|
string? selected = App.Settings.Prop.SelectedCustomTheme;
|
|
|
|
Directory.CreateDirectory(Paths.CustomThemes);
|
|
|
|
foreach (string directory in Directory.GetDirectories(Paths.CustomThemes))
|
|
{
|
|
if (!File.Exists(Path.Combine(directory, "Theme.xml")))
|
|
continue; // missing the main theme file, ignore
|
|
|
|
string name = Path.GetFileName(directory);
|
|
CustomThemes.Add(name);
|
|
}
|
|
|
|
if (selected != null)
|
|
{
|
|
int idx = CustomThemes.IndexOf(selected);
|
|
|
|
if (idx != -1)
|
|
{
|
|
SelectedCustomThemeIndex = idx;
|
|
OnPropertyChanged(nameof(SelectedCustomThemeIndex));
|
|
}
|
|
else
|
|
{
|
|
SelectedCustomTheme = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public string? SelectedCustomTheme
|
|
{
|
|
get => App.Settings.Prop.SelectedCustomTheme;
|
|
set => App.Settings.Prop.SelectedCustomTheme = value;
|
|
}
|
|
|
|
public string SelectedCustomThemeName { get; set; } = "";
|
|
|
|
public int SelectedCustomThemeIndex { get; set; }
|
|
|
|
public ObservableCollection<string> CustomThemes { get; set; } = new();
|
|
public bool IsCustomThemeSelected => SelectedCustomTheme is not null;
|
|
}
|
|
}
|