diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 3e7c3cb83..2ec69e81a 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -89,9 +89,12 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel if (window.ShowDialog() is not true) return; var index = Settings.CustomPluginHotkeys.IndexOf(settingItem); - Settings.CustomPluginHotkeys[index] = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword); - HotKeyMapper.RemoveHotkey(settingItem.Hotkey); // remove origin hotkey - HotKeyMapper.SetCustomQueryHotkey(Settings.CustomPluginHotkeys[index]); // set new hotkey + if (index >= 0 && index < Settings.CustomPluginHotkeys.Count) + { + Settings.CustomPluginHotkeys[index] = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword); + HotKeyMapper.RemoveHotkey(settingItem.Hotkey); // remove origin hotkey + HotKeyMapper.SetCustomQueryHotkey(Settings.CustomPluginHotkeys[index]); // set new hotkey + } } [RelayCommand] @@ -150,7 +153,10 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel if (window.ShowDialog() is not true) return; var index = Settings.CustomShortcuts.IndexOf(settingItem); - Settings.CustomShortcuts[index] = new CustomShortcutModel(window.Key, window.Value); + if (index >= 0 && index < Settings.CustomShortcuts.Count) + { + Settings.CustomShortcuts[index] = new CustomShortcutModel(window.Key, window.Value); + } } [RelayCommand] diff --git a/Plugins/Flow.Launcher.Plugin.Url/Converters/BoolToVisibilityConverter.cs b/Plugins/Flow.Launcher.Plugin.Url/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 000000000..00f41a174 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Url/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Flow.Launcher.Plugin.Url.Converters; + +[ValueConversion(typeof(bool), typeof(Visibility))] +public class BoolToVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not bool) + throw new ArgumentException("value should be boolean", nameof(value)); + + return (bool)value ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new InvalidOperationException(); + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Url/Converters/InverseBoolConverter.cs b/Plugins/Flow.Launcher.Plugin.Url/Converters/InverseBoolConverter.cs new file mode 100644 index 000000000..c46d416a8 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Url/Converters/InverseBoolConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace Flow.Launcher.Plugin.Url.Converters; + +[ValueConversion(typeof(bool), typeof(bool))] +public class InverseBoolConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not bool) + throw new ArgumentException("value should be boolean", nameof(value)); + + return !(bool)value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not bool) + throw new ArgumentException("value should be boolean", nameof(value)); + + return !(bool)value; + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml index 461ccd197..8fe0b77ce 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml @@ -1,18 +1,24 @@ - + - Open search in: - New Window - New Tab - Open url:{0} Can't open url:{0} URL Open the typed URL from Flow Launcher - Please set your browser path: Choose Application(*.exe)|*.exe|All files|*.* + + Use custom instead of Flow's default web browser + Browser path + + New tab + New window + + Private mode + + Prefer https over http \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Url/Main.cs b/Plugins/Flow.Launcher.Plugin.Url/Main.cs index db7cecbde..8e77dbd15 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Url/Main.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using System.Windows.Controls; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.Url { - public class Main : IPlugin, IPluginI18n + public class Main : IPlugin, IPluginI18n, ISettingProvider { //based on https://gist.github.com/dperini/729294 - private const string urlPattern = "^" + + private const string UrlPattern = "^" + // protocol identifier "(?:(?:https?|ftp)://|)" + // user:pass authentication @@ -39,18 +41,18 @@ namespace Flow.Launcher.Plugin.Url // resource path "(?:/\\S*)?" + "$"; - Regex reg = new Regex(urlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex UrlRegex = new(UrlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); internal static PluginInitContext Context { get; private set; } - private Settings _settings; - + internal static Settings Settings { get; private set; } + public List Query(Query query) { var raw = query.Search; if (IsURL(raw)) { - return new List - { - new Result + return + [ + new() { Title = raw, SubTitle = Localize.flowlauncher_plugin_url_open_url(raw), @@ -58,14 +60,28 @@ namespace Flow.Launcher.Plugin.Url Score = 8, Action = _ => { - if (!raw.ToLower().StartsWith("http")) + if (!raw.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !raw.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { - raw = "http://" + raw; + raw = GetHttpPreference() + "://" + raw; } try { - Context.API.OpenUrl(raw); - + if (Settings.UseCustomBrowser) + { + if (Settings.OpenInNewBrowserWindow) + { + SearchWeb.OpenInBrowserWindow(raw, Settings.BrowserPath, Settings.OpenInPrivateMode, Settings.PrivateModeArgument); + } + else + { + SearchWeb.OpenInBrowserTab(raw, Settings.BrowserPath, Settings.OpenInPrivateMode, Settings.PrivateModeArgument); + } + } + else + { + Context.API.OpenWebUrl(raw); + } + return true; } catch(Exception) @@ -75,16 +91,22 @@ namespace Flow.Launcher.Plugin.Url } } } - }; + ]; } - return new List(0); + + return []; + } + + private static string GetHttpPreference() + { + return Settings.AlwaysOpenWithHttps ? "https" : "http"; } public bool IsURL(string raw) { raw = raw.ToLower(); - if (reg.Match(raw).Value == raw) return true; + if (UrlRegex.Match(raw).Value == raw) return true; if (raw == "localhost" || raw.StartsWith("localhost:") || raw == "http://localhost" || raw.StartsWith("http://localhost:") || @@ -100,8 +122,8 @@ namespace Flow.Launcher.Plugin.Url public void Init(PluginInitContext context) { Context = context; - - _settings = context.API.LoadSettingJsonStorage(); + + Settings = context.API.LoadSettingJsonStorage(); } public string GetTranslatedPluginTitle() @@ -113,5 +135,10 @@ namespace Flow.Launcher.Plugin.Url { return Localize.flowlauncher_plugin_url_plugin_description(); } + + public Control CreateSettingPanel() + { + return new SettingsControl(); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Url/Settings.cs b/Plugins/Flow.Launcher.Plugin.Url/Settings.cs index a8d89e27f..21ac270da 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Url/Settings.cs @@ -1,9 +1,53 @@ namespace Flow.Launcher.Plugin.Url { - public class Settings + public class Settings : BaseModel { - public string BrowserPath { get; set; } + private bool _useCustomBrowser = false; + public bool UseCustomBrowser + { + get => _useCustomBrowser; + set + { + if (_useCustomBrowser != value) + { + _useCustomBrowser = value; + OnPropertyChanged(); + } + } + } - public bool OpenInNewBrowserWindow { get; set; } = true; + private string _browserPath = string.Empty; + public string BrowserPath + { + get => _browserPath; + set + { + if (_browserPath != value) + { + _browserPath = value; + OnPropertyChanged(); + } + } + } + + private bool _openInNewBrowserWindow = true; + public bool OpenInNewBrowserWindow + { + get => _openInNewBrowserWindow; + set + { + if (_openInNewBrowserWindow != value) + { + _openInNewBrowserWindow = value; + OnPropertyChanged(); + } + } + } + + public bool OpenInPrivateMode { get; set; } = false; + + public string PrivateModeArgument { get; set; } = string.Empty; + + public bool AlwaysOpenWithHttps { get; set; } = false; } } diff --git a/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml new file mode 100644 index 000000000..a7977551f --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +