diff --git a/src/IconPacks.Browser/App.config b/src/IconPacks.Browser/App.config new file mode 100644 index 0000000..d22fab2 --- /dev/null +++ b/src/IconPacks.Browser/App.config @@ -0,0 +1,78 @@ + + + + +
+ + + + + + 280 + + + 48 + + + Light + + + + + + 256 + + + 256 + + + 20 + + + 2 + + + True + + + 0 + + + 0 + + + 0 + + + True + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + + \ No newline at end of file diff --git a/src/IconPacks.Browser/Controls/SideBar.xaml b/src/IconPacks.Browser/Controls/SideBar.xaml index a3f5199..dd43d05 100644 --- a/src/IconPacks.Browser/Controls/SideBar.xaml +++ b/src/IconPacks.Browser/Controls/SideBar.xaml @@ -240,6 +240,7 @@ + + + + diff --git a/src/IconPacks.Browser/IcoExport/ExportIconView.xaml.cs b/src/IconPacks.Browser/IcoExport/ExportIconView.xaml.cs new file mode 100644 index 0000000..5db798f --- /dev/null +++ b/src/IconPacks.Browser/IcoExport/ExportIconView.xaml.cs @@ -0,0 +1,42 @@ +using MahApps.Metro.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace IconPacks.Browser.IcoExport +{ + /// + /// Interaktionslogik für ExportIconView.xaml + /// + public partial class ExportIconView : MetroWindow + { + public ExportIconView() + { + InitializeComponent(); + PreviewHolder.LayoutUpdated += PreviewHolder_LayoutUpdated; + } + + private void PreviewHolder_LayoutUpdated(object sender, EventArgs e) + { + //PreviewBorder.SetCurrentValue(Path.WidthProperty, PreviewHolder.Width); + //PreviewBorder.SetCurrentValue(Path.HeightProperty, PreviewHolder.Height); + //PreviewBorder.SetCurrentValue(Path.MarginProperty, PreviewHolder.Margin); + + } + + private void Slider_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + ((Slider)sender).SetCurrentValue(Slider.ValueProperty,0.0); + } + } +} diff --git a/src/IconPacks.Browser/IcoExport/ExportIconViewModel.cs b/src/IconPacks.Browser/IcoExport/ExportIconViewModel.cs new file mode 100644 index 0000000..6a05eb0 --- /dev/null +++ b/src/IconPacks.Browser/IcoExport/ExportIconViewModel.cs @@ -0,0 +1,307 @@ +using IconPacks.Browser.Properties; +using IconPacks.Browser.ViewModels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using static System.Net.Mime.MediaTypeNames; + +namespace IconPacks.Browser.IcoExport +{ + internal class ExportIconViewModel : ViewModelBase + { + /// + /// Reference to the selected Icon. Backing Var + /// + private IconViewModel icon; + + /// + /// class new + /// + public ExportIconViewModel() + { + OkCommand = new SimpleCommand(x => OkExecute(x), (_) => true); + CancelCommand = new SimpleCommand((_) => CancelExecute(), (_) => true); + } + /// + /// Reference to the form. I know, not MVVM but simple :) + /// + public ExportIconView Frm { get; set; } + + /// + /// Reference to the selected Icon. + /// + public IconViewModel Icon + { + get => icon; set + { + if (Set(ref icon, value)) + { + this.OnPropertyChanged(nameof(RectGeometry)); + } + } + } + + /// + /// The border around the Icon + /// + public Geometry RectGeometry + { + get + { + Geometry rg = null; + if (IcoExportRectEnabled) + { + var Offset = ((256 - IcoExportRectSize) / 2) + (IcoExportPathStrokeThickness / 2); + rg = new RectangleGeometry(new System.Windows.Rect(Offset, Offset, Math.Max(IcoExportRectSize - IcoExportPathStrokeThickness, 2), Math.Max(IcoExportRectSize - IcoExportPathStrokeThickness, 2)), IcoExportRectRadius, IcoExportRectRadius); + } + + return rg; + } + } + + /// + /// MahIcon Translation + /// + public Transform IconTranslate + { + get + { + TransformGroup t = new TransformGroup(); + t.Children.Add(new RotateTransform(IcoExportIconRotate)); + t.Children.Add(new TranslateTransform(IcoExportIconTranslateX, IcoExportIconTranslateY)); + return t; + } + } + + /// + /// The forecolor of the mah-icon + /// + public Color IcoExportIconForeground + { + get => Settings.Default.IcoExportIconForeground; set + { + Settings.Default.IcoExportIconForeground = value; + this.OnPropertyChanged(nameof(IcoExportIconForeground)); + } + } + + /// + /// the backcolor of the mahicon on canvas + /// + public Color IcoExportIconBackground + { + get => Settings.Default.IcoExportIconBackground; set + { + Settings.Default.IcoExportIconBackground = value; + this.OnPropertyChanged(nameof(IcoExportIconBackground)); + this.OnPropertyChanged(nameof(IcoExportPathBackground)); + } + } + + /// + /// The size of the mahicon on canvas + /// + public int IcoExportIconSize + { + get => Settings.Default.IcoExportIconSize; set + { + Settings.Default.IcoExportIconSize = value; + this.OnPropertyChanged(nameof(IcoExportIconSize)); + } + } + + /// + /// Rotation of the mahicon + /// + public int IcoExportIconRotate + { + get => Settings.Default.IcoExportIconRotate; set + { + Settings.Default.IcoExportIconRotate = value; + this.OnPropertyChanged(nameof(IconTranslate)); + } + } + + /// + /// Translation of the mahicon + /// + public int IcoExportIconTranslateX + { + get => Settings.Default.IcoExportIconTranslateX; set + { + Settings.Default.IcoExportIconTranslateX = value; + this.OnPropertyChanged(nameof(IconTranslate)); + } + } + + /// + /// Translation of the mahicon + /// + public int IcoExportIconTranslateY + { + get => Settings.Default.IcoExportIconTranslateY; set + { + Settings.Default.IcoExportIconTranslateY = value; + this.OnPropertyChanged(nameof(IconTranslate)); + } + } + + /// + /// Color of the Background of the rect, when Path is off + /// + public Color IcoExportPathBackground + { + get + { + if (IcoExportRectEnabled) return Colors.Transparent; + return Settings.Default.IcoExportIconBackground; + } + } + + + + /// + /// Fill color of the rect + /// + public Color IcoExportPathFill + { + get => Settings.Default.IcoExportPathFill; set + { + Settings.Default.IcoExportPathFill = value; + this.OnPropertyChanged(nameof(IcoExportPathFill)); + } + } + + /// + /// The color of the path stroke + /// + public Color IcoExportPathStroke + { + get => Settings.Default.IcoExportPathStroke; set + { + Settings.Default.IcoExportPathStroke = value; + this.OnPropertyChanged(nameof(IcoExportPathStroke)); + this.OnPropertyChanged(nameof(RectGeometry)); + } + } + + /// + /// the stroke width of thge rect + /// + public int IcoExportPathStrokeThickness + { + get => Settings.Default.IcoExportPathStrokeThickness; set + { + Settings.Default.IcoExportPathStrokeThickness = value; + this.OnPropertyChanged(nameof(IcoExportPathStrokeThickness)); + this.OnPropertyChanged(nameof(RectGeometry)); + } + } + + /// + /// Size of the rect + /// + public int IcoExportRectSize + { + get => Settings.Default.IcoExportRectSize; set + { + Settings.Default.IcoExportRectSize = value; + this.OnPropertyChanged(nameof(RectGeometry)); + } + } + + /// + /// Border Radius of the Rect + /// + public int IcoExportRectRadius + { + get => Settings.Default.IcoExportRectRadius; set + { + Settings.Default.IcoExportRectRadius = value; + this.OnPropertyChanged(nameof(RectGeometry)); + } + } + + + + /// + /// Enable the rect + /// + public bool IcoExportRectEnabled + { + get => Settings.Default.IcoExportRectEnabled; set + { + Settings.Default.IcoExportRectEnabled = value; + this.OnPropertyChanged(nameof(RectGeometry)); + this.OnPropertyChanged(nameof(IcoExportPathBackground)); + } + } + + public ICommand OkCommand { get; } + + public ICommand CancelCommand { get; } + + public List IconBitmaps { get; set; } = new List(); + + private void OkExecute(object parameter) + { + if (parameter is Grid grid) + { + if (Settings.Default.IcoExport256) IconBitmaps.Add(RenderBitmap(grid, 256)); + if (Settings.Default.IcoExport180) IconBitmaps.Add(RenderBitmap(grid, 180)); + if (Settings.Default.IcoExport128) IconBitmaps.Add(RenderBitmap(grid, 128)); + if (Settings.Default.IcoExport96) IconBitmaps.Add(RenderBitmap(grid, 96)); + if (Settings.Default.IcoExport72) IconBitmaps.Add(RenderBitmap(grid, 72)); + if (Settings.Default.IcoExport64) IconBitmaps.Add(RenderBitmap(grid, 64)); + if (Settings.Default.IcoExport48) IconBitmaps.Add(RenderBitmap(grid, 48)); + if (Settings.Default.IcoExport32) IconBitmaps.Add(RenderBitmap(grid, 32)); + if (Settings.Default.IcoExport24) IconBitmaps.Add(RenderBitmap(grid, 24)); + if (Settings.Default.IcoExport16) IconBitmaps.Add(RenderBitmap(grid, 16)); + Frm.DialogResult = true; + } + } + + private System.Drawing.Bitmap RenderBitmap(Grid grid, double size) + { + grid.SetCurrentValue(Grid.RenderTransformProperty, new ScaleTransform(size / 256, size / 256)); + Canvas.SetLeft(grid, 0); + Canvas.SetTop(grid, 0); + grid.Measure(new Size(grid.Width, grid.Height)); + grid.Arrange(new Rect(new Size(grid.Width, grid.Height))); + grid.UpdateLayout(); + + var rtb = new RenderTargetBitmap( + (int)size, + (int)size, + 96, + 96, + PixelFormats.Pbgra32); + + + + rtb.Render(grid); + MemoryStream stream = new MemoryStream(); + BitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(rtb)); + encoder.Save(stream); + + return new System.Drawing.Bitmap(stream); + } + + private void CancelExecute() + { + Frm.DialogResult = false; + } + + } +} diff --git a/src/IconPacks.Browser/IcoExport/IconFactory.cs b/src/IconPacks.Browser/IcoExport/IconFactory.cs new file mode 100644 index 0000000..fe5435c --- /dev/null +++ b/src/IconPacks.Browser/IcoExport/IconFactory.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +//see: https://stackoverflow.com/questions/3213999/how-to-create-an-icon-file-that-contains-multiple-sizes-images-in-c-sharp + +namespace IconPacks.Browser.IcoExport +{ + /// + /// Provides methods for creating icons. + /// + public class IconFactory + { + + #region constants + + /// + /// Represents the max allowed width of an icon. + /// + public const int MaxIconWidth = 256; + + /// + /// Represents the max allowed height of an icon. + /// + public const int MaxIconHeight = 256; + + private const ushort HeaderReserved = 0; + private const ushort HeaderIconType = 1; + private const byte HeaderLength = 6; + + private const byte EntryReserved = 0; + private const byte EntryLength = 16; + + private const byte PngColorsInPalette = 0; + private const ushort PngColorPlanes = 1; + + #endregion + + #region methods + + /// + /// Saves the specified objects as a single + /// icon into the output stream. + /// + /// The bitmaps to save as an icon. + /// The output stream. + /// + /// The expected input for the parameter are + /// portable network graphic files that have a + /// of and where the + /// width is less than or equal to and the + /// height is less than or equal to . + /// + /// + /// Occurs if any of the input images do + /// not follow the required image format. See remarks for details. + /// + /// + /// Occurs if any of the arguments are null. + /// + public static void SavePngsAsIcon(IEnumerable images, Stream stream) + { + if (images == null) + throw new ArgumentNullException("images"); + if (stream == null) + throw new ArgumentNullException("stream"); + + // validates the pngs + IconFactory.ThrowForInvalidPngs(images); + + Bitmap[] orderedImages = images.OrderBy(i => i.Width) + .ThenBy(i => i.Height) + .ToArray(); + + using (var writer = new BinaryWriter(stream)) + { + + // write the header + writer.Write(IconFactory.HeaderReserved); + writer.Write(IconFactory.HeaderIconType); + writer.Write((ushort)orderedImages.Length); + + // save the image buffers and offsets + Dictionary buffers = new Dictionary(); + + // tracks the length of the buffers as the iterations occur + // and adds that to the offset of the entries + uint lengthSum = 0; + uint baseOffset = (uint)(IconFactory.HeaderLength + + IconFactory.EntryLength * orderedImages.Length); + + for (int i = 0; i < orderedImages.Length; i++) + { + Bitmap image = orderedImages[i]; + + // creates a byte array from an image + byte[] buffer = IconFactory.CreateImageBuffer(image); + + // calculates what the offset of this image will be + // in the stream + uint offset = (baseOffset + lengthSum); + + // writes the image entry + writer.Write(IconFactory.GetIconWidth(image)); + writer.Write(IconFactory.GetIconHeight(image)); + writer.Write(IconFactory.PngColorsInPalette); + writer.Write(IconFactory.EntryReserved); + writer.Write(IconFactory.PngColorPlanes); + writer.Write((ushort)Image.GetPixelFormatSize(image.PixelFormat)); + writer.Write((uint)buffer.Length); + writer.Write(offset); + + lengthSum += (uint)buffer.Length; + + // adds the buffer to be written at the offset + buffers.Add(offset, buffer); + } + + // writes the buffers for each image + foreach (var kvp in buffers) + { + + // seeks to the specified offset required for the image buffer + writer.BaseStream.Seek(kvp.Key, SeekOrigin.Begin); + + // writes the buffer + writer.Write(kvp.Value); + } + } + + } + + private static void ThrowForInvalidPngs(IEnumerable images) + { + foreach (var image in images) + { + if (image.PixelFormat != PixelFormat.Format32bppArgb) + { + throw new InvalidOperationException + (string.Format("Required pixel format is PixelFormat.{0}.", + PixelFormat.Format32bppArgb.ToString())); + } + + if (image.RawFormat.Guid != ImageFormat.Png.Guid) + { + throw new InvalidOperationException + ("Required image format is a portable network graphic (png)."); + } + + if (image.Width > IconFactory.MaxIconWidth || + image.Height > IconFactory.MaxIconHeight) + { + throw new InvalidOperationException + (string.Format("Dimensions must be less than or equal to {0}x{1}", + IconFactory.MaxIconWidth, + IconFactory.MaxIconHeight)); + } + } + } + + private static byte GetIconHeight(Bitmap image) + { + if (image.Height == IconFactory.MaxIconHeight) + return 0; + + return (byte)image.Height; + } + + private static byte GetIconWidth(Bitmap image) + { + if (image.Width == IconFactory.MaxIconWidth) + return 0; + + return (byte)image.Width; + } + + private static byte[] CreateImageBuffer(Bitmap image) + { + using (var stream = new MemoryStream()) + { + image.Save(stream, image.RawFormat); + + return stream.ToArray(); + } + } + + #endregion + + } +} diff --git a/src/IconPacks.Browser/IconPacks.Browser.csproj b/src/IconPacks.Browser/IconPacks.Browser.csproj index 9761df2..f1e77c0 100644 --- a/src/IconPacks.Browser/IconPacks.Browser.csproj +++ b/src/IconPacks.Browser/IconPacks.Browser.csproj @@ -20,19 +20,19 @@ + all runtime; build; native; contentfiles; analyzers - - + - + - - + + @@ -41,6 +41,11 @@ <_SettingsFiles Remove="Properties\Settings.settings" /> + + True + True + Settings.settings + PreserveNewest @@ -71,6 +76,10 @@ PreserveNewest + + SettingsSingleFileGenerator + Settings.Designer.cs + diff --git a/src/IconPacks.Browser/MainWindow.xaml b/src/IconPacks.Browser/MainWindow.xaml index 5d9df84..d3ea567 100644 --- a/src/IconPacks.Browser/MainWindow.xaml +++ b/src/IconPacks.Browser/MainWindow.xaml @@ -1,35 +1,37 @@ - + - + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/IconPacks.Browser/MainWindow.xaml.cs b/src/IconPacks.Browser/MainWindow.xaml.cs index 504677b..248111f 100644 --- a/src/IconPacks.Browser/MainWindow.xaml.cs +++ b/src/IconPacks.Browser/MainWindow.xaml.cs @@ -1,3 +1,5 @@ +using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using IconPacks.Browser.Properties; using IconPacks.Browser.ViewModels; @@ -35,5 +37,22 @@ private void Find_Executed(object sender, System.Windows.Input.ExecutedRoutedEve FilterTextBox.Focus(); Keyboard.Focus(FilterTextBox); } + + /// + /// Forward Cell click + /// + /// + /// + private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (sender is DataGridCell c && c.TryFindParent()!=null) + { + var frm = c.TryFindParent(); + if (frm.DataContext !=null && frm.DataContext is MainViewModel mvm) + { + mvm.ThemeResourcesViewModel.DataGridCell_MouseDoubleClick(sender,e); + } + } + } } } \ No newline at end of file diff --git a/src/IconPacks.Browser/Model/ThemeResource.cs b/src/IconPacks.Browser/Model/ThemeResource.cs new file mode 100644 index 0000000..07be9db --- /dev/null +++ b/src/IconPacks.Browser/Model/ThemeResource.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Windows; +using System.Windows.Media; +using ControlzEx.Theming; +using IconPacks.Browser.ViewModels; +using JetBrains.Annotations; + +namespace IconPacks.Browser.Model +{ + /// + /// Clas for one single resource color in light and dark + /// + public class ThemeResource : ViewModelBase + { + private bool isHidden = false; + + public ThemeResource(Theme theme, LibraryTheme libraryTheme, ResourceDictionary resourceDictionary, DictionaryEntry dictionaryEntryDark, DictionaryEntry dictionaryEntryLight) + : this(theme, libraryTheme, resourceDictionary, dictionaryEntryDark.Key.ToString(), dictionaryEntryDark.Value, dictionaryEntryLight.Value) + { + } + + public ThemeResource(Theme theme, LibraryTheme libraryTheme, ResourceDictionary resourceDictionary, string? key, object? valueDark, object? valueLight) + { + this.Theme = theme; + this.LibraryTheme = libraryTheme; + + this.Source = (resourceDictionary.Source?.ToString() ?? "Runtime").ToLower(); + this.Source = CultureInfo.InstalledUICulture.TextInfo.ToTitleCase(this.Source) + .Replace("Pack", "pack") + .Replace("Application", "application") + .Replace("Xaml", "xaml"); + + this.Key = key; + + this.ValueLight = valueLight switch + { + Color color => new SolidColorBrush(color), + Brush brush => brush, + _ => null + }; + + this.AlphaLight = valueLight switch + { + Color color => color.A, + SolidColorBrush brush => brush.Color.A, + LinearGradientBrush brush => brush.GradientStops[0].Color.A, + _ => 255 + }; + + this.StringValueLight = valueLight?.ToString(); + + this.ValueDark = valueDark switch + { + Color color => new SolidColorBrush(color), + Brush brush => brush, + _ => null + }; + + this.AlphaDark = valueDark switch + { + Color color => color.A, + SolidColorBrush brush => brush.Color.A, + LinearGradientBrush brush => brush.GradientStops[0].Color.A, + _ => 255 + }; + + this.StringValueDark = valueDark?.ToString(); + } + + /// + /// The current selected theme. Switch in Settings of App + /// + public Theme Theme { get; } + + /// + /// The current selected theme. Switch in Settings of App + /// + public LibraryTheme LibraryTheme { get; } + + /// + /// Source of this theme + /// + public string Source { get; } + + /// + /// Key of this resource + /// + public string? Key { get; } + + /// + /// The light color of this resource + /// + public Brush? ValueLight { get; } + /// + /// The alpha value of this light color + /// + public int AlphaLight { get; } = 0; + /// + /// The light color as hex string + /// + public string? StringValueLight { get; } + /// + /// The dark color + /// + public Brush? ValueDark { get; } + /// + /// Alpha channel of dark color + /// + public int AlphaDark { get; } = 0; + /// + /// the dark color as hex string + /// + public string? StringValueDark { get; } + /// + /// when tru, don't show in grid + /// + public bool IsHidden + { + get => isHidden; set + { + if (Set(ref isHidden, value)) + { + + } + } + } + + /// + /// Filter like it's done in icons + /// + /// + /// + public bool CheckFilter(string filterText) + { + bool RetVal = true; + if (!string.IsNullOrWhiteSpace(filterText)) + { + var filterSubStrings = filterText.Split(new[] { '+', ',', ';', '&' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var filterSubString in filterSubStrings) + { + var filterOrSubStrings = filterSubString.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + var isInName = filterOrSubStrings.Any(x => Key.IndexOf(x.Trim(), StringComparison.CurrentCultureIgnoreCase) >= 0); + + if (!isInName) RetVal = false; + } + } + return RetVal; + } + + } +} \ No newline at end of file diff --git a/src/IconPacks.Browser/Properties/Settings.Designer.cs b/src/IconPacks.Browser/Properties/Settings.Designer.cs index 883a44a..c3c0752 100644 --- a/src/IconPacks.Browser/Properties/Settings.Designer.cs +++ b/src/IconPacks.Browser/Properties/Settings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. // //------------------------------------------------------------------------------ @@ -12,8 +12,8 @@ namespace IconPacks.Browser.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] - public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -106,5 +106,269 @@ public string ExportTemplatesDir { this["ExportTemplatesDir"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("#00FFFFFF")] + public global::System.Windows.Media.Color IcoExportIconBackground { + get { + return ((global::System.Windows.Media.Color)(this["IcoExportIconBackground"])); + } + set { + this["IcoExportIconBackground"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("#FF000000")] + public global::System.Windows.Media.Color IcoExportIconForeground { + get { + return ((global::System.Windows.Media.Color)(this["IcoExportIconForeground"])); + } + set { + this["IcoExportIconForeground"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("#FFFFFFFF")] + public global::System.Windows.Media.Color IcoExportPathFill { + get { + return ((global::System.Windows.Media.Color)(this["IcoExportPathFill"])); + } + set { + this["IcoExportPathFill"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("#FF000000")] + public global::System.Windows.Media.Color IcoExportPathStroke { + get { + return ((global::System.Windows.Media.Color)(this["IcoExportPathStroke"])); + } + set { + this["IcoExportPathStroke"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("256")] + public int IcoExportIconSize { + get { + return ((int)(this["IcoExportIconSize"])); + } + set { + this["IcoExportIconSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("256")] + public int IcoExportRectSize { + get { + return ((int)(this["IcoExportRectSize"])); + } + set { + this["IcoExportRectSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("20")] + public int IcoExportRectRadius { + get { + return ((int)(this["IcoExportRectRadius"])); + } + set { + this["IcoExportRectRadius"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2")] + public int IcoExportPathStrokeThickness { + get { + return ((int)(this["IcoExportPathStrokeThickness"])); + } + set { + this["IcoExportPathStrokeThickness"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IcoExportRectEnabled { + get { + return ((bool)(this["IcoExportRectEnabled"])); + } + set { + this["IcoExportRectEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int IcoExportIconRotate { + get { + return ((int)(this["IcoExportIconRotate"])); + } + set { + this["IcoExportIconRotate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int IcoExportIconTranslateX { + get { + return ((int)(this["IcoExportIconTranslateX"])); + } + set { + this["IcoExportIconTranslateX"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int IcoExportIconTranslateY { + get { + return ((int)(this["IcoExportIconTranslateY"])); + } + set { + this["IcoExportIconTranslateY"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IcoExport16 { + get { + return ((bool)(this["IcoExport16"])); + } + set { + this["IcoExport16"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport24 { + get { + return ((bool)(this["IcoExport24"])); + } + set { + this["IcoExport24"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IcoExport32 { + get { + return ((bool)(this["IcoExport32"])); + } + set { + this["IcoExport32"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IcoExport48 { + get { + return ((bool)(this["IcoExport48"])); + } + set { + this["IcoExport48"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport64 { + get { + return ((bool)(this["IcoExport64"])); + } + set { + this["IcoExport64"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport72 { + get { + return ((bool)(this["IcoExport72"])); + } + set { + this["IcoExport72"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport96 { + get { + return ((bool)(this["IcoExport96"])); + } + set { + this["IcoExport96"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport128 { + get { + return ((bool)(this["IcoExport128"])); + } + set { + this["IcoExport128"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool IcoExport180 { + get { + return ((bool)(this["IcoExport180"])); + } + set { + this["IcoExport180"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IcoExport256 { + get { + return ((bool)(this["IcoExport256"])); + } + set { + this["IcoExport256"] = value; + } + } } } diff --git a/src/IconPacks.Browser/Properties/Settings.cs b/src/IconPacks.Browser/Properties/Settings.cs index 64fc54e..a1fe6b6 100644 --- a/src/IconPacks.Browser/Properties/Settings.cs +++ b/src/IconPacks.Browser/Properties/Settings.cs @@ -1,12 +1,12 @@ namespace IconPacks.Browser.Properties { - - + + // This class allows you to handle specific events on the settings class: // The SettingChanging event is raised before a setting's value is changed. // The PropertyChanged event is raised after a setting's value is changed. // The SettingsLoaded event is raised after the setting values are loaded. // The SettingsSaving event is raised before the setting values are saved. - public sealed partial class Settings { + internal sealed partial class Settings { public Settings() { // // To add event handlers for saving and changing settings, uncomment the lines below: diff --git a/src/IconPacks.Browser/Properties/Settings.settings b/src/IconPacks.Browser/Properties/Settings.settings index 8810e04..8d58463 100644 --- a/src/IconPacks.Browser/Properties/Settings.settings +++ b/src/IconPacks.Browser/Properties/Settings.settings @@ -23,5 +23,71 @@ + + #00FFFFFF + + + #FF000000 + + + #FFFFFFFF + + + #FF000000 + + + 256 + + + 256 + + + 20 + + + 2 + + + True + + + 0 + + + 0 + + + 0 + + + True + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + \ No newline at end of file diff --git a/src/IconPacks.Browser/ViewModels/IconPackViewModel.cs b/src/IconPacks.Browser/ViewModels/IconPackViewModel.cs index 16b5fcc..e2ecddc 100644 --- a/src/IconPacks.Browser/ViewModels/IconPackViewModel.cs +++ b/src/IconPacks.Browser/ViewModels/IconPackViewModel.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; @@ -14,6 +15,7 @@ using System.Windows.Media.Imaging; using System.Windows.Shapes; using AsyncAwaitBestPractices; +using IconPacks.Browser.IcoExport; using IconPacks.Browser.Model; using IconPacks.Browser.Properties; using JetBrains.Annotations; @@ -23,6 +25,7 @@ using Microsoft.Win32; using IO = System.IO; + namespace IconPacks.Browser.ViewModels { public class IconPackViewModel : ViewModelBase @@ -47,6 +50,7 @@ private IconPackViewModel(MainViewModel mainViewModel, IDialogCoordinator dialog SaveAsPngCommand = new SimpleCommand((_) => SaveAsBitmapExecute(new PngBitmapEncoder()), (_) => SelectedIcon is not null); SaveAsJpegCommand = new SimpleCommand((_) => SaveAsBitmapExecute(new JpegBitmapEncoder()), (_) => SelectedIcon is not null); SaveAsBmpCommand = new SimpleCommand((_) => SaveAsBitmapExecute(new BmpBitmapEncoder()), (_) => SelectedIcon is not null); + SaveAsIcoCommand = new SimpleCommand((_) => SaveAsIconExecute(), (_) => SelectedIcon is not null); } public IconPackViewModel(MainViewModel mainViewModel, Type enumType, Type packType, IDialogCoordinator dialogCoordinator) @@ -70,6 +74,11 @@ public IconPackViewModel(MainViewModel mainViewModel, string caption, Type[] enu this.LoadAllEnumsAsync(enumTypes, packTypes).SafeFireAndForget(); } + //For Design Mode + public IconPackViewModel() + { + } + private async Task LoadEnumsAsync(Type enumType, Type packType) { var collection = await Task.Run(() => GetIcons(enumType, packType).OrderBy(i => i.Name, StringComparer.InvariantCultureIgnoreCase).ToList()); @@ -215,7 +224,7 @@ private async void SaveAsSvg_Execute() iconControl.EndInit(); iconControl.ApplyTemplate(); - var iconPath = iconControl.FindChild(); + var iconPath = iconControl.FindChild(); var bBox = iconPath.Data.Bounds; @@ -293,7 +302,7 @@ private async void SaveAsWpf_Execute() iconControl.EndInit(); iconControl.ApplyTemplate(); - var iconPath = iconControl.FindChild(); + var iconPath = iconControl.FindChild(); var bBox = iconPath.Data.Bounds; @@ -357,7 +366,7 @@ private async void SaveAsUwp_Execute() iconControl.EndInit(); iconControl.ApplyTemplate(); - var iconPath = iconControl.FindChild(); + var iconPath = iconControl.FindChild(); var bBox = iconPath.Data.Bounds; @@ -397,6 +406,59 @@ private async void SaveAsUwp_Execute() public ICommand SaveAsJpegCommand { get; } public ICommand SaveAsBmpCommand { get; } + public ICommand SaveAsIcoCommand { get; } + + private async void SaveAsIconExecute() + { + var progress = await dialogCoordinator.ShowProgressAsync(MainViewModel, "Export", "Saving selected icon as .ico file"); + progress.SetIndeterminate(); + + try + { + if (SelectedIcon is IconViewModel icon) + { + + + var IcoOptionsView = new ExportIconView(); + var IcoOptionViewModel = new ExportIconViewModel() { Frm = IcoOptionsView, Icon = icon }; + IcoOptionsView.DataContext = IcoOptionViewModel; + if (IcoOptionsView.ShowDialog() == true && IcoOptionViewModel.IconBitmaps.Count != 0) + { + + var fileSaveDialog = new SaveFileDialog() + { + AddExtension = true, + FileName = $"{SelectedIcon.IconPackName}-{SelectedIcon.Name}", + Filter = "Ico-File (*.ico)|*.ico", + OverwritePrompt = true + }; + + if (fileSaveDialog.ShowDialog() == true) + { + + + + using (var stream = new System.IO.FileStream(fileSaveDialog.FileName, System.IO.FileMode.Create)) + { + + IconFactory.SavePngsAsIcon(IcoOptionViewModel.IconBitmaps.ToArray(), stream); + foreach (var bm in IcoOptionViewModel.IconBitmaps) + { + bm.Dispose(); + } + } + } + + } + } + } + catch (Exception e) + { + await dialogCoordinator.ShowMessageAsync(MainViewModel, "Error", e.Message); + } + + await progress.CloseAsync(); + } private async void SaveAsBitmapExecute(BitmapEncoder encoder) { @@ -466,6 +528,7 @@ private async void SaveAsBitmapExecute(BitmapEncoder encoder) public interface IIconViewModel { string Name { get; set; } + string FullName { get; } string IconPackName { get; } string Description { get; set; } Type IconPackType { get; set; } @@ -484,6 +547,7 @@ public class IconViewModel : IIconViewModel public IconViewModel(Type enumType, Type packType, Enum k, MetaDataAttribute metaData) { Name = k.ToString(); + FullName = enumType.FullName + "." + k.ToString(); Description = GetDescription(k); IconPackType = packType; IconType = enumType; @@ -501,6 +565,7 @@ public IconViewModel(Type enumType, Type packType, Enum k, MetaDataAttribute met public string CopyToClipboardAsGeometryText => ExportHelper.FillTemplate(ExportHelper.ClipboardData, new ExportParameters(this)); // GetPackIconControlBase().Data; public string Name { get; set; } + public string FullName { get; } public string IconPackName => IconPackType.Name.Replace("PackIcon", ""); diff --git a/src/IconPacks.Browser/ViewModels/MainViewModel.cs b/src/IconPacks.Browser/ViewModels/MainViewModel.cs index 63c968d..32d13a2 100644 --- a/src/IconPacks.Browser/ViewModels/MainViewModel.cs +++ b/src/IconPacks.Browser/ViewModels/MainViewModel.cs @@ -5,10 +5,14 @@ using System.Linq; using System.Reflection; using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading; using MahApps.Metro.Controls.Dialogs; using MahApps.Metro.IconPacks; +using IconPacks.Browser.Model; +using ControlzEx.Theming; namespace IconPacks.Browser.ViewModels { @@ -98,6 +102,8 @@ public MainViewModel(Dispatcher dispatcher) this.IconPacksVersion = FileVersionInfo.GetVersionInfo(Assembly.GetAssembly(typeof(PackIconMaterial)).Location).FileVersion; this.Settings = new SettingsViewModel(this.dialogCoordinator); + + ThemeManager.Current.ThemeChanged += this.ThemeResourcesViewModel.ThemeManager_ThemeChanged; } private static async void OpenUrlLink(string link) @@ -214,8 +220,13 @@ private static void DoCopyTextToClipboard(string text) { CanExecuteDelegate = x => (x is IIconViewModel), ExecuteDelegate = x => DoCopyTextToClipboard(((IIconViewModel)x).CopyToClipboardAsGeometryText) - }; + }; public SettingsViewModel Settings { get; } + + + public ThemeResourcesViewModel ThemeResourcesViewModel { get; } = new ThemeResourcesViewModel(); + + } } \ No newline at end of file diff --git a/src/IconPacks.Browser/ViewModels/ThemeResourcesViewModel.cs b/src/IconPacks.Browser/ViewModels/ThemeResourcesViewModel.cs new file mode 100644 index 0000000..39af36d --- /dev/null +++ b/src/IconPacks.Browser/ViewModels/ThemeResourcesViewModel.cs @@ -0,0 +1,244 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using ControlzEx.Theming; +using IconPacks.Browser.Model; +using IconPacks.Browser.ViewModels; +using JetBrains.Annotations; + +namespace IconPacks.Browser.ViewModels +{ + /// + /// Viewmodel for Color Resources + /// + public class ThemeResourcesViewModel : ViewModelBase + { + private DispatcherTimer timer = new DispatcherTimer(); + private string header = "Colors"; + private string _filterTextColors; + private string statusColorFilter; + private int filterAlphaValue = 0; + + /// + /// this are the resources of this theme + /// + public ObservableCollection ThemeResources { get; } = new ObservableCollection(); + + public ThemeResourcesViewModel() + { + timer.Interval = TimeSpan.FromSeconds(3); + timer.Tick += Timer_Tick; + } + + /// + /// Disables the popup + /// + /// + /// + private void Timer_Tick(object sender, EventArgs e) + { + PopupIsOpen = false; + this.OnPropertyChanged(nameof(PopupIsOpen)); + timer.Stop(); + } + + /// + /// Theme changed==Updat colors + /// + /// + /// + internal void ThemeManager_ThemeChanged(object? sender, ThemeChangedEventArgs e) + { + UpdateThemeResources(); + } + + /// + /// Show the current Theme + /// + public string Header + { + get => header; set + { + header = value; + this.OnPropertyChanged(nameof(Header)); + } + } + + /// + /// Popup for doublick infomation + /// + public bool PopupIsOpen { get; set; } + public object PopupPlacementTarget { get; set; } + public string PopupText { get; set; } + + /// + /// Only colors with alpha channel greater this value will be shown + /// + public int FilterAlphaValue + { + get => filterAlphaValue; set + { + if (Set(ref filterAlphaValue, value)) + { + Filter(); + } + } + } + + /// + /// Search Textbox like it was for icons + /// + public string FilterTextColors + { + get => _filterTextColors; + set + { + if (Set(ref _filterTextColors, value)) + { + Filter(); + } + } + } + + /// + /// For Statusbar, count of colors visible + /// + public string StatusColorFilter + { + get => statusColorFilter; set + { + Set(ref statusColorFilter, value); + } + } + + /// + /// Filter the resources, IsHidden=true means not visible + /// + private void Filter() + { + var colorCount = 0; + foreach (var item in ThemeResources) + { + item.IsHidden = !item.CheckFilter(_filterTextColors) || item.AlphaLight< filterAlphaValue || item.AlphaDark< filterAlphaValue; + if (!item.IsHidden) colorCount += 1; + } + var filtered = (colorCount != ThemeResources.Count) ? " (filtered)" : ""; + StatusColorFilter = $"{Header} with {colorCount} Colors{filtered}"; + } + + /// + /// Forwarded from code behind. Sorry. MVVM to complicated for me :) + /// + /// + /// + public void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (sender != null && sender is DataGridCell cell && cell.DataContext != null && cell.DataContext is ThemeResource tr) + { + PopupText = ""; + if (cell.Column.DisplayIndex == 0) + { + System.Windows.Clipboard.SetText(tr.Key); + PopupText = $"Resourcename \"{tr.Key}\" copied to clipboard"; + } + else if (cell.Column.DisplayIndex == 1 || cell.Column.DisplayIndex == 2) + { + System.Windows.Clipboard.SetText(tr.StringValueLight); + PopupText = $"hex-value for light theme \"{tr.StringValueLight}\" copied to clipboard"; + } + else if (cell.Column.DisplayIndex == 3 || cell.Column.DisplayIndex == 4) + { + System.Windows.Clipboard.SetText(tr.StringValueDark); + PopupText = $"hex-value for dark theme \"{tr.StringValueDark}\" copied to clipboard"; + } + + + if (!string.IsNullOrEmpty(PopupText)) + { + PopupPlacementTarget = sender; + this.OnPropertyChanged(nameof(PopupPlacementTarget)); + this.OnPropertyChanged(nameof(PopupText)); + + PopupIsOpen = true; + this.OnPropertyChanged(nameof(PopupIsOpen)); + + timer.Start(); + } + + } + } + + /// + /// When Theme Changed, update the colos + /// + public void UpdateThemeResources() + { + this.ThemeResources.Clear(); + + if (Application.Current.MainWindow != null) + { + var themeOriginal = ThemeManager.Current.DetectTheme(Application.Current.MainWindow); + string Accent = ""; + foreach (var t in ThemeManager.Current.Themes) + { + if (t.PrimaryAccentColor.Equals(themeOriginal.PrimaryAccentColor)) + { + Accent = t.ColorScheme; + break; + } + } + + Header = $"App color: {Accent}"; + + + if (themeOriginal is not null) + { + var themeDark = ThemeManager.Current.GetTheme("Dark", Accent); + var themeLight = ThemeManager.Current.GetTheme("Light", Accent); + + if (themeDark != null && themeLight != null) + { + var libraryThemeDark = themeDark.LibraryThemes.FirstOrDefault(x => x.Origin == "MahApps.Metro"); + var resourceDictionaryDark = libraryThemeDark?.Resources.MergedDictionaries.FirstOrDefault(); + + var libraryThemeLight = themeLight.LibraryThemes.FirstOrDefault(x => x.Origin == "MahApps.Metro"); + var resourceDictionaryLight = libraryThemeLight?.Resources.MergedDictionaries.FirstOrDefault(); + + if (resourceDictionaryDark != null && resourceDictionaryLight != null) + { + var colorCount = 0; + foreach (var dictionaryEntryDark in resourceDictionaryDark.OfType()) + { + var dictionaryEntryLight = dictionaryEntryDark.Value; + + if (resourceDictionaryLight.Contains(dictionaryEntryDark.Key)) dictionaryEntryLight = resourceDictionaryLight[dictionaryEntryDark.Key]; + var tr = new ThemeResource(themeDark, libraryThemeDark!, resourceDictionaryDark, dictionaryEntryDark.Key.ToString(), dictionaryEntryDark.Value, dictionaryEntryLight); + //tr.isHidden = OnlyFullAlpha && (!tr.FullAlpha || !tr.FullAlphaDark); + + if (!tr.IsHidden) colorCount += 1; + this.ThemeResources.Add(tr); + } + FilterTextColors = ""; + } + } + + } + + } + } + } +} \ No newline at end of file