diff --git a/DeskFrame/Converters/IconSizeToImageHeightConverter.cs b/DeskFrame/Converters/IconSizeToImageHeightConverter.cs new file mode 100644 index 0000000..3292632 --- /dev/null +++ b/DeskFrame/Converters/IconSizeToImageHeightConverter.cs @@ -0,0 +1,21 @@ +using System.Windows.Data; + +namespace DeskFrame +{ + public class IconSizeToImageHeightConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is int iconSize) + { + return iconSize; + } + return 32; // Default size + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/DeskFrame/Converters/IconSizeToImageWidthConverter.cs b/DeskFrame/Converters/IconSizeToImageWidthConverter.cs new file mode 100644 index 0000000..b8d9cdc --- /dev/null +++ b/DeskFrame/Converters/IconSizeToImageWidthConverter.cs @@ -0,0 +1,21 @@ +using System.Windows.Data; + +namespace DeskFrame +{ + public class IconSizeToImageWidthConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is int iconSize) + { + return iconSize; + } + return 32; // Default size + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/DeskFrame/Converters/IconSizeToItemHeightConverter.cs b/DeskFrame/Converters/IconSizeToItemHeightConverter.cs new file mode 100644 index 0000000..1cb5ee7 --- /dev/null +++ b/DeskFrame/Converters/IconSizeToItemHeightConverter.cs @@ -0,0 +1,21 @@ +using System.Windows.Data; + +namespace DeskFrame +{ + public class IconSizeToItemHeightConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is int iconSize) + { + return iconSize + 40 + iconSize / 4; + } + return 72; // 32 + 40 + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/DeskFrame/Converters/IconSizeToItemWidthConverter.cs b/DeskFrame/Converters/IconSizeToItemWidthConverter.cs new file mode 100644 index 0000000..393bcee --- /dev/null +++ b/DeskFrame/Converters/IconSizeToItemWidthConverter.cs @@ -0,0 +1,21 @@ +using System.Windows.Data; + +namespace DeskFrame +{ + public class IconSizeToItemWidthConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is int iconSize) + { + return iconSize + 20; + } + return 85; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/DeskFrame/Converters/OffsetConverter.cs b/DeskFrame/Converters/OffsetConverter.cs index ed20b30..c0ec6de 100644 --- a/DeskFrame/Converters/OffsetConverter.cs +++ b/DeskFrame/Converters/OffsetConverter.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Globalization; +using System.Windows; using System.Windows.Data; namespace DeskFrame diff --git a/DeskFrame/Core/Instance.cs b/DeskFrame/Core/Instance.cs index 3c83510..f108eb6 100644 --- a/DeskFrame/Core/Instance.cs +++ b/DeskFrame/Core/Instance.cs @@ -40,6 +40,35 @@ public class Instance : INotifyPropertyChanged private int _folderOrder = 0; private int[] _showOnVirtualDesktops; private double _titleFontSize = 13; + private int _iconSize = 32; + private string _id; + + public string Id + { + get => _id; + set + { + if (_id != value) + { + _id = value; + OnPropertyChanged(nameof(Id), value); + } + } + } + + public int IconSize + { + get => _iconSize; + set + { + if (_iconSize != value) + { + _iconSize = value; + OnPropertyChanged(nameof(IconSize), value.ToString()); + } + } + } + public double PosX { get => _posX; @@ -466,6 +495,7 @@ public Instance(Instance instance) _sortBy = instance._sortBy; _titleFontSize = instance._titleFontSize; _titleFontFamily = instance._titleFontFamily; + _iconSize = instance._iconSize; } public Instance(string name) // default instance @@ -479,7 +509,7 @@ public Instance(Instance instance) _folder = "empty"; _showHiddenFiles = false; _isLocked = false; - + _id = Guid.NewGuid().ToString(); } protected void OnPropertyChanged(string propertyName, string value) { diff --git a/DeskFrame/Core/InstanceController.cs b/DeskFrame/Core/InstanceController.cs index e2e36f6..bea88ac 100644 --- a/DeskFrame/Core/InstanceController.cs +++ b/DeskFrame/Core/InstanceController.cs @@ -50,6 +50,7 @@ public void WriteOverInstanceToKey(Instance instance, string oldKey) key.SetValue("FolderOrder", instance.FolderOrder); key.SetValue("ShowOnVirtualDesktops", string.Join(",", instance.ShowOnVirtualDesktops)); key.SetValue("TitleFontSize", instance.TitleFontSize); + key.SetValue("IconSize", instance.IconSize); } Registry.CurrentUser.DeleteSubKey(@$"SOFTWARE\{appName}\Instances\{oldKey}", throwOnMissingSubKey: false); } @@ -103,6 +104,7 @@ public void WriteInstanceToKey(Instance instance) key.SetValue("FolderOrder", instance.FolderOrder); key.SetValue("ShowOnVirtualDesktops", string.Join(",", instance.ShowOnVirtualDesktops)); key.SetValue("TitleFontSize", instance.TitleFontSize); + key.SetValue("IconSize", instance.IconSize); } } catch { } @@ -364,6 +366,13 @@ public void InitInstances() Debug.WriteLine($"TitleFontSize loaded: {temp.TitleFontSize}"); } break; + case "IconSize": + if (int.TryParse(value.ToString(), out int parsedIconSize)) + { + temp.IconSize = parsedIconSize; + Debug.WriteLine($"IconSize loaded: {temp.IconSize}"); + } + break; default: Debug.WriteLine($"Unknown value: {valueName}"); break; diff --git a/DeskFrame/DeskFrame.csproj b/DeskFrame/DeskFrame.csproj index 1781281..60b77ba 100644 --- a/DeskFrame/DeskFrame.csproj +++ b/DeskFrame/DeskFrame.csproj @@ -20,6 +20,7 @@ + diff --git a/DeskFrame/DeskFrameWindow.xaml b/DeskFrame/DeskFrameWindow.xaml index 5ba44a3..79813d9 100644 --- a/DeskFrame/DeskFrameWindow.xaml +++ b/DeskFrame/DeskFrameWindow.xaml @@ -26,7 +26,8 @@ LocationChanged="Window_LocationChanged" KeyDown="Window_KeyDown" MouseLeave="Window_MouseLeave" - MouseEnter="Window_MouseEnter"> + MouseEnter="Window_MouseEnter" + MouseWheel="Window_MouseWheel"> @@ -38,15 +39,22 @@ + + + + - + - + diff --git a/DeskFrame/DeskFrameWindow.xaml.cs b/DeskFrame/DeskFrameWindow.xaml.cs index b81dbef..7660205 100644 --- a/DeskFrame/DeskFrameWindow.xaml.cs +++ b/DeskFrame/DeskFrameWindow.xaml.cs @@ -1,4 +1,6 @@ -using DeskFrame.Util; +#define smoothresize + +using DeskFrame.Util; using Microsoft.Win32; using System.Collections; using System.Collections.ObjectModel; @@ -36,6 +38,7 @@ using ListView = Wpf.Ui.Controls.ListView; using ListViewItem = System.Windows.Controls.ListViewItem; using WindowsDesktop; +using Microsoft.WindowsAPICodePack.Shell; namespace DeskFrame { public partial class DeskFrameWindow : System.Windows.Window @@ -54,6 +57,9 @@ public partial class DeskFrameWindow : System.Windows.Window private bool _isLocked = false; private bool _isOnEdge = false; private double _originalHeight; + private int _iconSize = 32; // Default icon size + private const int MIN_ICON_SIZE = 16; + private const int MAX_ICON_SIZE = 256; public int neighborFrameCount = 0; public bool isMouseDown = false; private ICollectionView _collectionView; @@ -207,8 +213,14 @@ private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref b { Interop.RECT rect = (Interop.RECT)Marshal.PtrToStructure(lParam, typeof(Interop.RECT)); double width = rect.Right - rect.Left; - double newWidth = (Math.Round(width / 85.0) * 85 + 13); - if (width != newWidth) + + #if (smoothresize) + double newWidth = width; + if (true) + #else + double newWidth = (Math.Round(width / 85.0) * 85 + 13); + if (width != newWidth) + #endif { if (wParam.ToInt32() == 2) // WMSZ_RIGHT { @@ -602,6 +614,7 @@ public DeskFrameWindow(Instance instance) this.MinWidth = 98; this.Loaded += MainWindow_Loaded; this.SourceInitialized += MainWindow_SourceInitialized!; + this.Closing += Window_Closing; this.StateChanged += (sender, args) => { @@ -917,6 +930,8 @@ public async void LoadFiles(string path) return; } + _iconSize = Instance.IconSize; + var fileEntries = await Task.Run(() => { if (loadFiles_cts.IsCancellationRequested) @@ -1300,112 +1315,49 @@ private void FileItem_MouseLeave(object sender, MouseEventArgs e) } private async Task GetThumbnailAsync(string path) { - return await Task.Run(async () => + try { - if (string.IsNullOrWhiteSpace(path) || (!File.Exists(path) && !Directory.Exists(path))) - { - Console.WriteLine("Invalid path: " + path); - return null; - } - - if (Path.GetExtension(path).ToLower() == ".svg") + if (File.Exists(path)) { - return await LoadSvgThumbnailAsync(path); - } - - IntPtr hBitmap = IntPtr.Zero; - Interop.IShellItemImageFactory? factory = null; - int attempts = 0; - while (attempts < 4) // Try 4 times if needed - { - try + using (var shellFile = ShellObject.FromParsingName(path)) { - Guid shellItemImageFactoryGuid = new Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b"); - int hr = Interop.SHCreateItemFromParsingName(path, IntPtr.Zero, ref shellItemImageFactoryGuid, out factory); - - if (hr != 0 || factory == null) - { - Console.WriteLine($"Failed to create factory (attempt {attempts + 1}). HRESULT: {hr}"); - } - else + var bitmap = shellFile.Thumbnail.ExtraLargeBitmapSource; + if (bitmap != null) { - int thumbnailSize = Directory.Exists(path) ? 128 : 64; - System.Drawing.Size desiredSize = new System.Drawing.Size(thumbnailSize, thumbnailSize); - hr = factory.GetImage(desiredSize, 0, out hBitmap); - - if (hr == 0 && hBitmap != IntPtr.Zero) - { - return Application.Current.Dispatcher.Invoke(() => - { - BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap( - hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); - Interop.DeleteObject(hBitmap); // Clean up the HBitmap - return bitmapSource; - }); - } - else - { - Debug.WriteLine($"Failed to get image (attempt {attempts + 1}). HRESULT: {hr}"); - } + return ResizeBitmap(bitmap, _iconSize, _iconSize); } } - catch (Exception ex) - { - Debug.WriteLine($"Exception occurred (attempt {attempts + 1}): {ex.Message}"); - } - finally - { - if (factory != null) Marshal.ReleaseComObject(factory); - if (hBitmap != IntPtr.Zero) Interop.DeleteObject(hBitmap); - } - - attempts++; } - - Debug.WriteLine("Failed to retrieve thumbnail after 2 attempts."); return null; - }); - } - - - - private async Task LoadSvgThumbnailAsync(string path) - { - try - { - var svgDocument = Svg.SvgDocument.Open(path); - - using (var bitmap = svgDocument.Draw(64, 64)) - { - using (var ms = new MemoryStream()) - { - bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); - ms.Seek(0, SeekOrigin.Begin); - - BitmapImage bitmapImage = null; - Application.Current.Dispatcher.Invoke(() => - { - bitmapImage = new BitmapImage(); - bitmapImage.BeginInit(); - bitmapImage.StreamSource = ms; - bitmapImage.DecodePixelWidth = 64; - bitmapImage.DecodePixelHeight = 64; - bitmapImage.CacheOption = BitmapCacheOption.OnLoad; - bitmapImage.EndInit(); - }); - return bitmapImage; - } - } } catch (Exception ex) { - Debug.WriteLine($"Failed to load SVG thumbnail: {ex.Message}"); + Debug.WriteLine($"Error getting thumbnail for {path}: {ex.Message}"); return null; } } + private BitmapSource ResizeBitmap(BitmapSource source, int width, int height) + { + var scaleX = (double)width / source.PixelWidth; + var scaleY = (double)height / source.PixelHeight; + + var scaleTransform = new ScaleTransform(scaleX, scaleY); + var transformedBitmap = new TransformedBitmap(source, scaleTransform); + + // Create a new BitmapSource with the exact dimensions we want + var finalBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null); + var stride = width * 4; // 4 bytes per pixel for Pbgra32 + var pixels = new byte[stride * height]; + transformedBitmap.CopyPixels(pixels, stride, 0); + finalBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0); + + return finalBitmap; + } + private void Window_Loaded(object sender, RoutedEventArgs e) { + LoadIconSizeFromRegistry(); UpdateFileExtensionIcon(); UpdateHiddenFilesIcon(); UpdateIconVisibility(); @@ -1419,6 +1371,35 @@ private void Window_Loaded(object sender, RoutedEventArgs e) } BackgroundType(toBlur); + + if (Instance != null) + { + Instance.IconSize = _iconSize; + } + + if (FileItems != null && FileItems.Count > 0) + { + Dispatcher.InvokeAsync(async () => + { + var batchSize = 5; + var items = FileItems.ToList(); + + for (int i = 0; i < items.Count; i += batchSize) + { + var batch = items.Skip(i).Take(batchSize).ToList(); + foreach (var item in batch) + { + var newThumbnail = await GetThumbnailAsync(item.FullPath!); + if (newThumbnail != null) + { + item.Thumbnail = newThumbnail; + item.OnPropertyChanged(nameof(item.Thumbnail)); + } + } + await Task.Delay(50); + } + }, System.Windows.Threading.DispatcherPriority.Background); + } } public void ChangeBackgroundOpacity(int num) @@ -1997,9 +1978,6 @@ public bool IsSelected { _isSelected = value; Background = _isSelected ? new SolidColorBrush(Color.FromArgb(50, 255, 255, 255)) : Brushes.Transparent; - - // int.MaxValue for full height, 70 for 4 lines - // MaxHeight = _isSelected ? 70 : 40; MaxHeight = _isSelected ? 40 : 40; TextTrimming = _isSelected ? TextTrimming.CharacterEllipsis : TextTrimming.CharacterEllipsis; @@ -2042,7 +2020,7 @@ private set } } - protected void OnPropertyChanged(string propertyName) + public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } @@ -2095,5 +2073,95 @@ public void UpdateIconVisibility() HiddenFilesIconGrid.Visibility = Instance.ShowHiddenFilesIcon ? Visibility.Visible : Visibility.Collapsed; } } + + private void Window_MouseWheel(object sender, MouseWheelEventArgs e) + { + if (Keyboard.Modifiers == ModifierKeys.Control && showFolder.Visibility == Visibility.Visible) + { + int delta = e.Delta > 0 ? 8 : -8; + int newSize = Math.Clamp(Instance.IconSize + delta, MIN_ICON_SIZE, MAX_ICON_SIZE); + + if (newSize != Instance.IconSize) + { + _iconSize = newSize; + Instance.IconSize = newSize; + SaveIconSizeToRegistry(); + + Dispatcher.InvokeAsync(async () => + { + var batchSize = 5; + var items = FileItems.ToList(); + + for (int i = 0; i < items.Count; i += batchSize) + { + var batch = items.Skip(i).Take(batchSize).ToList(); + foreach (var item in batch) + { + var newThumbnail = await GetThumbnailAsync(item.FullPath!); + if (newThumbnail != null) + { + item.Thumbnail = newThumbnail; + item.OnPropertyChanged(nameof(item.Thumbnail)); + } + } + await Task.Delay(50); + } + }, System.Windows.Threading.DispatcherPriority.Background); + } + } + } + + private void SaveIconSizeToRegistry() + { + try + { + using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\DeskFrame")) + { + if (key != null) + { + key.SetValue($"IconSize_{Instance.Id}", _iconSize, RegistryValueKind.DWord); + Debug.WriteLine($"Saved icon size {_iconSize} for instance {Instance.Id}"); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to save icon size to registry: {ex.Message}"); + } + } + + private void LoadIconSizeFromRegistry() + { + try + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\DeskFrame")) + { + if (key != null) + { + object value = key.GetValue($"IconSize_{Instance.Id}"); + if (value != null) + { + int size = Math.Clamp(Convert.ToInt32(value), MIN_ICON_SIZE, MAX_ICON_SIZE); + _iconSize = size; + Instance.IconSize = size; + Debug.WriteLine($"Loaded icon size {size} for instance {Instance.Id}"); + } + else + { + Debug.WriteLine($"No saved icon size found for instance {Instance.Id}"); + } + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to load icon size from registry: {ex.Message}"); + } + } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + SaveIconSizeToRegistry(); + } } } \ No newline at end of file