diff --git a/QuickLook.Common b/QuickLook.Common index eb72b2e..2dc2616 160000 --- a/QuickLook.Common +++ b/QuickLook.Common @@ -1 +1 @@ -Subproject commit eb72b2eabb45b45479d2adcd235d23f941da4a99 +Subproject commit 2dc2616aebfd79c428e5a4e94a40368df5bbe3d4 diff --git a/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Exporter.cs b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Exporter.cs index de010ea..1672b2d 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Exporter.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Exporter.cs @@ -1,4 +1,21 @@ -using MediaInfoLib; +// Copyright © 2017-2025 QL-Win Contributors +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using MediaInfoLib; using System.ComponentModel.Composition; namespace QuickLook.Plugin.MediaInfoViewer; diff --git a/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/MoreMenuProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/MoreMenuProvider.cs new file mode 100644 index 0000000..c948eaa --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/MoreMenuProvider.cs @@ -0,0 +1,53 @@ +// Copyright © 2017-2025 QL-Win Contributors +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using QuickLook.Common.Commands; +using QuickLook.Common.Helpers; +using QuickLook.Common.Plugin.MoreMenu; +using System; +using System.Collections.Generic; +using System.Windows.Input; + +namespace QuickLook.Plugin.MediaInfoViewer; + +public sealed class MoreMenuProvider +{ + public static Lazy Instance { get; set; } = new(() => new()); + + public ICommand ShowWithMediaInfoCommand { get; } + + public MoreMenuProvider() + { + ShowWithMediaInfoCommand = new RelayCommand(ShowWithMediaInfo); + } + + public IEnumerable Get() + { + yield return new MoreMenuItem() + { + Icon = "\xea69", + Header = "Show Media Info", + MenuItems = null, + Command = ShowWithMediaInfoCommand, + }; + } + + public void ShowWithMediaInfo() + { + PluginHelper.InvokePluginPreview("QuickLook.Plugin.MediaInfoViewer"); + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Plugin.cs index 172bf2c..be49dc4 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/Plugin.cs @@ -18,7 +18,9 @@ using MediaInfoLib; using QuickLook.Common.Helpers; using QuickLook.Common.Plugin; +using QuickLook.Common.Plugin.MoreMenu; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows; @@ -26,12 +28,14 @@ using System.Windows.Media; namespace QuickLook.Plugin.MediaInfoViewer; -public class Plugin : IViewer +public class Plugin : IViewer, IMoreMenuExtended { private TextViewerPanel _tvp; public int Priority => 0; + public IEnumerable MenuItems => MoreMenuProvider.Instance.Value.Get(); + public void Init() { } diff --git a/QuickLook/PipeServerManager.cs b/QuickLook/PipeServerManager.cs index 902ab76..5d281fe 100644 --- a/QuickLook/PipeServerManager.cs +++ b/QuickLook/PipeServerManager.cs @@ -27,7 +27,7 @@ using System.Windows.Threading; namespace QuickLook; -internal static class PipeMessages +public static class PipeMessages { public const string RunAndClose = "QuickLook.App.PipeMessages.RunAndClose"; public const string Switch = "QuickLook.App.PipeMessages.Switch"; @@ -38,7 +38,7 @@ internal static class PipeMessages public const string Quit = "QuickLook.App.PipeMessages.Quit"; } -internal class PipeServerManager : IDisposable +public class PipeServerManager : IDisposable { private static readonly string PipeName = "QuickLook.App.Pipe." + WindowsIdentity.GetCurrent().User?.Value; private static PipeServerManager _instance; diff --git a/QuickLook/PluginManager.cs b/QuickLook/PluginManager.cs index 2325880..b4ccb8f 100644 --- a/QuickLook/PluginManager.cs +++ b/QuickLook/PluginManager.cs @@ -29,7 +29,7 @@ using UnblockZoneIdentifier; namespace QuickLook; -internal class PluginManager +public class PluginManager { private static PluginManager _instance; diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index a821a0b..ee1ce2b 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -22,11 +22,10 @@ using System; using System.Diagnostics; using System.IO; using System.Runtime.ExceptionServices; -using System.Windows; namespace QuickLook; -internal class ViewWindowManager : IDisposable +public class ViewWindowManager : IDisposable { private static ViewWindowManager _instance; @@ -239,7 +238,7 @@ internal class ViewWindowManager : IDisposable }; } - internal static ViewWindowManager GetInstance() + public static ViewWindowManager GetInstance() { return _instance ??= new ViewWindowManager(); } diff --git a/QuickLook/ViewerWindow.Actions.cs b/QuickLook/ViewerWindow.Actions.cs index 6a01c27..8fee114 100644 --- a/QuickLook/ViewerWindow.Actions.cs +++ b/QuickLook/ViewerWindow.Actions.cs @@ -17,15 +17,22 @@ using QuickLook.Common.Helpers; using QuickLook.Common.Plugin; +using QuickLook.Common.Plugin.MoreMenu; using QuickLook.Helpers; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.ExceptionServices; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Threading; +using Wpf.Ui.Controls; +using MenuItem = System.Windows.Controls.MenuItem; namespace QuickLook; @@ -57,7 +64,7 @@ public partial class ViewerWindow private void PositionWindow(Size size) { - // if the window is now now maximized, do not move it + // If the window is now now maximized, do not move it if (WindowState == WindowState.Maximized) return; @@ -70,7 +77,7 @@ public partial class ViewerWindow private Rect ResizeAndCentreExistingWindow(Size size) { - // align window just like in macOS ... + // Align window just like in macOS ... // // |10%| 80% |10%| // |---|-----------|---|--- @@ -88,11 +95,11 @@ public partial class ViewerWindow var limitPercentX = 0.1 * scale.Horizontal; var limitPercentY = 0.1 * scale.Vertical; - // use absolute pixels for calculation + // Use absolute pixels for calculation var pxSize = new Size(scale.Horizontal * size.Width, scale.Vertical * size.Height); var pxOldRect = this.GetWindowRectInPixel(); - // scale to new size, maintain centre + // Scale to new size, maintain centre var pxNewRect = Rect.Inflate(pxOldRect, (pxSize.Width - pxOldRect.Width) / 2, (pxSize.Height - pxOldRect.Height) / 2); @@ -121,7 +128,7 @@ public partial class ViewerWindow pxNewRect.Offset(0, Math.Max(0, desktopRect.Top - pxNewRect.Top) + Math.Min(0, desktopRect.Bottom - pxNewRect.Bottom)); - // return absolute location and relative size + // Return absolute location and relative size return new Rect(pxNewRect.Location, size); } @@ -135,13 +142,13 @@ public partial class ViewerWindow desktopRect.X + (desktopRect.Width - pxSize.Width) / 2, desktopRect.Y + (desktopRect.Height - pxSize.Height) / 2); - // return absolute location and relative size + // Return absolute location and relative size return new Rect(pxLocation, size); } internal void UnloadPlugin() { - // the focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention + // The focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention FocusManager.SetFocusedElement(this, null); Keyboard.DefaultRestoreFocusMode = RestoreFocusMode.None; // WPF will put the focused item into a "_restoreFocus" list ... omg @@ -180,10 +187,10 @@ public partial class ViewerWindow ContextObject.Reset(); - // assign monitor color profile + // Assign monitor color profile ContextObject.ColorProfileName = DisplayDeviceHelper.GetMonitorColorProfileFromWindow(this); - // get window size before showing it + // Get window size before showing it try { Plugin.Prepare(path, ContextObject); @@ -194,9 +201,30 @@ public partial class ViewerWindow return; } + // Initial the more menu + ClearMoreMenuUnpin(); + foreach (var plugin in PluginManager.GetInstance().LoadedPlugins) + { + if (plugin == Plugin) + { + if (Plugin is IMoreMenu moreMenu && moreMenu.MenuItems is not null) + { + InsertMoreMenu(moreMenu.MenuItems); + } + continue; + } + else + { + if (plugin is IMoreMenuExtended moreMenu && moreMenu.MenuItems is not null) + { + InsertMoreMenu(moreMenu.MenuItems); + } + } + } + SetOpenWithButtonAndPath(); - // revert UI changes + // Revert UI changes ContextObject.IsBusy = true; var newHeight = ContextObject.PreferredSize.Height + BorderThickness.Top + BorderThickness.Bottom + @@ -204,7 +232,7 @@ public partial class ViewerWindow var newWidth = ContextObject.PreferredSize.Width + BorderThickness.Left + BorderThickness.Right; var newSize = new Size(newWidth, newHeight); - // if use has adjusted the window size, keep it + // If use has adjusted the window size, keep it if (_customWindowSize != Size.Empty) newSize = _customWindowSize; else @@ -231,7 +259,7 @@ public partial class ViewerWindow _autoReloadWatcher.EnableRaisingEvents = true; } - // load plugin, do not block UI + // Load plugin, do not block UI Dispatcher.BeginInvoke(new Action(() => { try @@ -246,12 +274,79 @@ public partial class ViewerWindow DispatcherPriority.Input); } + private void ClearMoreMenuUnpin() + { + var toRemove = buttonMore.ContextMenu.Items + .OfType() // MenuItem and Separator + .Where(item => item.Tag is not "PinMenu") + .ToArray(); + + foreach (var item in toRemove) + { + buttonMore.ContextMenu.Items.Remove(item); + } + } + + private void InsertMoreMenu(IEnumerable moreMenu) + { + int count = 0; + + foreach (IMenuItem item in moreMenu) + { + if (item is null) continue; + + if (!item.IsSeparator) + { + MenuItem menuItem = new() + { + Header = item.Header, + Icon = ResolveIcon(item.Icon), + Visibility = item.IsVisible ? Visibility.Visible : Visibility.Collapsed, + Command = item.Command, + }; + + buttonMore.ContextMenu.Items.Insert(count++, menuItem); + } + else + { + buttonMore.ContextMenu.Items.Insert(count++, new Separator()); + } + } + + if (moreMenu.Any()) + { + buttonMore.ContextMenu.Items.Insert(count++, new Separator()); + } + } + + private object ResolveIcon(object icon) + { + if (icon is string glyph) + { + return new FontIcon() + { + FontFamily = (FontFamily)Application.Current.Resources["SymbolThemeFontFamily"], + Glyph = glyph, + }; + } + else if (icon is UIElement) + { + return icon; + } + else + { + // Not supported yet + } + + return null; + } + private void SetOpenWithButtonAndPath() { - // share icon + // Share icon buttonShare.Visibility = ShareHelper.IsShareSupported(_path) ? Visibility.Visible : Visibility.Collapsed; - // open icon + // Open icon if (Directory.Exists(_path)) { buttonOpen.ToolTip = string.Format(TranslationHelper.Get("MW_BrowseFolder"), Path.GetFileName(_path)); @@ -265,7 +360,7 @@ public partial class ViewerWindow return; } - // not an exe + // Not an exe var found = FileHelper.GetAssocApplication(_path, out appFriendlyName); if (found) { @@ -273,7 +368,7 @@ public partial class ViewerWindow return; } - // assoc not found + // Assoc not found buttonOpen.ToolTip = string.Format(TranslationHelper.Get("MW_Open"), Path.GetFileName(_path)); } diff --git a/QuickLook/ViewerWindow.xaml b/QuickLook/ViewerWindow.xaml index 128719b..6f5fda0 100644 --- a/QuickLook/ViewerWindow.xaml +++ b/QuickLook/ViewerWindow.xaml @@ -132,14 +132,15 @@ ToolTip="More"> - + - +