mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' of github.com:Flow-Launcher/Flow.Launcher into add_windows_settings_plugin
This commit is contained in:
commit
47e074ca2a
72 changed files with 2184 additions and 1005 deletions
57
Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs
Normal file
57
Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (_settings.HideWhenDeactive)
|
||||
{
|
||||
Hide();
|
||||
_viewModel.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
42
Flow.Launcher/Notification.cs
Normal file
42
Flow.Launcher/Notification.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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">
|
||||

|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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}">
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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}">
|
||||
|
|
|
|||
|
|
@ -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}">
|
||||
|
|
|
|||
|
|
@ -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}">
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,4 +69,8 @@
|
|||
<PackageReference Include="UnidecodeSharp" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Bookmark.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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/>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue