mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
first port to avalonia
This commit is contained in:
parent
05a0455d91
commit
d788de8b90
21 changed files with 1434 additions and 1517 deletions
|
|
@ -134,6 +134,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private void RemoveOldLanguageFiles()
|
||||
{
|
||||
return;
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
foreach (var r in _oldResources)
|
||||
{
|
||||
|
|
@ -143,6 +144,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private void LoadLanguage(Language language)
|
||||
{
|
||||
return;
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
var filename = $"{language.LanguageCode}{Extension}";
|
||||
var files = _languageDirectories
|
||||
|
|
@ -171,6 +173,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
public string GetTranslation(string key)
|
||||
{
|
||||
return "";
|
||||
var translation = Application.Current.TryFindResource(key);
|
||||
if (translation is string)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ namespace Flow.Launcher.Core.Resource
|
|||
_themeDirectories.Add(UserDirectoryPath);
|
||||
MakeSureThemeDirectoriesExist();
|
||||
|
||||
return;
|
||||
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
_oldResource = dicts.First(d =>
|
||||
{
|
||||
|
|
@ -72,6 +74,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
public bool ChangeTheme(string theme)
|
||||
{
|
||||
return false;
|
||||
const string defaultTheme = Constant.DefaultTheme;
|
||||
|
||||
string path = GetThemePath(theme);
|
||||
|
|
@ -125,6 +128,8 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate)
|
||||
{
|
||||
return;
|
||||
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
|
||||
dicts.Remove(_oldResource);
|
||||
|
|
|
|||
|
|
@ -193,8 +193,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public bool AutoUpdates { get; set; } = false;
|
||||
|
||||
public double WindowLeft { get; set; }
|
||||
public double WindowTop { get; set; }
|
||||
public int WindowLeft { get; set; }
|
||||
public int WindowTop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom left position on selected monitor
|
||||
|
|
|
|||
11
Flow.Launcher/App.axaml
Normal file
11
Flow.Launcher/App.axaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="Flow.Launcher.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
213
Flow.Launcher/App.axaml.cs
Normal file
213
Flow.Launcher/App.axaml.cs
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using PropertyChanged;
|
||||
using Application = Avalonia.Application;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
[DoNotNotify]
|
||||
public partial class App : Application, IDisposable, ISingleInstanceApp
|
||||
{
|
||||
public static PublicAPIInstance API { get; private set; }
|
||||
private const string Unique = "Flow.Launcher_Unique_Application_Mutex";
|
||||
private static bool _disposed;
|
||||
private Settings _settings;
|
||||
private MainViewModel _mainVM;
|
||||
private SettingWindowViewModel _settingsVM;
|
||||
private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo);
|
||||
private readonly Portable _portable = new Portable();
|
||||
private readonly PinyinAlphabet _alphabet = new PinyinAlphabet();
|
||||
private StringMatcher _stringMatcher;
|
||||
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (!SingleInstance<App>.InitializeAsFirstInstance(Unique))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
new JoinableTaskFactory(new JoinableTaskContext()).Run(() => Stopwatch.NormalAsync(
|
||||
"|App.OnStartup|Startup cost",
|
||||
async () =>
|
||||
{
|
||||
_portable.PreStartCleanUpAfterPortabilityUpdate();
|
||||
|
||||
Log.Info(
|
||||
"|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
|
||||
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
|
||||
RegisterAppDomainExceptions();
|
||||
|
||||
var imageLoadertask = ImageLoader.InitializeAsync();
|
||||
|
||||
_settingsVM = new SettingWindowViewModel(_updater, _portable);
|
||||
_settings = _settingsVM.Settings;
|
||||
|
||||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||
|
||||
_alphabet.Initialize(_settings);
|
||||
_stringMatcher = new StringMatcher(_alphabet);
|
||||
StringMatcher.Instance = _stringMatcher;
|
||||
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
|
||||
|
||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
|
||||
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
|
||||
|
||||
Http.API = API;
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
await PluginManager.InitializePluginsAsync(API);
|
||||
await imageLoadertask;
|
||||
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
|
||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||
throw new NotSupportedException("desktop is not IClassicDesktopStyleApplicationLifetime");
|
||||
|
||||
desktop.MainWindow = window;
|
||||
desktop.MainWindow.Title = Constant.FlowLauncher;
|
||||
// desktop.Exit += (s, e) => Dispose();
|
||||
|
||||
HotKeyMapper.Initialize(_mainVM);
|
||||
|
||||
// todo temp fix for instance code logic
|
||||
// load plugin before change language, because plugin language also needs be changed
|
||||
InternationalizationManager.Instance.Settings = _settings;
|
||||
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
|
||||
// main windows needs initialized before theme change because of blur settings
|
||||
ThemeManager.Instance.Settings = _settings;
|
||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
RegisterExitEvents();
|
||||
|
||||
AutoStartup();
|
||||
AutoUpdates();
|
||||
|
||||
API.SaveAppAllSettings();
|
||||
Log.Info(
|
||||
"|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- ");
|
||||
}));
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void AutoStartup()
|
||||
{
|
||||
// we try to enable auto-startup on first launch, or reenable if it was removed
|
||||
// but the user still has the setting set
|
||||
if (_settings.StartFlowLauncherOnSystemStartup && !Helper.AutoStartup.IsEnabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Helper.AutoStartup.Enable();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// but if it fails (permissions, etc) then don't keep retrying
|
||||
// this also gives the user a visual indication in the Settings widget
|
||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
|
||||
e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//[Conditional("RELEASE")]
|
||||
private void AutoUpdates()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (_settings.AutoUpdates)
|
||||
{
|
||||
// check update every 5 hours
|
||||
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
|
||||
await _updater.UpdateAppAsync(API);
|
||||
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
// check updates on startup
|
||||
await _updater.UpdateAppAsync(API);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void RegisterExitEvents()
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private static void RegisterAppDomainExceptions()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// if sessionending is called, exit proverbially be called when log off / shutdown
|
||||
// but if sessionending is not called, exit won't be called when log off / shutdown
|
||||
if (!_disposed)
|
||||
{
|
||||
API.SaveAppAllSettings();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSecondAppStarted()
|
||||
{
|
||||
_mainVM.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<Application
|
||||
x:Class="Flow.Launcher.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
ShutdownMode="OnMainWindowClose"
|
||||
Startup="OnStartupAsync">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemeResources>
|
||||
<ui:ThemeResources.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Resources/Light.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Resources/Dark.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</ui:ThemeResources.ThemeDictionaries>
|
||||
</ui:ThemeResources>
|
||||
<ui:XamlControlsResources />
|
||||
<ResourceDictionary Source="pack://application:,,,/Resources/CustomControlTemplate.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Win11System.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Languages/en.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class App : IDisposable, ISingleInstanceApp
|
||||
{
|
||||
public static PublicAPIInstance API { get; private set; }
|
||||
private const string Unique = "Flow.Launcher_Unique_Application_Mutex";
|
||||
private static bool _disposed;
|
||||
private Settings _settings;
|
||||
private MainViewModel _mainVM;
|
||||
private SettingWindowViewModel _settingsVM;
|
||||
private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo);
|
||||
private readonly Portable _portable = new Portable();
|
||||
private readonly PinyinAlphabet _alphabet = new PinyinAlphabet();
|
||||
private StringMatcher _stringMatcher;
|
||||
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
|
||||
{
|
||||
using (var application = new App())
|
||||
{
|
||||
application.InitializeComponent();
|
||||
application.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnStartupAsync(object sender, StartupEventArgs e)
|
||||
{
|
||||
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
||||
{
|
||||
_portable.PreStartCleanUpAfterPortabilityUpdate();
|
||||
|
||||
Log.Info(
|
||||
"|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
|
||||
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
|
||||
RegisterAppDomainExceptions();
|
||||
RegisterDispatcherUnhandledException();
|
||||
|
||||
var imageLoadertask = ImageLoader.InitializeAsync();
|
||||
|
||||
_settingsVM = new SettingWindowViewModel(_updater, _portable);
|
||||
_settings = _settingsVM.Settings;
|
||||
|
||||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||
|
||||
_alphabet.Initialize(_settings);
|
||||
_stringMatcher = new StringMatcher(_alphabet);
|
||||
StringMatcher.Instance = _stringMatcher;
|
||||
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
|
||||
|
||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
|
||||
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
|
||||
|
||||
Http.API = API;
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
await PluginManager.InitializePluginsAsync(API);
|
||||
await imageLoadertask;
|
||||
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
|
||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||
|
||||
Current.MainWindow = window;
|
||||
Current.MainWindow.Title = Constant.FlowLauncher;
|
||||
|
||||
HotKeyMapper.Initialize(_mainVM);
|
||||
|
||||
// todo temp fix for instance code logic
|
||||
// load plugin before change language, because plugin language also needs be changed
|
||||
InternationalizationManager.Instance.Settings = _settings;
|
||||
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
|
||||
// main windows needs initialized before theme change because of blur settings
|
||||
ThemeManager.Instance.Settings = _settings;
|
||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
RegisterExitEvents();
|
||||
|
||||
AutoStartup();
|
||||
AutoUpdates();
|
||||
|
||||
API.SaveAppAllSettings();
|
||||
Log.Info(
|
||||
"|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- ");
|
||||
});
|
||||
}
|
||||
|
||||
private void AutoStartup()
|
||||
{
|
||||
// we try to enable auto-startup on first launch, or reenable if it was removed
|
||||
// but the user still has the setting set
|
||||
if (_settings.StartFlowLauncherOnSystemStartup && !Helper.AutoStartup.IsEnabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Helper.AutoStartup.Enable();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// but if it fails (permissions, etc) then don't keep retrying
|
||||
// this also gives the user a visual indication in the Settings widget
|
||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
|
||||
e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//[Conditional("RELEASE")]
|
||||
private void AutoUpdates()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (_settings.AutoUpdates)
|
||||
{
|
||||
// check update every 5 hours
|
||||
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
|
||||
await _updater.UpdateAppAsync(API);
|
||||
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
// check updates on startup
|
||||
await _updater.UpdateAppAsync(API);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void RegisterExitEvents()
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
|
||||
Current.Exit += (s, e) => Dispose();
|
||||
Current.SessionEnding += (s, e) => Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private void RegisterDispatcherUnhandledException()
|
||||
{
|
||||
DispatcherUnhandledException += ErrorReporting.DispatcherUnhandledException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private static void RegisterAppDomainExceptions()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// if sessionending is called, exit proverbially be called when log off / shutdown
|
||||
// but if sessionending is not called, exit won't be called when log off / shutdown
|
||||
if (!_disposed)
|
||||
{
|
||||
API.SaveAppAllSettings();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSecondAppStarted()
|
||||
{
|
||||
_mainVM.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<StartupObject>Flow.Launcher.App</StartupObject>
|
||||
<ApplicationIcon>Resources\app.ico</ApplicationIcon>
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
|
|
@ -23,7 +25,6 @@
|
|||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>true</UseVSHostingProcess>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
@ -42,10 +43,6 @@
|
|||
<ApplicationDefinition Remove="App.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Include="App.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Remove="Themes\ThemeBuilder\Template.xaml" />
|
||||
</ItemGroup>
|
||||
|
|
@ -83,7 +80,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.0.7" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.7" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.7" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.7" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.7" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.7"/>
|
||||
<PackageReference Include="Fody" Version="6.5.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ using System.Security;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
|
||||
// http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/
|
||||
// modified to allow single instace restart
|
||||
|
|
@ -119,8 +120,10 @@ namespace Flow.Launcher.Helper
|
|||
DWMWINDOWMAXIMIZEDCHANGE = 0x0321,
|
||||
|
||||
#region Windows 7
|
||||
|
||||
DWMSENDICONICTHUMBNAIL = 0x0323,
|
||||
DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326,
|
||||
|
||||
#endregion
|
||||
|
||||
USER = 0x0400,
|
||||
|
|
@ -140,7 +143,8 @@ namespace Flow.Launcher.Helper
|
|||
public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled);
|
||||
|
||||
[DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs);
|
||||
private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine,
|
||||
out int numArgs);
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)]
|
||||
|
|
@ -159,6 +163,7 @@ namespace Flow.Launcher.Helper
|
|||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
var result = new string[numArgs];
|
||||
|
||||
for (int i = 0; i < numArgs; i++)
|
||||
|
|
@ -171,19 +176,17 @@ namespace Flow.Launcher.Helper
|
|||
}
|
||||
finally
|
||||
{
|
||||
|
||||
IntPtr p = _LocalFree(argv);
|
||||
// Otherwise LocalFree failed.
|
||||
// Assert.AreEqual(IntPtr.Zero, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface ISingleInstanceApp
|
||||
{
|
||||
void OnSecondAppStarted();
|
||||
}
|
||||
public interface ISingleInstanceApp
|
||||
{
|
||||
void OnSecondAppStarted();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class checks to make sure that only one instance of
|
||||
|
|
@ -196,9 +199,9 @@ namespace Flow.Launcher.Helper
|
|||
/// running as Administrator, can activate it with command line arguments.
|
||||
/// For most apps, this will not be much of an issue.
|
||||
/// </remarks>
|
||||
public static class SingleInstance<TApplication>
|
||||
where TApplication: Application , ISingleInstanceApp
|
||||
|
||||
public static class SingleInstance<TApplication>
|
||||
where TApplication : Avalonia.Application, ISingleInstanceApp
|
||||
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
|
|
@ -230,7 +233,7 @@ namespace Flow.Launcher.Helper
|
|||
/// If not, activates the first instance.
|
||||
/// </summary>
|
||||
/// <returns>True if this is the first instance of the application.</returns>
|
||||
public static bool InitializeAsFirstInstance( string uniqueName )
|
||||
public static bool InitializeAsFirstInstance(string uniqueName)
|
||||
{
|
||||
// Build unique application Id and the IPC channel name.
|
||||
string applicationIdentifier = uniqueName + Environment.UserName;
|
||||
|
|
@ -268,7 +271,7 @@ namespace Flow.Launcher.Helper
|
|||
/// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved.
|
||||
/// </summary>
|
||||
/// <returns>List of command line arg strings.</returns>
|
||||
private static IList<string> GetCommandLineArgs( string uniqueApplicationName )
|
||||
private static IList<string> GetCommandLineArgs(string uniqueApplicationName)
|
||||
{
|
||||
string[] args = null;
|
||||
|
||||
|
|
@ -279,7 +282,6 @@ namespace Flow.Launcher.Helper
|
|||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
|
||||
// The application was clickonce deployed
|
||||
// Clickonce deployed apps cannot recieve traditional commandline arguments
|
||||
// As a workaround commandline arguments can be written to a shared location before
|
||||
|
|
@ -323,15 +325,17 @@ namespace Flow.Launcher.Helper
|
|||
{
|
||||
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In))
|
||||
{
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
// Wait for connection to the pipe
|
||||
await pipeServer.WaitForConnectionAsync();
|
||||
if (Application.Current != null)
|
||||
{
|
||||
|
||||
// Do an asynchronous call to ActivateFirstInstance function
|
||||
Application.Current.Dispatcher.Invoke(ActivateFirstInstance);
|
||||
Dispatcher.UIThread.Invoke(ActivateFirstInstance);
|
||||
}
|
||||
|
||||
// Disconect client
|
||||
pipeServer.Disconnect();
|
||||
}
|
||||
|
|
@ -378,7 +382,7 @@ namespace Flow.Launcher.Helper
|
|||
return;
|
||||
}
|
||||
|
||||
((TApplication)Application.Current).OnSecondAppStarted();
|
||||
((TApplication)Application.Current)!.OnSecondAppStarted();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
454
Flow.Launcher/MainWindow.axaml
Normal file
454
Flow.Launcher/MainWindow.axaml
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.MainWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:Flow.Launcher.Converters"
|
||||
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
|
||||
Name="FlowMainWindow"
|
||||
Title="Flow Launcher"
|
||||
x:DataType="vm:MainViewModel"
|
||||
MinWidth="{Binding MainWindowWidth, Mode=OneWay}"
|
||||
MaxWidth="{Binding MainWindowWidth, Mode=OneWay}"
|
||||
Background="Transparent"
|
||||
Deactivated="OnDeactivated"
|
||||
Initialized="OnInitialized"
|
||||
Loaded="OnLoaded"
|
||||
Opacity="{Binding MainWindowOpacity, Mode=OneWay}"
|
||||
ShowInTaskbar="False"
|
||||
SizeToContent="Height"
|
||||
Topmost="True"
|
||||
IsVisible="{Binding MainWindowVisibility, Mode=TwoWay}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ExtendClientAreaChromeHints="NoChrome">
|
||||
<Window.Resources>
|
||||
<converters:QuerySuggestionBoxConverter x:Key="QuerySuggestionBoxConverter" />
|
||||
<converters:BorderClipConverter x:Key="BorderClipConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:SplitterConverter x:Key="SplitterConverter" />
|
||||
<converters:BoolToIMEConversionModeConverter x:Key="BoolToIMEConversionModeConverter" />
|
||||
<converters:BoolToIMEStateConverter x:Key="BoolToIMEStateConverter" />
|
||||
<converters:StringToKeyBindingConverter x:Key="StringToKeyBindingConverter" />
|
||||
</Window.Resources>
|
||||
<!-- <Window.InputBindings> -->
|
||||
<!-- <KeyBinding Key="Escape" Command="{Binding EscCommand}" /> -->
|
||||
<!-- <KeyBinding Key="F5" Command="{Binding ReloadPluginDataCommand}" /> -->
|
||||
<!-- <KeyBinding Key="Tab" Command="{Binding AutocompleteQueryCommand}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Tab" -->
|
||||
<!-- Command="{Binding AutocompleteQueryCommand}" -->
|
||||
<!-- Modifiers="Shift" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="I" -->
|
||||
<!-- Command="{Binding OpenSettingCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="N" -->
|
||||
<!-- Command="{Binding SelectNextItemCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="J" -->
|
||||
<!-- Command="{Binding SelectNextItemCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D" -->
|
||||
<!-- Command="{Binding SelectNextPageCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="P" -->
|
||||
<!-- Command="{Binding SelectPrevItemCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="K" -->
|
||||
<!-- Command="{Binding SelectPrevItemCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="U" -->
|
||||
<!-- Command="{Binding SelectPrevPageCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Home" -->
|
||||
<!-- Command="{Binding SelectFirstResultCommand}" -->
|
||||
<!-- Modifiers="Alt" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="O" -->
|
||||
<!-- Command="{Binding LoadContextMenuCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="R" -->
|
||||
<!-- Command="{Binding ReQueryCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="H" -->
|
||||
<!-- Command="{Binding LoadHistoryCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="OemCloseBrackets" -->
|
||||
<!-- Command="{Binding IncreaseWidthCommand}" -->
|
||||
<!-- Modifiers="Control" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="OemOpenBrackets" -->
|
||||
<!-- Command="{Binding DecreaseWidthCommand}" -->
|
||||
<!-- Modifiers="Control" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="OemPlus" -->
|
||||
<!-- Command="{Binding IncreaseMaxResultCommand}" -->
|
||||
<!-- Modifiers="Control" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="OemMinus" -->
|
||||
<!-- Command="{Binding DecreaseMaxResultCommand}" -->
|
||||
<!-- Modifiers="Control" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="H" -->
|
||||
<!-- Command="{Binding LoadHistoryCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Enter" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- Modifiers="Ctrl+Shift" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Enter" -->
|
||||
<!-- Command="{Binding LoadContextMenuCommand}" -->
|
||||
<!-- Modifiers="Shift" /> -->
|
||||
<!-- <KeyBinding Key="Enter" Command="{Binding OpenResultCommand}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Enter" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="Enter" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- Modifiers="Alt" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D1" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="0" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D2" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="1" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D3" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="2" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D4" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="3" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D5" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="4" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D6" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="5" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D7" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="6" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D8" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="7" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D9" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="8" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="D0" -->
|
||||
<!-- Command="{Binding OpenResultCommand}" -->
|
||||
<!-- CommandParameter="9" -->
|
||||
<!-- Modifiers="{Binding OpenResultCommandModifiers}" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="F12" -->
|
||||
<!-- Command="{Binding ToggleGameModeCommand}" -->
|
||||
<!-- Modifiers="Ctrl" /> -->
|
||||
<!-- <KeyBinding -->
|
||||
<!-- Key="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}" -->
|
||||
<!-- Command="{Binding TogglePreviewCommand}" -->
|
||||
<!-- Modifiers="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" /> -->
|
||||
<!-- </Window.InputBindings> -->
|
||||
<Grid>
|
||||
<Border PointerPressed="OnMouseDown">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid>
|
||||
<Border>
|
||||
<Grid>
|
||||
<TextBox
|
||||
x:Name="QueryTextSuggestionBox"
|
||||
IsEnabled="False">
|
||||
<TextBox.Text>
|
||||
<!-- <MultiBinding Converter="{StaticResource QuerySuggestionBoxConverter}"> -->
|
||||
<!-- <Binding ElementName="QueryTextBox" Mode="OneTime" /> -->
|
||||
<!-- <Binding ElementName="QueryTextBox" Path="Text" /> -->
|
||||
<!-- </MultiBinding> -->
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<TextBox
|
||||
x:Name="QueryTextBox"
|
||||
Height="40"
|
||||
Text="{Binding QueryText, Mode=TwoWay}">
|
||||
<!-- <TextBox.CommandBindings> -->
|
||||
<!-- <CommandBinding Command="ApplicationCommands.Copy" Executed="OnCopy" /> -->
|
||||
<!-- <CommandBinding Command="ApplicationCommands.Paste" Executed="OnPaste" /> -->
|
||||
<!-- </TextBox.CommandBindings> -->
|
||||
<TextBox.ContextMenu>
|
||||
<ContextMenu MinWidth="160">
|
||||
<!-- <MenuItem Command="ApplicationCommands.Cut" Header="{DynamicResource cut}"> -->
|
||||
<!-- <MenuItem.Icon> -->
|
||||
<!-- <ui:FontIcon Glyph="" /> -->
|
||||
<!-- </MenuItem.Icon> -->
|
||||
<!-- </MenuItem> -->
|
||||
<!-- <MenuItem Command="ApplicationCommands.Copy" Header="{DynamicResource copy}"> -->
|
||||
<!-- <MenuItem.Icon> -->
|
||||
<!-- <ui:FontIcon Glyph="" /> -->
|
||||
<!-- </MenuItem.Icon> -->
|
||||
<!-- </MenuItem> -->
|
||||
<!-- <MenuItem Command="ApplicationCommands.Paste" Header="{DynamicResource paste}"> -->
|
||||
<!-- <MenuItem.Icon> -->
|
||||
<!-- <ui:FontIcon Glyph="" /> -->
|
||||
<!-- </MenuItem.Icon> -->
|
||||
<!-- </MenuItem> -->
|
||||
<Separator
|
||||
Margin="0"
|
||||
Padding="0,4,0,4"
|
||||
Background="{DynamicResource ContextSeparator}" />
|
||||
<MenuItem Click="OnContextMenusForSettingsClick"
|
||||
Header="{DynamicResource flowlauncher_settings}">
|
||||
<MenuItem.Icon>
|
||||
<!-- <FontIcon Glyph="" /> -->
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<!-- <MenuItem Command="{Binding EscCommand}" Header="{DynamicResource closeWindow}"> -->
|
||||
<!-- <MenuItem.Icon> -->
|
||||
<!-- <ui:FontIcon Glyph="" /> -->
|
||||
<!-- </MenuItem.Icon> -->
|
||||
<!-- </MenuItem> -->
|
||||
</ContextMenu>
|
||||
</TextBox.ContextMenu>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel
|
||||
x:Name="ClockPanel"
|
||||
IsHitTestVisible="False"
|
||||
HorizontalAlignment="Right">
|
||||
<TextBlock
|
||||
x:Name="ClockBox"
|
||||
Text="{Binding ClockText}"
|
||||
IsVisible="{Binding Settings.UseClock}" />
|
||||
<TextBlock
|
||||
x:Name="DateBox"
|
||||
Text="{Binding DateText}"
|
||||
IsVisible="{Binding Settings.UseDate}" />
|
||||
</StackPanel>
|
||||
<Border>
|
||||
<Grid>
|
||||
<Image
|
||||
x:Name="PluginActivationIcon"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Source="{Binding PluginIconPath}"
|
||||
Stretch="Uniform" />
|
||||
<Canvas>
|
||||
<Path
|
||||
Name="SearchIcon"
|
||||
Margin="0"
|
||||
Data="{DynamicResource SearchIconImg}"
|
||||
Stretch="Fill"
|
||||
IsVisible="{Binding SearchIconVisibility}" />
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
<!-- <Line -->
|
||||
<!-- x:Name="ProgressBar" -->
|
||||
<!-- Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth}" -->
|
||||
<!-- Height="2" -->
|
||||
<!-- Margin="12,0,12,0" -->
|
||||
<!-- HorizontalAlignment="Center" -->
|
||||
<!-- VerticalAlignment="Bottom" -->
|
||||
<!-- StrokeThickness="2" -->
|
||||
<!-- IsVisible="{Binding ProgressBarVisibility, Mode=TwoWay}" -->
|
||||
<!-- X1="-100" -->
|
||||
<!-- X2="0" -->
|
||||
<!-- Y1="0" -->
|
||||
<!-- Y2="0" /> -->
|
||||
</Grid>
|
||||
|
||||
<Grid ClipToBounds="True">
|
||||
<ContentControl>
|
||||
<!-- <ContentControl.Style> -->
|
||||
<!-- <Style TargetType="ContentControl"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Collapsed" /> -->
|
||||
<!-- <Style.Triggers> -->
|
||||
<!-- <DataTrigger Binding="{Binding ElementName=ResultListBox, Path=Visibility}" -->
|
||||
<!-- Value="Visible"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Visible" /> -->
|
||||
<!-- </DataTrigger> -->
|
||||
<!-- <DataTrigger Binding="{Binding ElementName=ContextMenu, Path=Visibility}" -->
|
||||
<!-- Value="Visible"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Visible" /> -->
|
||||
<!-- </DataTrigger> -->
|
||||
<!-- <DataTrigger Binding="{Binding ElementName=History, Path=Visibility}" -->
|
||||
<!-- Value="Visible"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Visible" /> -->
|
||||
<!-- </DataTrigger> -->
|
||||
<!-- </Style.Triggers> -->
|
||||
<!-- </Style> -->
|
||||
<!-- </ContentControl.Style> -->
|
||||
<Rectangle
|
||||
x:Name="MiddleSeparator"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</ContentControl>
|
||||
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="100" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="0.85*" MinWidth="244" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
x:Name="ResultArea"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="{Binding ResultAreaColumn}">
|
||||
<Border DataContext="{Binding ResultsVM}">
|
||||
<!-- <Border.Clip> -->
|
||||
<!-- <MultiBinding Converter="{StaticResource BorderClipConverter}"> -->
|
||||
<!-- <Binding Path="Width" RelativeSource="{RelativeSource Self}" /> -->
|
||||
<!-- <Binding Path="Height" RelativeSource="{RelativeSource Self}" /> -->
|
||||
<!-- <Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" /> -->
|
||||
<!-- </MultiBinding> -->
|
||||
<!-- </Border.Clip> -->
|
||||
<flowlauncher:ResultListBox x:Name="ResultListBox" />
|
||||
</Border>
|
||||
<!-- <Border DataContext="{Binding ContextMenu}"> -->
|
||||
<!-- <Border.Clip> -->
|
||||
<!-- ~1~ <MultiBinding Converter="{StaticResource BorderClipConverter}"> @1@ -->
|
||||
<!-- ~1~ <Binding Path="Width" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ <Binding Path="Height" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ <Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ </MultiBinding> @1@ -->
|
||||
<!-- </Border.Clip> -->
|
||||
<!-- <flowlauncher:ResultListBox -->
|
||||
<!-- x:Name="ContextMenu" /> -->
|
||||
<!-- </Border> -->
|
||||
<!-- <Border DataContext="{Binding History}"> -->
|
||||
<!-- <Border.Clip> -->
|
||||
<!-- ~1~ <MultiBinding Converter="{StaticResource BorderClipConverter}"> @1@ -->
|
||||
<!-- ~1~ <Binding Path="Width" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ <Binding Path="Height" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ <Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" /> @1@ -->
|
||||
<!-- ~1~ </MultiBinding> @1@ -->
|
||||
<!-- </Border.Clip> -->
|
||||
<!-- <flowlauncher:ResultListBox -->
|
||||
<!-- x:Name="History" /> -->
|
||||
<!-- </Border> -->
|
||||
</StackPanel>
|
||||
<!-- <GridSplitter -->
|
||||
<!-- Grid.Column="1" -->
|
||||
<!-- HorizontalAlignment="Center" -->
|
||||
<!-- VerticalAlignment="Stretch" -->
|
||||
<!-- Background="Transparent" -->
|
||||
<!-- ShowsPreview="True" /> -->
|
||||
<!-- <Grid -->
|
||||
<!-- x:Name="Preview" -->
|
||||
<!-- Grid.Column="2" -->
|
||||
<!-- VerticalAlignment="Stretch" -->
|
||||
<!-- IsVisible="{Binding PreviewVisible}"> -->
|
||||
<!-- <Border -->
|
||||
<!-- x:DataType="vm:ResultViewModel" -->
|
||||
<!-- IsVisible="{Binding ShowDefaultPreview}"> -->
|
||||
<!-- <Grid -->
|
||||
<!-- Margin="20,0,10,0" -->
|
||||
<!-- VerticalAlignment="Stretch" -->
|
||||
<!-- Background="Transparent"> -->
|
||||
<!-- <Grid.RowDefinitions> -->
|
||||
<!-- <RowDefinition Height="*" /> -->
|
||||
<!-- <RowDefinition Height="Auto" /> -->
|
||||
<!-- </Grid.RowDefinitions> -->
|
||||
<!-- <Grid Grid.Row="0" VerticalAlignment="Center"> -->
|
||||
<!-- <Grid.RowDefinitions> -->
|
||||
<!-- <RowDefinition Height="Auto" /> -->
|
||||
<!-- <RowDefinition Height="Auto" /> -->
|
||||
<!-- </Grid.RowDefinitions> -->
|
||||
<!-- <TextBlock -->
|
||||
<!-- x:Name="PreviewGlyphIcon" -->
|
||||
<!-- Grid.Row="0" -->
|
||||
<!-- Margin="0,16,0,0" -->
|
||||
<!-- FontFamily="{Binding Glyph.FontFamily}" -->
|
||||
<!-- Text="{Binding Glyph.Glyph}" -->
|
||||
<!-- IsVisible="{Binding ShowGlyph}" /> -->
|
||||
<!-- <Image -->
|
||||
<!-- x:Name="PreviewImageIcon" -->
|
||||
<!-- Grid.Row="0" -->
|
||||
<!-- MaxHeight="320" -->
|
||||
<!-- Margin="0,16,0,0" -->
|
||||
<!-- HorizontalAlignment="Center" -->
|
||||
<!-- Source="{Binding PreviewImage}" -->
|
||||
<!-- StretchDirection="DownOnly" -->
|
||||
<!-- IsVisible="{Binding ShowPreviewImage}"> -->
|
||||
<!-- ~1~ <Image.Style> @1@ -->
|
||||
<!-- ~1~ <Style TargetType="{x:Type Image}"> @1@ -->
|
||||
<!-- ~1~ <Setter Property="MaxWidth" Value="96" /> @1@ -->
|
||||
<!-- ~1~ <Style.Triggers> @1@ -->
|
||||
<!-- ~1~ <DataTrigger Binding="{Binding UseBigThumbnail}" Value="True"> @1@ -->
|
||||
<!-- ~1~ <Setter Property="MaxWidth" @1@ -->
|
||||
<!-- ~1~ Value="{Binding ElementName=Preview, Path=ActualWidth}" /> @1@ -->
|
||||
<!-- ~1~ </DataTrigger> @1@ -->
|
||||
<!-- ~1~ </Style.Triggers> @1@ -->
|
||||
<!-- ~1~ </Style> @1@ -->
|
||||
<!-- ~1~ </Image.Style> @1@ -->
|
||||
<!-- </Image> -->
|
||||
<!-- <TextBlock -->
|
||||
<!-- x:Name="PreviewTitle" -->
|
||||
<!-- Grid.Row="1" -->
|
||||
<!-- Margin="0,6,0,16" -->
|
||||
<!-- HorizontalAlignment="Stretch" -->
|
||||
<!-- Text="{Binding Result.Title}" -->
|
||||
<!-- TextAlignment="Center" -->
|
||||
<!-- TextWrapping="Wrap" /> -->
|
||||
<!-- </Grid> -->
|
||||
<!-- <StackPanel Grid.Row="1"> -->
|
||||
<!-- ~1~ <StackPanel.Style> @1@ -->
|
||||
<!-- ~1~ <Style TargetType="{x:Type StackPanel}"> @1@ -->
|
||||
<!-- ~1~ <Style.Triggers> @1@ -->
|
||||
<!-- ~1~ <DataTrigger @1@ -->
|
||||
<!-- ~1~ Binding="{Binding ElementName=PreviewSubTitle, UpdateSourceTrigger=PropertyChanged, Path=Text.Length}" @1@ -->
|
||||
<!-- ~1~ Value="0"> @1@ -->
|
||||
<!-- ~1~ <Setter Property="Visibility" Value="Collapsed" /> @1@ -->
|
||||
<!-- ~1~ </DataTrigger> @1@ -->
|
||||
<!-- ~1~ </Style.Triggers> @1@ -->
|
||||
<!-- ~1~ </Style> @1@ -->
|
||||
<!-- ~1~ </StackPanel.Style> @1@ -->
|
||||
<!-- <Separator /> -->
|
||||
<!-- <TextBlock -->
|
||||
<!-- x:Name="PreviewSubTitle" -->
|
||||
<!-- Text="{Binding Result.SubTitle}" /> -->
|
||||
<!-- </StackPanel> -->
|
||||
<!-- </Grid> -->
|
||||
<!-- </Border> -->
|
||||
<!-- <Border -->
|
||||
<!-- x:DataType="vm:ResultViewModel" -->
|
||||
<!-- IsVisible="{Binding ShowCustomizedPreview}"> -->
|
||||
<!-- <ContentControl Content="{Binding Result.PreviewPanel.Value}" /> -->
|
||||
<!-- </Border> -->
|
||||
<!-- </Grid> -->
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
@ -1,10 +1,6 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
|
|
@ -19,20 +15,30 @@ using NotifyIcon = System.Windows.Forms.NotifyIcon;
|
|||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Windows.Threading;
|
||||
using System.Windows.Data;
|
||||
using ModernWpf.Controls;
|
||||
using Key = System.Windows.Input.Key;
|
||||
using System.Media;
|
||||
using System.Windows;
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using PropertyChanged;
|
||||
using static Flow.Launcher.ViewModel.SettingWindowViewModel;
|
||||
using ContextMenu = System.Windows.Controls.ContextMenu;
|
||||
using MenuItem = System.Windows.Controls.MenuItem;
|
||||
using MouseButton = System.Windows.Input.MouseButton;
|
||||
using RoutedEventArgs = Avalonia.Interactivity.RoutedEventArgs;
|
||||
using Thickness = System.Windows.Thickness;
|
||||
using Window = Avalonia.Controls.Window;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class MainWindow
|
||||
[DoNotNotify]
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private readonly Storyboard _progressBarStoryboard = new Storyboard();
|
||||
// private readonly Storyboard _progressBarStoryboard = new Storyboard();
|
||||
private bool isProgressBarStoryboardPaused;
|
||||
private Settings _settings;
|
||||
private NotifyIcon _notifyIcon;
|
||||
|
|
@ -50,7 +56,7 @@ namespace Flow.Launcher
|
|||
_settings = settings;
|
||||
|
||||
InitializeComponent();
|
||||
InitializePosition();
|
||||
// InitializePosition();
|
||||
}
|
||||
|
||||
public MainWindow()
|
||||
|
|
@ -58,30 +64,31 @@ namespace Flow.Launcher
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnCopy(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
var result = _viewModel.Results.SelectedItem?.Result;
|
||||
if (QueryTextBox.SelectionLength == 0 && result != null)
|
||||
{
|
||||
string copyText = result.CopyText;
|
||||
App.API.CopyToClipboard(copyText, directCopy: true);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(QueryTextBox.Text))
|
||||
{
|
||||
App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false);
|
||||
}
|
||||
}
|
||||
// private void OnCopy(object sender, ExecutedRoutedEventArgs e)
|
||||
// {
|
||||
// var result = _viewModel.Results.SelectedItem?.Result;
|
||||
// if (QueryTextBox.SelectionLength == 0 && result != null)
|
||||
// {
|
||||
// string copyText = result.CopyText;
|
||||
// App.API.CopyToClipboard(copyText, directCopy: true);
|
||||
// }
|
||||
// else if (!string.IsNullOrEmpty(QueryTextBox.Text))
|
||||
// {
|
||||
// App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void OnPaste(object sender, ExecutedRoutedEventArgs e)
|
||||
// {
|
||||
// if (System.Windows.Clipboard.ContainsText())
|
||||
// {
|
||||
// _viewModel.ChangeQueryText(System.Windows.Clipboard.GetText().Replace("\n", String.Empty)
|
||||
// .Replace("\r", String.Empty));
|
||||
// e.Handled = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
private void OnPaste(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
if (System.Windows.Clipboard.ContainsText())
|
||||
{
|
||||
_viewModel.ChangeQueryText(System.Windows.Clipboard.GetText().Replace("\n", String.Empty).Replace("\r", String.Empty));
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnClosing(object sender, CancelEventArgs e)
|
||||
public async void OnClosing(object sender, CancelEventArgs e)
|
||||
{
|
||||
_notifyIcon.Visible = false;
|
||||
App.API.SaveAppAllSettings();
|
||||
|
|
@ -91,20 +98,20 @@ namespace Flow.Launcher
|
|||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private void OnInitialized(object sender, EventArgs e)
|
||||
public void OnInitialized(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs _)
|
||||
public void OnLoaded(object sender, RoutedEventArgs _)
|
||||
{
|
||||
CheckFirstLaunch();
|
||||
HideStartup();
|
||||
// show notify icon when flowlauncher is hidden
|
||||
InitializeNotifyIcon();
|
||||
InitializeColorScheme();
|
||||
WindowsInteropHelper.DisableControlBox(this);
|
||||
// WindowsInteropHelper.DisableControlBox(this);
|
||||
InitProgressbarAnimation();
|
||||
InitializePosition();
|
||||
// InitializePosition();
|
||||
PreviewReset();
|
||||
// since the default main window visibility is visible
|
||||
// so we need set focus during startup
|
||||
|
|
@ -116,7 +123,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
case nameof(MainViewModel.MainWindowVisibilityStatus):
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (_viewModel.MainWindowVisibilityStatus)
|
||||
{
|
||||
|
|
@ -124,6 +131,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
animationSound.Play();
|
||||
}
|
||||
|
||||
UpdatePosition();
|
||||
PreviewReset();
|
||||
Activate();
|
||||
|
|
@ -135,18 +143,18 @@ namespace Flow.Launcher
|
|||
_viewModel.LastQuerySelected = true;
|
||||
}
|
||||
|
||||
if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused)
|
||||
if (_viewModel.ProgressBarVisibility && isProgressBarStoryboardPaused)
|
||||
{
|
||||
_progressBarStoryboard.Begin(ProgressBar, true);
|
||||
// _progressBarStoryboard.Begin(ProgressBar, true);
|
||||
isProgressBarStoryboardPaused = false;
|
||||
}
|
||||
|
||||
if (_settings.UseAnimation)
|
||||
WindowAnimator();
|
||||
// if (_settings.UseAnimation)
|
||||
// WindowAnimator();
|
||||
}
|
||||
else if (!isProgressBarStoryboardPaused)
|
||||
{
|
||||
_progressBarStoryboard.Stop(ProgressBar);
|
||||
// _progressBarStoryboard.Stop(ProgressBar);
|
||||
isProgressBarStoryboardPaused = true;
|
||||
}
|
||||
});
|
||||
|
|
@ -154,17 +162,17 @@ namespace Flow.Launcher
|
|||
}
|
||||
case nameof(MainViewModel.ProgressBarVisibility):
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused)
|
||||
if (!_viewModel.ProgressBarVisibility && !isProgressBarStoryboardPaused)
|
||||
{
|
||||
_progressBarStoryboard.Stop(ProgressBar);
|
||||
// _progressBarStoryboard.Stop(ProgressBar);
|
||||
isProgressBarStoryboardPaused = true;
|
||||
}
|
||||
else if (_viewModel.MainWindowVisibilityStatus &&
|
||||
isProgressBarStoryboardPaused)
|
||||
{
|
||||
_progressBarStoryboard.Begin(ProgressBar, true);
|
||||
// _progressBarStoryboard.Begin(ProgressBar, true);
|
||||
isProgressBarStoryboardPaused = false;
|
||||
}
|
||||
});
|
||||
|
|
@ -176,9 +184,12 @@ namespace Flow.Launcher
|
|||
MoveQueryTextToEnd();
|
||||
_viewModel.QueryTextCursorMovedToEnd = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case nameof(MainViewModel.GameModeStatus):
|
||||
_notifyIcon.Icon = _viewModel.GameModeStatus ? Properties.Resources.gamemode : Properties.Resources.app;
|
||||
_notifyIcon.Icon = _viewModel.GameModeStatus
|
||||
? Properties.Resources.gamemode
|
||||
: Properties.Resources.app;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
@ -197,10 +208,10 @@ namespace Flow.Launcher
|
|||
UpdateNotifyIconText();
|
||||
break;
|
||||
case nameof(Settings.WindowLeft):
|
||||
Left = _settings.WindowLeft;
|
||||
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
|
||||
break;
|
||||
case nameof(Settings.WindowTop):
|
||||
Top = _settings.WindowTop;
|
||||
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
@ -210,8 +221,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
|
||||
{
|
||||
Top = _settings.WindowTop;
|
||||
Left = _settings.WindowLeft;
|
||||
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -219,39 +229,36 @@ namespace Flow.Launcher
|
|||
switch (_settings.SearchWindowAlign)
|
||||
{
|
||||
case SearchWindowAligns.Center:
|
||||
Left = HorizonCenter(screen);
|
||||
Top = VerticalCenter(screen);
|
||||
Position = new PixelPoint(HorizonCenter(screen), VerticalCenter(screen));
|
||||
break;
|
||||
case SearchWindowAligns.CenterTop:
|
||||
Left = HorizonCenter(screen);
|
||||
Top = 10;
|
||||
Position = new PixelPoint(HorizonCenter(screen), 10);
|
||||
break;
|
||||
case SearchWindowAligns.LeftTop:
|
||||
Left = HorizonLeft(screen);
|
||||
Top = 10;
|
||||
Position = new PixelPoint(HorizonLeft(screen), 10);
|
||||
break;
|
||||
case SearchWindowAligns.RightTop:
|
||||
Left = HorizonRight(screen);
|
||||
Top = 10;
|
||||
Position = new PixelPoint(HorizonRight(screen), 10);
|
||||
break;
|
||||
case SearchWindowAligns.Custom:
|
||||
Left = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X;
|
||||
Top = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y + _settings.CustomWindowTop).Y;
|
||||
// Left = WindowsInteropHelper
|
||||
// .TransformPixelsToDIP(this, screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X;
|
||||
// Top = WindowsInteropHelper
|
||||
// .TransformPixelsToDIP(this, 0, screen.WorkingArea.Y + _settings.CustomWindowTop).Y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateNotifyIconText()
|
||||
{
|
||||
var menu = contextMenu;
|
||||
((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")";
|
||||
((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") +
|
||||
" (" + _settings.Hotkey + ")";
|
||||
((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("GameMode");
|
||||
((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset");
|
||||
((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
|
||||
((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
|
||||
|
||||
}
|
||||
|
||||
private void InitializeNotifyIcon()
|
||||
|
|
@ -265,42 +272,31 @@ namespace Flow.Launcher
|
|||
|
||||
contextMenu = new ContextMenu();
|
||||
|
||||
var openIcon = new FontIcon
|
||||
{
|
||||
Glyph = "\ue71e"
|
||||
};
|
||||
var openIcon = new FontIcon { Glyph = "\ue71e" };
|
||||
var open = new MenuItem
|
||||
{
|
||||
Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", Icon = openIcon
|
||||
};
|
||||
var gamemodeIcon = new FontIcon
|
||||
{
|
||||
Glyph = "\ue7fc"
|
||||
Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" +
|
||||
_settings.Hotkey + ")",
|
||||
Icon = openIcon
|
||||
};
|
||||
var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" };
|
||||
var gamemode = new MenuItem
|
||||
{
|
||||
Header = InternationalizationManager.Instance.GetTranslation("GameMode"), Icon = gamemodeIcon
|
||||
};
|
||||
var positionresetIcon = new FontIcon
|
||||
{
|
||||
Glyph = "\ue73f"
|
||||
};
|
||||
var positionresetIcon = new FontIcon { Glyph = "\ue73f" };
|
||||
var positionreset = new MenuItem
|
||||
{
|
||||
Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), Icon = positionresetIcon
|
||||
};
|
||||
var settingsIcon = new FontIcon
|
||||
{
|
||||
Glyph = "\ue713"
|
||||
Header = InternationalizationManager.Instance.GetTranslation("PositionReset"),
|
||||
Icon = positionresetIcon
|
||||
};
|
||||
var settingsIcon = new FontIcon { Glyph = "\ue713" };
|
||||
var settings = new MenuItem
|
||||
{
|
||||
Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), Icon = settingsIcon
|
||||
};
|
||||
var exitIcon = new FontIcon
|
||||
{
|
||||
Glyph = "\ue7e8"
|
||||
Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"),
|
||||
Icon = settingsIcon
|
||||
};
|
||||
var exitIcon = new FontIcon { Glyph = "\ue7e8" };
|
||||
var exit = new MenuItem
|
||||
{
|
||||
Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), Icon = exitIcon
|
||||
|
|
@ -321,7 +317,8 @@ namespace Flow.Launcher
|
|||
contextMenu.Items.Add(settings);
|
||||
contextMenu.Items.Add(exit);
|
||||
|
||||
_notifyIcon.ContextMenuStrip = new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close.
|
||||
_notifyIcon.ContextMenuStrip =
|
||||
new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close.
|
||||
_notifyIcon.MouseClick += (o, e) =>
|
||||
{
|
||||
switch (e.Button)
|
||||
|
|
@ -358,128 +355,130 @@ namespace Flow.Launcher
|
|||
_viewModel.Show();
|
||||
await Task.Delay(300); // If don't give a time, Positioning will be weird.
|
||||
var screen = SelectedScreen();
|
||||
Left = HorizonCenter(screen);
|
||||
Top = VerticalCenter(screen);
|
||||
Position = new PixelPoint(HorizonCenter(screen), VerticalCenter(screen));
|
||||
}
|
||||
|
||||
private void InitProgressbarAnimation()
|
||||
{
|
||||
var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600)));
|
||||
var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 0, new Duration(new TimeSpan(0, 0, 0, 0, 1600)));
|
||||
Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)"));
|
||||
Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)"));
|
||||
_progressBarStoryboard.Children.Add(da);
|
||||
_progressBarStoryboard.Children.Add(da1);
|
||||
_progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever;
|
||||
_viewModel.ProgressBarVisibility = Visibility.Hidden;
|
||||
isProgressBarStoryboardPaused = true;
|
||||
// var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100,
|
||||
// new Duration(new TimeSpan(0, 0, 0, 0, 1600)));
|
||||
// var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 0,
|
||||
// new Duration(new TimeSpan(0, 0, 0, 0, 1600)));
|
||||
// Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)"));
|
||||
// Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)"));
|
||||
// _progressBarStoryboard.Children.Add(da);
|
||||
// _progressBarStoryboard.Children.Add(da1);
|
||||
// _progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever;
|
||||
// _viewModel.ProgressBarVisibility = Visibility.Hidden;
|
||||
// isProgressBarStoryboardPaused = true;
|
||||
}
|
||||
|
||||
public void WindowAnimator()
|
||||
// public void WindowAnimator()
|
||||
// {
|
||||
// if (_animating)
|
||||
// return;
|
||||
//
|
||||
// _animating = true;
|
||||
// UpdatePosition();
|
||||
//
|
||||
// Storyboard windowsb = new Storyboard();
|
||||
// Storyboard clocksb = new Storyboard();
|
||||
// Storyboard iconsb = new Storyboard();
|
||||
// CircleEase easing = new CircleEase();
|
||||
// easing.EasingMode = EasingMode.EaseInOut;
|
||||
//
|
||||
// var animationLength = _settings.AnimationSpeed switch
|
||||
// {
|
||||
// AnimationSpeeds.Slow => 560,
|
||||
// AnimationSpeeds.Medium => 360,
|
||||
// AnimationSpeeds.Fast => 160,
|
||||
// _ => _settings.CustomAnimationLength
|
||||
// };
|
||||
//
|
||||
// var WindowOpacity = new DoubleAnimation
|
||||
// {
|
||||
// From = 0,
|
||||
// To = 1,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
//
|
||||
// var WindowMotion = new DoubleAnimation
|
||||
// {
|
||||
// From = Top + 10,
|
||||
// To = Top,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
// var IconMotion = new DoubleAnimation
|
||||
// {
|
||||
// From = 12,
|
||||
// To = 0,
|
||||
// EasingFunction = easing,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
//
|
||||
// var ClockOpacity = new DoubleAnimation
|
||||
// {
|
||||
// From = 0,
|
||||
// To = 1,
|
||||
// EasingFunction = easing,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
// double TargetIconOpacity = SearchIcon.Opacity; // Animation Target Opacity from Style
|
||||
// var IconOpacity = new DoubleAnimation
|
||||
// {
|
||||
// From = 0,
|
||||
// To = TargetIconOpacity,
|
||||
// EasingFunction = easing,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
//
|
||||
// double right = ClockPanel.Margin.Right;
|
||||
// var thicknessAnimation = new ThicknessAnimation
|
||||
// {
|
||||
// From = new Thickness(0, 12, right, 0),
|
||||
// To = new Thickness(0, 0, right, 0),
|
||||
// EasingFunction = easing,
|
||||
// Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
// FillBehavior = FillBehavior.Stop
|
||||
// };
|
||||
//
|
||||
// Storyboard.SetTargetProperty(ClockOpacity, new PropertyPath(OpacityProperty));
|
||||
// Storyboard.SetTargetName(thicknessAnimation, "ClockPanel");
|
||||
// Storyboard.SetTargetProperty(thicknessAnimation, new PropertyPath(MarginProperty));
|
||||
// Storyboard.SetTarget(WindowOpacity, this);
|
||||
// Storyboard.SetTargetProperty(WindowOpacity, new PropertyPath(Window.OpacityProperty));
|
||||
// Storyboard.SetTargetProperty(WindowMotion, new PropertyPath(Window.TopProperty));
|
||||
// Storyboard.SetTargetProperty(IconMotion, new PropertyPath(TopProperty));
|
||||
// Storyboard.SetTargetProperty(IconOpacity, new PropertyPath(OpacityProperty));
|
||||
//
|
||||
// clocksb.Children.Add(thicknessAnimation);
|
||||
// clocksb.Children.Add(ClockOpacity);
|
||||
// windowsb.Children.Add(WindowOpacity);
|
||||
// windowsb.Children.Add(WindowMotion);
|
||||
// iconsb.Children.Add(IconMotion);
|
||||
// iconsb.Children.Add(IconOpacity);
|
||||
//
|
||||
// windowsb.Completed += (_, _) => _animating = false;
|
||||
// _settings.WindowLeft = Left;
|
||||
// _settings.WindowTop = Top;
|
||||
//
|
||||
// if (QueryTextBox.Text.Length == 0)
|
||||
// {
|
||||
// clocksb.Begin(ClockPanel);
|
||||
// }
|
||||
//
|
||||
// iconsb.Begin(SearchIcon);
|
||||
// windowsb.Begin(FlowMainWindow);
|
||||
// }
|
||||
|
||||
private void OnMouseDown(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (_animating)
|
||||
return;
|
||||
|
||||
_animating = true;
|
||||
UpdatePosition();
|
||||
|
||||
Storyboard windowsb = new Storyboard();
|
||||
Storyboard clocksb = new Storyboard();
|
||||
Storyboard iconsb = new Storyboard();
|
||||
CircleEase easing = new CircleEase();
|
||||
easing.EasingMode = EasingMode.EaseInOut;
|
||||
|
||||
var animationLength = _settings.AnimationSpeed switch
|
||||
{
|
||||
AnimationSpeeds.Slow => 560,
|
||||
AnimationSpeeds.Medium => 360,
|
||||
AnimationSpeeds.Fast => 160,
|
||||
_ => _settings.CustomAnimationLength
|
||||
};
|
||||
|
||||
var WindowOpacity = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
|
||||
var WindowMotion = new DoubleAnimation
|
||||
{
|
||||
From = Top + 10,
|
||||
To = Top,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
var IconMotion = new DoubleAnimation
|
||||
{
|
||||
From = 12,
|
||||
To = 0,
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
|
||||
var ClockOpacity = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
double TargetIconOpacity = SearchIcon.Opacity; // Animation Target Opacity from Style
|
||||
var IconOpacity = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = TargetIconOpacity,
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
|
||||
double right = ClockPanel.Margin.Right;
|
||||
var thicknessAnimation = new ThicknessAnimation
|
||||
{
|
||||
From = new Thickness(0, 12, right, 0),
|
||||
To = new Thickness(0, 0, right, 0),
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(animationLength),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
|
||||
Storyboard.SetTargetProperty(ClockOpacity, new PropertyPath(OpacityProperty));
|
||||
Storyboard.SetTargetName(thicknessAnimation, "ClockPanel");
|
||||
Storyboard.SetTargetProperty(thicknessAnimation, new PropertyPath(MarginProperty));
|
||||
Storyboard.SetTarget(WindowOpacity, this);
|
||||
Storyboard.SetTargetProperty(WindowOpacity, new PropertyPath(Window.OpacityProperty));
|
||||
Storyboard.SetTargetProperty(WindowMotion, new PropertyPath(Window.TopProperty));
|
||||
Storyboard.SetTargetProperty(IconMotion, new PropertyPath(TopProperty));
|
||||
Storyboard.SetTargetProperty(IconOpacity, new PropertyPath(OpacityProperty));
|
||||
|
||||
clocksb.Children.Add(thicknessAnimation);
|
||||
clocksb.Children.Add(ClockOpacity);
|
||||
windowsb.Children.Add(WindowOpacity);
|
||||
windowsb.Children.Add(WindowMotion);
|
||||
iconsb.Children.Add(IconMotion);
|
||||
iconsb.Children.Add(IconOpacity);
|
||||
|
||||
windowsb.Completed += (_, _) => _animating = false;
|
||||
_settings.WindowLeft = Left;
|
||||
_settings.WindowTop = Top;
|
||||
|
||||
if (QueryTextBox.Text.Length == 0)
|
||||
{
|
||||
clocksb.Begin(ClockPanel);
|
||||
}
|
||||
iconsb.Begin(SearchIcon);
|
||||
windowsb.Begin(FlowMainWindow);
|
||||
}
|
||||
|
||||
private void OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left) DragMove();
|
||||
// if (e.Pointer.IsPrimary) DragMove();
|
||||
}
|
||||
|
||||
private void OnPreviewDragOver(object sender, DragEventArgs e)
|
||||
|
|
@ -499,8 +498,9 @@ namespace Flow.Launcher
|
|||
|
||||
private async void OnDeactivated(object sender, EventArgs e)
|
||||
{
|
||||
_settings.WindowLeft = Left;
|
||||
_settings.WindowTop = Top;
|
||||
return;
|
||||
_settings.WindowLeft = Position.X;
|
||||
_settings.WindowTop = Position.Y;
|
||||
//This condition stops extra hide call when animator is on,
|
||||
// which causes the toggling to occasional hide instead of show.
|
||||
if (_viewModel.MainWindowVisibilityStatus)
|
||||
|
|
@ -522,7 +522,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (_animating)
|
||||
return;
|
||||
InitializePosition();
|
||||
// InitializePosition();
|
||||
}
|
||||
|
||||
private void OnLocationChanged(object sender, EventArgs e)
|
||||
|
|
@ -531,8 +531,8 @@ namespace Flow.Launcher
|
|||
return;
|
||||
if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
|
||||
{
|
||||
_settings.WindowLeft = Left;
|
||||
_settings.WindowTop = Top;
|
||||
_settings.WindowLeft = Position.X;
|
||||
_settings.WindowTop = Position.Y;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +552,7 @@ namespace Flow.Launcher
|
|||
public Screen SelectedScreen()
|
||||
{
|
||||
Screen screen = null;
|
||||
switch(_settings.SearchWindowScreen)
|
||||
switch (_settings.SearchWindowScreen)
|
||||
{
|
||||
case SearchWindowScreens.Cursor:
|
||||
screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
|
|
@ -574,38 +574,39 @@ namespace Flow.Launcher
|
|||
screen = Screen.AllScreens[0];
|
||||
break;
|
||||
}
|
||||
|
||||
return screen ?? Screen.AllScreens[0];
|
||||
}
|
||||
|
||||
public double HorizonCenter(Screen screen)
|
||||
|
||||
public int HorizonCenter(Screen screen)
|
||||
{
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
|
||||
return left;
|
||||
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip2.X - Width) / 2 + dip1.X;
|
||||
return (int) left;
|
||||
}
|
||||
|
||||
public double VerticalCenter(Screen screen)
|
||||
public int VerticalCenter(Screen screen)
|
||||
{
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||
var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y;
|
||||
return top;
|
||||
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||
var top = (dip2.Y - QueryTextBox.Height) / 4 + dip1.Y;
|
||||
return (int)top;
|
||||
}
|
||||
|
||||
public double HorizonRight(Screen screen)
|
||||
public int HorizonRight(Screen screen)
|
||||
{
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip1.X + dip2.X - ActualWidth) - 10;
|
||||
return left;
|
||||
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip1.X + dip2.X - Width) - 10;
|
||||
return (int) left;
|
||||
}
|
||||
|
||||
public double HorizonLeft(Screen screen)
|
||||
public int HorizonLeft(Screen screen)
|
||||
{
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var left = dip1.X + 10;
|
||||
return left;
|
||||
return (int) left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -641,6 +642,7 @@ namespace Flow.Launcher
|
|||
_viewModel.LoadContextMenuCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Key.Left:
|
||||
if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0)
|
||||
|
|
@ -648,6 +650,7 @@ namespace Flow.Launcher
|
|||
_viewModel.EscCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Key.Back:
|
||||
if (specialKeyState.CtrlPressed)
|
||||
|
|
@ -666,10 +669,10 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -682,7 +685,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
// QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle.
|
||||
// To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority
|
||||
Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length);
|
||||
Dispatcher.UIThread.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length);
|
||||
}
|
||||
|
||||
public void InitializeColorScheme()
|
||||
|
|
@ -701,8 +704,8 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (_viewModel.QueryText != QueryTextBox.Text)
|
||||
{
|
||||
BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty);
|
||||
be.UpdateSource();
|
||||
// BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty);
|
||||
// be.UpdateSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,501 +0,0 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:Flow.Launcher.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
|
||||
Name="FlowMainWindow"
|
||||
Title="Flow Launcher"
|
||||
MinWidth="{Binding MainWindowWidth, Mode=OneWay}"
|
||||
MaxWidth="{Binding MainWindowWidth, Mode=OneWay}"
|
||||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
|
||||
AllowDrop="True"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
Closing="OnClosing"
|
||||
Deactivated="OnDeactivated"
|
||||
Icon="Images/app.png"
|
||||
Initialized="OnInitialized"
|
||||
Loaded="OnLoaded"
|
||||
LocationChanged="OnLocationChanged"
|
||||
Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
SizeToContent="Height"
|
||||
Style="{DynamicResource WindowStyle}"
|
||||
Topmost="True"
|
||||
Visibility="{Binding MainWindowVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
WindowStartupLocation="Manual"
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<converters:QuerySuggestionBoxConverter x:Key="QuerySuggestionBoxConverter" />
|
||||
<converters:BorderClipConverter x:Key="BorderClipConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:SplitterConverter x:Key="SplitterConverter" />
|
||||
<converters:BoolToIMEConversionModeConverter x:Key="BoolToIMEConversionModeConverter" />
|
||||
<converters:BoolToIMEStateConverter x:Key="BoolToIMEStateConverter" />
|
||||
<converters:StringToKeyBindingConverter x:Key="StringToKeyBindingConverter" />
|
||||
</Window.Resources>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="{Binding EscCommand}" />
|
||||
<KeyBinding Key="F5" Command="{Binding ReloadPluginDataCommand}" />
|
||||
<KeyBinding Key="Tab" Command="{Binding AutocompleteQueryCommand}" />
|
||||
<KeyBinding
|
||||
Key="Tab"
|
||||
Command="{Binding AutocompleteQueryCommand}"
|
||||
Modifiers="Shift" />
|
||||
<KeyBinding
|
||||
Key="I"
|
||||
Command="{Binding OpenSettingCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="N"
|
||||
Command="{Binding SelectNextItemCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="J"
|
||||
Command="{Binding SelectNextItemCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="D"
|
||||
Command="{Binding SelectNextPageCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="P"
|
||||
Command="{Binding SelectPrevItemCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="K"
|
||||
Command="{Binding SelectPrevItemCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="U"
|
||||
Command="{Binding SelectPrevPageCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="Home"
|
||||
Command="{Binding SelectFirstResultCommand}"
|
||||
Modifiers="Alt" />
|
||||
<KeyBinding
|
||||
Key="O"
|
||||
Command="{Binding LoadContextMenuCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="R"
|
||||
Command="{Binding ReQueryCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="H"
|
||||
Command="{Binding LoadHistoryCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="OemCloseBrackets"
|
||||
Command="{Binding IncreaseWidthCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemOpenBrackets"
|
||||
Command="{Binding DecreaseWidthCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemPlus"
|
||||
Command="{Binding IncreaseMaxResultCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemMinus"
|
||||
Command="{Binding DecreaseMaxResultCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="H"
|
||||
Command="{Binding LoadHistoryCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Ctrl+Shift" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding LoadContextMenuCommand}"
|
||||
Modifiers="Shift" />
|
||||
<KeyBinding Key="Enter" Command="{Binding OpenResultCommand}" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Alt" />
|
||||
<KeyBinding
|
||||
Key="D1"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="0"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D2"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="1"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D3"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="2"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D4"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="3"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D5"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="4"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D6"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="5"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D7"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="6"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D8"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="7"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D9"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="8"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D0"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="9"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="F12"
|
||||
Command="{Binding ToggleGameModeCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding TogglePreviewCommand}"
|
||||
Modifiers="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
</Window.InputBindings>
|
||||
<Grid>
|
||||
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid>
|
||||
<Border Style="{DynamicResource QueryBoxBgStyle}">
|
||||
<Grid>
|
||||
<TextBox
|
||||
x:Name="QueryTextSuggestionBox"
|
||||
IsEnabled="False"
|
||||
Style="{DynamicResource QuerySuggestionBoxStyle}">
|
||||
<TextBox.Text>
|
||||
<MultiBinding Converter="{StaticResource QuerySuggestionBoxConverter}">
|
||||
<Binding ElementName="QueryTextBox" Mode="OneTime" />
|
||||
<Binding ElementName="ResultListBox" Path="SelectedItem" />
|
||||
<Binding ElementName="QueryTextBox" Path="Text" />
|
||||
</MultiBinding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<TextBox
|
||||
x:Name="QueryTextBox"
|
||||
AllowDrop="True"
|
||||
InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}"
|
||||
InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}"
|
||||
PreviewDragOver="OnPreviewDragOver"
|
||||
PreviewKeyUp="QueryTextBox_KeyUp"
|
||||
Style="{DynamicResource QueryBoxStyle}"
|
||||
Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Visibility="Visible">
|
||||
<TextBox.CommandBindings>
|
||||
<CommandBinding Command="ApplicationCommands.Copy" Executed="OnCopy" />
|
||||
<CommandBinding Command="ApplicationCommands.Paste" Executed="OnPaste" />
|
||||
</TextBox.CommandBindings>
|
||||
<TextBox.ContextMenu>
|
||||
<ContextMenu MinWidth="160">
|
||||
<MenuItem Command="ApplicationCommands.Cut" Header="{DynamicResource cut}">
|
||||
<MenuItem.Icon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="ApplicationCommands.Copy" Header="{DynamicResource copy}">
|
||||
<MenuItem.Icon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="ApplicationCommands.Paste" Header="{DynamicResource paste}">
|
||||
<MenuItem.Icon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator
|
||||
Margin="0"
|
||||
Padding="0,4,0,4"
|
||||
Background="{DynamicResource ContextSeparator}" />
|
||||
<MenuItem Click="OnContextMenusForSettingsClick" Header="{DynamicResource flowlauncher_settings}">
|
||||
<MenuItem.Icon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="{Binding EscCommand}" Header="{DynamicResource closeWindow}">
|
||||
<MenuItem.Icon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</TextBox.ContextMenu>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel
|
||||
x:Name="ClockPanel"
|
||||
IsHitTestVisible="False"
|
||||
Style="{DynamicResource ClockPanel}">
|
||||
<TextBlock
|
||||
x:Name="ClockBox"
|
||||
Style="{DynamicResource ClockBox}"
|
||||
Text="{Binding ClockText}"
|
||||
Visibility="{Binding Settings.UseClock, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="DateBox"
|
||||
Style="{DynamicResource DateBox}"
|
||||
Text="{Binding DateText}"
|
||||
Visibility="{Binding Settings.UseDate, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</StackPanel>
|
||||
<Border>
|
||||
<Grid>
|
||||
<Image
|
||||
x:Name="PluginActivationIcon"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Panel.ZIndex="2"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="{Binding PluginIconPath}"
|
||||
Stretch="Uniform"
|
||||
Style="{DynamicResource PluginActivationIcon}" />
|
||||
<Canvas Style="{DynamicResource SearchIconPosition}">
|
||||
<Path
|
||||
Name="SearchIcon"
|
||||
Margin="0"
|
||||
Data="{DynamicResource SearchIconImg}"
|
||||
Stretch="Fill"
|
||||
Style="{DynamicResource SearchIconStyle}"
|
||||
Visibility="{Binding SearchIconVisibility}" />
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Line
|
||||
x:Name="ProgressBar"
|
||||
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth}"
|
||||
Height="2"
|
||||
Margin="12,0,12,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
StrokeThickness="2"
|
||||
Style="{DynamicResource PendingLineStyle}"
|
||||
Visibility="{Binding ProgressBarVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
X1="-100"
|
||||
X2="0"
|
||||
Y1="0"
|
||||
Y2="0" />
|
||||
</Grid>
|
||||
|
||||
<Grid ClipToBounds="True">
|
||||
<ContentControl>
|
||||
<ContentControl.Style>
|
||||
<Style TargetType="ContentControl">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ElementName=ResultListBox, Path=Visibility}" Value="Visible">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Visible">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ElementName=History, Path=Visibility}" Value="Visible">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ContentControl.Style>
|
||||
<Rectangle
|
||||
Name="MiddleSeparator"
|
||||
Width="Auto"
|
||||
HorizontalAlignment="Stretch"
|
||||
Style="{DynamicResource SeparatorStyle}" />
|
||||
</ContentControl>
|
||||
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="100" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="0.85*" MinWidth="244" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
x:Name="ResultArea"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="{Binding ResultAreaColumn}">
|
||||
<Border Style="{DynamicResource WindowRadius}">
|
||||
<Border.Clip>
|
||||
<MultiBinding Converter="{StaticResource BorderClipConverter}">
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
|
||||
</MultiBinding>
|
||||
</Border.Clip>
|
||||
<ContentControl>
|
||||
<flowlauncher:ResultListBox
|
||||
x:Name="ResultListBox"
|
||||
DataContext="{Binding Results}"
|
||||
LeftClickResultCommand="{Binding LeftClickResultCommand}"
|
||||
RightClickResultCommand="{Binding RightClickResultCommand}" />
|
||||
</ContentControl>
|
||||
</Border>
|
||||
<Border Style="{DynamicResource WindowRadius}">
|
||||
<Border.Clip>
|
||||
<MultiBinding Converter="{StaticResource BorderClipConverter}">
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
|
||||
</MultiBinding>
|
||||
</Border.Clip>
|
||||
<ContentControl>
|
||||
<flowlauncher:ResultListBox
|
||||
x:Name="ContextMenu"
|
||||
DataContext="{Binding ContextMenu}"
|
||||
LeftClickResultCommand="{Binding LeftClickResultCommand}"
|
||||
RightClickResultCommand="{Binding RightClickResultCommand}" />
|
||||
</ContentControl>
|
||||
</Border>
|
||||
<Border Style="{DynamicResource WindowRadius}">
|
||||
<Border.Clip>
|
||||
<MultiBinding Converter="{StaticResource BorderClipConverter}">
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
|
||||
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
|
||||
</MultiBinding>
|
||||
</Border.Clip>
|
||||
<ContentControl>
|
||||
<flowlauncher:ResultListBox
|
||||
x:Name="History"
|
||||
DataContext="{Binding History}"
|
||||
LeftClickResultCommand="{Binding LeftClickResultCommand}"
|
||||
RightClickResultCommand="{Binding RightClickResultCommand}" />
|
||||
</ContentControl>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<GridSplitter
|
||||
Grid.Column="1"
|
||||
Width="{Binding PreviewVisible, Converter={StaticResource SplitterConverter}}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
ShowsPreview="True" />
|
||||
<Grid
|
||||
x:Name="Preview"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Stretch"
|
||||
Style="{DynamicResource PreviewArea}"
|
||||
Visibility="{Binding PreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Border
|
||||
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
||||
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
|
||||
Style="{DynamicResource PreviewBorderStyle}"
|
||||
Visibility="{Binding ShowDefaultPreview}">
|
||||
<Grid
|
||||
Margin="20,0,10,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
x:Name="PreviewGlyphIcon"
|
||||
Grid.Row="0"
|
||||
Height="Auto"
|
||||
Margin="0,16,0,0"
|
||||
FontFamily="{Binding Glyph.FontFamily}"
|
||||
Style="{DynamicResource PreviewGlyph}"
|
||||
Text="{Binding Glyph.Glyph}"
|
||||
Visibility="{Binding ShowGlyph}" />
|
||||
<Image
|
||||
x:Name="PreviewImageIcon"
|
||||
Grid.Row="0"
|
||||
MaxHeight="320"
|
||||
Margin="0,16,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Source="{Binding PreviewImage}"
|
||||
StretchDirection="DownOnly"
|
||||
Visibility="{Binding ShowPreviewImage}">
|
||||
<Image.Style>
|
||||
<Style TargetType="{x:Type Image}">
|
||||
<Setter Property="MaxWidth" Value="96" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding UseBigThumbnail}" Value="True">
|
||||
<Setter Property="MaxWidth" Value="{Binding ElementName=Preview, Path=ActualWidth}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
<TextBlock
|
||||
x:Name="PreviewTitle"
|
||||
Grid.Row="1"
|
||||
Margin="0,6,0,16"
|
||||
HorizontalAlignment="Stretch"
|
||||
Style="{DynamicResource PreviewItemTitleStyle}"
|
||||
Text="{Binding Result.Title}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="1">
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="{x:Type StackPanel}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ElementName=PreviewSubTitle, UpdateSourceTrigger=PropertyChanged, Path=Text.Length}" Value="0">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<Separator Style="{DynamicResource PreviewSep}" />
|
||||
<TextBlock
|
||||
x:Name="PreviewSubTitle"
|
||||
Style="{DynamicResource PreviewItemSubTitleStyle}"
|
||||
Text="{Binding Result.SubTitle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border
|
||||
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
||||
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
|
||||
Style="{DynamicResource PreviewBorderStyle}"
|
||||
Visibility="{Binding ShowCustomizedPreview}">
|
||||
<ContentControl Content="{Binding Result.PreviewPanel.Value}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
@ -25,6 +25,7 @@ using Flow.Launcher.Infrastructure.Storage;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Specialized;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
|
|
@ -102,7 +103,7 @@ namespace Flow.Launcher
|
|||
|
||||
public void OpenSettingDialog()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
SettingWindow sw = SingletonWindowOpener.Open<SettingWindow>(this, _settingsVM);
|
||||
});
|
||||
|
|
@ -147,9 +148,9 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;
|
||||
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = true;
|
||||
|
||||
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = false;
|
||||
|
||||
public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key);
|
||||
|
||||
|
|
|
|||
191
Flow.Launcher/ResultListBox.axaml
Normal file
191
Flow.Launcher/ResultListBox.axaml
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
<UserControl
|
||||
x:Class="Flow.Launcher.ResultListBox"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converter="using:Flow.Launcher.Converters"
|
||||
xmlns:vm="using:Flow.Launcher.ViewModel"
|
||||
x:DataType="vm:ResultsViewModel">
|
||||
<ListBox
|
||||
HorizontalAlignment="Stretch"
|
||||
Focusable="False"
|
||||
ItemsSource="{Binding Results}"
|
||||
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
SelectionMode="AlwaysSelected"
|
||||
AutoScrollToSelectedItem="True"
|
||||
IsVisible="{Binding Visibility}"
|
||||
MaxHeight="{Binding MaxHeight}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button HorizontalAlignment="Stretch" Height="52">
|
||||
<Button.Template>
|
||||
<ControlTemplate>
|
||||
<ContentPresenter Content="{TemplateBinding Button.Content}" />
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
<Button.Content>
|
||||
<Grid
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Cursor="Hand"
|
||||
UseLayoutRounding="False">
|
||||
<Grid.Resources>
|
||||
<converter:HighlightTextConverter x:Key="HighlightTextConverter" />
|
||||
<converter:OrdinalConverter x:Key="OrdinalConverter" />
|
||||
<converter:OpenResultHotkeyVisibilityConverter
|
||||
x:Key="OpenResultHotkeyVisibilityConverter" />
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="9*" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="8" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
x:Name="HotkeyArea"
|
||||
Grid.Column="2"
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowOpenResultHotkey}">
|
||||
<TextBlock
|
||||
x:Name="Hotkey"
|
||||
Margin="12,0,12,0"
|
||||
Padding="0,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Text>
|
||||
<!-- <MultiBinding StringFormat="{}{0}+{1}"> -->
|
||||
<!-- <Binding Path="OpenResultModifiers" /> -->
|
||||
<!-- <Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" -->
|
||||
<!-- RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" /> -->
|
||||
<!-- </MultiBinding> -->
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
x:Name="Bullet"
|
||||
Grid.Column="0" />
|
||||
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Margin="9,0,0,0"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="1">
|
||||
<Image
|
||||
x:Name="ImageIcon"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Source="{Binding Image, TargetNullValue={x:Null}}"
|
||||
Stretch="Uniform"
|
||||
IsVisible="{Binding ShowIcon}">
|
||||
</Image>
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Margin="9,0,0,0"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0">
|
||||
<TextBlock
|
||||
x:Name="GlyphIcon"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{Binding Glyph.FontFamily}"
|
||||
Text="{Binding Glyph.Glyph}"
|
||||
IsVisible="{Binding ShowGlyph}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Margin="6,0,10,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ProgressBar
|
||||
x:Name="progressbarResult"
|
||||
Grid.Row="0"
|
||||
Foreground="{Binding Result.ProgressBarColor}"
|
||||
Value="{Binding ResultProgress, Mode=OneWay}">
|
||||
</ProgressBar>
|
||||
<TextBlock
|
||||
x:Name="Title"
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
IsEnabled="False"
|
||||
Text="{Binding Result.Title}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip.Tip="{Binding ShowTitleToolTip}">
|
||||
<!-- <vm:ResultsViewModel.FormattedText> -->
|
||||
<!-- <MultiBinding Converter="{StaticResource HighlightTextConverter}"> -->
|
||||
<!-- <Binding Path="Result.Title" /> -->
|
||||
<!-- <Binding Path="Result.TitleHighlightData" /> -->
|
||||
<!-- </MultiBinding> -->
|
||||
<!-- </vm:ResultsViewModel.FormattedText> -->
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
x:Name="SubTitle"
|
||||
Grid.Row="1"
|
||||
IsEnabled="False"
|
||||
Text="{Binding Result.SubTitle}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip.Tip="{Binding ShowSubTitleToolTip}" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<!-- http://stackoverflow.com/questions/16819577/setting-background-color-or-wpf-4-0-listbox-windows-8/#16820062 -->
|
||||
<!-- <ListBox.ItemContainerStyle> -->
|
||||
<!-- <Style TargetType="{x:Type ListBoxItem}"> -->
|
||||
<!-- <EventSetter Event="MouseEnter" Handler="OnMouseEnter" /> -->
|
||||
<!-- <EventSetter Event="MouseMove" Handler="OnMouseMove" /> -->
|
||||
<!-- <Setter Property="Height" Value="{DynamicResource ResultItemHeight}" /> -->
|
||||
<!-- <Setter Property="Margin" Value="0" /> -->
|
||||
<!-- <Setter Property="Padding" Value="0" /> -->
|
||||
<!-- <Setter Property="BorderThickness" Value="0" /> -->
|
||||
<!-- <Setter Property="Template"> -->
|
||||
<!-- <Setter.Value> -->
|
||||
<!-- <ControlTemplate TargetType="{x:Type ListBoxItem}"> -->
|
||||
<!-- <Border -->
|
||||
<!-- x:Name="Bd" -->
|
||||
<!-- Margin="{DynamicResource ItemMargin}" -->
|
||||
<!-- Background="{TemplateBinding Background}" -->
|
||||
<!-- BorderBrush="{TemplateBinding BorderBrush}" -->
|
||||
<!-- CornerRadius="{DynamicResource ItemRadius}" -->
|
||||
<!-- SnapsToDevicePixels="True"> -->
|
||||
<!-- <ContentPresenter -->
|
||||
<!-- HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" -->
|
||||
<!-- VerticalAlignment="{TemplateBinding VerticalContentAlignment}" -->
|
||||
<!-- Content="{TemplateBinding Content}" -->
|
||||
<!-- ContentStringFormat="{TemplateBinding ContentStringFormat}" -->
|
||||
<!-- ContentTemplate="{TemplateBinding ContentTemplate}" -->
|
||||
<!-- SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> -->
|
||||
<!-- </Border> -->
|
||||
<!-- <ControlTemplate.Triggers> -->
|
||||
<!-- <Trigger Property="IsSelected" Value="True"> -->
|
||||
<!-- <Setter TargetName="Bd" Property="Background" Value="{DynamicResource ItemSelectedBackgroundColor}" /> -->
|
||||
<!-- <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource ItemSelectedBackgroundColor}" /> -->
|
||||
<!-- </Trigger> -->
|
||||
<!-- </ControlTemplate.Triggers> -->
|
||||
<!-- </ControlTemplate> -->
|
||||
<!-- </Setter.Value> -->
|
||||
<!-- </Setter> -->
|
||||
<!-- </Style> -->
|
||||
<!-- </ListBox.ItemContainerStyle> -->
|
||||
</ListBox>
|
||||
|
||||
</UserControl>
|
||||
162
Flow.Launcher/ResultListBox.axaml.cs
Normal file
162
Flow.Launcher/ResultListBox.axaml.cs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using PropertyChanged;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
[DoNotNotify]
|
||||
public partial class ResultListBox : UserControl
|
||||
{
|
||||
protected object _lock = new object();
|
||||
private PixelPoint _lastpos;
|
||||
private ListBoxItem curItem = null;
|
||||
public ResultListBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
//
|
||||
// public static readonly DependencyProperty RightClickResultCommandProperty =
|
||||
// DependencyProperty.Register("RightClickResultCommand", typeof(ICommand), typeof(ResultListBox), new UIPropertyMetadata(null));
|
||||
//
|
||||
// public ICommand RightClickResultCommand
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// return (ICommand)GetValue(RightClickResultCommandProperty);
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// SetValue(RightClickResultCommandProperty, value);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static readonly DependencyProperty LeftClickResultCommandProperty =
|
||||
// DependencyProperty.Register("LeftClickResultCommand", typeof(ICommand), typeof(ResultListBox), new UIPropertyMetadata(null));
|
||||
//
|
||||
// public ICommand LeftClickResultCommand
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// return (ICommand)GetValue(LeftClickResultCommandProperty);
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// SetValue(LeftClickResultCommandProperty, value);
|
||||
// }
|
||||
// }
|
||||
|
||||
//
|
||||
// private void OnMouseEnter(object sender, MouseEventArgs e)
|
||||
// {
|
||||
// lock (_lock)
|
||||
// {
|
||||
// curItem = (ListBoxItem)sender;
|
||||
// var p = e.GetPosition((IInputElement)sender);
|
||||
// _lastpos = p;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
// {
|
||||
// lock (_lock)
|
||||
// {
|
||||
// var p = e.GetPosition((IInputElement)sender);
|
||||
// if (_lastpos != p)
|
||||
// {
|
||||
// ((ListBoxItem)sender).IsSelected = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private void ListBox_PreviewMouseDown(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (curItem != null)
|
||||
{
|
||||
curItem.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Point start;
|
||||
private string path;
|
||||
private string query;
|
||||
// this method is called by the UI thread, which is single threaded, so we can be sloppy with locking
|
||||
private bool isDragging;
|
||||
//
|
||||
// private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
// {
|
||||
// if (Mouse.DirectlyOver is not FrameworkElement
|
||||
// {
|
||||
// DataContext: ResultViewModel
|
||||
// {
|
||||
// Result:
|
||||
// {
|
||||
// CopyText: { } copyText,
|
||||
// OriginQuery.RawQuery: { } rawQuery
|
||||
// }
|
||||
// }
|
||||
// }) return;
|
||||
//
|
||||
// path = copyText;
|
||||
// query = rawQuery;
|
||||
// start = e.GetPosition(null);
|
||||
// isDragging = true;
|
||||
// }
|
||||
// private void ResultList_MouseMove(object sender, MouseEventArgs e)
|
||||
// {
|
||||
// if (e.LeftButton != MouseButtonState.Pressed || !isDragging)
|
||||
// {
|
||||
// start = default;
|
||||
// path = string.Empty;
|
||||
// query = string.Empty;
|
||||
// isDragging = false;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (!File.Exists(path) && !Directory.Exists(path))
|
||||
// return;
|
||||
//
|
||||
// Point mousePosition = e.GetPosition(null);
|
||||
// Vector diff = this.start - mousePosition;
|
||||
//
|
||||
// if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
|
||||
// || Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
|
||||
// return;
|
||||
//
|
||||
// isDragging = false;
|
||||
//
|
||||
// var data = new DataObject(DataFormats.FileDrop, new[]
|
||||
// {
|
||||
// path
|
||||
// });
|
||||
//
|
||||
// // Reassigning query to a new variable because for some reason
|
||||
// // after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string
|
||||
// var rawQuery = query;
|
||||
// var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
|
||||
// if (effect == DragDropEffects.Move)
|
||||
// App.API.ChangeQuery(rawQuery, true);
|
||||
// }
|
||||
// private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
// {
|
||||
// if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
|
||||
// return;
|
||||
//
|
||||
// RightClickResultCommand?.Execute(result.Result);
|
||||
// }
|
||||
// private void ResultListBox_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
|
||||
// {
|
||||
// if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
|
||||
// return;
|
||||
//
|
||||
// LeftClickResultCommand?.Execute(null);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
<ListBox
|
||||
x:Class="Flow.Launcher.ResultListBox"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converter="clr-namespace:Flow.Launcher.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
|
||||
MaxHeight="{Binding MaxHeight}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
d:DataContext="{d:DesignInstance vm:ResultsViewModel}"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="100"
|
||||
Focusable="False"
|
||||
IsSynchronizedWithCurrentItem="True"
|
||||
ItemsSource="{Binding Results}"
|
||||
KeyboardNavigation.DirectionalNavigation="Cycle"
|
||||
PreviewMouseDown="ListBox_PreviewMouseDown"
|
||||
PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown"
|
||||
PreviewMouseLeftButtonUp="ResultListBox_OnPreviewMouseUp"
|
||||
PreviewMouseMove="ResultList_MouseMove"
|
||||
PreviewMouseRightButtonDown="ResultListBox_OnPreviewMouseRightButtonDown"
|
||||
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
SelectionMode="Single"
|
||||
Style="{DynamicResource BaseListboxStyle}"
|
||||
VirtualizingStackPanel.IsVirtualizing="True"
|
||||
VirtualizingStackPanel.VirtualizationMode="Standard"
|
||||
Visibility="{Binding Visibility}"
|
||||
mc:Ignorable="d">
|
||||
<!-- IsSynchronizedWithCurrentItem: http://stackoverflow.com/a/7833798/2833083 -->
|
||||
|
||||
<ListBox.Resources>
|
||||
<converter:IconRadiusConverter x:Key="IconRadiusConverter" />
|
||||
<converter:DiameterToCenterPointConverter x:Key="DiameterToCenterPointConverter" />
|
||||
</ListBox.Resources>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button HorizontalAlignment="Stretch">
|
||||
<Button.Template>
|
||||
<ControlTemplate>
|
||||
<ContentPresenter Content="{TemplateBinding Button.Content}" />
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
<Button.Content>
|
||||
<Grid
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Cursor="Hand"
|
||||
UseLayoutRounding="False">
|
||||
<Grid.Resources>
|
||||
<converter:HighlightTextConverter x:Key="HighlightTextConverter" />
|
||||
<converter:OrdinalConverter x:Key="OrdinalConverter" />
|
||||
<converter:OpenResultHotkeyVisibilityConverter x:Key="OpenResultHotkeyVisibilityConverter" />
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Style="{DynamicResource ImageAreaWidth}" />
|
||||
<ColumnDefinition Width="9*" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="8" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
x:Name="HotkeyArea"
|
||||
Grid.Column="2"
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding ShowOpenResultHotkey}">
|
||||
<TextBlock
|
||||
x:Name="Hotkey"
|
||||
Margin="12,0,12,0"
|
||||
Padding="0,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Style="{DynamicResource ItemHotkeyStyle}">
|
||||
<TextBlock.Visibility>
|
||||
<Binding Converter="{StaticResource ResourceKey=OpenResultHotkeyVisibilityConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
||||
</TextBlock.Visibility>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}{0}+{1}">
|
||||
<Binding Path="OpenResultModifiers" />
|
||||
<Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
x:Name="Bullet"
|
||||
Grid.Column="0"
|
||||
Style="{DynamicResource BulletStyle}" />
|
||||
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Margin="9,0,0,0"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="1">
|
||||
<Image
|
||||
x:Name="ImageIcon"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
RenderOptions.BitmapScalingMode="Fant"
|
||||
Source="{Binding Image, TargetNullValue={x:Null}}"
|
||||
Stretch="Uniform"
|
||||
Style="{DynamicResource ImageIconStyle}"
|
||||
Visibility="{Binding ShowIcon}">
|
||||
<Image.Clip>
|
||||
<EllipseGeometry Center="{Binding ElementName=ImageIcon, Path=ActualWidth, Converter={StaticResource DiameterToCenterPointConverter}}">
|
||||
<EllipseGeometry.RadiusX>
|
||||
<MultiBinding Converter="{StaticResource IconRadiusConverter}">
|
||||
<Binding ElementName="ImageIcon" Path="ActualWidth" />
|
||||
<Binding Path="Result.RoundedIcon" />
|
||||
</MultiBinding>
|
||||
</EllipseGeometry.RadiusX>
|
||||
<EllipseGeometry.RadiusY>
|
||||
<MultiBinding Converter="{StaticResource IconRadiusConverter}">
|
||||
<Binding ElementName="ImageIcon" Path="ActualWidth" />
|
||||
<Binding Path="Result.RoundedIcon" />
|
||||
</MultiBinding>
|
||||
</EllipseGeometry.RadiusY>
|
||||
</EllipseGeometry>
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Margin="9,0,0,0"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0">
|
||||
<TextBlock
|
||||
x:Name="GlyphIcon"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{Binding Glyph.FontFamily}"
|
||||
Style="{DynamicResource ItemGlyph}"
|
||||
Text="{Binding Glyph.Glyph}"
|
||||
Visibility="{Binding ShowGlyph}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Margin="6,0,10,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition x:Name="SubTitleRowDefinition" Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ProgressBar
|
||||
x:Name="progressbarResult"
|
||||
Grid.Row="0"
|
||||
Foreground="{Binding Result.ProgressBarColor}"
|
||||
Value="{Binding ResultProgress, Mode=OneWay}">
|
||||
<ProgressBar.Style>
|
||||
<Style BasedOn="{StaticResource ProgressBarResult}" TargetType="ProgressBar">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Result.ProgressBar}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ProgressBar.Style>
|
||||
</ProgressBar>
|
||||
<TextBlock
|
||||
x:Name="Title"
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
IsEnabled="False"
|
||||
Style="{DynamicResource ItemTitleStyle}"
|
||||
Text="{Binding Result.Title}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding ShowTitleToolTip}"
|
||||
ToolTipService.ShowOnDisabled="True">
|
||||
<vm:ResultsViewModel.FormattedText>
|
||||
<MultiBinding Converter="{StaticResource HighlightTextConverter}">
|
||||
<Binding Path="Result.Title" />
|
||||
<Binding Path="Result.TitleHighlightData" />
|
||||
</MultiBinding>
|
||||
</vm:ResultsViewModel.FormattedText>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
x:Name="SubTitle"
|
||||
Grid.Row="1"
|
||||
IsEnabled="False"
|
||||
Style="{DynamicResource ItemSubTitleStyle}"
|
||||
Text="{Binding Result.SubTitle}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding ShowSubTitleToolTip}"
|
||||
ToolTipService.ShowOnDisabled="True" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<!-- a result item height is 52 including margin -->
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
|
||||
<Setter TargetName="Bullet" Property="Style" Value="{DynamicResource ItemBulletSelectedStyle}" />
|
||||
<Setter TargetName="Title" Property="Style" Value="{DynamicResource ItemTitleSelectedStyle}" />
|
||||
<Setter TargetName="SubTitle" Property="Style" Value="{DynamicResource ItemSubTitleSelectedStyle}" />
|
||||
<Setter TargetName="Hotkey" Property="Style" Value="{DynamicResource ItemHotkeySelectedStyle}" />
|
||||
<Setter TargetName="GlyphIcon" Property="Style" Value="{DynamicResource ItemGlyphSelectedStyle}" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<!-- http://stackoverflow.com/questions/16819577/setting-background-color-or-wpf-4-0-listbox-windows-8/#16820062 -->
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListBoxItem}">
|
||||
<EventSetter Event="MouseEnter" Handler="OnMouseEnter" />
|
||||
<EventSetter Event="MouseMove" Handler="OnMouseMove" />
|
||||
<Setter Property="Height" Value="{DynamicResource ResultItemHeight}" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
||||
<Border
|
||||
x:Name="Bd"
|
||||
Margin="{DynamicResource ItemMargin}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
CornerRadius="{DynamicResource ItemRadius}"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentStringFormat="{TemplateBinding ContentStringFormat}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ItemSelectedBackgroundColor}" />
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource ItemSelectedBackgroundColor}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
</ListBox>
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class ResultListBox
|
||||
{
|
||||
protected object _lock = new object();
|
||||
private Point _lastpos;
|
||||
private ListBoxItem curItem = null;
|
||||
public ResultListBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RightClickResultCommandProperty =
|
||||
DependencyProperty.Register("RightClickResultCommand", typeof(ICommand), typeof(ResultListBox), new UIPropertyMetadata(null));
|
||||
|
||||
public ICommand RightClickResultCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ICommand)GetValue(RightClickResultCommandProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(RightClickResultCommandProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LeftClickResultCommandProperty =
|
||||
DependencyProperty.Register("LeftClickResultCommand", typeof(ICommand), typeof(ResultListBox), new UIPropertyMetadata(null));
|
||||
|
||||
public ICommand LeftClickResultCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ICommand)GetValue(LeftClickResultCommandProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(LeftClickResultCommandProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.AddedItems.Count > 0 && e.AddedItems[0] != null)
|
||||
{
|
||||
ScrollIntoView(e.AddedItems[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
curItem = (ListBoxItem)sender;
|
||||
var p = e.GetPosition((IInputElement)sender);
|
||||
_lastpos = p;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var p = e.GetPosition((IInputElement)sender);
|
||||
if (_lastpos != p)
|
||||
{
|
||||
((ListBoxItem)sender).IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (curItem != null)
|
||||
{
|
||||
curItem.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Point start;
|
||||
private string path;
|
||||
private string query;
|
||||
// this method is called by the UI thread, which is single threaded, so we can be sloppy with locking
|
||||
private bool isDragging;
|
||||
|
||||
private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (Mouse.DirectlyOver is not FrameworkElement
|
||||
{
|
||||
DataContext: ResultViewModel
|
||||
{
|
||||
Result:
|
||||
{
|
||||
CopyText: { } copyText,
|
||||
OriginQuery.RawQuery: { } rawQuery
|
||||
}
|
||||
}
|
||||
}) return;
|
||||
|
||||
path = copyText;
|
||||
query = rawQuery;
|
||||
start = e.GetPosition(null);
|
||||
isDragging = true;
|
||||
}
|
||||
private void ResultList_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.LeftButton != MouseButtonState.Pressed || !isDragging)
|
||||
{
|
||||
start = default;
|
||||
path = string.Empty;
|
||||
query = string.Empty;
|
||||
isDragging = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(path) && !Directory.Exists(path))
|
||||
return;
|
||||
|
||||
Point mousePosition = e.GetPosition(null);
|
||||
Vector diff = this.start - mousePosition;
|
||||
|
||||
if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
|
||||
|| Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
|
||||
return;
|
||||
|
||||
isDragging = false;
|
||||
|
||||
var data = new DataObject(DataFormats.FileDrop, new[]
|
||||
{
|
||||
path
|
||||
});
|
||||
|
||||
// Reassigning query to a new variable because for some reason
|
||||
// after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string
|
||||
var rawQuery = query;
|
||||
var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
|
||||
if (effect == DragDropEffects.Move)
|
||||
App.API.ChangeQuery(rawQuery, true);
|
||||
}
|
||||
private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
|
||||
return;
|
||||
|
||||
RightClickResultCommand?.Execute(result.Result);
|
||||
}
|
||||
private void ResultListBox_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
|
||||
return;
|
||||
|
||||
LeftClickResultCommand?.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1975,10 +1975,10 @@
|
|||
</Border>
|
||||
|
||||
<ContentControl Grid.Row="2">
|
||||
<flowlauncher:ResultListBox
|
||||
DataContext="{Binding PreviewResults, Mode=OneTime}"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="Visible" />
|
||||
<!-- <flowlauncher:ResultListBox -->
|
||||
<!-- DataContext="{Binding PreviewResults, Mode=OneTime}" -->
|
||||
<!-- IsHitTestVisible="False" -->
|
||||
<!-- IsVisible="True" /> -->
|
||||
</ContentControl>
|
||||
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ using CommunityToolkit.Mvvm.Input;
|
|||
using System.Globalization;
|
||||
using System.Windows.Input;
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
|
|
@ -93,7 +94,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand
|
||||
};
|
||||
Results = new ResultsViewModel(Settings)
|
||||
ResultsVM = new ResultsViewModel(Settings)
|
||||
{
|
||||
LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand
|
||||
};
|
||||
|
|
@ -101,13 +102,13 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand
|
||||
};
|
||||
_selectedResults = Results;
|
||||
_selectedResults = ResultsVM;
|
||||
|
||||
Results.PropertyChanged += (_, args) =>
|
||||
ResultsVM.PropertyChanged += (_, args) =>
|
||||
{
|
||||
switch (args.PropertyName)
|
||||
{
|
||||
case nameof(Results.SelectedItem):
|
||||
case nameof(ResultsVM.SelectedItem):
|
||||
UpdatePreview();
|
||||
break;
|
||||
}
|
||||
|
|
@ -174,7 +175,8 @@ namespace Flow.Launcher.ViewModel
|
|||
var token = e.Token == default ? _updateToken : e.Token;
|
||||
|
||||
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
|
||||
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token)))
|
||||
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query,
|
||||
token)))
|
||||
{
|
||||
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
|
||||
}
|
||||
|
|
@ -188,7 +190,8 @@ namespace Flow.Launcher.ViewModel
|
|||
Hide();
|
||||
|
||||
await PluginManager.ReloadDataAsync().ConfigureAwait(false);
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"),
|
||||
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -201,7 +204,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
else
|
||||
{
|
||||
SelectedResults = Results;
|
||||
SelectedResults = ResultsVM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +229,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
else
|
||||
{
|
||||
SelectedResults = Results;
|
||||
SelectedResults = ResultsVM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,11 +281,13 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
results.SelectedIndex = int.Parse(index);
|
||||
}
|
||||
|
||||
var result = results.SelectedItem?.Result;
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hideWindow = await result.ExecuteAsync(new ActionContext
|
||||
{
|
||||
// not null means pressing modifier key + number, should ignore the modifier key
|
||||
|
|
@ -350,7 +355,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (!SelectedIsFromQueryResults())
|
||||
{
|
||||
SelectedResults = Results;
|
||||
SelectedResults = ResultsVM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -386,7 +391,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public ResultsViewModel Results { get; private set; }
|
||||
public ResultsViewModel ResultsVM { get; private set; }
|
||||
|
||||
public ResultsViewModel ContextMenu { get; private set; }
|
||||
|
||||
|
|
@ -395,6 +400,7 @@ namespace Flow.Launcher.ViewModel
|
|||
public bool GameModeStatus { get; set; } = false;
|
||||
|
||||
private string _queryText;
|
||||
|
||||
public string QueryText
|
||||
{
|
||||
get => _queryText;
|
||||
|
|
@ -418,6 +424,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.WindowSize += 100;
|
||||
Settings.WindowLeft -= 50;
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
|
||||
|
|
@ -433,6 +440,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.WindowLeft += 50;
|
||||
Settings.WindowSize -= 100;
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
|
||||
|
|
@ -471,7 +479,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
ResultAreaColumn = 1;
|
||||
PreviewVisible = true;
|
||||
Results.SelectedItem?.LoadPreviewImage();
|
||||
ResultsVM.SelectedItem?.LoadPreviewImage();
|
||||
}
|
||||
|
||||
private void HidePreview()
|
||||
|
|
@ -496,7 +504,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (PreviewVisible)
|
||||
{
|
||||
Results.SelectedItem?.LoadPreviewImage();
|
||||
ResultsVM.SelectedItem?.LoadPreviewImage();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,22 +516,21 @@ namespace Flow.Launcher.ViewModel
|
|||
/// <param name="isReQuery">Force query even when Query Text doesn't change</param>
|
||||
public void ChangeQueryText(string queryText, bool isReQuery = false)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (QueryText != queryText)
|
||||
{
|
||||
|
||||
// re-query is done in QueryText's setter method
|
||||
QueryText = queryText;
|
||||
// set to false so the subsequent set true triggers
|
||||
// PropertyChanged and MoveQueryTextToEnd is called
|
||||
QueryTextCursorMovedToEnd = false;
|
||||
|
||||
}
|
||||
else if (isReQuery)
|
||||
{
|
||||
Query(isReQuery: true);
|
||||
}
|
||||
|
||||
QueryTextCursorMovedToEnd = true;
|
||||
});
|
||||
}
|
||||
|
|
@ -543,13 +550,13 @@ namespace Flow.Launcher.ViewModel
|
|||
_selectedResults = value;
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
ContextMenu.Visibility = Visibility.Collapsed;
|
||||
History.Visibility = Visibility.Collapsed;
|
||||
ContextMenu.Visibility = false;
|
||||
History.Visibility = false;
|
||||
ChangeQueryText(_queryTextBeforeLeaveResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
Results.Visibility = Visibility.Collapsed;
|
||||
ResultsVM.Visibility = false;
|
||||
_queryTextBeforeLeaveResults = QueryText;
|
||||
|
||||
|
||||
|
|
@ -567,12 +574,12 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
_selectedResults.Visibility = Visibility.Visible;
|
||||
_selectedResults.Visibility = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility ProgressBarVisibility { get; set; }
|
||||
public Visibility MainWindowVisibility { get; set; }
|
||||
public bool ProgressBarVisibility { get; set; }
|
||||
public bool MainWindowVisibility { get; set; }
|
||||
public double MainWindowOpacity { get; set; } = 1;
|
||||
|
||||
// This is to be used for determining the visibility status of the mainwindow instead of MainWindowVisibility
|
||||
|
|
@ -581,7 +588,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public event VisibilityChangedEventHandler VisibilityChanged;
|
||||
|
||||
public Visibility SearchIconVisibility { get; set; }
|
||||
public bool SearchIconVisibility { get; set; }
|
||||
|
||||
public double MainWindowWidth
|
||||
{
|
||||
|
|
@ -593,8 +600,8 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public string OpenResultCommandModifiers => Settings.OpenResultModifiers;
|
||||
|
||||
public string PreviewHotkey
|
||||
{
|
||||
public string PreviewHotkey
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO try to patch issue #1755
|
||||
|
|
@ -608,6 +615,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
Settings.PreviewHotkey = "F1";
|
||||
}
|
||||
|
||||
return Settings.PreviewHotkey;
|
||||
}
|
||||
}
|
||||
|
|
@ -646,7 +654,7 @@ namespace Flow.Launcher.ViewModel
|
|||
var query = QueryText.ToLower().Trim();
|
||||
ContextMenu.Clear();
|
||||
|
||||
var selected = Results.SelectedItem?.Result;
|
||||
var selected = ResultsVM.SelectedItem?.Result;
|
||||
|
||||
if (selected != null) // SelectedItem returns null if selection is empty.
|
||||
{
|
||||
|
|
@ -670,7 +678,6 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
r.Score = match.Score;
|
||||
return true;
|
||||
|
||||
}).ToList();
|
||||
ContextMenu.AddResults(filtered, id);
|
||||
}
|
||||
|
|
@ -697,13 +704,10 @@ namespace Flow.Launcher.ViewModel
|
|||
Title = string.Format(title, h.Query),
|
||||
SubTitle = string.Format(time, h.ExecutedDateTime),
|
||||
IcoPath = "Images\\history.png",
|
||||
OriginQuery = new Query
|
||||
{
|
||||
RawQuery = h.Query
|
||||
},
|
||||
OriginQuery = new Query { RawQuery = h.Query },
|
||||
Action = _ =>
|
||||
{
|
||||
SelectedResults = Results;
|
||||
SelectedResults = ResultsVM;
|
||||
ChangeQueryText(h.Query);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -736,10 +740,10 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
if (query == null) // shortcut expanded
|
||||
{
|
||||
Results.Clear();
|
||||
Results.Visibility = Visibility.Collapsed;
|
||||
ResultsVM.Clear();
|
||||
ResultsVM.Visibility = false;
|
||||
PluginIconPath = null;
|
||||
SearchIconVisibility = Visibility.Visible;
|
||||
SearchIconVisibility = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -750,7 +754,7 @@ namespace Flow.Launcher.ViewModel
|
|||
var currentCancellationToken = _updateSource.Token;
|
||||
_updateToken = currentCancellationToken;
|
||||
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
ProgressBarVisibility = false;
|
||||
_isQueryRunning = true;
|
||||
|
||||
// Switch to ThreadPool thread
|
||||
|
|
@ -772,12 +776,12 @@ namespace Flow.Launcher.ViewModel
|
|||
if (plugins.Count == 1)
|
||||
{
|
||||
PluginIconPath = plugins.Single().Metadata.IcoPath;
|
||||
SearchIconVisibility = Visibility.Hidden;
|
||||
SearchIconVisibility = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginIconPath = null;
|
||||
SearchIconVisibility = Visibility.Visible;
|
||||
SearchIconVisibility = true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -795,7 +799,7 @@ namespace Flow.Launcher.ViewModel
|
|||
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
|
||||
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
|
||||
{
|
||||
ProgressBarVisibility = Visibility.Visible;
|
||||
ProgressBarVisibility = true;
|
||||
}
|
||||
}, currentCancellationToken, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
|
||||
|
||||
|
|
@ -827,7 +831,7 @@ namespace Flow.Launcher.ViewModel
|
|||
if (!currentCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// update to hidden if this is still the current query
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
ProgressBarVisibility = false;
|
||||
}
|
||||
|
||||
// Local function
|
||||
|
|
@ -837,20 +841,23 @@ namespace Flow.Launcher.ViewModel
|
|||
// Task.Yield will force it to run in ThreadPool
|
||||
await Task.Yield();
|
||||
|
||||
IReadOnlyList<Result> results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
|
||||
IReadOnlyList<Result> results =
|
||||
await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
|
||||
|
||||
currentCancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
results ??= _emptyResult;
|
||||
|
||||
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken)))
|
||||
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query,
|
||||
currentCancellationToken)))
|
||||
{
|
||||
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Query ConstructQuery(string queryText, IEnumerable<CustomShortcutModel> customShortcuts, IEnumerable<BuiltinShortcutModel> builtInShortcuts)
|
||||
private Query ConstructQuery(string queryText, IEnumerable<CustomShortcutModel> customShortcuts,
|
||||
IEnumerable<BuiltinShortcutModel> builtInShortcuts)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queryText))
|
||||
{
|
||||
|
|
@ -872,7 +879,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
string customExpanded = queryBuilder.ToString();
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
foreach (var shortcut in builtInShortcuts)
|
||||
{
|
||||
|
|
@ -887,7 +894,9 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e);
|
||||
Log.Exception(
|
||||
$"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}",
|
||||
e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -904,7 +913,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (_lastQuery?.ActionKeyword != query?.ActionKeyword)
|
||||
{
|
||||
Results.Clear();
|
||||
ResultsVM.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -976,7 +985,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
internal bool SelectedIsFromQueryResults()
|
||||
{
|
||||
var selected = SelectedResults == Results;
|
||||
var selected = SelectedResults == ResultsVM;
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
|
@ -1010,9 +1019,9 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public void Show()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
MainWindowVisibility = Visibility.Visible;
|
||||
MainWindowVisibility = true;
|
||||
|
||||
MainWindowOpacity = 1;
|
||||
|
||||
|
|
@ -1028,8 +1037,9 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
if (!SelectedIsFromQueryResults())
|
||||
{
|
||||
SelectedResults = Results;
|
||||
SelectedResults = ResultsVM;
|
||||
}
|
||||
|
||||
switch (Settings.LastQueryMode)
|
||||
{
|
||||
case LastQueryMode.Empty:
|
||||
|
|
@ -1051,7 +1061,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
MainWindowVisibilityStatus = false;
|
||||
MainWindowVisibility = Visibility.Collapsed;
|
||||
MainWindowVisibility = false;
|
||||
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false });
|
||||
}
|
||||
|
||||
|
|
@ -1116,7 +1126,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
Results.AddResults(resultsForUpdates, token);
|
||||
ResultsVM.AddResults(resultsForUpdates, token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Result = result;
|
||||
|
||||
if (Result.Glyph is { FontFamily: not null } glyph)
|
||||
|
|
@ -41,19 +42,14 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
if (fonts.ContainsKey(fontFamilyPath))
|
||||
{
|
||||
Glyph = glyph with
|
||||
{
|
||||
FontFamily = fonts[fontFamilyPath]
|
||||
};
|
||||
Glyph = glyph with { FontFamily = fonts[fontFamilyPath] };
|
||||
}
|
||||
else
|
||||
{
|
||||
fontCollection.AddFontFile(fontFamilyPath);
|
||||
fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
|
||||
Glyph = glyph with
|
||||
{
|
||||
FontFamily = fonts[fontFamilyPath]
|
||||
};
|
||||
fonts[fontFamilyPath] =
|
||||
$"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
|
||||
Glyph = glyph with { FontFamily = fonts[fontFamilyPath] };
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -61,42 +57,40 @@ namespace Flow.Launcher.ViewModel
|
|||
Glyph = glyph;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Settings Settings { get; }
|
||||
|
||||
public Visibility ShowOpenResultHotkey =>
|
||||
Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Collapsed;
|
||||
public bool ShowOpenResultHotkey => Settings.ShowOpenResultHotkey;
|
||||
|
||||
public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed;
|
||||
public bool ShowDefaultPreview => Result.PreviewPanel != null;
|
||||
|
||||
public Visibility ShowCustomizedPreview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible;
|
||||
public bool ShowCustomizedPreview => Result.PreviewPanel != null;
|
||||
|
||||
public Visibility ShowIcon
|
||||
public bool ShowIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
// If both glyph and image icons are not available, it will then be the default icon
|
||||
if (!ImgIconAvailable && !GlyphAvailable)
|
||||
return Visibility.Visible;
|
||||
return true;
|
||||
|
||||
// Although user can choose to use glyph icons, plugins may choose to supply only image icons.
|
||||
// In this case we ignore the setting because otherwise icons will not display as intended
|
||||
if (Settings.UseGlyphIcons && !GlyphAvailable && ImgIconAvailable)
|
||||
return Visibility.Visible;
|
||||
return true;
|
||||
|
||||
return !Settings.UseGlyphIcons && ImgIconAvailable ? Visibility.Visible : Visibility.Hidden;
|
||||
return !Settings.UseGlyphIcons && ImgIconAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility ShowPreviewImage
|
||||
public bool ShowPreviewImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PreviewImageAvailable)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -114,21 +108,21 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
return IconXY / 2;
|
||||
}
|
||||
|
||||
return IconXY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Visibility ShowGlyph
|
||||
public bool ShowGlyph
|
||||
{
|
||||
get
|
||||
{
|
||||
// Although user can choose to not use glyph icons, plugins may choose to supply only glyph icons.
|
||||
// In this case we ignore the setting because otherwise icons will not display as intended
|
||||
if (!Settings.UseGlyphIcons && !ImgIconAvailable && GlyphAvailable)
|
||||
return Visibility.Visible;
|
||||
return true;
|
||||
|
||||
return Settings.UseGlyphIcons && GlyphAvailable ? Visibility.Visible : Visibility.Collapsed;
|
||||
return Settings.UseGlyphIcons && GlyphAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +130,8 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
private bool ImgIconAvailable => !string.IsNullOrEmpty(Result.IcoPath) || Result.Icon is not null;
|
||||
|
||||
private bool PreviewImageAvailable => !string.IsNullOrEmpty(Result.Preview.PreviewImagePath) || Result.Preview.PreviewDelegate != null;
|
||||
private bool PreviewImageAvailable => !string.IsNullOrEmpty(Result.Preview.PreviewImagePath) ||
|
||||
Result.Preview.PreviewDelegate != null;
|
||||
|
||||
public string OpenResultModifiers => Settings.OpenResultModifiers;
|
||||
|
||||
|
|
@ -182,7 +177,8 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public GlyphInfo Glyph { get; set; }
|
||||
|
||||
private async Task<ImageSource> LoadImageInternalAsync(string imagePath, Result.IconDelegate icon, bool loadFullImage)
|
||||
private async Task<ImageSource> LoadImageInternalAsync(string imagePath, Result.IconDelegate icon,
|
||||
bool loadFullImage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imagePath) && icon != null)
|
||||
{
|
||||
|
|
@ -234,9 +230,9 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public void LoadPreviewImage()
|
||||
{
|
||||
if (ShowDefaultPreview == Visibility.Visible)
|
||||
if (ShowDefaultPreview)
|
||||
{
|
||||
if (!PreviewImageLoaded && ShowPreviewImage == Visibility.Visible)
|
||||
if (!PreviewImageLoaded && ShowPreviewImage)
|
||||
{
|
||||
PreviewImageLoaded = true;
|
||||
_ = LoadPreviewImageAsync();
|
||||
|
|
@ -245,6 +241,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
public Result Result { get; }
|
||||
|
||||
public int ResultProgress
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Results = new ResultCollection();
|
||||
BindingOperations.EnableCollectionSynchronization(Results, _collectionLock);
|
||||
}
|
||||
|
||||
public ResultsViewModel(Settings settings) : this()
|
||||
{
|
||||
_settings = settings;
|
||||
|
|
@ -43,14 +44,14 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region Properties
|
||||
|
||||
public double MaxHeight => MaxResults * (double)Application.Current.FindResource("ResultItemHeight")!;
|
||||
public double MaxHeight => MaxResults * 52;
|
||||
|
||||
public int SelectedIndex { get; set; }
|
||||
|
||||
public ResultViewModel SelectedItem { get; set; }
|
||||
public Thickness Margin { get; set; }
|
||||
public Visibility Visibility { get; set; } = Visibility.Collapsed;
|
||||
|
||||
public bool Visibility { get; set; } = true;
|
||||
|
||||
public ICommand RightClickResultCommand { get; init; }
|
||||
public ICommand LeftClickResultCommand { get; init; }
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ namespace Flow.Launcher.ViewModel
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +89,6 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
|
@ -144,6 +145,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
UpdateResults(newResults);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
|
|
@ -169,12 +171,12 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
switch (Visibility)
|
||||
{
|
||||
case Visibility.Collapsed when Results.Count > 0:
|
||||
case false when Results.Count > 0:
|
||||
SelectedIndex = 0;
|
||||
Visibility = Visibility.Visible;
|
||||
Visibility = true;
|
||||
break;
|
||||
case Visibility.Visible when Results.Count == 0:
|
||||
Visibility = Visibility.Collapsed;
|
||||
case true when Results.Count == 0:
|
||||
Visibility = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -199,28 +201,30 @@ namespace Flow.Launcher.ViewModel
|
|||
return Results;
|
||||
|
||||
return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID))
|
||||
.Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
|
||||
.OrderByDescending(rv => rv.Result.Score)
|
||||
.ToList();
|
||||
.Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
|
||||
.OrderByDescending(rv => rv.Result.Score)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FormattedText Dependency Property
|
||||
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
|
||||
"FormattedText",
|
||||
typeof(Inline),
|
||||
typeof(ResultsViewModel),
|
||||
new PropertyMetadata(null, FormattedTextPropertyChanged));
|
||||
|
||||
public static void SetFormattedText(DependencyObject textBlock, IList<int> value)
|
||||
{
|
||||
textBlock.SetValue(FormattedTextProperty, value);
|
||||
}
|
||||
// public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
|
||||
// "FormattedText",
|
||||
// typeof(Inline),
|
||||
// typeof(ResultsViewModel),
|
||||
// new PropertyMetadata(null, FormattedTextPropertyChanged));
|
||||
//
|
||||
// public static void SetFormattedText(DependencyObject textBlock, IList<int> value)
|
||||
// {
|
||||
// textBlock.SetValue(FormattedTextProperty, value);
|
||||
// }
|
||||
|
||||
public static Inline GetFormattedText(DependencyObject textBlock)
|
||||
{
|
||||
return (Inline)textBlock.GetValue(FormattedTextProperty);
|
||||
}
|
||||
// public static Inline GetFormattedText(DependencyObject textBlock)
|
||||
// {
|
||||
// return (Inline)textBlock.GetValue(FormattedTextProperty);
|
||||
// }
|
||||
|
||||
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
|
|
@ -234,6 +238,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
textBlock.Inlines.Add(inline);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public class ResultCollection : List<ResultViewModel>, INotifyCollectionChanged
|
||||
|
|
@ -262,6 +267,7 @@ namespace Flow.Launcher.ViewModel
|
|||
// wpf use DirectX / double buffered already, so just reset all won't cause ui flickering
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
|
||||
private void AddAll(List<ResultViewModel> Items)
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
|
|
@ -270,9 +276,11 @@ namespace Flow.Launcher.ViewModel
|
|||
if (_token.IsCancellationRequested)
|
||||
return;
|
||||
Add(item);
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i));
|
||||
OnCollectionChanged(
|
||||
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAll(int Capacity = 512)
|
||||
{
|
||||
Clear();
|
||||
|
|
@ -307,6 +315,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
Capacity = newItems.Count;
|
||||
}
|
||||
|
||||
editTime++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue