mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge dev into fork/040427SplitSettingWindow with merge conflicts resolved
This commit is contained in:
commit
2202fce587
15 changed files with 313 additions and 79 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins
|
||||
{
|
||||
|
|
@ -13,9 +13,11 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
public string Website { get; set; }
|
||||
public string UrlDownload { get; set; }
|
||||
public string UrlSourceCode { get; set; }
|
||||
public string LocalInstallPath { get; set; }
|
||||
public string IcoPath { get; set; }
|
||||
public DateTime? LatestReleaseDate { get; set; }
|
||||
public DateTime? DateAdded { get; set; }
|
||||
|
||||
public bool IsFromLocalInstallPath => !string.IsNullOrEmpty(LocalInstallPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -380,7 +380,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Update a plugin to new version, from a zip file. Will Delete zip after updating.
|
||||
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
|
||||
/// unless it's a local path installation
|
||||
/// </summary>
|
||||
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
|
||||
{
|
||||
|
|
@ -390,11 +391,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Install a plugin. Will Delete zip after updating.
|
||||
/// Install a plugin. By default will remove the zip file if installation is from url, unless it's a local path installation
|
||||
/// </summary>
|
||||
public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
|
||||
{
|
||||
InstallPlugin(plugin, zipFilePath, true);
|
||||
InstallPlugin(plugin, zipFilePath, checkModified: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -420,7 +421,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// Unzip plugin files to temp folder
|
||||
var tempFolderPluginPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath);
|
||||
File.Delete(zipFilePath);
|
||||
|
||||
if(!plugin.IsFromLocalInstallPath)
|
||||
File.Delete(zipFilePath);
|
||||
|
||||
var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public string SelectPrevPageHotkey { get; set; } = $"PageDown";
|
||||
public string OpenContextMenuHotkey { get; set; } = $"Ctrl+O";
|
||||
public string SettingWindowHotkey { get; set; } = $"Ctrl+I";
|
||||
public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up";
|
||||
public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down";
|
||||
|
||||
public string Language
|
||||
{
|
||||
|
|
@ -280,42 +282,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
{
|
||||
get
|
||||
{
|
||||
var list = new List<RegisteredHotkeyData>
|
||||
{
|
||||
new("Up", "HotkeyLeftRightDesc"),
|
||||
new("Down", "HotkeyLeftRightDesc"),
|
||||
new("Left", "HotkeyUpDownDesc"),
|
||||
new("Right", "HotkeyUpDownDesc"),
|
||||
new("Escape", "HotkeyESCDesc"),
|
||||
new("F5", "ReloadPluginHotkey"),
|
||||
new("Alt+Home", "HotkeySelectFirstResult"),
|
||||
new("Alt+End", "HotkeySelectLastResult"),
|
||||
new("Ctrl+R", "HotkeyRequery"),
|
||||
new("Ctrl+H", "ToggleHistoryHotkey"),
|
||||
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemPlus", "QuickHeightHotkey"),
|
||||
new("Ctrl+OemMinus", "QuickHeightHotkey"),
|
||||
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"),
|
||||
new("Shift+Enter", "OpenContextMenuHotkey"),
|
||||
new("Enter", "HotkeyRunDesc"),
|
||||
new("Ctrl+Enter", "OpenContainFolderHotkey"),
|
||||
new("Alt+Enter", "HotkeyOpenResult"),
|
||||
new("Ctrl+F12", "ToggleGameModeHotkey"),
|
||||
new("Ctrl+Shift+C", "CopyFilePathHotkey"),
|
||||
|
||||
new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1),
|
||||
new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2),
|
||||
new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3),
|
||||
new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4),
|
||||
new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5),
|
||||
new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6),
|
||||
new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7),
|
||||
new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8),
|
||||
new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9),
|
||||
new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10)
|
||||
};
|
||||
var list = FixedHotkeys();
|
||||
|
||||
// Customizeable hotkeys
|
||||
if(!string.IsNullOrEmpty(Hotkey))
|
||||
list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = ""));
|
||||
if(!string.IsNullOrEmpty(PreviewHotkey))
|
||||
|
|
@ -340,7 +309,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(SelectPrevPageHotkey))
|
||||
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(CycleHistoryUpHotkey))
|
||||
list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(CycleHistoryDownHotkey))
|
||||
list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = ""));
|
||||
|
||||
// Custom Query Hotkeys
|
||||
foreach (var customPluginHotkey in CustomPluginHotkeys)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey))
|
||||
|
|
@ -350,6 +324,45 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
private List<RegisteredHotkeyData> FixedHotkeys()
|
||||
{
|
||||
return new List<RegisteredHotkeyData>
|
||||
{
|
||||
new("Up", "HotkeyLeftRightDesc"),
|
||||
new("Down", "HotkeyLeftRightDesc"),
|
||||
new("Left", "HotkeyUpDownDesc"),
|
||||
new("Right", "HotkeyUpDownDesc"),
|
||||
new("Escape", "HotkeyESCDesc"),
|
||||
new("F5", "ReloadPluginHotkey"),
|
||||
new("Alt+Home", "HotkeySelectFirstResult"),
|
||||
new("Alt+End", "HotkeySelectLastResult"),
|
||||
new("Ctrl+R", "HotkeyRequery"),
|
||||
new("Ctrl+H", "ToggleHistoryHotkey"),
|
||||
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemPlus", "QuickHeightHotkey"),
|
||||
new("Ctrl+OemMinus", "QuickHeightHotkey"),
|
||||
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"),
|
||||
new("Shift+Enter", "OpenContextMenuHotkey"),
|
||||
new("Enter", "HotkeyRunDesc"),
|
||||
new("Ctrl+Enter", "OpenContainFolderHotkey"),
|
||||
new("Alt+Enter", "HotkeyOpenResult"),
|
||||
new("Ctrl+F12", "ToggleGameModeHotkey"),
|
||||
new("Ctrl+Shift+C", "CopyFilePathHotkey"),
|
||||
|
||||
new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1),
|
||||
new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2),
|
||||
new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3),
|
||||
new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4),
|
||||
new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5),
|
||||
new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6),
|
||||
new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7),
|
||||
new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8),
|
||||
new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9),
|
||||
new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum LastQueryMode
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#pragma warning disable IDE0005
|
||||
using System.Windows;
|
||||
#pragma warning restore IDE0005
|
||||
|
|
@ -200,6 +201,24 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// This checks whether a given string is a zip file path.
|
||||
/// By default does not check if the zip file actually exist on disk, can do so by
|
||||
/// setting checkFileExists = true.
|
||||
///</summary>
|
||||
public static bool IsZipFilePath(string querySearchString, bool checkFileExists = false)
|
||||
{
|
||||
if (IsLocationPathString(querySearchString) && querySearchString.Split('.').Last() == "zip")
|
||||
{
|
||||
if (checkFileExists)
|
||||
return FileExists(querySearchString);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// This checks whether a given string is a directory path or network location string.
|
||||
/// It does not check if location actually exists.
|
||||
|
|
|
|||
|
|
@ -191,11 +191,15 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase(@"\c:\", false)]
|
||||
[TestCase(@"cc:\", false)]
|
||||
[TestCase(@"\\\SomeNetworkLocation\", false)]
|
||||
[TestCase(@"\\SomeNetworkLocation\", true)]
|
||||
[TestCase("RandomFile", false)]
|
||||
[TestCase(@"c:\>*", true)]
|
||||
[TestCase(@"c:\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation\SomeFile.exe", true)]
|
||||
[TestCase(@"\\SomeNetworkLocation\SomeFile.exe", true)]
|
||||
|
||||
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
|
||||
{
|
||||
// When, Given
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@
|
|||
<system:String x:Key="SelectPrevItemHotkey">Select Previous Item</system:String>
|
||||
<system:String x:Key="SelectNextPageHotkey">Next Page</system:String>
|
||||
<system:String x:Key="SelectPrevPageHotkey">Previous Page</system:String>
|
||||
<system:String x:Key="CycleHistoryUpHotkey">Cycle Previous Query</system:String>
|
||||
<system:String x:Key="CycleHistoryDownHotkey">Cycle Next Query</system:String>
|
||||
<system:String x:Key="OpenContextMenuHotkey">Open Context Menu</system:String>
|
||||
<system:String x:Key="SettingWindowHotkey">Open Setting Window</system:String>
|
||||
<system:String x:Key="CopyFilePathHotkey">Copy File Path</system:String>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
LocationChanged="OnLocationChanged"
|
||||
Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
PreviewKeyUp="OnKeyUp"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
SizeToContent="Height"
|
||||
|
|
@ -197,6 +198,14 @@
|
|||
Key="{Binding SelectPrevPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectPrevPageCommand}"
|
||||
Modifiers="{Binding SelectPrevPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding CycleHistoryUpHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding ReverseHistoryCommand}"
|
||||
Modifiers="{Binding CycleHistoryUpHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding ForwardHistoryCommand}"
|
||||
Modifiers="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
</Window.InputBindings>
|
||||
<Grid>
|
||||
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ namespace Flow.Launcher
|
|||
private MainViewModel _viewModel;
|
||||
private bool _animating;
|
||||
MediaPlayer animationSound = new MediaPlayer();
|
||||
private bool isArrowKeyPressed = false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -109,9 +110,11 @@ namespace Flow.Launcher
|
|||
private void OnInitialized(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs _)
|
||||
{
|
||||
// MouseEventHandler
|
||||
PreviewMouseMove += MainPreviewMouseMove;
|
||||
|
||||
CheckFirstLaunch();
|
||||
HideStartup();
|
||||
// show notify icon when flowlauncher is hidden
|
||||
|
|
@ -406,6 +409,7 @@ namespace Flow.Launcher
|
|||
if (_animating)
|
||||
return;
|
||||
|
||||
isArrowKeyPressed = true;
|
||||
_animating = true;
|
||||
UpdatePosition();
|
||||
|
||||
|
|
@ -494,6 +498,7 @@ namespace Flow.Launcher
|
|||
windowsb.Completed += (_, _) => _animating = false;
|
||||
_settings.WindowLeft = Left;
|
||||
_settings.WindowTop = Top;
|
||||
isArrowKeyPressed = false;
|
||||
|
||||
if (QueryTextBox.Text.Length == 0)
|
||||
{
|
||||
|
|
@ -644,10 +649,12 @@ namespace Flow.Launcher
|
|||
switch (e.Key)
|
||||
{
|
||||
case Key.Down:
|
||||
isArrowKeyPressed = true;
|
||||
_viewModel.SelectNextItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
break;
|
||||
case Key.Up:
|
||||
isArrowKeyPressed = true;
|
||||
_viewModel.SelectPrevItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
|
@ -698,7 +705,21 @@ namespace Flow.Launcher
|
|||
|
||||
}
|
||||
}
|
||||
private void OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Up || e.Key == Key.Down)
|
||||
{
|
||||
isArrowKeyPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void MainPreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
if (isArrowKeyPressed)
|
||||
{
|
||||
e.Handled = true; // Ignore Mouse Hover when press Arrowkeys
|
||||
}
|
||||
}
|
||||
public void PreviewReset()
|
||||
{
|
||||
_viewModel.ResetPreview();
|
||||
|
|
|
|||
|
|
@ -139,6 +139,26 @@
|
|||
Type="Inside">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+R" />
|
||||
</cc:Card>
|
||||
<cc:Card
|
||||
Title="{DynamicResource CycleHistoryUpHotkey}"
|
||||
Icon=""
|
||||
Type="Inside">
|
||||
<flowlauncher:HotkeyControl
|
||||
DefaultHotkey="Alt+Up"
|
||||
Hotkey="{Binding Settings.CycleHistoryUpHotkey}"
|
||||
HotkeySettings="{Binding Settings}"
|
||||
ValidateKeyGesture="False" />
|
||||
</cc:Card>
|
||||
<cc:Card
|
||||
Title="{DynamicResource CycleHistoryDownHotkey}"
|
||||
Icon=""
|
||||
Type="Inside">
|
||||
<flowlauncher:HotkeyControl
|
||||
DefaultHotkey="Alt+Down"
|
||||
Hotkey="{Binding Settings.CycleHistoryDownHotkey}"
|
||||
HotkeySettings="{Binding Settings}"
|
||||
ValidateKeyGesture="False" />
|
||||
</cc:Card>
|
||||
<cc:Card
|
||||
Title="{DynamicResource ReloadPluginHotkey}"
|
||||
Icon=""
|
||||
|
|
|
|||
|
|
@ -108,18 +108,18 @@ public partial class SettingWindow
|
|||
RefreshMaximizeRestoreButton();
|
||||
}
|
||||
|
||||
private void InitializePosition()
|
||||
public void InitializePosition()
|
||||
{
|
||||
if (_settings.SettingWindowTop >= 0 && _settings.SettingWindowLeft >= 0)
|
||||
{
|
||||
Top = _settings.SettingWindowTop;
|
||||
Left = _settings.SettingWindowLeft;
|
||||
}
|
||||
else
|
||||
if (_settings.SettingWindowTop == null)
|
||||
{
|
||||
Top = WindowTop();
|
||||
Left = WindowLeft();
|
||||
}
|
||||
else
|
||||
{
|
||||
Top = _settings.SettingWindowTop;
|
||||
Left = _settings.SettingWindowLeft;
|
||||
}
|
||||
WindowState = _settings.SettingWindowState;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ namespace Flow.Launcher.ViewModel
|
|||
private readonly FlowLauncherJsonStorage<UserSelectedRecord> _userSelectedRecordStorage;
|
||||
private readonly FlowLauncherJsonStorage<TopMostRecord> _topMostRecordStorage;
|
||||
private readonly History _history;
|
||||
private int lastHistoryIndex = 1;
|
||||
private readonly UserSelectedRecord _userSelectedRecord;
|
||||
private readonly TopMostRecord _topMostRecord;
|
||||
|
||||
|
|
@ -83,6 +84,12 @@ namespace Flow.Launcher.ViewModel
|
|||
case nameof(Settings.AutoCompleteHotkey):
|
||||
OnPropertyChanged(nameof(AutoCompleteHotkey));
|
||||
break;
|
||||
case nameof(Settings.CycleHistoryUpHotkey):
|
||||
OnPropertyChanged(nameof(CycleHistoryUpHotkey));
|
||||
break;
|
||||
case nameof(Settings.CycleHistoryDownHotkey):
|
||||
OnPropertyChanged(nameof(CycleHistoryDownHotkey));
|
||||
break;
|
||||
case nameof(Settings.AutoCompleteHotkey2):
|
||||
OnPropertyChanged(nameof(AutoCompleteHotkey2));
|
||||
break;
|
||||
|
|
@ -256,6 +263,32 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void ReverseHistory()
|
||||
{
|
||||
if (_history.Items.Count > 0)
|
||||
{
|
||||
ChangeQueryText(_history.Items[_history.Items.Count - lastHistoryIndex].Query.ToString());
|
||||
if (lastHistoryIndex < _history.Items.Count)
|
||||
{
|
||||
lastHistoryIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void ForwardHistory()
|
||||
{
|
||||
if (_history.Items.Count > 0)
|
||||
{
|
||||
ChangeQueryText(_history.Items[_history.Items.Count - lastHistoryIndex].Query.ToString());
|
||||
if (lastHistoryIndex > 1)
|
||||
{
|
||||
lastHistoryIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LoadContextMenu()
|
||||
{
|
||||
|
|
@ -346,6 +379,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
_userSelectedRecord.Add(result);
|
||||
_history.Add(result.OriginQuery.RawQuery);
|
||||
lastHistoryIndex = 1;
|
||||
}
|
||||
|
||||
if (hideWindow)
|
||||
|
|
@ -394,7 +428,18 @@ namespace Flow.Launcher.ViewModel
|
|||
[RelayCommand]
|
||||
private void SelectPrevItem()
|
||||
{
|
||||
SelectedResults.SelectPrevResult();
|
||||
if (_history.Items.Count > 0
|
||||
&& QueryText == string.Empty
|
||||
&& SelectedIsFromQueryResults())
|
||||
{
|
||||
lastHistoryIndex = 1;
|
||||
ReverseHistory();
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedResults.SelectPrevResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -690,6 +735,8 @@ namespace Flow.Launcher.ViewModel
|
|||
public string SelectPrevPageHotkey => VerifyOrSetDefaultHotkey(Settings.SelectPrevPageHotkey, "");
|
||||
public string OpenContextMenuHotkey => VerifyOrSetDefaultHotkey(Settings.OpenContextMenuHotkey, "Ctrl+O");
|
||||
public string SettingWindowHotkey => VerifyOrSetDefaultHotkey(Settings.SettingWindowHotkey, "Ctrl+I");
|
||||
public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up");
|
||||
public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down");
|
||||
|
||||
|
||||
public string Image => Constant.QueryTextBoxIconImagePath;
|
||||
|
|
@ -1116,6 +1163,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public async void Hide()
|
||||
{
|
||||
lastHistoryIndex = 1;
|
||||
// Trick for no delay
|
||||
MainWindowOpacity = 0;
|
||||
lastContextMenuResult = new Result();
|
||||
|
|
|
|||
BIN
Plugins/Flow.Launcher.Plugin.PluginsManager/Images/zipfolder.png
Normal file
BIN
Plugins/Flow.Launcher.Plugin.PluginsManager/Images/zipfolder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -26,7 +26,6 @@
|
|||
<system:String x:Key="plugin_pluginsmanager_update_prompt">{0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_prompt_no_restart">{0} by {1} {2}{2}Would you like to update this plugin?</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_title">Plugin Update</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_exists">This plugin has an update, would you like to see it?</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_alreadyexists">This plugin is already installed</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_failed_title">Plugin Manifest Download Failed</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_failed_subtitle">Please check if you can connect to github.com. This error means you may not be able to install or update plugins.</system:String>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@ using Flow.Launcher.Core.Plugin;
|
|||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
|
@ -99,13 +97,12 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
if (Context.API.GetAllPlugins()
|
||||
.Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0))
|
||||
{
|
||||
if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
Context
|
||||
.API
|
||||
.ChangeQuery(
|
||||
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UpdateCommand} {plugin.Name}");
|
||||
var updateDetail = !plugin.IsFromLocalInstallPath ? plugin.Name : plugin.LocalInstallPath;
|
||||
|
||||
Context
|
||||
.API
|
||||
.ChangeQuery(
|
||||
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UpdateCommand} {updateDetail}");
|
||||
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
mainWindow.Show();
|
||||
|
|
@ -147,12 +144,17 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
try
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
if (!plugin.IsFromLocalInstallPath)
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
|
||||
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
|
||||
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
filePath = plugin.LocalInstallPath;
|
||||
}
|
||||
|
||||
Install(plugin, filePath);
|
||||
}
|
||||
|
|
@ -193,24 +195,38 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
await PluginsManifest.UpdateManifestAsync(token, usePrimaryUrlOnly);
|
||||
|
||||
var pluginFromLocalPath = null as UserPlugin;
|
||||
var updateFromLocalPath = false;
|
||||
|
||||
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
|
||||
{
|
||||
pluginFromLocalPath = Utilities.GetPluginInfoFromZip(search);
|
||||
pluginFromLocalPath.LocalInstallPath = search;
|
||||
updateFromLocalPath = true;
|
||||
}
|
||||
|
||||
var updateSource = !updateFromLocalPath
|
||||
? PluginsManifest.UserPlugins
|
||||
: new List<UserPlugin> { pluginFromLocalPath };
|
||||
|
||||
var resultsForUpdate = (
|
||||
from existingPlugin in Context.API.GetAllPlugins()
|
||||
join pluginFromManifest in PluginsManifest.UserPlugins
|
||||
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
|
||||
where String.Compare(existingPlugin.Metadata.Version, pluginFromManifest.Version,
|
||||
join pluginUpdateSource in updateSource
|
||||
on existingPlugin.Metadata.ID equals pluginUpdateSource.ID
|
||||
where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version,
|
||||
StringComparison.InvariantCulture) <
|
||||
0 // if current version precedes manifest version
|
||||
0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
|
||||
&& !PluginManager.PluginModified(existingPlugin.Metadata.ID)
|
||||
select
|
||||
new
|
||||
{
|
||||
pluginFromManifest.Name,
|
||||
pluginFromManifest.Author,
|
||||
pluginUpdateSource.Name,
|
||||
pluginUpdateSource.Author,
|
||||
CurrentVersion = existingPlugin.Metadata.Version,
|
||||
NewVersion = pluginFromManifest.Version,
|
||||
NewVersion = pluginUpdateSource.Version,
|
||||
existingPlugin.Metadata.IcoPath,
|
||||
PluginExistingMetadata = existingPlugin.Metadata,
|
||||
PluginNewUserPlugin = pluginFromManifest
|
||||
PluginNewUserPlugin = pluginUpdateSource
|
||||
}).ToList();
|
||||
|
||||
if (!resultsForUpdate.Any())
|
||||
|
|
@ -261,13 +277,21 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
_ = Task.Run(async delegate
|
||||
{
|
||||
if (File.Exists(downloadToFilePath))
|
||||
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
|
||||
{
|
||||
File.Delete(downloadToFilePath);
|
||||
}
|
||||
if (File.Exists(downloadToFilePath))
|
||||
{
|
||||
File.Delete(downloadToFilePath);
|
||||
}
|
||||
|
||||
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
|
||||
.ConfigureAwait(false);
|
||||
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
|
||||
}
|
||||
|
||||
|
||||
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
|
||||
downloadToFilePath);
|
||||
|
|
@ -396,7 +420,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
results = results.Prepend(updateAllResult);
|
||||
}
|
||||
|
||||
return Search(results, search);
|
||||
return !updateFromLocalPath ? Search(results, search) : results.ToList();
|
||||
}
|
||||
|
||||
internal bool PluginExists(string id)
|
||||
|
|
@ -470,6 +494,42 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
return new List<Result> { result };
|
||||
}
|
||||
|
||||
internal List<Result> InstallFromLocalPath(string localPath)
|
||||
{
|
||||
var plugin = Utilities.GetPluginInfoFromZip(localPath);
|
||||
|
||||
plugin.LocalInstallPath = localPath;
|
||||
|
||||
return new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = $"{plugin.Name} by {plugin.Author}",
|
||||
SubTitle = plugin.Description,
|
||||
IcoPath = plugin.IcoPath,
|
||||
Action = e =>
|
||||
{
|
||||
if (Settings.WarnFromUnknownSource)
|
||||
{
|
||||
if (!InstallSourceKnown(plugin.Website)
|
||||
&& MessageBox.Show(string.Format(
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_install_unknown_source_warning"),
|
||||
Environment.NewLine),
|
||||
Context.API.GetTranslation(
|
||||
"plugin_pluginsmanager_install_unknown_source_warning_title"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
return false;
|
||||
}
|
||||
|
||||
Application.Current.MainWindow.Hide();
|
||||
_ = InstallOrUpdateAsync(plugin);
|
||||
|
||||
return ShouldHideWindow;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private bool InstallSourceKnown(string url)
|
||||
{
|
||||
var author = url.Split('/')[3];
|
||||
|
|
@ -489,6 +549,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
&& search.Split('.').Last() == zip)
|
||||
return InstallFromWeb(search);
|
||||
|
||||
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
|
||||
return InstallFromLocalPath(search);
|
||||
|
||||
var results =
|
||||
PluginsManifest
|
||||
.UserPlugins
|
||||
|
|
@ -522,10 +585,13 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
if (!File.Exists(downloadedFilePath))
|
||||
throw new FileNotFoundException($"Plugin {plugin.ID} zip file not found at {downloadedFilePath}",
|
||||
downloadedFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
PluginManager.InstallPlugin(plugin, downloadedFilePath);
|
||||
File.Delete(downloadedFilePath);
|
||||
|
||||
if (!plugin.IsFromLocalInstallPath)
|
||||
File.Delete(downloadedFilePath);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Flow.Launcher.Core.ExternalPlugins;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
|
||||
namespace Flow.Launcher.Plugin.PluginsManager
|
||||
{
|
||||
|
|
@ -55,5 +60,28 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
internal static UserPlugin GetPluginInfoFromZip(string filePath)
|
||||
{
|
||||
var plugin = null as UserPlugin;
|
||||
|
||||
using (ZipArchive archive = System.IO.Compression.ZipFile.OpenRead(filePath))
|
||||
{
|
||||
var pluginJsonPath = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json").ToString();
|
||||
ZipArchiveEntry pluginJsonEntry = archive.GetEntry(pluginJsonPath);
|
||||
|
||||
if (pluginJsonEntry != null)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(pluginJsonEntry.Open()))
|
||||
{
|
||||
string pluginJsonContent = reader.ReadToEnd();
|
||||
plugin = JsonConvert.DeserializeObject<UserPlugin>(pluginJsonContent);
|
||||
plugin.IcoPath = "Images\\zipfolder.png";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue