mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into administrator_mode
This commit is contained in:
commit
cf1d162f2c
12 changed files with 413 additions and 37 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,24 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_open_search_in">Open search in:</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_new_window">New Window</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_new_tab">New Tab</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_open_url">Open url:{0}</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_cannot_open_url">Can't open url:{0}</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_plugin_name">URL</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_plugin_description">Open the typed URL from Flow Launcher</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_plugin_set_tip">Please set your browser path:</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_plugin_choose">Choose</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_plugin_filter">Application(*.exe)|*.exe|All files|*.*</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_use_custom_browser">Use custom instead of Flow's default web browser</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_browser_path">Browser path</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_new_tab">New tab</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_url_new_window">New window</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_private_mode">Private mode</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_url_usehttps">Prefer https over http</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -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<Result> Query(Query query)
|
||||
{
|
||||
var raw = query.Search;
|
||||
if (IsURL(raw))
|
||||
{
|
||||
return new List<Result>
|
||||
{
|
||||
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<Result>(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>();
|
||||
|
||||
Settings = context.API.LoadSettingJsonStorage<Settings>();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
126
Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
Normal file
126
Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<UserControl
|
||||
x:Class="Flow.Launcher.Plugin.Url.SettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:Flow.Launcher.Plugin.Url.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Flow.Launcher.Plugin.Url"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_url_use_custom_browser}"
|
||||
IsChecked="{Binding Settings.UseCustomBrowser, Mode=TwoWay}" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Left"
|
||||
Visibility="{Binding Settings.UseCustomBrowser, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_url_browser_path}" />
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Settings.BrowserPath, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Click="SelectBrowserPath"
|
||||
Content="{DynamicResource flowlauncher_plugin_url_plugin_choose}" />
|
||||
</Grid>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
|
||||
Orientation="Horizontal">
|
||||
<RadioButton Content="{DynamicResource flowlauncher_plugin_url_new_tab}" IsChecked="{Binding Settings.OpenInNewBrowserWindow, Converter={StaticResource InverseBoolConverter}, Mode=TwoWay}" />
|
||||
<RadioButton Content="{DynamicResource flowlauncher_plugin_url_new_window}" IsChecked="{Binding Settings.OpenInNewBrowserWindow, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_url_private_mode}" />
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Settings.PrivateModeArgument, Mode=TwoWay}" />
|
||||
<CheckBox
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content=""
|
||||
IsChecked="{Binding Settings.OpenInPrivateMode, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="2"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_url_usehttps}"
|
||||
IsChecked="{Binding Settings.AlwaysOpenWithHttps, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
27
Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml.cs
Normal file
27
Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Url;
|
||||
|
||||
public partial class SettingsControl : UserControl
|
||||
{
|
||||
public Settings Settings { get; } = Main.Settings;
|
||||
|
||||
public SettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SelectBrowserPath(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
Filter = Localize.flowlauncher_plugin_url_plugin_filter()
|
||||
};
|
||||
|
||||
if (dlg.ShowDialog() == true && !string.IsNullOrEmpty(dlg.FileName))
|
||||
{
|
||||
Settings.BrowserPath = dlg.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@
|
|||
<system:String x:Key="flowlauncher_plugin_websearch_input_url">Please enter a URL</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_websearch_action_keyword_exist">Action keyword already exists, please enter a different one</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_websearch_succeed">Success</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_websearch_edit_failed">Failed to update search source. The item may have been removed.</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_websearch_iconpath_hint">Hint: You do not need to place custom images in this directory, if Flow's version is updated they will be lost. Flow will automatically copy any images outside of this directory across to WebSearch's custom image location.</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_websearch_plugin_name">Web Searches</system:String>
|
||||
|
|
|
|||
|
|
@ -108,9 +108,19 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
_context.API.AddActionKeyword(id, newKeyword);
|
||||
|
||||
var index = _searchSources.IndexOf(_oldSearchSource);
|
||||
_searchSources[index] = _searchSource;
|
||||
|
||||
Close();
|
||||
|
||||
// Only update if we found the item in the collection
|
||||
if (index >= 0 && index < _searchSources.Count)
|
||||
{
|
||||
_searchSources[index] = _searchSource;
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
var warning = _api.GetTranslation("flowlauncher_plugin_websearch_edit_failed");
|
||||
_context.API.ShowMsgBox(warning);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@
|
|||
MouseDoubleClick="MouseDoubleClickItem"
|
||||
SelectedItem="{Binding Settings.SelectedSearchSource}"
|
||||
SizeChanged="ListView_SizeChanged"
|
||||
PreviewMouseLeftButtonDown="ListView_PreviewMouseLeftButtonDown"
|
||||
PreviewMouseMove="ListView_PreviewMouseMove"
|
||||
AllowDrop="True"
|
||||
Drop="ListView_Drop"
|
||||
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.Plugin.WebSearch
|
||||
{
|
||||
|
|
@ -12,6 +15,7 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
{
|
||||
private readonly Settings _settings;
|
||||
private readonly PluginInitContext _context;
|
||||
private Point _dragStartPoint;
|
||||
|
||||
public SettingsControl(PluginInitContext context, SettingsViewModel viewModel)
|
||||
{
|
||||
|
|
@ -163,5 +167,78 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
gView.Columns[3].Width = workingWidth * col4;
|
||||
gView.Columns[4].Width = workingWidth * col5;
|
||||
}
|
||||
|
||||
private void ListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_dragStartPoint = e.GetPosition(null);
|
||||
}
|
||||
|
||||
private void ListView_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
Point mousePos = e.GetPosition(null);
|
||||
Vector diff = _dragStartPoint - mousePos;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
{
|
||||
var listView = (ListView)sender;
|
||||
ListViewItem listViewItem = FindAncestor<ListViewItem>((DependencyObject)e.OriginalSource);
|
||||
|
||||
if (listViewItem == null) return;
|
||||
|
||||
SearchSource item = (SearchSource)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
|
||||
if (item == null) return;
|
||||
|
||||
DragDrop.DoDragDrop(listViewItem, item, DragDropEffects.Move);
|
||||
}
|
||||
}
|
||||
|
||||
private void ListView_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(typeof(SearchSource)))
|
||||
{
|
||||
SearchSource droppedData = e.Data.GetData(typeof(SearchSource)) as SearchSource;
|
||||
var listView = (ListView)sender;
|
||||
var target = GetNearestContainer(e.OriginalSource);
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
SearchSource targetData = (SearchSource)listView.ItemContainerGenerator.ItemFromContainer(target);
|
||||
|
||||
if (targetData == null)
|
||||
return;
|
||||
|
||||
var items = _settings.SearchSources;
|
||||
int removedIdx = items.IndexOf(droppedData);
|
||||
int targetIdx = items.IndexOf(targetData);
|
||||
|
||||
if (removedIdx == targetIdx)
|
||||
return;
|
||||
|
||||
items.Move(removedIdx, targetIdx);
|
||||
}
|
||||
}
|
||||
|
||||
private ListViewItem GetNearestContainer(object source)
|
||||
{
|
||||
var element = source as UIElement;
|
||||
while (element != null && !(element is ListViewItem))
|
||||
element = VisualTreeHelper.GetParent(element) as UIElement;
|
||||
|
||||
return element as ListViewItem;
|
||||
}
|
||||
|
||||
private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T)
|
||||
return (T)current;
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue