Merge branch 'dev' of github.com:Flow-Launcher/Flow.Launcher into add_windows_settings_plugin

This commit is contained in:
Kevin Zhang 2021-10-29 15:00:22 -05:00
commit 47e074ca2a
72 changed files with 2184 additions and 1005 deletions

View file

@ -0,0 +1,57 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Core.ExternalPlugins
{
public static class PluginsManifest
{
static PluginsManifest()
{
UpdateTask = UpdateManifestAsync();
}
public static List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
public static Task UpdateTask { get; private set; }
private static readonly SemaphoreSlim manifestUpdateLock = new(1);
public static Task UpdateManifestAsync()
{
if (manifestUpdateLock.CurrentCount == 0)
{
return UpdateTask;
}
return UpdateTask = DownloadManifestAsync();
}
private async static Task DownloadManifestAsync()
{
try
{
await manifestUpdateLock.WaitAsync().ConfigureAwait(false);
await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json")
.ConfigureAwait(false);
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
UserPlugins = new List<UserPlugin>();
}
finally
{
manifestUpdateLock.Release();
}
}
}
}

View file

@ -1,7 +1,6 @@

namespace Flow.Launcher.Plugin.PluginsManager.Models
namespace Flow.Launcher.Core.ExternalPlugins
{
public class UserPlugin
public record UserPlugin
{
public string ID { get; set; }
public string Name { get; set; }
@ -12,5 +11,6 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models
public string Website { get; set; }
public string UrlDownload { get; set; }
public string UrlSourceCode { get; set; }
public string IcoPath { get; set; }
}
}

View file

@ -10,35 +10,31 @@ namespace Flow.Launcher.Core.Plugin
public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalPlugins)
{
// replace multiple white spaces with one white space
var terms = text.Split(new[] { Query.TermSeperater }, StringSplitOptions.RemoveEmptyEntries);
var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries);
if (terms.Length == 0)
{ // nothing was typed
return null;
}
var rawQuery = string.Join(Query.TermSeperater, terms);
var rawQuery = string.Join(Query.TermSeparator, terms);
string actionKeyword, search;
string possibleActionKeyword = terms[0];
List<string> actionParameters;
string[] searchTerms;
if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled)
{ // use non global plugin for query
actionKeyword = possibleActionKeyword;
actionParameters = terms.Skip(1).ToList();
search = actionParameters.Count > 0 ? rawQuery.Substring(actionKeyword.Length + 1) : string.Empty;
search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..] : string.Empty;
searchTerms = terms[1..];
}
else
{ // non action keyword
actionKeyword = string.Empty;
search = rawQuery;
searchTerms = terms;
}
var query = new Query
{
Terms = terms,
RawQuery = rawQuery,
ActionKeyword = actionKeyword,
Search = search
};
var query = new Query(rawQuery, search,terms, searchTerms, actionKeyword);
return query;
}

View file

@ -16,6 +16,7 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.Text.Json.Serialization;
using System.Threading;
namespace Flow.Launcher.Core
{
@ -28,21 +29,21 @@ namespace Flow.Launcher.Core
GitHubRepository = gitHubRepository;
}
public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true)
private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1);
public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true)
{
await UpdateLock.WaitAsync();
try
{
UpdateInfo newUpdateInfo;
if (!silentUpdate)
api.ShowMsg(api.GetTranslation("pleaseWait"),
api.GetTranslation("update_flowlauncher_update_check"));
api.GetTranslation("update_flowlauncher_update_check"));
using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
var newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
@ -58,7 +59,7 @@ namespace Flow.Launcher.Core
if (!silentUpdate)
api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"),
api.GetTranslation("update_flowlauncher_updating"));
api.GetTranslation("update_flowlauncher_updating"));
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
@ -70,8 +71,8 @@ namespace Flow.Launcher.Core
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
MessageBox.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
DataLocation.PortableDataPath,
targetDestination));
DataLocation.PortableDataPath,
targetDestination));
}
else
{
@ -87,12 +88,15 @@ namespace Flow.Launcher.Core
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
}
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e.InnerException is TimeoutException)
catch (Exception e) when (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
{
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
api.GetTranslation("update_flowlauncher_check_connection"));
return;
api.GetTranslation("update_flowlauncher_check_connection"));
}
finally
{
UpdateLock.Release();
}
}
@ -115,13 +119,16 @@ namespace Flow.Launcher.Core
var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
await using var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
var releases = await System.Text.Json.JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
var client = new WebClient { Proxy = Http.WebProxy };
var client = new WebClient
{
Proxy = Http.WebProxy
};
var downloader = new FileDownloader(client);
var manager = new UpdateManager(latestUrl, urlDownloader: downloader);

View file

@ -99,8 +99,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool RememberLastLaunchLocation { get; set; }
public bool IgnoreHotkeysOnFullscreen { get; set; }
public bool AutoHideScrollBar { get; set; }
public HttpProxy Proxy { get; set; } = new HttpProxy();
[JsonConverter(typeof(JsonStringEnumConverter))]

View file

@ -60,8 +60,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fody" Version="6.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
</ItemGroup>
</Project>

View file

@ -59,6 +59,11 @@ namespace Flow.Launcher.Plugin
/// <param name="subTitle">Optional message subtitle</param>
void ShowMsgError(string title, string subTitle = "");
/// <summary>
/// Show the MainWindow when hiding
/// </summary>
void ShowMainWindow();
/// <summary>
/// Show message box
/// </summary>

View file

@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
@ -11,11 +12,12 @@ namespace Flow.Launcher.Plugin
/// <summary>
/// to allow unit tests for plug ins
/// </summary>
public Query(string rawQuery, string search, string[] terms, string actionKeyword = "")
public Query(string rawQuery, string search, string[] terms, string[] searchTerms, string actionKeyword = "")
{
Search = search;
RawQuery = rawQuery;
Terms = terms;
SearchTerms = searchTerms;
ActionKeyword = actionKeyword;
}
@ -23,7 +25,7 @@ namespace Flow.Launcher.Plugin
/// Raw query, this includes action keyword if it has
/// We didn't recommend use this property directly. You should always use Search property.
/// </summary>
public string RawQuery { get; internal set; }
public string RawQuery { get; internal init; }
/// <summary>
/// Search part of a query.
@ -31,45 +33,53 @@ namespace Flow.Launcher.Plugin
/// Since we allow user to switch a exclusive plugin to generic plugin,
/// so this property will always give you the "real" query part of the query
/// </summary>
public string Search { get; internal set; }
public string Search { get; internal init; }
/// <summary>
/// The raw query splited into a string array.
/// The search string split into a string array.
/// </summary>
public string[] Terms { get; set; }
public string[] SearchTerms { get; init; }
/// <summary>
/// The raw query split into a string array
/// </summary>
[Obsolete("It may or may not include action keyword, which can be confusing. Use SearchTerms instead")]
public string[] Terms { get; init; }
/// <summary>
/// Query can be splited into multiple terms by whitespace
/// </summary>
public const string TermSeperater = " ";
public const string TermSeparator = " ";
[Obsolete("Typo")]
public const string TermSeperater = TermSeparator;
/// <summary>
/// User can set multiple action keywords seperated by ';'
/// </summary>
public const string ActionKeywordSeperater = ";";
public const string ActionKeywordSeparator = ";";
[Obsolete("Typo")]
public const string ActionKeywordSeperater = ActionKeywordSeparator;
/// <summary>
/// '*' is used for System Plugin
/// </summary>
public const string GlobalPluginWildcardSign = "*";
public string ActionKeyword { get; set; }
public string ActionKeyword { get; init; }
/// <summary>
/// Return first search split by space if it has
/// </summary>
public string FirstSearch => SplitSearch(0);
private string _secondToEndSearch;
/// <summary>
/// strings from second search (including) to last search
/// </summary>
public string SecondToEndSearch
{
get
{
var index = string.IsNullOrEmpty(ActionKeyword) ? 1 : 2;
return string.Join(TermSeperater, Terms.Skip(index).ToArray());
}
}
public string SecondToEndSearch => SearchTerms.Length > 1 ? (_secondToEndSearch ??= string.Join(' ', SearchTerms[1..])) : "";
/// <summary>
/// Return second search split by space if it has
@ -83,16 +93,9 @@ namespace Flow.Launcher.Plugin
private string SplitSearch(int index)
{
try
{
return string.IsNullOrEmpty(ActionKeyword) ? Terms[index] : Terms[index + 1];
}
catch (IndexOutOfRangeException)
{
return string.Empty;
}
return index < SearchTerms.Length ? SearchTerms[index] : string.Empty;
}
public override string ToString() => RawQuery;
}
}
}

View file

@ -30,7 +30,7 @@ namespace Flow.Launcher
private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
{
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, plugin.Metadata.ActionKeywords.ToArray());
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeparator, plugin.Metadata.ActionKeywords.ToArray());
tbAction.Focus();
}
@ -44,7 +44,7 @@ namespace Flow.Launcher
var oldActionKeyword = plugin.Metadata.ActionKeywords[0];
var newActionKeyword = tbAction.Text.Trim();
newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*";
if (!pluginViewModel.IsActionKeywordRegistered(newActionKeyword))
if (!PluginViewModel.IsActionKeywordRegistered(newActionKeyword))
{
pluginViewModel.ChangeActionKeyword(newActionKeyword, oldActionKeyword);
Close();

View file

@ -6,6 +6,227 @@
Startup="OnStartupAsync">
<Application.Resources>
<ResourceDictionary>
<!--#region Expander for Setting Window-->
<Style x:Key="ExpanderRightHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.LayoutTransform>
<TransformGroup>
<TransformGroup.Children>
<TransformCollection>
<RotateTransform Angle="-90"/>
</TransformCollection>
</TransformGroup.Children>
</TransformGroup>
</Grid.LayoutTransform>
<Ellipse x:Name="circle" HorizontalAlignment="Center" Height="19" Stroke="DarkGray" VerticalAlignment="Center" Width="19"/>
<Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="false" Stroke="#666" StrokeThickness="2" VerticalAlignment="Center"/>
</Grid>
<ContentPresenter HorizontalAlignment="Center" Margin="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Data" TargetName="arrow" Value="M 1,4.5 L 4.5,1 L 8,4.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="#FF3C7FB1"/>
<Setter Property="Stroke" TargetName="arrow" Value="#222"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="#FF526C7B"/>
<Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF003366"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderUpHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.LayoutTransform>
<TransformGroup>
<TransformGroup.Children>
<TransformCollection>
<RotateTransform Angle="180"/>
</TransformCollection>
</TransformGroup.Children>
</TransformGroup>
</Grid.LayoutTransform>
<Ellipse x:Name="circle" HorizontalAlignment="Center" Height="19" Stroke="Transparent" VerticalAlignment="Center" Width="19"/>
<Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="false" Stroke="#666" StrokeThickness="1" VerticalAlignment="Center"/>
</Grid>
<ContentPresenter Grid.Column="1" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Data" TargetName="arrow" Value="M 1,4.5 L 4.5,1 L 8,4.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="Transparent"/>
<Setter Property="Stroke" TargetName="arrow" Value="#222"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="Transparent"/>
<Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF003366"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderLeftHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.LayoutTransform>
<TransformGroup>
<TransformGroup.Children>
<TransformCollection>
<RotateTransform Angle="90"/>
</TransformCollection>
</TransformGroup.Children>
</TransformGroup>
</Grid.LayoutTransform>
<Ellipse x:Name="circle" HorizontalAlignment="Center" Height="19" Stroke="DarkGray" VerticalAlignment="Center" Width="19"/>
<Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="false" Stroke="#666" StrokeThickness="2" VerticalAlignment="Center"/>
</Grid>
<ContentPresenter HorizontalAlignment="Center" Margin="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Data" TargetName="arrow" Value="M 1,4.5 L 4.5,1 L 8,4.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="#FF3C7FB1"/>
<Setter Property="Stroke" TargetName="arrow" Value="#222"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="#FF526C7B"/>
<Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF003366"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderHeaderFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="0" SnapsToDevicePixels="true" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse x:Name="circle" HorizontalAlignment="Center" Height="19" Stroke="Transparent" VerticalAlignment="Center" Width="19"/>
<Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="false" Stroke="#666" StrokeThickness="1" VerticalAlignment="Center"/>
<ContentPresenter Grid.Column="1" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Data" TargetName="arrow" Value="M 1,4.5 L 4.5,1 L 8,4.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="Transparent"/>
<Setter Property="Stroke" TargetName="arrow" Value="#222"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Stroke" TargetName="circle" Value="Transparent"/>
<Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF003366"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderStyle1" TargetType="{x:Type Expander}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3" SnapsToDevicePixels="true">
<DockPanel>
<ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="18 0 0 0" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" Style="{StaticResource ExpanderDownHeaderStyle}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
<ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Right">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderRightHeaderStyle}"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Up">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderUpHeaderStyle}"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Left">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderLeftHeaderStyle}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources RequestedTheme="Light" />
<ui:XamlControlsResources />

View file

@ -129,12 +129,12 @@ namespace Flow.Launcher
var timer = new Timer(1000 * 60 * 60 * 5);
timer.Elapsed += async (s, e) =>
{
await _updater.UpdateApp(API);
await _updater.UpdateAppAsync(API);
};
timer.Start();
// check updates on startup
await _updater.UpdateApp(API);
await _updater.UpdateAppAsync(API);
}
});
}

View file

@ -16,12 +16,10 @@ namespace Flow.Launcher.Converters
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var hotkeyNumber = int.MaxValue;
if (value is ListBoxItem listBoxItem)
{
ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
if (value is ListBoxItem listBoxItem
&& ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
hotkeyNumber = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
}
return hotkeyNumber <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
}

View file

@ -8,11 +8,9 @@ namespace Flow.Launcher.Converters
{
public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
{
if (value is ListBoxItem listBoxItem)
{
ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
if (value is ListBoxItem listBoxItem
&& ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
return listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
}
return 0;
}

View file

@ -83,6 +83,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fody" Version="6.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
<PackageReference Include="NHotkey.Wpf" Version="2.1.0" />

View file

@ -6,6 +6,8 @@ using NHotkey.Wpf;
using Flow.Launcher.Core.Resource;
using System.Windows;
using Flow.Launcher.ViewModel;
using System.Threading.Tasks;
using System.Threading;
namespace Flow.Launcher.Helper
{
@ -19,10 +21,15 @@ namespace Flow.Launcher.Helper
mainViewModel = mainVM;
settings = mainViewModel._settings;
SetHotkey(settings.Hotkey, OnHotkey);
SetHotkey(settings.Hotkey, OnToggleHotkey);
LoadCustomPluginHotkey();
}
internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
{
mainViewModel.ToggleFlowLauncher();
}
private static void SetHotkey(string hotkeyStr, EventHandler<HotkeyEventArgs> action)
{
var hotkey = new HotkeyModel(hotkeyStr);
@ -53,44 +60,6 @@ namespace Flow.Launcher.Helper
}
}
internal static void OnHotkey(object sender, HotkeyEventArgs e)
{
if (!ShouldIgnoreHotkeys())
{
UpdateLastQUeryMode();
mainViewModel.ToggleFlowLauncher();
e.Handled = true;
}
}
/// <summary>
/// Checks if Flow Launcher should ignore any hotkeys
/// </summary>
private static bool ShouldIgnoreHotkeys()
{
return settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen();
}
private static void UpdateLastQUeryMode()
{
switch(settings.LastQueryMode)
{
case LastQueryMode.Empty:
mainViewModel.ChangeQueryText(string.Empty);
break;
case LastQueryMode.Preserved:
mainViewModel.LastQuerySelected = true;
break;
case LastQueryMode.Selected:
mainViewModel.LastQuerySelected = false;
break;
default:
throw new ArgumentException($"wrong LastQueryMode: <{settings.LastQueryMode}>");
}
}
internal static void LoadCustomPluginHotkey()
{
if (settings.CustomPluginHotkeys == null)
@ -106,7 +75,7 @@ namespace Flow.Launcher.Helper
{
SetHotkey(hotkey.Hotkey, (s, e) =>
{
if (ShouldIgnoreHotkeys())
if (mainViewModel.ShouldIgnoreHotkeys())
return;
mainViewModel.MainWindowVisibility = Visibility.Visible;

View file

@ -13,50 +13,59 @@
<system:String x:Key="iconTraySettings">Settings</system:String>
<system:String x:Key="iconTrayAbout">About</system:String>
<system:String x:Key="iconTrayExit">Exit</system:String>
<system:String x:Key="closeWindow">Close</system:String>
<!--Setting General-->
<system:String x:Key="flowlauncher_settings">Flow Launcher Settings</system:String>
<system:String x:Key="general">General</system:String>
<system:String x:Key="portableMode">Portable Mode</system:String>
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
<system:String x:Key="rememberLastLocation">Remember last launch location</system:String>
<system:String x:Key="language">Language</system:String>
<system:String x:Key="lastQueryMode">Last Query Style</system:String>
<system:String x:Key="lastQueryModeToolTip">Show/Hide previous results when Flow Launcher is reactivated.</system:String>
<system:String x:Key="LastQueryPreserved">Preserve Last Query</system:String>
<system:String x:Key="LastQuerySelected">Select last Query</system:String>
<system:String x:Key="LastQueryEmpty">Empty last Query</system:String>
<system:String x:Key="maxShowResults">Maximum results shown</system:String>
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignore hotkeys in fullscreen mode</system:String>
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Disable Flow Launcher activation when a full screen application is active (Recommended for games).</system:String>
<system:String x:Key="pythonDirectory">Python Directory</system:String>
<system:String x:Key="autoUpdates">Auto Update</system:String>
<system:String x:Key="autoHideScrollBar">Auto Hide Scroll Bar</system:String>
<system:String x:Key="autoHideScrollBarToolTip">Automatically hides the Settings window scroll bar and show when hover the mouse over it</system:String>
<system:String x:Key="selectPythonDirectory">Select</system:String>
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
<system:String x:Key="hideNotifyIcon">Hide tray icon</system:String>
<system:String x:Key="querySearchPrecision">Query Search Precision</system:String>
<system:String x:Key="querySearchPrecisionToolTip">Changes minimum match score required for results.</system:String>
<system:String x:Key="ShouldUsePinyin">Should Use Pinyin</system:String>
<system:String x:Key="ShouldUsePinyinToolTip">Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese</system:String>
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
<!--Setting Plugin-->
<system:String x:Key="plugin">Plugin</system:String>
<system:String x:Key="plugin">Plugins</system:String>
<system:String x:Key="browserMorePlugins">Find more plugins</system:String>
<system:String x:Key="enable">Enable</system:String>
<system:String x:Key="disable">Disable</system:String>
<system:String x:Key="actionKeywords">Action keyword:</system:String>
<system:String x:Key="enable">On</system:String>
<system:String x:Key="disable">Off</system:String>
<system:String x:Key="actionKeywords">Action keyword</system:String>
<system:String x:Key="currentActionKeywords">Current action keyword:</system:String>
<system:String x:Key="newActionKeyword">New action keyword:</system:String>
<system:String x:Key="currentPriority">Current Priority:</system:String>
<system:String x:Key="newPriority">New Priority:</system:String>
<system:String x:Key="priority">Priority:</system:String>
<system:String x:Key="priority">Priority</system:String>
<system:String x:Key="pluginDirectory">Plugin Directory</system:String>
<system:String x:Key="author">Author</system:String>
<system:String x:Key="author">Author:</system:String>
<system:String x:Key="plugin_init_time">Init time:</system:String>
<system:String x:Key="plugin_query_time">Query time:</system:String>
<!--Setting Plugin Store-->
<system:String x:Key="pluginStore">Plugin Store</system:String>
<system:String x:Key="refresh">Refresh</system:String>
<system:String x:Key="install">Install</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Theme</system:String>
<system:String x:Key="browserMoreThemes">Browse for more themes</system:String>
@ -67,12 +76,17 @@
<system:String x:Key="opacity">Opacity</system:String>
<system:String x:Key="theme_load_failure_path_not_exists">Theme {0} not exists, fallback to default theme</system:String>
<system:String x:Key="theme_load_failure_parse_error">Fail to load theme {0}, fallback to default theme</system:String>
<system:String x:Key="ThemeFolder">Theme Folder</system:String>
<system:String x:Key="OpenThemeFolder">Open Theme Folder</system:String>
<!--Setting Hotkey-->
<system:String x:Key="hotkey">Hotkey</system:String>
<system:String x:Key="flowlauncherHotkey">Flow Launcher Hotkey</system:String>
<system:String x:Key="openResultModifiers">Open Result Modifiers</system:String>
<system:String x:Key="flowlauncherHotkeyToolTip">Enter shortcut to show/hide Flow Launcher.</system:String>
<system:String x:Key="openResultModifiers">Open Result Modifier Key</system:String>
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
<system:String x:Key="showOpenResultHotkey">Show Hotkey</system:String>
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
<system:String x:Key="customQueryHotkey">Custom Query Hotkey</system:String>
<system:String x:Key="customQuery">Query</system:String>
<system:String x:Key="delete">Delete</system:String>
@ -117,6 +131,7 @@
<system:String x:Key="documentation">Usage Tips:</system:String>
<!--Priority Setting Dialog-->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
<system:String x:Key="priority_tips">Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number</system:String>
<system:String x:Key="invalidPriority">Please provide an valid integer for Priority!</system:String>
@ -180,4 +195,4 @@
<system:String x:Key="update_flowlauncher_update_files">Update files</system:String>
<system:String x:Key="update_flowlauncher_update_upadte_description">Update description</system:String>
</ResourceDictionary>
</ResourceDictionary>

View file

@ -13,55 +13,90 @@
<system:String x:Key="iconTraySettings">설정</system:String>
<system:String x:Key="iconTrayAbout">정보</system:String>
<system:String x:Key="iconTrayExit">종료</system:String>
<system:String x:Key="closeWindow">닫기</system:String>
<!--Setting General-->
<system:String x:Key="flowlauncher_settings">Flow Launcher 설정</system:String>
<system:String x:Key="general">일반</system:String>
<system:String x:Key="portableMode">포터블 모드</system:String>
<system:String x:Key="portableModeToolTIp">모든 설정이 폴더안에 들어갑니다. USB 드라이브나 클라우드로 사용 가능합니다.</system:String>
<system:String x:Key="startFlowLauncherOnSystemStartup">시스템 시작 시 Flow Launcher 실행</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">포커스 잃으면 Flow Launcher 숨김</system:String>
<system:String x:Key="dontPromptUpdateMsg">새 버전 알림 끄기</system:String>
<system:String x:Key="rememberLastLocation">마지막 실행 위치 기억</system:String>
<system:String x:Key="language">언어</system:String>
<system:String x:Key="lastQueryMode">마지막 쿼리 스타일</system:String>
<system:String x:Key="lastQueryModeToolTip">쿼리박스를 열었을 때 쿼리 처리 방식</system:String>
<system:String x:Key="LastQueryPreserved">직전 쿼리에 계속 입력</system:String>
<system:String x:Key="LastQuerySelected">직전 쿼리 내용 선택</system:String>
<system:String x:Key="LastQueryEmpty">직전 쿼리 지우기</system:String>
<system:String x:Key="maxShowResults">표시할 결과 수</system:String>
<system:String x:Key="ignoreHotkeysOnFullscreen">전체화면 모드에서는 핫키 무시</system:String>
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">게이머라면 켜는 것을 추천합니다.</system:String>
<system:String x:Key="pythonDirectory">Python 디렉토리</system:String>
<system:String x:Key="autoUpdates">자동 업데이트</system:String>
<system:String x:Key="selectPythonDirectory">선택</system:String>
<system:String x:Key="hideOnStartup">시작 시 Flow Launcher 숨김</system:String>
<system:String x:Key="hideNotifyIcon">트레이 아이콘 숨기기</system:String>
<system:String x:Key="querySearchPrecision">쿼리 검색 정확도</system:String>
<system:String x:Key="querySearchPrecisionToolTip">검색 결과가 좀 더 정확해집니다.</system:String>
<system:String x:Key="ShouldUsePinyin">항상 Pinyin 사용</system:String>
<system:String x:Key="ShouldUsePinyinToolTip">Pinyin을 사용하여 검색할 수 있습니다. Pinyin(병음)은 로마자 중국어 입력 방식입니다.</system:String>
<system:String x:Key="shadowEffectNotAllowed">반투명 흐림 효과를 사용하는 경우, 그림자 효과를 쓸 수 없습니다.</system:String>
<!--Setting Plugin-->
<system:String x:Key="plugin">플러그인</system:String>
<system:String x:Key="browserMorePlugins">플러그인 더 찾아보기</system:String>
<system:String x:Key="disable">비활성화</system:String>
<system:String x:Key="enable">On</system:String>
<system:String x:Key="disable">Off</system:String>
<system:String x:Key="actionKeywords">액션 키워드</system:String>
<system:String x:Key="currentPriority">현재 중요도:</system:String>
<system:String x:Key="newPriority">새 중요도:</system:String>
<system:String x:Key="priority">중요도</system:String>
<system:String x:Key="pluginDirectory">플러그인 디렉토리</system:String>
<system:String x:Key="author">저자</system:String>
<system:String x:Key="plugin_init_time">초기화 시간:</system:String>
<system:String x:Key="plugin_query_time">쿼리 시간:</system:String>
<!--Setting Plugin Store-->
<system:String x:Key="pluginStore">플러그인 스토어</system:String>
<system:String x:Key="refresh">새로고침</system:String>
<system:String x:Key="install">설치</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">테마</system:String>
<system:String x:Key="browserMoreThemes">테마 더 찾아보기</system:String>
<system:String x:Key="hiThere">Hi There</system:String>
<system:String x:Key="queryBoxFont">쿼리 상자 글꼴</system:String>
<system:String x:Key="resultItemFont">결과 항목 글꼴</system:String>
<system:String x:Key="windowMode">윈도우 모드</system:String>
<system:String x:Key="opacity">투명도</system:String>
<system:String x:Key="theme_load_failure_path_not_exists">{0} 테마가 존재하지 않습니다. 기본 테마로 변경합니다.</system:String>
<system:String x:Key="theme_load_failure_parse_error">{0} 테마 로드에 실패했습니다. 기본 테마로 변경합니다.</system:String>
<system:String x:Key="ThemeFolder">테마 폴더</system:String>
<system:String x:Key="OpenThemeFolder">테마 폴더 열기</system:String>
<!--Setting Hotkey-->
<system:String x:Key="hotkey">핫키</system:String>
<system:String x:Key="flowlauncherHotkey">Flow Launcher 핫키</system:String>
<system:String x:Key="openResultModifiers">결과 수정 자 열기</system:String>
<system:String x:Key="customQueryHotkey">사용자지정 쿼리 핫키</system:String>
<system:String x:Key="flowlauncherHotkeyToolTip">Flow Launcher를 열 때 사용할 단축키를 입력합니다.</system:String>
<system:String x:Key="openResultModifiers">결과 선택 단축키</system:String>
<system:String x:Key="openResultModifiersToolTip">결과 목록을 선택하는 단축키입니다.</system:String>
<system:String x:Key="showOpenResultHotkey">단축키 표시</system:String>
<system:String x:Key="showOpenResultHotkeyToolTip">결과창에서 결과 선택 단축키를 표시합니다.</system:String>
<system:String x:Key="customQueryHotkey">사용자지정 쿼리 핫키</system:String>
<system:String x:Key="customQuery">Query</system:String>
<system:String x:Key="delete">삭제</system:String>
<system:String x:Key="edit">편집</system:String>
<system:String x:Key="add">추가</system:String>
<system:String x:Key="pleaseSelectAnItem">항목을 선택하세요.</system:String>
<system:String x:Key="deleteCustomHotkeyWarning">{0} 플러그인 핫키를 삭제하시겠습니까?</system:String>
<system:String x:Key="queryWindowShadowEffect">그림자 효과</system:String>
<system:String x:Key="shadowEffectCPUUsage">그림자 효과는 GPU를 사용합니다. 컴퓨터 퍼포먼스가 제한적인 경우 사용을 추천하지 않습니다.</system:String>
<system:String x:Key="useGlyphUI">플루언트 아이콘 사용</system:String>
<system:String x:Key="useGlyphUIEffect">결과 및 일부 메뉴에서 플루언트 아이콘을 사용합니다.</system:String>
<!--Setting Proxy-->
<system:String x:Key="proxy">HTTP 프록시</system:String>
@ -86,8 +121,18 @@
<system:String x:Key="about_activate_times">Flow Launcher를 {0}번 실행했습니다.</system:String>
<system:String x:Key="checkUpdates">업데이트 확인</system:String>
<system:String x:Key="newVersionTips">새 버전({0})이 있습니다. Flow Launcher를 재시작하세요.</system:String>
<system:String x:Key="checkUpdatesFailed">업데이트 확인을 실패했습니다. api.github.com로의 연결 또는 프록시 설정을 확인해주세요.</system:String>
<system:String x:Key="downloadUpdatesFailed">
업데이트 다운로드에 실패했습니다. github-cloud.s3.amazonaws.com의 연결 또는 프록시 설정을 확인해주세요.
수동 다운로드를 하려면 https://github.com/Flow-Launcher/Flow.Launcher/releases 으로 방문하세요.
</system:String>
<system:String x:Key="releaseNotes">릴리즈 노트:</system:String>
<system:String x:Key="documentation">사용 팁:</system:String>
<!--Priority Setting Dialog-->
<system:String x:Key="changePriorityWindow">중요도 변경</system:String>
<system:String x:Key="priority_tips">높은 수를 넣을수록 상위 결과에 표시됩니다. 5를 시도해보세요. 다른 플러그인 보다 결과를 낮추고 싶다면, 그보다 낮은 수를 입력하세요.</system:String>
<system:String x:Key="invalidPriority">중요도에 올바른 정수를 입력하세요.</system:String>
<!--Action Keyword Setting Dialog-->
<system:String x:Key="oldActionKeywords">예전 액션 키워드</system:String>
<system:String x:Key="newActionKeywords">새 액션 키워드</system:String>
@ -97,9 +142,11 @@
<system:String x:Key="newActionKeywordsCannotBeEmpty">새 액션 키워드를 입력하세요.</system:String>
<system:String x:Key="newActionKeywordsHasBeenAssigned">새 액션 키워드가 할당된 플러그인이 이미 있습니다. 다른 액션 키워드를 입력하세요.</system:String>
<system:String x:Key="success">성공</system:String>
<system:String x:Key="completedSuccessfully">성공적으로 완료했습니다.</system:String>
<system:String x:Key="actionkeyword_tips">액션 키워드를 지정하지 않으려면 *를 사용하세요.</system:String>
<!--Custom Query Hotkey Dialog-->
<system:String x:Key="customeQueryHotkeyTitle">커스텀 플러그인 핫키</system:String>
<system:String x:Key="preview">미리보기</system:String>
<system:String x:Key="hotkeyIsNotUnavailable">핫키를 사용할 수 없습니다. 다른 핫키를 입력하세요.</system:String>
<system:String x:Key="invalidPluginHotkey">플러그인 핫키가 유효하지 않습니다.</system:String>
@ -123,12 +170,23 @@
<system:String x:Key="reportWindow_report_succeed">보고서를 정상적으로 보냈습니다.</system:String>
<system:String x:Key="reportWindow_report_failed">보고서를 보내지 못했습니다.</system:String>
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher에 문제가 발생했습니다.</system:String>
<!--General Notice-->
<system:String x:Key="pleaseWait">잠시 기다려주세요...</system:String>
<!--update-->
<system:String x:Key="update_flowlauncher_update_check">새 업데이트 확인 중</system:String>
<system:String x:Key="update_flowlauncher_update_new_version_available">새 Flow Launcher 버전({0})을 사용할 수 있습니다.</system:String>
<system:String x:Key="update_flowlauncher_already_on_latest">이미 가장 최신 버전의 Flow Launcher를 사용중입니다.</system:String>
<system:String x:Key="update_flowlauncher_update_found">업데이트 발견</system:String>
<system:String x:Key="update_flowlauncher_updating">업데이트 중...</system:String>
<system:String x:Key="update_flowlauncher_fail_moving_portable_user_profile_data">Flow Launcher가 유저 정보 데이터를 새버전으로 옮길 수 없습니다.
프로필 데이터 폴더를 수동으로 {0} 에서 {1}로 옮겨주세요. </system:String>
<system:String x:Key="update_flowlauncher_new_update">새 업데이트</system:String>
<system:String x:Key="update_flowlauncher_update_error">소프트웨어 업데이트를 설치하는 중에 오류가 발생했습니다.</system:String>
<system:String x:Key="update_flowlauncher_update">업데이트</system:String>
<system:String x:Key="update_flowlauncher_update_cancel">취소</system:String>
<system:String x:Key="update_flowlauncher_fail">업데이트 실패</system:String>
<system:String x:Key="update_flowlauncher_check_connection">Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.</system:String>
<system:String x:Key="update_flowlauncher_update_restart_flowlauncher_tip">업데이트를 위해 Flow Launcher를 재시작합니다.</system:String>
<system:String x:Key="update_flowlauncher_update_upadte_files">아래 파일들이 업데이트됩니다.</system:String>
<system:String x:Key="update_flowlauncher_update_files">업데이트 파일</system:String>

View file

@ -31,8 +31,6 @@
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorovať klávesové skratky v režime na celú obrazovku</system:String>
<system:String x:Key="pythonDirectory">Priečinok s Pythonom</system:String>
<system:String x:Key="autoUpdates">Automatická aktualizácia</system:String>
<system:String x:Key="autoHideScrollBar">Automaticky skryť posuvník</system:String>
<system:String x:Key="autoHideScrollBarToolTip">Automaticky skrývať posuvník v okne nastavení a zobraziť ho, keď naň prejdete myšou</system:String>
<system:String x:Key="selectPythonDirectory">Vybrať</system:String>
<system:String x:Key="hideOnStartup">Schovať Flow Launcher po spustení</system:String>
<system:String x:Key="hideNotifyIcon">Schovať ikonu z oblasti oznámení</system:String>
@ -82,6 +80,8 @@
<system:String x:Key="deleteCustomHotkeyWarning">Ste si istý, že chcete odstrániť klávesovú skratku {0} pre plugin?</system:String>
<system:String x:Key="queryWindowShadowEffect">Tieňový efekt v poli vyhľadávania</system:String>
<system:String x:Key="shadowEffectCPUUsage">Tieňový efekt významne využíva GPU. Neodporúča sa, ak je výkon počítača obmedzený.</system:String>
<system:String x:Key="useGlyphUI">Použiť ikony Segoe Fluent</system:String>
<system:String x:Key="useGlyphUIEffect">Použiť ikony Segoe Fluent, ak sú podporované</system:String>
<!--Setting Proxy-->
<system:String x:Key="proxy">HTTP Proxy</system:String>
@ -178,4 +178,4 @@
<system:String x:Key="update_flowlauncher_update_files">Aktualizovať súbory</system:String>
<system:String x:Key="update_flowlauncher_update_upadte_description">Aktualizovať popis</system:String>
</ResourceDictionary>
</ResourceDictionary>

View file

@ -92,7 +92,7 @@
<MenuItem Command="ApplicationCommands.Paste"/>
<Separator />
<MenuItem Header="{DynamicResource flowlauncher_settings}" Click="OnContextMenusForSettingsClick" />
<MenuItem Command="{Binding EscCommand}" Header="Close"/>
<MenuItem Command="{Binding EscCommand}" Header="{DynamicResource closeWindow}"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>

View file

@ -262,7 +262,7 @@ namespace Flow.Launcher
{
if (_settings.HideWhenDeactive)
{
Hide();
_viewModel.Hide();
}
}

View file

@ -0,0 +1,42 @@
using Flow.Launcher.Infrastructure;
using System;
using System.IO;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
namespace Flow.Launcher
{
internal static class Notification
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public static void Show(string title, string subTitle, string iconPath)
{
var legacy = Environment.OSVersion.Version.Major < 10;
// Handle notification for win7/8
if (legacy)
{
LegacyShow(title, subTitle, iconPath);
return;
}
// Using Windows Notification System
var Icon = !File.Exists(iconPath)
? Path.Combine(Constant.ProgramDirectory, "Images\\app.png")
: iconPath;
var xml = $"<?xml version=\"1.0\"?><toast><visual><binding template=\"ToastImageAndText04\"><image id=\"1\" src=\"{Icon}\" alt=\"meziantou\"/><text id=\"1\">{title}</text>" +
$"<text id=\"2\">{subTitle}</text></binding></visual></toast>";
var toastXml = new XmlDocument();
toastXml.LoadXml(xml);
var toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier("Flow Launcher").Show(toast);
}
private static void LegacyShow(string title, string subTitle, string iconPath)
{
var msg = new Msg();
msg.Show(title, subTitle, iconPath);
}
}
}

View file

@ -7,37 +7,51 @@
Loaded="PriorityChangeWindow_Loaded"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="PriorityChangeWindow" Height="250" Width="300">
Title="{DynamicResource changePriorityWindow}" Height="400" Width="350" ResizeMode="NoResize" Background="#f3f3f3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="60"/>
<RowDefinition Height="75"/>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock FontSize="14" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Left" Text="{DynamicResource currentPriority}" />
<TextBlock x:Name="OldPriority" Grid.Row="0" Grid.Column="1" Margin="170 10 10 10" FontSize="14"
VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBlock FontSize="14" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Left" Text="{DynamicResource newPriority}" />
<StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="1">
<TextBox x:Name="tbAction" Margin="140 10 15 10" Width="105" VerticalAlignment="Center" HorizontalAlignment="Left" />
<StackPanel Grid.Row="0" VerticalAlignment="Center">
<TextBlock Grid.Column="0" FontFamily="/Resources/#Segoe Fluent Icons" FontSize="40" TextAlignment="Center"
Margin="0 0 0 0">
&#xe8d0;
</TextBlock>
</StackPanel>
<TextBlock Grid.Row="2" Grid.ColumnSpan="1" Grid.Column="1" Foreground="Gray"
Text="{DynamicResource priority_tips}" TextWrapping="Wrap"
Margin="0,0,20,0"/>
<Border Background="#ffffff" Grid.Row="1" BorderBrush="#cecece" BorderThickness="1" Margin="14 0 14 0" Padding="0 14 0 0" CornerRadius="5">
<StackPanel Orientation="Vertical" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1">
<TextBlock FontSize="14" Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Left" Text="{DynamicResource currentPriority}" />
<TextBlock x:Name="OldPriority" Grid.Row="0" Grid.Column="1" Margin="14 0 10 0" FontSize="14"
VerticalAlignment="Center" HorizontalAlignment="Left" FontWeight="Bold" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1">
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="30"
<StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center">
<TextBlock FontSize="14" VerticalAlignment="Center"
HorizontalAlignment="Right" Text="{DynamicResource newPriority}" />
<TextBox x:Name="tbAction" Margin="14 10 15 10" Width="105" VerticalAlignment="Center" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</Border>
<StackPanel Grid.Row="2" VerticalAlignment="Center">
<TextBlock Foreground="Gray"
Text="{DynamicResource priority_tips}" TextWrapping="WrapWithOverflow" TextAlignment="Center"
Margin="20,0,20,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="3" Grid.Column="1">
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="0 0 10 0" Width="80" Height="30"
Content="{DynamicResource cancel}" />
<Button x:Name="btnDone" Margin="10 0 10 0" Width="80" Height="30" Click="btnDone_OnClick">
<Button x:Name="btnDone" Margin="10 0 0 0" Width="80" Height="30" Click="btnDone_OnClick">
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
</Button>
</StackPanel>

View file

@ -68,6 +68,8 @@ namespace Flow.Launcher
public void RestarApp() => RestartApp();
public void ShowMainWindow() => _mainVM.MainWindowVisibility = Visibility.Visible;
public void CheckForNewUpdate() => _settingsVM.UpdateApp();
public void SaveAppAllSettings()
@ -90,8 +92,7 @@ namespace Flow.Launcher
{
Application.Current.Dispatcher.Invoke(() =>
{
var msg = useMainWindowAsOwner ? new Msg {Owner = Application.Current.MainWindow} : new Msg();
msg.Show(title, subTitle, iconPath);
Notification.Show(title, subTitle, iconPath);
});
}

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.ViewModel;
using Flow.Launcher.Helper;
using System.Windows.Controls;
using Flow.Launcher.Core.ExternalPlugins;
namespace Flow.Launcher
{
@ -23,6 +25,7 @@ namespace Flow.Launcher
public readonly IPublicAPI API;
private Settings settings;
private SettingWindowViewModel viewModel;
private static MainViewModel mainViewModel;
public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel)
{
@ -126,7 +129,7 @@ namespace Flow.Launcher
if (HotkeyControl.CurrentHotkeyAvailable)
{
HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnHotkey);
HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey);
HotKeyMapper.RemoveHotkey(settings.Hotkey);
settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
}
@ -184,23 +187,20 @@ namespace Flow.Launcher
settings.PluginSettings.Plugins[id].Disabled = viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
}
private void OnPluginPriorityClick(object sender, MouseButtonEventArgs e)
private void OnPluginPriorityClick(object sender, RoutedEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
if (sender is Control { DataContext: PluginViewModel pluginViewModel })
{
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(viewModel.SelectedPlugin.PluginPair.Metadata.ID, settings, viewModel.SelectedPlugin);
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(pluginViewModel.PluginPair.Metadata.ID, settings, pluginViewModel);
priorityChangeWindow.ShowDialog();
}
}
private void OnPluginActionKeywordsClick(object sender, MouseButtonEventArgs e)
private void OnPluginActionKeywordsClick(object sender, RoutedEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
ActionKeywords changeKeywordsWindow = new ActionKeywords(id, settings, viewModel.SelectedPlugin);
changeKeywordsWindow.ShowDialog();
}
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
ActionKeywords changeKeywordsWindow = new ActionKeywords(id, settings, viewModel.SelectedPlugin);
changeKeywordsWindow.ShowDialog();
}
private void OnPluginNameClick(object sender, MouseButtonEventArgs e)
@ -266,5 +266,20 @@ namespace Flow.Launcher
FilesFolders.OpenPath(Path.Combine(DataLocation.DataDirectory(), Constant.Themes));
}
private void OnPluginStoreRefreshClick(object sender, RoutedEventArgs e)
{
_ = viewModel.RefreshExternalPluginsAsync();
}
private void OnExternalPluginInstallClick(object sender, RoutedEventArgs e)
{
if(sender is Button { DataContext: UserPlugin plugin })
{
var pluginsManagerPlugin = PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7");
var actionKeywrod = pluginsManagerPlugin.Metadata.ActionKeywords.Count == 0 ? "" : pluginsManagerPlugin.Metadata.ActionKeywords[0];
API.ChangeQuery($"{actionKeywrod} install {plugin.Name}");
API.ShowMainWindow();
}
}
}
}

View file

@ -8,14 +8,13 @@
<Setter Property="FontSize" Value="28" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="Margin" Value="16 7 0 7" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Padding" Value="0 4 68 0" />
<Setter Property="Background" Value="#2F2F2F" />
<Setter Property="Width" Value="664" />
<Setter Property="Height" Value="48" />
<Setter Property="Foreground" Value="#E3E0E3" />
<Setter Property="CaretBrush" Value="#E3E0E3" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
@ -42,10 +41,9 @@
<Setter Property="Foreground" Value="DarkGray" />
<Setter Property="Height" Value="48" />
<Setter Property="Margin" Value="16 7 0 7" />
<Setter Property="Width" Value="664" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="0 4 68 0" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>

View file

@ -12,16 +12,16 @@
<Setter Property="Background" Value="#36393f" />
<Setter Property="CaretBrush" Value="#ffffff" />
<Setter Property="SelectionBrush" Value="#0a68d8"/>
<Setter Property="Width" Value="490" />
<Setter Property="Height" Value="42" />
<Setter Property="FontSize" Value="24" />
<Setter Property="Padding" Value="0 0 66 0" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="#72767d" />
<Setter Property="Background" Value="#36393f" />
<Setter Property="Width" Value="490" />
<Setter Property="Height" Value="42" />
<Setter Property="FontSize" Value="24" />
<Setter Property="Padding" Value="0 0 66 0" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="2" />

View file

@ -6,7 +6,7 @@
<Setter Property="Background" Value="#161614"/>
<Setter Property="Foreground" Value="#b88f3a" />
<Setter Property="CaretBrush" Value="#b88f3a" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 0 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="ItemGlyph" BasedOn="{StaticResource BaseGlyphStyle}" TargetType="{x:Type TextBlock}">
@ -15,7 +15,7 @@
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#161614"/>
<Setter Property="Foreground" Value="#a09b8c" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 0 66 0" />
<Setter Property="Height" Value="42" />
</Style>

View file

@ -8,10 +8,12 @@
<Style x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="#FFFFFF" />
<Setter Property="Background" Value="#001e4e"/>
<Setter Property="Padding" Value="0 0 18 0" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="#344c71" />
<Setter Property="Background" Value="#001e4e" />
<Setter Property="Padding" Value="0 0 18 0" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">

View file

@ -13,17 +13,15 @@
<Setter Property="Foreground" Value="#d2d8e5" />
<Setter Property="CaretBrush" Value="#FFAA47" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Width" Value="490" />
<Setter Property="Height" Value="42" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Padding" Value="0 4 66 0" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#303840" />
<Setter Property="Foreground" Value="#798189" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Width" Value="490" />
<Setter Property="Height" Value="42" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Padding" Value="0 4 66 0" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="2" />

View file

@ -16,16 +16,14 @@
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="28" />
<Setter Property="Width" Value="490" />
<Setter Property="Height" Value="42" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Padding" Value="0 4 66 0" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#dddddd" />
<Setter Property="Foreground" Value="#c6c6c6" />
<Setter Property="FontSize" Value="28" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 4 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">

View file

@ -14,16 +14,14 @@
<Setter Property="Foreground" Value="#FFFFFF" />
<Setter Property="CaretBrush" Value="#FFFFFF" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 4 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#202020" />
<Setter Property="Foreground" Value="#7b7b7b" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 4 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">

View file

@ -14,16 +14,14 @@
<Setter Property="Foreground" Value="#000000" />
<Setter Property="CaretBrush" Value="#000000" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 4 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#f3f3f3" />
<Setter Property="Foreground" Value="#b5b5b5" />
<Setter Property="FontSize" Value="26" />
<Setter Property="Padding" Value="0 4 0 0" />
<Setter Property="Width" Value="490" />
<Setter Property="Padding" Value="0 4 66 0" />
<Setter Property="Height" Value="42" />
</Style>
<Style x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}">

View file

@ -21,6 +21,8 @@ using Microsoft.VisualStudio.Threading;
using System.Threading.Channels;
using ISavable = Flow.Launcher.Plugin.ISavable;
using System.Windows.Threading;
using NHotkey;
namespace Flow.Launcher.ViewModel
{
@ -108,7 +110,9 @@ namespace Flow.Launcher.ViewModel
}
Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends");
};
}
;
void continueAction(Task t)
{
@ -145,6 +149,24 @@ namespace Flow.Launcher.ViewModel
}
}
private void UpdateLastQUeryMode()
{
switch (_settings.LastQueryMode)
{
case LastQueryMode.Empty:
ChangeQueryText(string.Empty);
break;
case LastQueryMode.Preserved:
LastQuerySelected = true;
break;
case LastQueryMode.Selected:
LastQuerySelected = false;
break;
default:
throw new ArgumentException($"wrong LastQueryMode: <{_settings.LastQueryMode}>");
}
}
private void InitializeKeyCommands()
{
@ -156,7 +178,18 @@ namespace Flow.Launcher.ViewModel
}
else
{
MainWindowVisibility = Visibility.Collapsed;
Hide();
}
});
ClearQueryCommand = new RelayCommand(_ =>
{
if (!string.IsNullOrEmpty(QueryText))
{
ChangeQueryText(string.Empty);
// Push Event to UI SystemQuery has changed
//OnPropertyChanged(nameof(SystemQueryText));
}
});
@ -194,7 +227,7 @@ namespace Flow.Launcher.ViewModel
if (hideWindow)
{
MainWindowVisibility = Visibility.Collapsed;
Hide();
}
if (SelectedIsFromQueryResults())
@ -239,19 +272,14 @@ namespace Flow.Launcher.ViewModel
ReloadPluginDataCommand = new RelayCommand(_ =>
{
var msg = new Msg
{
Owner = Application.Current.MainWindow
};
MainWindowVisibility = Visibility.Collapsed;
Hide();
PluginManager
.ReloadData()
.ContinueWith(_ =>
Application.Current.Dispatcher.Invoke(() =>
{
msg.Show(
Notification.Show(
InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"),
"");
@ -343,7 +371,6 @@ namespace Flow.Launcher.ViewModel
}
public Visibility ProgressBarVisibility { get; set; }
public Visibility MainWindowVisibility { get; set; }
public ICommand EscCommand { get; set; }
@ -357,6 +384,7 @@ namespace Flow.Launcher.ViewModel
public ICommand LoadHistoryCommand { get; set; }
public ICommand OpenResultCommand { get; set; }
public ICommand ReloadPluginDataCommand { get; set; }
public ICommand ClearQueryCommand { get; private set; }
public string OpenResultCommandModifiers { get; private set; }
@ -668,7 +696,7 @@ namespace Flow.Launcher.ViewModel
OpenResultCommandModifiers = _settings.OpenResultModifiers ?? DefaultOpenResultModifiers;
}
internal void ToggleFlowLauncher()
public async void ToggleFlowLauncher()
{
if (MainWindowVisibility != Visibility.Visible)
{
@ -676,12 +704,48 @@ namespace Flow.Launcher.ViewModel
}
else
{
switch (_settings.LastQueryMode)
{
case LastQueryMode.Empty:
ChangeQueryText(string.Empty);
Application.Current.MainWindow.Opacity = 0; // Trick for no delay
await Task.Delay(100);
Application.Current.MainWindow.Opacity = 1;
break;
case LastQueryMode.Preserved:
LastQuerySelected = true;
break;
case LastQueryMode.Selected:
LastQuerySelected = false;
break;
default:
throw new ArgumentException($"wrong LastQueryMode: <{_settings.LastQueryMode}>");
}
MainWindowVisibility = Visibility.Collapsed;
}
}
public void Hide()
{
if (MainWindowVisibility != Visibility.Collapsed)
{
ToggleFlowLauncher();
}
}
#endregion
/// <summary>
/// Checks if Flow Launcher should ignore any hotkeys
/// </summary>
public bool ShouldIgnoreHotkeys()
{
return _settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen();
}
#region Public Methods
public void Save()

View file

@ -3,32 +3,46 @@ using System.Windows.Media;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Core.Plugin;
using System.Windows.Controls;
namespace Flow.Launcher.ViewModel
{
public class PluginViewModel : BaseModel
{
public PluginPair PluginPair { get; set; }
private readonly PluginPair _pluginPair;
public PluginPair PluginPair
{
get => _pluginPair;
init
{
_pluginPair = value;
value.Metadata.PropertyChanged += (_, args) =>
{
if (args.PropertyName == nameof(PluginPair.Metadata.AvgQueryTime))
OnPropertyChanged(nameof(QueryTime));
};
}
}
public ImageSource Image => ImageLoader.Load(PluginPair.Metadata.IcoPath);
public bool PluginState
{
get { return !PluginPair.Metadata.Disabled; }
set
{
PluginPair.Metadata.Disabled = !value;
}
get => !PluginPair.Metadata.Disabled;
set => PluginPair.Metadata.Disabled = !value;
}
private Control _settingControl;
public Control SettingControl => _settingControl ??= PluginPair.Plugin is not ISettingProvider settingProvider ? new Control() : settingProvider.CreateSettingPanel();
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count == 1 ? Visibility.Visible : Visibility.Collapsed;
public string InitilizaTime => PluginPair.Metadata.InitTime.ToString() + "ms";
public string InitilizaTime => PluginPair.Metadata.InitTime + "ms";
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords);
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords);
public int Priority => PluginPair.Metadata.Priority;
public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword)
{
PluginManager.ReplaceActionKeyword(PluginPair.Metadata.ID, oldActionKeyword, newActionKeyword);
OnPropertyChanged(nameof(ActionKeywordsText));
}
@ -38,6 +52,7 @@ namespace Flow.Launcher.ViewModel
OnPropertyChanged(nameof(Priority));
}
public bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
}
}
}

View file

@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Configuration;
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
@ -46,7 +48,7 @@ namespace Flow.Launcher.ViewModel
public async void UpdateApp()
{
await _updater.UpdateApp(App.API, false);
await _updater.UpdateAppAsync(App.API, false);
}
public bool AutoUpdates
@ -61,12 +63,6 @@ namespace Flow.Launcher.ViewModel
}
}
public bool AutoHideScrollBar
{
get => Settings.AutoHideScrollBar;
set => Settings.AutoHideScrollBar = value;
}
// This is only required to set at startup. When portable mode enabled/disabled a restart is always required
private bool _portableMode = DataLocation.PortableDataLocationInUse();
public bool PortableMode
@ -237,6 +233,14 @@ namespace Flow.Launcher.ViewModel
}
}
public IList<UserPlugin> ExternalPlugins
{
get
{
return PluginsManifest.UserPlugins;
}
}
public Control SettingProvider
{
get
@ -256,6 +260,12 @@ namespace Flow.Launcher.ViewModel
}
}
public async Task RefreshExternalPluginsAsync()
{
await PluginsManifest.UpdateManifestAsync();
OnPropertyChanged(nameof(ExternalPlugins));
}
#endregion

View file

@ -1,56 +0,0 @@
using BinaryAnalysis.UnidecodeSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class Bookmark : IEquatable<Bookmark>, IEqualityComparer<Bookmark>
{
private string m_Name;
public string Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
}
}
public string Url { get; set; }
public string Source { get; set; }
public int Score { get; set; }
/* TODO: since Source maybe unimportant, we just need to compare Name and Url */
public bool Equals(Bookmark other)
{
return Equals(this, other);
}
public bool Equals(Bookmark x, Bookmark y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Name == y.Name && x.Url == y.Url;
}
public int GetHashCode(Bookmark bookmark)
{
if (Object.ReferenceEquals(bookmark, null)) return 0;
int hashName = bookmark.Name == null ? 0 : bookmark.Name.GetHashCode();
int hashUrl = bookmark.Url == null ? 0 : bookmark.Url.GetHashCode();
return hashName ^ hashUrl;
}
public override int GetHashCode()
{
return GetHashCode(this);
}
}
}

View file

@ -0,0 +1,27 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class ChromeBookmarkLoader : ChromiumBookmarkLoader
{
public override List<Bookmark> GetBookmarks()
{
return LoadChromeBookmarks();
}
private List<Bookmark> LoadChromeBookmarks()
{
var bookmarks = new List<Bookmark>();
var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome"));
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary"));
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium"));
return bookmarks;
}
}
}

View file

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class ChromeBookmarks
{
private List<Bookmark> bookmarks = new List<Bookmark>();
public List<Bookmark> GetBookmarks()
{
bookmarks.Clear();
LoadChromeBookmarks();
return bookmarks;
}
private void ParseChromeBookmarks(String path, string source)
{
if (!File.Exists(path)) return;
string all = File.ReadAllText(path);
Regex nameRegex = new Regex("\"name\": \"(?<name>.*?)\"");
MatchCollection nameCollection = nameRegex.Matches(all);
Regex typeRegex = new Regex("\"type\": \"(?<type>.*?)\"");
MatchCollection typeCollection = typeRegex.Matches(all);
Regex urlRegex = new Regex("\"url\": \"(?<url>.*?)\"");
MatchCollection urlCollection = urlRegex.Matches(all);
List<string> names = (from Match match in nameCollection select match.Groups["name"].Value).ToList();
List<string> types = (from Match match in typeCollection select match.Groups["type"].Value).ToList();
List<string> urls = (from Match match in urlCollection select match.Groups["url"].Value).ToList();
int urlIndex = 0;
for (int i = 0; i < names.Count; i++)
{
string name = DecodeUnicode(names[i]);
string type = types[i];
if (type == "url")
{
string url = urls[urlIndex];
urlIndex++;
if (url == null) continue;
if (url.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase)) continue;
if (url.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase)) continue;
bookmarks.Add(new Bookmark()
{
Name = name,
Url = url,
Source = source
});
}
}
}
private void LoadChromeBookmarks(string path, string name)
{
if (!Directory.Exists(path)) return;
var paths = Directory.GetDirectories(path);
foreach (var profile in paths)
{
if (File.Exists(Path.Combine(profile, "Bookmarks")))
ParseChromeBookmarks(Path.Combine(profile, "Bookmarks"), name + (Path.GetFileName(profile) == "Default" ? "" : (" (" + Path.GetFileName(profile) + ")")));
}
}
private void LoadChromeBookmarks()
{
String platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
LoadChromeBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome");
LoadChromeBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary");
LoadChromeBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium");
}
private String DecodeUnicode(String dataStr)
{
Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})");
return reg.Replace(dataStr, m => ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString());
}
}
}

View file

@ -0,0 +1,65 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using Microsoft.AspNetCore.Authentication;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public abstract class ChromiumBookmarkLoader : IBookmarkLoader
{
public abstract List<Bookmark> GetBookmarks();
protected List<Bookmark> LoadBookmarks(string browserDataPath, string name)
{
var bookmarks = new List<Bookmark>();
if (!Directory.Exists(browserDataPath)) return bookmarks;
var paths = Directory.GetDirectories(browserDataPath);
foreach (var profile in paths)
{
var bookmarkPath = Path.Combine(profile, "Bookmarks");
if (!File.Exists(bookmarkPath))
continue;
var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})");
bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source));
}
return bookmarks;
}
protected List<Bookmark> LoadBookmarksFromFile(string path, string source)
{
if (!File.Exists(path))
return new();
var bookmarks = new List<Bookmark>();
using var jsonDocument = JsonDocument.Parse(File.ReadAllText(path));
if (!jsonDocument.RootElement.TryGetProperty("roots", out var rootElement))
return new();
foreach (var folder in rootElement.EnumerateObject())
{
EnumerateFolderBookmark(folder.Value, bookmarks, source);
}
return bookmarks;
}
private void EnumerateFolderBookmark(JsonElement folderElement, List<Bookmark> bookmarks, string source)
{
foreach (var subElement in folderElement.GetProperty("children").EnumerateArray())
{
switch (subElement.GetProperty("type").GetString())
{
case "folder":
EnumerateFolderBookmark(subElement, bookmarks, source);
break;
default:
bookmarks.Add(new Bookmark(
subElement.GetProperty("name").GetString(),
subElement.GetProperty("url").GetString(),
source));
break;
}
}
}
}
}

View file

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using Flow.Launcher.Plugin.SharedModels;
namespace Flow.Launcher.Plugin.BrowserBookmark.Commands
{
internal static class BookmarkLoader
{
internal static MatchResult MatchProgram(Bookmark bookmark, string queryString)
{
var match = StringMatcher.FuzzySearch(queryString, bookmark.Name);
if (match.IsSearchPrecisionScoreMet())
return match;
return StringMatcher.FuzzySearch(queryString, bookmark.Url);
}
internal static List<Bookmark> LoadAllBookmarks(Settings setting)
{
var chromeBookmarks = new ChromeBookmarkLoader();
var mozBookmarks = new FirefoxBookmarkLoader();
var edgeBookmarks = new EdgeBookmarkLoader();
var allBookmarks = new List<Bookmark>();
// Add Firefox bookmarks
allBookmarks.AddRange(mozBookmarks.GetBookmarks());
// Add Chrome bookmarks
allBookmarks.AddRange(chromeBookmarks.GetBookmarks());
// Add Edge (Chromium) bookmarks
allBookmarks.AddRange(edgeBookmarks.GetBookmarks());
foreach (var browser in setting.CustomChromiumBrowsers)
{
var loader = new CustomChromiumBookmarkLoader(browser);
allBookmarks.AddRange(loader.GetBookmarks());
}
return allBookmarks.Distinct().ToList();
}
}
}

View file

@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.SharedModels;
namespace Flow.Launcher.Plugin.BrowserBookmark.Commands
{
internal static class Bookmarks
{
internal static MatchResult MatchProgram(Bookmark bookmark, string queryString)
{
var match = StringMatcher.FuzzySearch(queryString, bookmark.Name);
if (match.IsSearchPrecisionScoreMet())
return match;
return StringMatcher.FuzzySearch(queryString, bookmark.Url);
}
internal static List<Bookmark> LoadAllBookmarks()
{
var allbookmarks = new List<Bookmark>();
var chromeBookmarks = new ChromeBookmarks();
var mozBookmarks = new FirefoxBookmarks();
var edgeBookmarks = new EdgeBookmarks();
//TODO: Let the user select which browser's bookmarks are displayed
// Add Firefox bookmarks
mozBookmarks.GetBookmarks().ForEach(x => allbookmarks.Add(x));
// Add Chrome bookmarks
chromeBookmarks.GetBookmarks().ForEach(x => allbookmarks.Add(x));
// Add Edge (Chromium) bookmarks
edgeBookmarks.GetBookmarks().ForEach(x => allbookmarks.Add(x));
return allbookmarks.Distinct().ToList();
}
}
}

View file

@ -0,0 +1,19 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System.Collections.Generic;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class CustomChromiumBookmarkLoader : ChromiumBookmarkLoader
{
public CustomChromiumBookmarkLoader(CustomBrowser browser)
{
BrowserName = browser.Name;
BrowserDataPath = browser.DataDirectoryPath;
}
public string BrowserDataPath { get; init; }
public string BookmarkFilePath { get; init; }
public string BrowserName { get; init; }
public override List<Bookmark> GetBookmarks() => BrowserDataPath != null ? LoadBookmarks(BrowserDataPath, BrowserName) : LoadBookmarksFromFile(BookmarkFilePath, BrowserName);
}
}

View file

@ -0,0 +1,26 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class EdgeBookmarkLoader : ChromiumBookmarkLoader
{
private List<Bookmark> LoadEdgeBookmarks()
{
var bookmarks = new List<Bookmark>();
var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"));
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"));
bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"));
return bookmarks;
}
public override List<Bookmark> GetBookmarks() => LoadEdgeBookmarks();
}
}

View file

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class EdgeBookmarks
{
private List<Bookmark> bookmarks = new List<Bookmark>();
public List<Bookmark> GetBookmarks()
{
bookmarks.Clear();
LoadEdgeBookmarks();
return bookmarks;
}
private void ParseEdgeBookmarks(string path, string source)
{
if (!File.Exists(path)) return;
string all = File.ReadAllText(path);
Regex nameRegex = new Regex("\"name\": \"(?<name>.*?)\"");
MatchCollection nameCollection = nameRegex.Matches(all);
Regex typeRegex = new Regex("\"type\": \"(?<type>.*?)\"");
MatchCollection typeCollection = typeRegex.Matches(all);
Regex urlRegex = new Regex("\"url\": \"(?<url>.*?)\"");
MatchCollection urlCollection = urlRegex.Matches(all);
List<string> names = (from Match match in nameCollection select match.Groups["name"].Value).ToList();
List<string> types = (from Match match in typeCollection select match.Groups["type"].Value).ToList();
List<string> urls = (from Match match in urlCollection select match.Groups["url"].Value).ToList();
int urlIndex = 0;
for (int i = 0; i < names.Count; i++)
{
string name = DecodeUnicode(names[i]);
string type = types[i];
if (type == "url")
{
string url = urls[urlIndex];
urlIndex++;
if (url == null) continue;
if (url.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase)) continue;
if (url.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase)) continue;
bookmarks.Add(new Bookmark()
{
Name = name,
Url = url,
Source = source
});
}
}
}
private void LoadEdgeBookmarks(string path, string name)
{
if (!Directory.Exists(path)) return;
var paths = Directory.GetDirectories(path);
foreach (var profile in paths)
{
if (File.Exists(Path.Combine(profile, "Bookmarks")))
ParseEdgeBookmarks(Path.Combine(profile, "Bookmarks"), name + (Path.GetFileName(profile) == "Default" ? "" : (" (" + Path.GetFileName(profile) + ")")));
}
}
private void LoadEdgeBookmarks()
{
string platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge");
LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev");
LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary");
}
private string DecodeUnicode(string dataStr)
{
Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})");
return reg.Replace(dataStr, m => ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString());
}
}
}

View file

@ -1,3 +1,4 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
@ -6,7 +7,7 @@ using System.Linq;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class FirefoxBookmarks
public class FirefoxBookmarkLoader : IBookmarkLoader
{
private const string queryAllBookmarks = @"SELECT moz_places.url, moz_bookmarks.title
FROM moz_places
@ -27,10 +28,10 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
if (string.IsNullOrEmpty(PlacesPath) || !File.Exists(PlacesPath))
return new List<Bookmark>();
var bookmarList = new List<Bookmark>();
var bookmarkList = new List<Bookmark>();
// create the connection string and init the connection
string dbPath = string.Format(dbPathFormat, PlacesPath);
string dbPath = string.Format(dbPathFormat, PlacesPath);
using (var dbConnection = new SQLiteConnection(dbPath))
{
// Open connection to the database file and execute the query
@ -38,14 +39,13 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
var reader = new SQLiteCommand(queryAllBookmarks, dbConnection).ExecuteReader();
// return results in List<Bookmark> format
bookmarList = reader.Select(x => new Bookmark()
{
Name = (x["title"] is DBNull) ? string.Empty : x["title"].ToString(),
Url = x["url"].ToString()
}).ToList();
bookmarkList = reader.Select(
x => new Bookmark(x["title"] is DBNull ? string.Empty : x["title"].ToString(),
x["url"].ToString())
).ToList();
}
return bookmarList;
return bookmarkList;
}
/// <summary>
@ -63,7 +63,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
// get firefox default profile directory from profiles.ini
string ini;
using (var sReader = new StreamReader(profileIni)) {
using (var sReader = new StreamReader(profileIni))
{
ini = sReader.ReadToEnd();
}
@ -95,7 +96,10 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
Version=2
*/
var lines = ini.Split(new string[] { "\r\n" }, StringSplitOptions.None).ToList();
var lines = ini.Split(new string[]
{
"\r\n"
}, StringSplitOptions.None).ToList();
var defaultProfileFolderNameRaw = lines.Where(x => x.Contains("Default=") && x != "Default=1").FirstOrDefault() ?? string.Empty;
@ -104,14 +108,14 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
var defaultProfileFolderName = defaultProfileFolderNameRaw.Split('=').Last();
var indexOfDefaultProfileAtttributePath = lines.IndexOf("Path="+ defaultProfileFolderName);
var indexOfDefaultProfileAtttributePath = lines.IndexOf("Path=" + defaultProfileFolderName);
// Seen in the example above, the IsRelative attribute is always above the Path attribute
var relativeAttribute = lines[indexOfDefaultProfileAtttributePath - 1];
return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0
? defaultProfileFolderName + @"\places.sqlite"
: Path.Combine(profileFolderPath, defaultProfileFolderName) + @"\places.sqlite";
? defaultProfileFolderName + @"\places.sqlite"
: Path.Combine(profileFolderPath, defaultProfileFolderName) + @"\places.sqlite";
}
}
}
@ -126,4 +130,4 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
}
}
}
}
}

View file

@ -69,4 +69,8 @@
<PackageReference Include="UnidecodeSharp" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Bookmark.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,10 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System.Collections.Generic;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public interface IBookmarkLoader
{
public List<Bookmark> GetBookmarks();
}
}

View file

@ -14,4 +14,9 @@
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_choose">Choose</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_title">Copy url</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_subtitle">Copy the bookmark's url to clipboard</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_loadBrowserFrom">Load Browser From:</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_browserName">Browser Name</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_browserBookmarkDataDirectory">DataDirectoryPath</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_addBrowserBookmark">Add</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_removeBrowserBookmark">Delete</system:String>
</ResourceDictionary>

View file

@ -14,4 +14,9 @@
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_choose">Prehliadať</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_title">Kopírovať URL</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_subtitle">Kopírovať URL záložky do schránky</system:String>
</ResourceDictionary>
<system:String x:Key="flowlauncher_plugin_browserbookmark_loadBrowserFrom" >Načítať prehliadač z:</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_browserName">Názov prehliadača</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_browserBookDataDirectory">Umiestnenie priečinka Data</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_addBrowserBookmark">Pridať</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_removeBrowserBookmark">Odstrániť</system:String>
</ResourceDictionary>

View file

@ -26,7 +26,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
_settings = context.API.LoadSettingJsonStorage<Settings>();
cachedBookmarks = Bookmarks.LoadAllBookmarks();
cachedBookmarks = BookmarkLoader.LoadAllBookmarks(_settings);
}
public List<Result> Query(Query query)
@ -45,7 +45,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
Title = c.Name,
SubTitle = c.Url,
IcoPath = @"Images\bookmark.png",
Score = Bookmarks.MatchProgram(c, param).Score,
Score = BookmarkLoader.MatchProgram(c, param).Score,
Action = _ =>
{
if (_settings.OpenInNewBrowserWindow)
@ -93,7 +93,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
{
cachedBookmarks.Clear();
cachedBookmarks = Bookmarks.LoadAllBookmarks();
cachedBookmarks = BookmarkLoader.LoadAllBookmarks(_settings);
}
public string GetTranslatedPluginTitle()

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Flow.Launcher.Plugin.BrowserBookmark.Models
{
// Source may be important in the future
public record Bookmark(string Name, string Url, string Source = "")
{
public override int GetHashCode()
{
var hashName = Name?.GetHashCode() ?? 0;
var hashUrl = Url?.GetHashCode() ?? 0;
return hashName ^ hashUrl;
}
public virtual bool Equals(Bookmark other)
{
return other != null && Name == other.Name && Url == other.Url;
}
public List<CustomBrowser> CustomBrowsers { get; set; }= new();
}
}

View file

@ -0,0 +1,26 @@
namespace Flow.Launcher.Plugin.BrowserBookmark.Models
{
public class CustomBrowser : BaseModel
{
private string _name;
private string _dataDirectoryPath;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
public string DataDirectoryPath
{
get => _dataDirectoryPath;
set
{
_dataDirectoryPath = value;
OnPropertyChanged(nameof(DataDirectoryPath));
}
}
}
}

View file

@ -1,9 +1,15 @@
namespace Flow.Launcher.Plugin.BrowserBookmark.Models
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin.BrowserBookmark.Models
{
public class Settings : BaseModel
{
public bool OpenInNewBrowserWindow { get; set; } = true;
public string BrowserPath { get; set; }
public ObservableCollection<CustomBrowser> CustomChromiumBrowsers { get; set; } = new();
}
}

View file

@ -0,0 +1,38 @@
<Window x:Class="Flow.Launcher.Plugin.BrowserBookmark.Views.CustomBrowserSettingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Flow.Launcher.Plugin.BrowserBookmark.Models"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="CustomBrowserSetting" Height="350" Width="600"
KeyDown="WindowKeyDown"
>
<Window.DataContext>
<local:CustomBrowser/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Browser Name" FontSize="15"
HorizontalAlignment="Left" VerticalAlignment="Center" Margin="80 0 0 0"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Browser Data Directory Path" FontSize="15"
HorizontalAlignment="Left" VerticalAlignment="Center" Margin="80 0 0 0"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name}"
HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Height="30" Margin="50 0 0 0"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DataDirectoryPath}"
HorizontalAlignment="Left" VerticalAlignment="Center" Width="200" Height="30" Margin="50 0 0 0"/>
<StackPanel HorizontalAlignment="Center" Grid.Row="2" Orientation="Horizontal" Grid.Column="1" Height="70">
<Button Name="btnConfirm" Content="Confirm" Margin="15" Click="ConfirmCancelEditCustomBrowser"/>
<Button Content="Cancel" Margin="15" Click="ConfirmCancelEditCustomBrowser"/>
</StackPanel>
</Grid>
</Window>

View file

@ -0,0 +1,57 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Flow.Launcher.Plugin.BrowserBookmark.Views
{
/// <summary>
/// Interaction logic for CustomBrowserSetting.xaml
/// </summary>
public partial class CustomBrowserSettingWindow : Window
{
private CustomBrowser currentCustomBrowser;
public CustomBrowserSettingWindow(CustomBrowser browser)
{
InitializeComponent();
currentCustomBrowser = browser;
DataContext = new CustomBrowser
{
Name = browser.Name, DataDirectoryPath = browser.DataDirectoryPath
};
}
private void ConfirmCancelEditCustomBrowser(object sender, RoutedEventArgs e)
{
if (DataContext is CustomBrowser editBrowser && e.Source is Button button)
{
if (button.Name == "btnConfirm")
{
currentCustomBrowser.Name = editBrowser.Name;
currentCustomBrowser.DataDirectoryPath = editBrowser.DataDirectoryPath;
Close();
}
}
Close();
}
private void WindowKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
ConfirmCancelEditCustomBrowser(sender, e);
}
}
}
}

View file

@ -5,35 +5,69 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Background="White"
d:DesignHeight="300" d:DesignWidth="500">
<Grid>
d:DesignHeight="300" d:DesignWidth="500"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="90" />
<RowDefinition />
<RowDefinition Height="50" />
<RowDefinition Height="80"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<StackPanel>
<Grid Grid.Row="0" Margin="40 40 0 0">
<Grid Grid.Row="0" Margin="30 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_openBookmarks}"
FontSize="15" Margin="0 5 0 0"/>
<RadioButton Grid.Column="1" Name="NewWindowBrowser" GroupName="OpenSearchBehaviour"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_newWindow}"
Click="OnNewBrowserWindowClick" />
<RadioButton Grid.Column="2" Name="NewTabInBrowser" GroupName="OpenSearchBehaviour"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_newTab}"
Click="OnNewTabClick" />
FontSize="15" Margin="10 5 0 0"/>
<RadioButton Grid.Column="1" Name="NewWindowBrowser"
IsChecked="{Binding OpenInNewBrowserWindow}"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_newWindow}"/>
<RadioButton Grid.Column="2" Name="NewTabInBrowser"
IsChecked="{Binding OpenInNewTab, Mode=OneTime}"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_newTab}"/>
</Grid>
</StackPanel>
<StackPanel VerticalAlignment="Top" Grid.Row="1" Height="106" Margin="41,13,0,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Grid.Row="1" Height="60" Margin="30,20,0,0">
<Label Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_setBrowserFromPath}"
Height="28" Margin="0,0,155,0" HorizontalAlignment="Left" Width="290"/>
<TextBox x:Name="browserPathBox" HorizontalAlignment="Left" Height="34" TextWrapping="NoWrap" VerticalAlignment="Top" Width="311" RenderTransformOrigin="0.502,-1.668" TextChanged="OnBrowserPathTextChanged" />
<Button x:Name="viewButton" Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_choose}"
HorizontalAlignment="Left" Margin="340,-35,-1,0" Width="100" Height="34" Click="OnChooseClick" FontSize="14" />
Height="28" Margin="10"/>
<TextBox x:Name="BrowserPathBox"
HorizontalAlignment="Left"
Height="30"
TextWrapping="NoWrap"
VerticalAlignment="Center"
Text="{Binding Settings.BrowserPath}"
Width="240"
Margin="10"/>
<Button x:Name="ViewButton" Content="{DynamicResource flowlauncher_plugin_browserbookmark_settings_choose}"
HorizontalAlignment="Left" Margin="10" Width="100" Height="30" Click="OnChooseClick" FontSize="14" />
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Vertical" Margin="30,20,0,0">
<TextBlock Text="{DynamicResource flowlauncher_plugin_browserbookmark_loadBrowserFrom}" Margin="10"/>
<ListView Grid.Row="2" ItemsSource="{Binding Settings.CustomChromiumBrowsers}"
SelectedItem="{Binding SelectedCustomBrowser}"
Margin="10"
BorderBrush="DarkGray"
BorderThickness="1"
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}"
Height="auto"
Name="CustomBrowsers"
MouseDoubleClick="MouseDoubleClickOnSelectedCustomBrowser">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserName}"/>
<GridViewColumn DisplayMemberBinding="{Binding DataDirectoryPath, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserBookmarkDataDirectory}"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Content="{DynamicResource flowlauncher_plugin_browserbookmark_addBrowserBookmark}"
Margin="10" Click="NewCustomBrowser" Width="80" />
<Button Content="{DynamicResource flowlauncher_plugin_browserbookmark_removeBrowserBookmark}"
Margin="10" Click="DeleteCustomBrowser" Width="80"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

View file

@ -2,34 +2,39 @@ using Microsoft.Win32;
using System.Windows;
using System.Windows.Controls;
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System.Windows.Input;
using System.ComponentModel;
namespace Flow.Launcher.Plugin.BrowserBookmark.Views
{
/// <summary>
/// Interaction logic for BrowserBookmark.xaml
/// </summary>
public partial class SettingsControl : UserControl
public partial class SettingsControl : INotifyPropertyChanged
{
private readonly Settings _settings;
public Settings Settings { get; }
public CustomBrowser SelectedCustomBrowser { get; set; }
public bool OpenInNewBrowserWindow
{
get => Settings.OpenInNewBrowserWindow;
set
{
Settings.OpenInNewBrowserWindow = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OpenInNewBrowserWindow)));
}
}
public bool OpenInNewTab
{
get => !OpenInNewBrowserWindow;
}
public SettingsControl(Settings settings)
{
Settings = settings;
InitializeComponent();
_settings = settings;
browserPathBox.Text = _settings.BrowserPath;
NewWindowBrowser.IsChecked = _settings.OpenInNewBrowserWindow;
NewTabInBrowser.IsChecked = !_settings.OpenInNewBrowserWindow;
}
private void OnNewBrowserWindowClick(object sender, RoutedEventArgs e)
{
_settings.OpenInNewBrowserWindow = true;
}
private void OnNewTabClick(object sender, RoutedEventArgs e)
{
_settings.OpenInNewBrowserWindow = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnChooseClick(object sender, RoutedEventArgs e)
{
@ -39,14 +44,39 @@ namespace Flow.Launcher.Plugin.BrowserBookmark.Views
fileBrowserDialog.CheckPathExists = true;
if (fileBrowserDialog.ShowDialog() == true)
{
browserPathBox.Text = fileBrowserDialog.FileName;
_settings.BrowserPath = fileBrowserDialog.FileName;
Settings.BrowserPath = fileBrowserDialog.FileName;
}
}
private void OnBrowserPathTextChanged(object sender, TextChangedEventArgs e)
private void NewCustomBrowser(object sender, RoutedEventArgs e)
{
_settings.BrowserPath = browserPathBox.Text;
var newBrowser = new CustomBrowser();
var window = new CustomBrowserSettingWindow(newBrowser);
window.ShowDialog();
if (newBrowser is not
{
Name: null,
DataDirectoryPath: null
})
{
Settings.CustomChromiumBrowsers.Add(newBrowser);
}
}
private void DeleteCustomBrowser(object sender, RoutedEventArgs e)
{
if (CustomBrowsers.SelectedItem is CustomBrowser selectedCustomBrowser)
{
Settings.CustomChromiumBrowsers.Remove(selectedCustomBrowser);
}
}
private void MouseDoubleClickOnSelectedCustomBrowser(object sender, MouseButtonEventArgs e)
{
if (SelectedCustomBrowser is null)
return;
var window = new CustomBrowserSettingWindow(SelectedCustomBrowser);
window.ShowDialog();
}
}
}

View file

@ -4,7 +4,7 @@
"Name": "Browser Bookmarks",
"Description": "Search your browser bookmarks",
"Author": "qianlifeng, Ioannis G.",
"Version": "1.4.4",
"Version": "1.5.2",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",

View file

@ -15,7 +15,7 @@
<core:LocalizationConverter x:Key="LocalizationConverter"/>
</UserControl.Resources>
<Grid Margin="20 50 0 0">
<Grid Margin="20 14 0 14">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>

View file

@ -12,11 +12,11 @@ namespace Flow.Launcher.Plugin.PluginIndicator
{
// if query contains more than one word, eg. github tips
// user has decided to type something else rather than wanting to see the available action keywords
if (query.Terms.Length > 1)
if (query.SearchTerms.Length > 1)
return new List<Result>();
var results = from keyword in PluginManager.NonGlobalPlugins.Keys
where keyword.StartsWith(query.Terms[0])
where keyword.StartsWith(query.SearchTerms[0])
let metadata = PluginManager.NonGlobalPlugins[keyword].Metadata
where !metadata.Disabled
select new Result
@ -27,7 +27,7 @@ namespace Flow.Launcher.Plugin.PluginIndicator
IcoPath = metadata.IcoPath,
Action = c =>
{
context.API.ChangeQuery($"{keyword}{Plugin.Query.TermSeperater}");
context.API.ChangeQuery($"{keyword}{Plugin.Query.TermSeparator}");
return false;
}
};

View file

@ -1,5 +1,5 @@
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.PluginsManager.Models;
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.Collections.Generic;
using System.Text;

View file

@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
viewModel = new SettingsViewModel(context, Settings);
contextMenu = new ContextMenu(Context);
pluginManager = new PluginsManager(Context, Settings);
_manifestUpdateTask = pluginManager.UpdateManifest().ContinueWith(_ =>
_manifestUpdateTask = pluginManager.UpdateManifestAsync().ContinueWith(_ =>
{
lastUpdateTime = DateTime.Now;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
@ -62,7 +62,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
if ((DateTime.Now - lastUpdateTime).TotalHours > 12 && _manifestUpdateTask.IsCompleted) // 12 hours
{
_manifestUpdateTask = pluginManager.UpdateManifest().ContinueWith(t =>
_manifestUpdateTask = pluginManager.UpdateManifestAsync().ContinueWith(t =>
{
lastUpdateTime = DateTime.Now;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
@ -93,7 +93,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
public async Task ReloadDataAsync()
{
await pluginManager.UpdateManifest();
await pluginManager.UpdateManifestAsync();
lastUpdateTime = DateTime.Now;
}
}

View file

@ -1,31 +0,0 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager.Models
{
internal class PluginsManifest
{
internal List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
internal async Task DownloadManifest()
{
try
{
await using var jsonStream = await Http.GetStreamAsync("https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest/plugins.json")
.ConfigureAwait(false);
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
UserPlugins = new List<UserPlugin>();
}
}
}
}

View file

@ -1,9 +1,9 @@
using Flow.Launcher.Core.ExternalPlugins;
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.PluginsManager.Models;
using Flow.Launcher.Plugin.SharedCommands;
using System;
using System.Collections.Generic;
@ -17,8 +17,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
internal class PluginsManager
{
private PluginsManifest pluginsManifest;
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
@ -43,7 +41,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
internal PluginsManager(PluginInitContext context, Settings settings)
{
pluginsManifest = new PluginsManifest();
Context = context;
Settings = settings;
}
@ -51,7 +48,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
private Task _downloadManifestTask = Task.CompletedTask;
internal Task UpdateManifest()
internal Task UpdateManifestAsync()
{
if (_downloadManifestTask.Status == TaskStatus.Running)
{
@ -59,7 +56,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
}
else
{
_downloadManifestTask = pluginsManifest.DownloadManifest();
_downloadManifestTask = PluginsManifest.UpdateTask;
_downloadManifestTask.ContinueWith(_ =>
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_failed_title"),
Context.API.GetTranslation("plugin_pluginsmanager_update_failed_subtitle"), icoPath, false),
@ -171,9 +168,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
internal async ValueTask<List<Result>> RequestUpdate(string search, CancellationToken token)
{
if (!pluginsManifest.UserPlugins.Any())
if (!PluginsManifest.UserPlugins.Any())
{
await UpdateManifest();
await UpdateManifestAsync();
}
token.ThrowIfCancellationRequested();
@ -190,7 +187,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
var resultsForUpdate =
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins
join pluginFromManifest in PluginsManifest.UserPlugins
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) <
0 // if current version precedes manifest version
@ -307,9 +304,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
internal async ValueTask<List<Result>> RequestInstallOrUpdate(string searchName, CancellationToken token)
{
if (!pluginsManifest.UserPlugins.Any())
if (!PluginsManifest.UserPlugins.Any())
{
await UpdateManifest();
await UpdateManifestAsync();
}
token.ThrowIfCancellationRequested();
@ -317,7 +314,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
var results =
pluginsManifest
PluginsManifest
.UserPlugins
.Select(x =>
new Result

View file

@ -23,9 +23,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller
public List<Result> Query(Query query)
{
var termToSearch = query.Terms.Length <= 1
? null
: string.Join(Plugin.Query.TermSeperater, query.Terms.Skip(1)).ToLower();
var termToSearch = query.Search;
var processlist = processHelper.GetMatchingProcesses(termToSearch);

View file

@ -325,7 +325,7 @@ namespace Flow.Launcher.Plugin.Shell
private void OnWinRPressed()
{
context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeperater}");
context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}");
// show the main window and set focus to the query box
Window mainWindow = Application.Current.MainWindow;

View file

@ -6,7 +6,7 @@
mc:Ignorable="d"
Loaded="CMDSetting_OnLoaded"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Margin="7,50" VerticalAlignment="Top" >
<Grid Margin="7,10" VerticalAlignment="Top" >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>

View file

@ -4,7 +4,7 @@
"Name": "Shell",
"Description": "Provide executing commands from Flow Launcher",
"Author": "qianlifeng",
"Version": "1.4.4",
"Version": "1.4.5",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll",

View file

@ -11,9 +11,9 @@
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<Label Content="{DynamicResource flowlauncher_plugin_url_open_search_in}" Margin="0 10 15 0" />
<RadioButton x:Name="NewWindowBrowser" GroupName="OpenSearchBehaviour"
<RadioButton x:Name="NewWindowBrowser"
Content="{DynamicResource flowlauncher_plugin_new_window}" Click="OnNewBrowserWindowClick" />
<RadioButton x:Name="NewTabInBrowser" GroupName="OpenSearchBehaviour"
<RadioButton x:Name="NewTabInBrowser"
Content="{DynamicResource flowlauncher_plugin_new_tab}" Click="OnNewTabClick" />
</StackPanel>
<StackPanel VerticalAlignment="Top" Grid.Row="1" Height="106" Margin="0 20 0 0">

View file

@ -4,7 +4,7 @@
"Name": "URL",
"Description": "Open the typed URL from Flow Launcher",
"Author": "qianlifeng",
"Version": "1.1.6",
"Version": "1.1.7",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Url.dll",

View file

@ -32,7 +32,7 @@
</DockPanel>
</DataTemplate>
</UserControl.Resources>
<Grid Margin="15 0 0 0">
<Grid Margin="14">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="48" />