mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 10:16:24 +02:00
Somil55/merge custom ui into launcher (#2271)
* Remove Autosuggest box (#2192)
* Update Settings.Designer.cs
* Revert "Update Settings.Designer.cs"
This reverts commit a1bc0dda56.
* Updated LauncherControl XAML to add textbox and listview
* List View displayed
* Hooking up execution on the selected index, removing two way binding on selection, and experimenting with popup that doesn't work
* Updated MainViewModel to Remove context menu and history
* Added Resultist XAML Island project
* Updated SelectedItem and SelectedList Binding.
Issues : List box doesn't open when query is written for first time but opens in subsequent queries.
* 1. Mouse Click working
2. List View is can't be focused
3. Fixed width of Launcher
* Removed two way QueryText box binding
* Removed SelectedItem two way binding and replaced with a callback
* [Cleaning] Remove redundant UWP project
* [Cleaning] Updated files to keep only atomic changes against dev/powerLauncher
* Thmbnail fixed for NEW UI
* Removed PreviewMouseDown function required by older WOX code
Co-authored-by: ryanbodrug-microsoft <56318517+ryanbodrug-microsoft@users.noreply.github.com>
* Added the auto-complete feature
* Removing ContextMenuPluginInfo, and ContextMenuTopMost as these commands are not used int the new design.
* Fixed merge conflicts
* Set only when index is 0
* One way binding
* Removed unnecessary binding
* Deleting unused (commented out code) that was legacy from wox project.
* Binding Buttons to appropriate context menu commands.
1. Buttons are dynamically loaded in a listview based on the actions supported be each plugin.
This change also deletes unused commands.
Note: Most button events don't seem to be getting routed to the Selected Item. Currently using 'PointerEntered' to validate the behavior. The actions should be trigged by the button command property in the future.
* manually handling tab in mainwindow
* Loading context buttons on Selecting a suggestion list item
* Allowing hover event to load content menu items and display them as well.
* Adding context buttons to Indexer plugin. This allows for the following:
1. [Files] Open Containing folder
2. [Folders/Files] Copy Path
* Remove White background of list (#2218)
* Remove white background of list
* Removed comments
* Changed to ContainerContentChanging event
* add const variables instead of numbers
* Added comment before the updatelistSize function
* Search box UI (#2224)
* Added backdrop and rounded corner
* Fix for two alt+space press to bring searchbox issue
* Fixed merge conflict
* Clean Mainwindow.xaml code
* Fix for textbox focus on first visible
* Allowing users to tab between the context buttons for the selected resut. Also allowing users to press 'enter' to action on the selected items.
* Renaming SelectedIndex to ContextMenuSelectedIndex
* Enabling key accelerators on context buttons.
1. Add new object ContextMenuResult instead instead of reusing Result for both query results and context menu results.
2. Binding KeyboardAccelerator keys to contextmenuitemviewmodel
3. Enabling and disabling contextmenu items when selecting or deselecting each row. Because we are manually maintaining selectionwe can't use ScopeOwners as the textbox is really the only item ever in focus.
* Launching explorer instead of the UWP application when selecting 'open file location'.
* Added fix for border showing up when result count is zero
* Updated fix for border on no result
* Adding visibility after clearing result in MainViewmodel
* Launcher Light/Dark mode (#2235)
* Fixed issue with list view background not updating with Windows theme change
* Added theme change for WPF
* updated ShadowDepth for dropshadow
* Updated border thicknes of searchbox and listview
* Diff issue with ResultList.xaml
* Removed change in result delay
* Added code to pull colors from UWP
* Updated border resource to use system based SystemControlHighlightAccentBrush
* Updated corner radius in dark mode
* Updated Launcher description text
Co-authored-by: ryanbodrug-microsoft <56318517+ryanbodrug-microsoft@users.noreply.github.com>
Co-authored-by: Alekhya Reddy <reddykalekhya@gmail.com>
This commit is contained in:
committed by
GitHub
parent
afd22768fc
commit
7da8689bf2
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin;
|
||||
using Microsoft.Plugin.Indexer.SearchHelper;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer
|
||||
{
|
||||
internal class ContextMenuLoader : IContextMenu
|
||||
{
|
||||
private readonly PluginInitContext _context;
|
||||
|
||||
public enum ResultType
|
||||
{
|
||||
Folder,
|
||||
File
|
||||
}
|
||||
|
||||
public ContextMenuLoader(PluginInitContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var contextMenus = new List<ContextMenuResult>();
|
||||
if (selectedResult.ContextData is SearchResult record)
|
||||
{
|
||||
ResultType type = Path.HasExtension(record.Path) ? ResultType.File : ResultType.Folder;
|
||||
|
||||
if (type == ResultType.File)
|
||||
{
|
||||
contextMenus.Add(CreateOpenContainingFolderResult(record));
|
||||
}
|
||||
|
||||
var fileOrFolder = (type == ResultType.File) ? "file" : "folder";
|
||||
contextMenus.Add(new ContextMenuResult
|
||||
{
|
||||
Title = "Copy path",
|
||||
Glyph = "\xE8C8",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
|
||||
AcceleratorKey = "C",
|
||||
AcceleratorModifiers = "Control",
|
||||
|
||||
Action = (context) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(record.Path);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = "Fail to set text in clipboard";
|
||||
LogException(message, e);
|
||||
_context.API.ShowMsg(message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return contextMenus;
|
||||
}
|
||||
|
||||
private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record)
|
||||
{
|
||||
return new ContextMenuResult
|
||||
{
|
||||
Title = "Open containing folder",
|
||||
Glyph = "\xE838",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "E",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start("explorer.exe", $" /select,\"{record.Path}\"");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
var message = $"Fail to open file at {record.Path}";
|
||||
LogException(message, e);
|
||||
_context.API.ShowMsg(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public void LogException(string message, Exception e)
|
||||
{
|
||||
Log.Exception($"|Wox.Plugin.Folder.ContextMenu|{message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using Microsoft.Search.Interop;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer
|
||||
{
|
||||
class Main : IPlugin, ISavable, IPluginI18n
|
||||
class Main : IPlugin, ISavable, IPluginI18n, IContextMenu
|
||||
{
|
||||
|
||||
// This variable contains metadata about the Plugin
|
||||
@@ -26,7 +26,9 @@ namespace Microsoft.Plugin.Indexer
|
||||
private PluginJsonStorage<Settings> _storage;
|
||||
|
||||
// To access Windows Search functionalities
|
||||
private readonly WindowsSearchAPI _api = new WindowsSearchAPI();
|
||||
private readonly WindowsSearchAPI _api = new WindowsSearchAPI();
|
||||
|
||||
private IContextMenu _contextMenuLoader;
|
||||
|
||||
// To save the configurations of plugins
|
||||
public void Save()
|
||||
@@ -109,6 +111,7 @@ namespace Microsoft.Plugin.Indexer
|
||||
{
|
||||
// initialize the context of the plugin
|
||||
_context = context;
|
||||
_contextMenuLoader = new ContextMenuLoader(context);
|
||||
_storage = new PluginJsonStorage<Settings>();
|
||||
_settings = _storage.Load();
|
||||
}
|
||||
@@ -125,8 +128,11 @@ namespace Microsoft.Plugin.Indexer
|
||||
public string GetTranslatedPluginDescription()
|
||||
{
|
||||
return "Returns files and folders";
|
||||
}
|
||||
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
return _contextMenuLoader.LoadContextMenus(selectedResult);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,26 @@ namespace Wox.Plugin.Folder
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var contextMenus = new List<Result>();
|
||||
var contextMenus = new List<ContextMenuResult>();
|
||||
if (selectedResult.ContextData is SearchResult record)
|
||||
{
|
||||
if (record.Type == ResultType.File)
|
||||
{
|
||||
contextMenus.Add(CreateOpenWithEditorResult(record));
|
||||
contextMenus.Add(CreateOpenContainingFolderResult(record));
|
||||
}
|
||||
|
||||
var icoPath = (record.Type == ResultType.File) ? Main.FileImagePath : Main.FolderImagePath;
|
||||
var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder";
|
||||
contextMenus.Add(new Result
|
||||
contextMenus.Add(new ContextMenuResult
|
||||
{
|
||||
Title = "Copy path",
|
||||
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
|
||||
Glyph = "\xE8C8",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
|
||||
AcceleratorKey = "C",
|
||||
AcceleratorModifiers = "Control",
|
||||
Action = (context) =>
|
||||
{
|
||||
try
|
||||
@@ -50,91 +53,22 @@ namespace Wox.Plugin.Folder
|
||||
_context.API.ShowMsg(message);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
IcoPath = Main.CopyImagePath
|
||||
}
|
||||
});
|
||||
|
||||
contextMenus.Add(new Result
|
||||
{
|
||||
Title = $"Copy {fileOrFolder}",
|
||||
SubTitle = $"Copy the {fileOrFolder} to clipboard",
|
||||
Action = (context) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath });
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = $"Fail to set {fileOrFolder} in clipboard";
|
||||
LogException(message, e);
|
||||
_context.API.ShowMsg(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
IcoPath = icoPath
|
||||
});
|
||||
|
||||
if (record.Type == ResultType.File || record.Type == ResultType.Folder)
|
||||
contextMenus.Add(new Result
|
||||
{
|
||||
Title = $"Delete {fileOrFolder}",
|
||||
SubTitle = $"Delete the selected {fileOrFolder}",
|
||||
Action = (context) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (record.Type == ResultType.File)
|
||||
File.Delete(record.FullPath);
|
||||
else
|
||||
Directory.Delete(record.FullPath);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
var message = $"Fail to delete {fileOrFolder} at {record.FullPath}";
|
||||
LogException(message, e);
|
||||
_context.API.ShowMsg(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = Main.DeleteFileFolderImagePath
|
||||
});
|
||||
|
||||
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
|
||||
contextMenus.Add(new Result
|
||||
{
|
||||
Title = "Run as different user",
|
||||
Action = (context) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Task.Run(()=> ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo()));
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
var name = "Plugin: Folder";
|
||||
var message = $"File not found: {e.Message}";
|
||||
_context.API.ShowMsg(name, message);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/user.png"
|
||||
});
|
||||
}
|
||||
|
||||
return contextMenus;
|
||||
}
|
||||
|
||||
private Result CreateOpenContainingFolderResult(SearchResult record)
|
||||
private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record)
|
||||
{
|
||||
return new Result
|
||||
return new ContextMenuResult
|
||||
{
|
||||
Title = "Open containing folder",
|
||||
Glyph = "\xE838",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "E",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = _ =>
|
||||
{
|
||||
try
|
||||
@@ -150,8 +84,7 @@ namespace Wox.Plugin.Folder
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = Main.FolderImagePath
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Wox.Plugin.Folder
|
||||
return _context.API.GetTranslation("wox_plugin_folder_plugin_description");
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
return _contextMenuLoader.LoadContextMenus(selectedResult);
|
||||
}
|
||||
|
||||
@@ -141,30 +141,15 @@ namespace Wox.Plugin.Program
|
||||
return _context.API.GetTranslation("wox_plugin_program_plugin_description");
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var menuOptions = new List<Result>();
|
||||
var menuOptions = new List<ContextMenuResult>();
|
||||
var program = selectedResult.ContextData as IProgram;
|
||||
if (program != null)
|
||||
{
|
||||
menuOptions = program.ContextMenus(_context.API);
|
||||
}
|
||||
|
||||
menuOptions.Add(
|
||||
new Result
|
||||
{
|
||||
Title = _context.API.GetTranslation("wox_plugin_program_disable_program"),
|
||||
Action = c =>
|
||||
{
|
||||
DisableProgram(program);
|
||||
_context.API.ShowMsg(_context.API.GetTranslation("wox_plugin_program_disable_dlgtitle_success"),
|
||||
_context.API.GetTranslation("wox_plugin_program_disable_dlgtitle_success_message"));
|
||||
return false;
|
||||
},
|
||||
IcoPath = "Images/disable.png"
|
||||
}
|
||||
);
|
||||
|
||||
return menuOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
public interface IProgram
|
||||
{
|
||||
List<Result> ContextMenus(IPublicAPI api);
|
||||
List<ContextMenuResult> ContextMenus(IPublicAPI api);
|
||||
Result Result(string query, IPublicAPI api);
|
||||
string UniqueIdentifier { get; set; }
|
||||
string Name { get; }
|
||||
|
||||
@@ -18,7 +18,8 @@ using IStream = AppxPackaing.IStream;
|
||||
using Rect = System.Windows.Rect;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
[Serializable]
|
||||
@@ -312,22 +313,23 @@ namespace Wox.Plugin.Program.Programs
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Result> ContextMenus(IPublicAPI api)
|
||||
public List<ContextMenuResult> ContextMenus(IPublicAPI api)
|
||||
{
|
||||
var contextMenus = new List<Result>
|
||||
var contextMenus = new List<ContextMenuResult>
|
||||
{
|
||||
new Result
|
||||
new ContextMenuResult
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
|
||||
|
||||
Glyph = "\xE838",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "E",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = _ =>
|
||||
{
|
||||
Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location));
|
||||
Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", Package.Location));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
IcoPath = "Images/folder.png"
|
||||
}
|
||||
}
|
||||
};
|
||||
return contextMenus;
|
||||
|
||||
@@ -94,31 +94,17 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
|
||||
|
||||
public List<Result> ContextMenus(IPublicAPI api)
|
||||
public List<ContextMenuResult> ContextMenus(IPublicAPI api)
|
||||
{
|
||||
var contextMenus = new List<Result>
|
||||
var contextMenus = new List<ContextMenuResult>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_run_as_different_user"),
|
||||
Action = _ =>
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = FullPath,
|
||||
WorkingDirectory = ParentDirectory,
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/user.png"
|
||||
},
|
||||
new Result
|
||||
new ContextMenuResult
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_run_as_administrator"),
|
||||
Glyph = "\xE7EF",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "Enter",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = _ =>
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
@@ -132,12 +118,15 @@ namespace Wox.Plugin.Program.Programs
|
||||
Task.Run(() => Main.StartProcess(Process.Start, info));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/cmd.png"
|
||||
}
|
||||
},
|
||||
new Result
|
||||
new ContextMenuResult
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
|
||||
Glyph = "\xE838",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "E",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = _ =>
|
||||
{
|
||||
|
||||
@@ -145,8 +134,7 @@ namespace Wox.Plugin.Program.Programs
|
||||
Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", ParentDirectory));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/folder.png"
|
||||
}
|
||||
}
|
||||
};
|
||||
return contextMenus;
|
||||
|
||||
@@ -320,29 +320,22 @@ namespace Wox.Plugin.Shell
|
||||
return _context.API.GetTranslation("wox_plugin_cmd_plugin_description");
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var resultlist = new List<Result>
|
||||
var resultlist = new List<ContextMenuResult>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_different_user"),
|
||||
Action = c =>
|
||||
{
|
||||
Task.Run(() =>Execute(ShellCommand.RunAsDifferentUser, PrepareProcessStartInfo(selectedResult.Title)));
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/user.png"
|
||||
},
|
||||
new Result
|
||||
new ContextMenuResult
|
||||
{
|
||||
Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"),
|
||||
Glyph = "\xE7EF",
|
||||
FontFamily = "Segoe MDL2 Assets",
|
||||
AcceleratorKey = "Enter",
|
||||
AcceleratorModifiers = "Control,Shift",
|
||||
Action = c =>
|
||||
{
|
||||
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
|
||||
return true;
|
||||
},
|
||||
IcoPath = Image
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="PrimaryTextColor" Color="Black"/>
|
||||
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush" Color="White"/>
|
||||
<AcrylicBrush x:Key="BackdropAcrylicBrush" BackgroundSource="Backdrop" TintColor="White" TintOpacity="0.9" FallbackColor="White"/>
|
||||
<AcrylicBrush x:Key="KeyboardShortcutAcrylicBrush" BackgroundSource="Backdrop" TintColor="White" TintOpacity="0.9" FallbackColor="White"/>
|
||||
<SolidColorBrush x:Key="ItemBackgroundColor" Color="#FFFFFFFF"/>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<SolidColorBrush x:Key="PrimaryTextColor" Color="White"/>
|
||||
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush" Color="#484848"/>
|
||||
<AcrylicBrush x:Key="BackdropAcrylicBrush" BackgroundSource="Backdrop" TintColor="#88484848" TintOpacity="0.9" FallbackColor="#FF484848"/>
|
||||
<AcrylicBrush x:Key="KeyboardShortcutAcrylicBrush" BackgroundSource="Backdrop" TintColor="Black" TintOpacity="0.9" FallbackColor="Black"/>
|
||||
<SolidColorBrush x:Key="ItemBackgroundColor" Color="#FFFFFFFF"/>
|
||||
|
||||
@@ -1,74 +1,19 @@
|
||||
<UserControl
|
||||
<UserControl
|
||||
x:Class="PowerLauncher.UI.LauncherControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
xmlns:ToolkitBehaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:ToolkitBehaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
ActualThemeChanged="UserControl_ActualThemeChanged"
|
||||
Loaded="UserControl_Loaded"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="720">
|
||||
<UserControl.Resources>
|
||||
|
||||
<Style TargetType="AutoSuggestBox" x:Name="CustomStyledAutoSuggestBox">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="TextBoxStyle" Value="{StaticResource AutoSuggestBoxTextBoxStyle}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="AutoSuggestBox">
|
||||
<Grid x:Name="LayoutRoot">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="Orientation">
|
||||
<VisualState x:Name="Landscape" />
|
||||
<VisualState x:Name="Portrait" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<TextBox
|
||||
x:Name="TextBox"
|
||||
Style="{StaticResource CustomAutoSuggestBoxTextBoxStyle}"
|
||||
PlaceholderText="{TemplateBinding PlaceholderText}"
|
||||
Header="{TemplateBinding Header}"
|
||||
Height="72"
|
||||
Width="{TemplateBinding Width}"
|
||||
Description="{TemplateBinding Description}"
|
||||
ScrollViewer.BringIntoViewOnFocusChange="False"
|
||||
Canvas.ZIndex="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="24"
|
||||
DesiredCandidateWindowAlignment="BottomEdge"
|
||||
UseSystemFocusVisuals="{TemplateBinding UseSystemFocusVisuals}" />
|
||||
|
||||
<Popup x:Name="SuggestionsPopup" Margin="0,24,0,0">
|
||||
<Border x:Name="SuggestionsContainer">
|
||||
<ListView
|
||||
x:Name="SuggestionsList"
|
||||
Background="{ThemeResource AutoSuggestBoxSuggestionsListBackground}"
|
||||
BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}"
|
||||
BorderBrush="{ThemeResource AutoSuggestBoxSuggestionsListBorderBrush}"
|
||||
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemTemplate="{TemplateBinding ItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
|
||||
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
|
||||
MaxHeight="{ThemeResource AutoSuggestListMaxHeight}"
|
||||
Margin="{ThemeResource AutoSuggestListMargin}"
|
||||
Padding="{ThemeResource AutoSuggestListPadding}" />
|
||||
</Border>
|
||||
</Popup>
|
||||
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBox" x:Key="CustomAutoSuggestBoxTextBoxStyle">
|
||||
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
|
||||
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
|
||||
@@ -409,79 +354,37 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid
|
||||
x:Name="PowerBar"
|
||||
Background="{StaticResource BackdropAcrylicBrush}"
|
||||
Background="{ThemeResource BackdropAcrylicBrush}"
|
||||
Height="72"
|
||||
Translation="0,0,16"
|
||||
VerticalAlignment="Top">
|
||||
<AutoSuggestBox
|
||||
x:Name="SearchBox"
|
||||
x:FieldModifier="public"
|
||||
<TextBox
|
||||
x:Name="AutoCompleteTextBox"
|
||||
x:FieldModifier="public"
|
||||
Style="{StaticResource CustomAutoSuggestBoxTextBoxStyle}"
|
||||
Height="72"
|
||||
IsReadOnly="True"
|
||||
AllowFocusOnInteraction="False"
|
||||
ScrollViewer.BringIntoViewOnFocusChange="False"
|
||||
Canvas.ZIndex="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="24"
|
||||
DesiredCandidateWindowAlignment="BottomEdge" />
|
||||
|
||||
<TextBox
|
||||
x:Name="TextBox"
|
||||
x:FieldModifier="public"
|
||||
Style="{StaticResource CustomAutoSuggestBoxTextBoxStyle}"
|
||||
PlaceholderText="Start typing"
|
||||
FontSize="24"
|
||||
Style="{StaticResource CustomStyledAutoSuggestBox}"
|
||||
ItemsSource="{Binding Results.Results}">
|
||||
<AutoSuggestBox.QueryIcon>
|
||||
<SymbolIcon Symbol="Find"/>
|
||||
</AutoSuggestBox.QueryIcon>
|
||||
<AutoSuggestBox.ItemTemplate>
|
||||
<DataTemplate >
|
||||
<Grid Height="72" Width="690" Background="Transparent" RowSpacing="0">
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<Core:EventTriggerBehavior EventName="PointerEntered">
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=ShowActionButtons}" MethodName="StartAnimation"/>
|
||||
</Core:EventTriggerBehavior>
|
||||
<Core:EventTriggerBehavior EventName="PointerExited">
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=HideActionsButtons}" MethodName="StartAnimation"/>
|
||||
</Core:EventTriggerBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="64" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions >
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image x:Name="AppIcon" Height="36" Margin="8,0,0,0" Grid.RowSpan="2" HorizontalAlignment="Left" Source="{Binding Image}" />
|
||||
<TextBlock x:Name="Title" Grid.Column="1" Text="{Binding Result.Title}" FontWeight="SemiBold" FontSize="20" VerticalAlignment="Bottom"/>
|
||||
<TextBlock x:Name="Path" Grid.Column="1" Text= "{Binding Result.SubTitle}" Grid.Row="1" Opacity="0.6" VerticalAlignment="Top"/>
|
||||
<StackPanel Orientation="Horizontal" Opacity="0" HorizontalAlignment="Right" Grid.RowSpan="2" Grid.Column="1">
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<ToolkitBehaviors:Fade x:Name="ShowActionButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="1" />
|
||||
<ToolkitBehaviors:Fade x:Name="HideActionsButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="0" />
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<Button Background="Transparent" Height="42" Width="42" BorderThickness="1" Style="{ThemeResource ButtonRevealStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="Run as administrator"/>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets" FontSize="16" Glyph=""/>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button Background="Transparent" Height="42" Width="42" BorderThickness="1" Style="{ThemeResource ButtonRevealStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="Open file location"/>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets" FontSize="16" Glyph=""/>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button Background="Transparent" Height="42" Width="42" BorderThickness="1" Style="{ThemeResource ButtonRevealStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="Open in console"/>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets" FontSize="16" Glyph=""/>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</AutoSuggestBox.ItemTemplate>
|
||||
</AutoSuggestBox>
|
||||
Height="72"
|
||||
ScrollViewer.BringIntoViewOnFocusChange="False"
|
||||
Canvas.ZIndex="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="24"
|
||||
DesiredCandidateWindowAlignment="BottomEdge" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,12 +1,50 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace PowerLauncher.UI
|
||||
{
|
||||
public sealed partial class LauncherControl : UserControl
|
||||
{
|
||||
public LauncherControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace PowerLauncher.UI
|
||||
{
|
||||
public sealed partial class LauncherControl : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
private Brush _borderBrush;
|
||||
|
||||
public LauncherControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public Brush SolidBorderBrush
|
||||
{
|
||||
get { return _borderBrush; }
|
||||
set { Set(ref _borderBrush, value); }
|
||||
}
|
||||
|
||||
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
|
||||
{
|
||||
if (Equals(storage, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
storage = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
|
||||
private void UserControl_ActualThemeChanged(FrameworkElement sender, object args)
|
||||
{
|
||||
SolidBorderBrush = Application.Current.Resources["SystemControlHighlightAccentBrush"] as SolidColorBrush;
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SolidBorderBrush = Application.Current.Resources["SystemControlHighlightAccentBrush"] as SolidColorBrush;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,9 @@
|
||||
<DependentUpon>LauncherControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ResultList.xaml.cs">
|
||||
<DependentUpon>ResultList.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
@@ -78,6 +81,10 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ResultList.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
|
||||
101
src/modules/launcher/PowerLauncher.UI/ResultList.xaml
Normal file
101
src/modules/launcher/PowerLauncher.UI/ResultList.xaml
Normal file
@@ -0,0 +1,101 @@
|
||||
<UserControl
|
||||
x:Class="PowerLauncher.UI.ResultList"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
xmlns:ToolkitBehaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="720">
|
||||
<Grid
|
||||
x:Name="PowerBar"
|
||||
Background="{ThemeResource BackdropAcrylicBrush}"
|
||||
Translation="0,0,16"
|
||||
VerticalAlignment="Top">
|
||||
<ListView
|
||||
x:Name="SuggestionsList"
|
||||
x:FieldModifier="public"
|
||||
MaxHeight="{Binding Results.MaxHeight}"
|
||||
MinHeight="{Binding Results.MaxHeight}"
|
||||
AllowFocusOnInteraction="False"
|
||||
IsItemClickEnabled="True"
|
||||
Margin="{ThemeResource AutoSuggestListMargin}"
|
||||
Padding="{ThemeResource AutoSuggestListPadding}"
|
||||
ItemsSource="{Binding Results.Results, Mode=OneWay}"
|
||||
SelectionMode="Single"
|
||||
SelectedIndex="{Binding Results.SelectedIndex, Mode=TwoWay}"
|
||||
>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate >
|
||||
<Grid Height="72" Width="690" Background="Transparent" RowSpacing="0">
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<Core:DataTriggerBehavior Binding="{Binding IsSelected}" ComparisonCondition="Equal" Value="true" >
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=ShowActionButtons}" MethodName="StartAnimation"/>
|
||||
</Core:DataTriggerBehavior>
|
||||
<Core:DataTriggerBehavior Binding="{Binding IsSelected}" ComparisonCondition="Equal" Value="false">
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=HideActionsButtons}" MethodName="StartAnimation"/>
|
||||
</Core:DataTriggerBehavior>
|
||||
<Core:EventTriggerBehavior EventName="PointerEntered">
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=ShowActionButtons}" MethodName="StartAnimation"/>
|
||||
<Core:InvokeCommandAction Command="{Binding LoadContextMenuCommand}"/>
|
||||
</Core:EventTriggerBehavior>
|
||||
<Core:EventTriggerBehavior EventName="PointerExited">
|
||||
<Core:CallMethodAction TargetObject="{Binding ElementName=HideActionsButtons}" MethodName="StartAnimation"/>
|
||||
</Core:EventTriggerBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="64" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions >
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image x:Name="AppIcon" Height="36" Margin="8,0,0,0" Grid.RowSpan="2" HorizontalAlignment="Left" Source="{Binding Image}" />
|
||||
<TextBlock x:Name="Title" Grid.Column="1" Text="{Binding Result.Title}" FontWeight="SemiBold" FontSize="20" VerticalAlignment="Bottom"/>
|
||||
<TextBlock x:Name="Path" Grid.Column="1" Text= "{Binding Result.SubTitle}" Grid.Row="1" Opacity="0.6" VerticalAlignment="Top"/>
|
||||
<ListView Opacity="0"
|
||||
HorizontalAlignment="Right"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
ItemsSource="{Binding ContextMenuItems}"
|
||||
SelectionMode="Single"
|
||||
SelectedIndex="{Binding ContextMenuSelectedIndex}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<ToolkitBehaviors:Fade x:Name="ShowActionButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="1" />
|
||||
<ToolkitBehaviors:Fade x:Name="HideActionsButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="0" />
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{Binding Command}" Background="Transparent" Height="42" Width="42" BorderThickness="1" Style="{ThemeResource ButtonRevealStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="{Binding Title}"/>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontFamily="{Binding FontFamily}" FontSize="16" Glyph="{Binding Glyph}"/>
|
||||
</Button.Content>
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator
|
||||
Key="{Binding AcceleratorKey}"
|
||||
Modifiers="{Binding AcceleratorModifiers}"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
/>
|
||||
</Button.KeyboardAccelerators>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
18
src/modules/launcher/PowerLauncher.UI/ResultList.xaml.cs
Normal file
18
src/modules/launcher/PowerLauncher.UI/ResultList.xaml.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace PowerLauncher.UI
|
||||
{
|
||||
public sealed partial class ResultList : UserControl
|
||||
{
|
||||
public ResultList()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ namespace PowerLauncher
|
||||
RegisterExitEvents();
|
||||
|
||||
|
||||
_mainVM.MainWindowVisibility = Visibility.Hidden;
|
||||
_mainVM.MainWindowVisibility = Visibility.Visible;
|
||||
Log.Info("|App.OnStartup|End Wox startup ---------------------------------------------------- ");
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:vm="clr-namespace:Wox.ViewModel;assembly=Wox"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
xmlns:xaml="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
|
||||
Title="PowerLaunch"
|
||||
Topmost="True"
|
||||
@@ -23,17 +23,31 @@
|
||||
Drop="OnDrop"
|
||||
LocationChanged="OnLocationChanged"
|
||||
Deactivated="OnDeactivated"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
Background="Transparent"
|
||||
Background="Transparent"
|
||||
Visibility="{Binding MainWindowVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
d:DataContext="{d:DesignInstance vm:MainViewModel}">
|
||||
<Border Margin="10" Background="#fff" BorderBrush="#ccc" BorderThickness="1" CornerRadius="4" Padding="4" Width="720" >
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="12" Opacity="0.3" ShadowDepth="4" />
|
||||
</Border.Effect>
|
||||
<xaml:WindowsXamlHost InitialTypeName="PowerLauncher.UI.LauncherControl" ChildChanged="WindowsXamlHost_ChildChanged" />
|
||||
</Border>
|
||||
<Grid Width="720">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border x:Name="SearchBoxBorder" Margin="10" Grid.Row="0" BorderThickness="4" CornerRadius="4">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="12" Opacity="0.3" ShadowDepth="4" />
|
||||
</Border.Effect>
|
||||
<xaml:WindowsXamlHost InitialTypeName="PowerLauncher.UI.LauncherControl" ChildChanged="WindowsXamlHostTextBox_ChildChanged" />
|
||||
</Border>
|
||||
<Border Grid.Row="1" Name="myElement" Background="Transparent" Height="10"/>
|
||||
<Border x:Name="ListBoxBorder" Margin="10" Grid.Row="2" BorderThickness="4" CornerRadius="4" Visibility="{Binding Results.Visbility}">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="12" Opacity="0.3" ShadowDepth="4" />
|
||||
</Border.Effect>
|
||||
<xaml:WindowsXamlHost PreviewMouseDown="WindowsXamlHost_PreviewMouseDown" InitialTypeName="PowerLauncher.UI.ResultList" ChildChanged="WindowsXamlHostListView_ChildChanged" />
|
||||
</Border>
|
||||
</Grid>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="{Binding EscCommand}" />
|
||||
<KeyBinding Key="Enter" Command="{Binding OpenResultCommand}"></KeyBinding>
|
||||
</Window.InputBindings>
|
||||
</Window>
|
||||
|
||||
@@ -16,7 +16,12 @@ using KeyEventArgs = System.Windows.Input.KeyEventArgs;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using Microsoft.Toolkit.Wpf.UI.XamlHost;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Core;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PowerLauncher
|
||||
{
|
||||
@@ -29,6 +34,10 @@ namespace PowerLauncher
|
||||
private Settings _settings;
|
||||
private MainViewModel _viewModel;
|
||||
|
||||
const int ROW_COUNT = 4;
|
||||
const int ROW_HEIGHT = 75;
|
||||
const int MAX_LIST_HEIGHT = 300;
|
||||
|
||||
#endregion
|
||||
|
||||
public MainWindow(Settings settings, MainViewModel mainVM)
|
||||
@@ -37,6 +46,7 @@ namespace PowerLauncher
|
||||
_viewModel = mainVM;
|
||||
_settings = settings;
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
public MainWindow()
|
||||
{
|
||||
@@ -52,35 +62,8 @@ namespace PowerLauncher
|
||||
{
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs _)
|
||||
private void OnLoaded(object sender, System.Windows.RoutedEventArgs _)
|
||||
{
|
||||
// todo is there a way to set blur only once?
|
||||
//ThemeManager.Instance.SetBlurForWindow();
|
||||
//WindowsInteropHelper.DisableControlBox(this);
|
||||
//InitProgressbarAnimation();
|
||||
//InitializePosition();
|
||||
//// since the default main window visibility is visible
|
||||
//// so we need set focus during startup
|
||||
//QueryTextBox.Focus();
|
||||
|
||||
//_viewModel.PropertyChanged += (o, e) =>
|
||||
//{
|
||||
// if (e.PropertyName == nameof(MainViewModel.MainWindowVisibility))
|
||||
// {
|
||||
// if (Visibility == Visibility.Visible)
|
||||
// {
|
||||
// Activate();
|
||||
// QueryTextBox.Focus();
|
||||
// UpdatePosition();
|
||||
// _settings.ActivateTimes++;
|
||||
// if (!_viewModel.LastQuerySelected)
|
||||
// {
|
||||
// QueryTextBox.SelectAll();
|
||||
// _viewModel.LastQuerySelected = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
InitializePosition();
|
||||
}
|
||||
|
||||
@@ -92,49 +75,11 @@ namespace PowerLauncher
|
||||
_settings.WindowLeft = Left;
|
||||
}
|
||||
|
||||
//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, 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;
|
||||
// ProgressBar.BeginStoryboard(_progressBarStoryboard);
|
||||
// _viewModel.ProgressBarVisibility = Visibility.Hidden;
|
||||
//}
|
||||
|
||||
private void OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left) DragMove();
|
||||
}
|
||||
|
||||
//private void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
|
||||
//{
|
||||
// if (sender != null && e.OriginalSource != null)
|
||||
// {
|
||||
// var r = (ResultListBox)sender;
|
||||
// var d = (DependencyObject)e.OriginalSource;
|
||||
// var item = ItemsControl.ContainerFromElement(r, d) as ListBoxItem;
|
||||
// var result = (ResultViewModel)item?.DataContext;
|
||||
// if (result != null)
|
||||
// {
|
||||
// if (e.ChangedButton == MouseButton.Left)
|
||||
// {
|
||||
// _viewModel.OpenResultCommand.Execute(null);
|
||||
// }
|
||||
// else if (e.ChangedButton == MouseButton.Right)
|
||||
// {
|
||||
// _viewModel.LoadContextMenuCommand.Execute(null);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
private void OnDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
@@ -158,10 +103,6 @@ namespace PowerLauncher
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnDeactivated(object sender, EventArgs e)
|
||||
{
|
||||
if (_settings.HideWhenDeactive)
|
||||
@@ -202,63 +143,19 @@ namespace PowerLauncher
|
||||
return left;
|
||||
}
|
||||
|
||||
//private double WindowTop()
|
||||
//{
|
||||
// var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
// 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;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Register up and down key
|
||||
/// todo: any way to put this in xaml ?
|
||||
/// </summary>
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Down)
|
||||
{
|
||||
_viewModel.SelectNextItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Up)
|
||||
{
|
||||
_viewModel.SelectPrevItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.PageDown)
|
||||
{
|
||||
_viewModel.SelectNextPageCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.PageUp)
|
||||
{
|
||||
_viewModel.SelectPrevPageCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
//private void OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
//{
|
||||
// if (_viewModel.QueryTextCursorMovedToEnd)
|
||||
// {
|
||||
// QueryTextBox.CaretIndex = QueryTextBox.Text.Length;
|
||||
// _viewModel.QueryTextCursorMovedToEnd = false;
|
||||
// }
|
||||
//}
|
||||
|
||||
private PowerLauncher.UI.LauncherControl _launcher = null;
|
||||
private void WindowsXamlHost_ChildChanged(object sender, EventArgs ev)
|
||||
private void WindowsXamlHostTextBox_ChildChanged(object sender, EventArgs ev)
|
||||
{
|
||||
if (sender == null) return;
|
||||
|
||||
var host = (WindowsXamlHost)sender;
|
||||
_launcher = (PowerLauncher.UI.LauncherControl)host.Child;
|
||||
_launcher.DataContext = _viewModel;
|
||||
_launcher.SearchBox.TextChanged += QueryTextBox_TextChanged;
|
||||
_launcher.SearchBox.QuerySubmitted += AutoSuggestBox_QuerySubmitted;
|
||||
_launcher.SearchBox.Focus(Windows.UI.Xaml.FocusState.Programmatic);
|
||||
_launcher.KeyDown += _launcher_KeyDown;
|
||||
_launcher.TextBox.TextChanged += QueryTextBox_TextChanged;
|
||||
_launcher.TextBox.Loaded += TextBox_Loaded;
|
||||
_launcher.PropertyChanged += UserControl_PropertyChanged;
|
||||
_viewModel.PropertyChanged += (o, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(MainViewModel.MainWindowVisibility))
|
||||
@@ -266,16 +163,141 @@ namespace PowerLauncher
|
||||
if (Visibility == System.Windows.Visibility.Visible)
|
||||
{
|
||||
Activate();
|
||||
_launcher.SearchBox.Focus(Windows.UI.Xaml.FocusState.Programmatic);
|
||||
UpdatePosition();
|
||||
_settings.ActivateTimes++;
|
||||
if (!_viewModel.LastQuerySelected)
|
||||
{
|
||||
_viewModel.LastQuerySelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private void UserControl_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "SolidBorderBrush")
|
||||
{
|
||||
if (_launcher != null)
|
||||
{
|
||||
Windows.UI.Xaml.Media.SolidColorBrush uwpBrush = _launcher.SolidBorderBrush as Windows.UI.Xaml.Media.SolidColorBrush;
|
||||
System.Windows.Media.Color borderColor = System.Windows.Media.Color.FromArgb(uwpBrush.Color.A, uwpBrush.Color.R, uwpBrush.Color.G, uwpBrush.Color.B);
|
||||
this.SearchBoxBorder.BorderBrush = new SolidColorBrush(borderColor);
|
||||
this.ListBoxBorder.BorderBrush = new SolidColorBrush(borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TextBox_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
TextBox tb = (TextBox)sender;
|
||||
tb.Focus(FocusState.Programmatic);
|
||||
_viewModel.MainWindowVisibility = System.Windows.Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private UI.ResultList _resultList = null;
|
||||
private void WindowsXamlHostListView_ChildChanged(object sender, EventArgs ev)
|
||||
{
|
||||
if (sender == null) return;
|
||||
|
||||
var host = (WindowsXamlHost)sender;
|
||||
_resultList = (UI.ResultList)host.Child;
|
||||
_resultList.DataContext = _viewModel;
|
||||
_resultList.Tapped += SuggestionsList_Tapped;
|
||||
_resultList.SuggestionsList.SelectionChanged += SuggestionsList_SelectionChanged;
|
||||
_resultList.SuggestionsList.ContainerContentChanging += SuggestionList_UpdateListSize;
|
||||
}
|
||||
|
||||
private bool IsKeyDown(VirtualKey key)
|
||||
{
|
||||
var keyState = CoreWindow.GetForCurrentThread().GetKeyState(key);
|
||||
return (keyState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
|
||||
}
|
||||
|
||||
private void _launcher_KeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == VirtualKey.Tab && IsKeyDown(VirtualKey.Shift))
|
||||
{
|
||||
_viewModel.SelectPrevTabItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Tab)
|
||||
{
|
||||
_viewModel.SelectNextTabItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Down)
|
||||
{
|
||||
_viewModel.SelectNextItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Up)
|
||||
{
|
||||
_viewModel.SelectPrevItemCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.PageDown)
|
||||
{
|
||||
_viewModel.SelectNextPageCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.PageUp)
|
||||
{
|
||||
_viewModel.SelectPrevPageCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestionsList_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var result = ((Windows.UI.Xaml.FrameworkElement)e.OriginalSource).DataContext;
|
||||
if (result != null)
|
||||
{
|
||||
var resultVM = result as ResultViewModel;
|
||||
|
||||
//This may be null if the tapped item was one of the context buttons (run as admin etc).
|
||||
if (resultVM != null)
|
||||
{
|
||||
_viewModel.Results.SelectedItem = resultVM;
|
||||
_viewModel.OpenResultCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: This function has been added because a white-background was observed when the list resized,
|
||||
* when the number of elements were lesser than the maximum capacity of the list (ie. 4).
|
||||
* Binding Height/MaxHeight Properties did not solve this issue.
|
||||
*/
|
||||
private void SuggestionList_UpdateListSize(object sender, ContainerContentChangingEventArgs e)
|
||||
{
|
||||
int count = _viewModel?.Results?.Results.Count ?? 0;
|
||||
int maxHeight = count < ROW_COUNT ? count * ROW_HEIGHT : MAX_LIST_HEIGHT;
|
||||
_resultList.Height = maxHeight;
|
||||
}
|
||||
|
||||
private void SuggestionsList_SelectionChanged(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
Windows.UI.Xaml.Controls.ListView listview = (Windows.UI.Xaml.Controls.ListView)sender;
|
||||
_viewModel.Results.SelectedItem = (ResultViewModel) listview.SelectedItem;
|
||||
if (e.AddedItems.Count > 0 && e.AddedItems[0] != null)
|
||||
{
|
||||
listview.ScrollIntoView(e.AddedItems[0]);
|
||||
}
|
||||
|
||||
// To populate the AutoCompleteTextBox as soon as the selection is changed or set.
|
||||
// Setting it here instead of when the text is changed as there is a delay in executing the query and populating the result
|
||||
_launcher.AutoCompleteTextBox.PlaceholderText = ListView_FirstItem(_viewModel.QueryText);
|
||||
|
||||
}
|
||||
|
||||
private void ResultsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
ResultViewModel result = e?.ClickedItem as ResultViewModel;
|
||||
if(result != null)
|
||||
{
|
||||
_viewModel.Results.SelectedItem = result;
|
||||
_viewModel.OpenResultCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||
@@ -291,12 +313,75 @@ namespace PowerLauncher
|
||||
}
|
||||
}
|
||||
|
||||
private void QueryTextBox_TextChanged(Windows.UI.Xaml.Controls.AutoSuggestBox sender, Windows.UI.Xaml.Controls.AutoSuggestBoxTextChangedEventArgs args)
|
||||
private const int millisecondsToWait = 200;
|
||||
private static DateTime s_lastTimeOfTyping;
|
||||
|
||||
private string ListView_FirstItem(String input)
|
||||
{
|
||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
string s = input;
|
||||
if (s.Length > 0)
|
||||
{
|
||||
_viewModel.QueryText = sender.Text;
|
||||
String selectedItem = _viewModel.Results?.SelectedItem?.ToString();
|
||||
int selectedIndex = _viewModel.Results.SelectedIndex;
|
||||
if (selectedItem != null && selectedIndex == 0)
|
||||
{
|
||||
if (selectedItem.IndexOf(input) == 0)
|
||||
{
|
||||
return selectedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
|
||||
private void QueryTextBox_TextChanged(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs e)
|
||||
{
|
||||
var latestTimeOfTyping = DateTime.Now;
|
||||
var text = ((Windows.UI.Xaml.Controls.TextBox)sender).Text;
|
||||
Task.Run(() => DelayedCheck(latestTimeOfTyping, text));
|
||||
s_lastTimeOfTyping = latestTimeOfTyping;
|
||||
|
||||
//To clear the auto-suggest immediately instead of waiting for selection changed
|
||||
if(text == String.Empty)
|
||||
{
|
||||
_launcher.AutoCompleteTextBox.PlaceholderText = String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DelayedCheck(DateTime latestTimeOfTyping, string text)
|
||||
{
|
||||
await Task.Delay(millisecondsToWait);
|
||||
if (latestTimeOfTyping.Equals(s_lastTimeOfTyping))
|
||||
{
|
||||
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_viewModel.QueryText = text;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowsXamlHost_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// if (sender != null && e.OriginalSource != null)
|
||||
// {
|
||||
// //var r = (ResultListBox)sender;
|
||||
// //var d = (DependencyObject)e.OriginalSource;
|
||||
// //var item = ItemsControl.ContainerFromElement(r, d) as ListBoxItem;
|
||||
// //var result = (ResultViewModel)item?.DataContext;
|
||||
// //if (result != null)
|
||||
// //{
|
||||
// // if (e.ChangedButton == MouseButton.Left)
|
||||
// // {
|
||||
// // _viewModel.OpenResultCommand.Execute(null);
|
||||
// // }
|
||||
// // else if (e.ChangedButton == MouseButton.Right)
|
||||
// // {
|
||||
// // _viewModel.LoadContextMenuCommand.Execute(null);
|
||||
// // }
|
||||
// //}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Mages" Version="1.6.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.UI.XamlHost" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.UI" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="2.0.0" />
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
xmlns:converter="clr-namespace:Wox.Converters;assembly=Wox"
|
||||
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100"
|
||||
d:DataContext="{d:DesignInstance vm:ResultsViewModel}"
|
||||
MaxHeight="{Binding MaxHeight}"
|
||||
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=OneWayToSource}"
|
||||
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Results}"
|
||||
|
||||
@@ -45,16 +45,20 @@ namespace Wox.Core.Plugin
|
||||
}
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
string output = ExecuteContextMenu(selectedResult);
|
||||
try
|
||||
{
|
||||
return DeserializedResult(output);
|
||||
//This should not hit. If it does it's because Wox shares the same interface for querying context menu items as well as search results. In this case please file a bug.
|
||||
//To my knowledge we aren't supporting this JSonRPC commands in Launcher, and am not able to repro this, but I will leave this here for the time being in case I'm proven wrong.
|
||||
//We should remove this, or identify and test officially supported use cases and Deserialize this properly.
|
||||
//return DeserializedResult(output);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|JsonRPCPlugin.LoadContextMenus|Exception on result <{selectedResult}>", e);
|
||||
Log.Exception($"|JsonRPCPlugin.LoadContextMenus| THIS IS A BUG - Exception on result <{selectedResult}>", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace Wox.Core.Plugin
|
||||
return AllPlugins.Where(p => p.Plugin is T);
|
||||
}
|
||||
|
||||
public static List<Result> GetContextMenusForPlugin(Result result)
|
||||
public static List<ContextMenuResult> GetContextMenusForPlugin(Result result)
|
||||
{
|
||||
var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID);
|
||||
if (pluginPair != null)
|
||||
@@ -209,23 +209,17 @@ namespace Wox.Core.Plugin
|
||||
try
|
||||
{
|
||||
var results = plugin.LoadContextMenus(result);
|
||||
foreach (var r in results)
|
||||
{
|
||||
r.PluginDirectory = metadata.PluginDirectory;
|
||||
r.PluginID = metadata.ID;
|
||||
r.OriginQuery = result.OriginQuery;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{metadata.Name}>", e);
|
||||
return new List<Result>();
|
||||
return new List<ContextMenuResult>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<Result>();
|
||||
return new List<ContextMenuResult>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
32
src/modules/launcher/Wox.Plugin/ContextMenuResult.cs
Normal file
32
src/modules/launcher/Wox.Plugin/ContextMenuResult.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Wox.Plugin
|
||||
{
|
||||
|
||||
public class ContextMenuResult
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string SubTitle { get; set; }
|
||||
|
||||
public string Glyph { get; set; }
|
||||
|
||||
public string FontFamily { get; set; }
|
||||
|
||||
public string AcceleratorKey { get; set; }
|
||||
|
||||
public string AcceleratorModifiers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// return true to hide wox after select result
|
||||
/// </summary>
|
||||
public Func<ActionContext, bool> Action { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Title + SubTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Wox.Plugin
|
||||
|
||||
public interface IContextMenu : IFeatures
|
||||
{
|
||||
List<Result> LoadContextMenus(Result selectedResult);
|
||||
List<ContextMenuResult> LoadContextMenus(Result selectedResult);
|
||||
}
|
||||
|
||||
[Obsolete("If a plugin has a action keyword, then it is exclusive. This interface will be remove in v1.3.0")]
|
||||
|
||||
@@ -14,6 +14,10 @@ namespace Wox.Plugin
|
||||
public string Title { get; set; }
|
||||
public string SubTitle { get; set; }
|
||||
|
||||
public string Glyph { get; set; }
|
||||
|
||||
public string FontFamily { get; set; }
|
||||
|
||||
public string IcoPath
|
||||
{
|
||||
get { return _icoPath; }
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Input;
|
||||
using Wox.Plugin;
|
||||
|
||||
namespace Wox.ViewModel
|
||||
{
|
||||
public class ContextMenuItemViewModel : BaseModel
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Glyph { get; set; }
|
||||
public string FontFamily { get; set; }
|
||||
public ICommand Command { get; set; }
|
||||
public string AcceleratorKey { get; set; }
|
||||
public string AcceleratorModifiers { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,16 @@ namespace Wox.ViewModel
|
||||
SelectPrevItemCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectPrevResult();
|
||||
});
|
||||
|
||||
SelectNextTabItemCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectNextTabItem();
|
||||
});
|
||||
|
||||
SelectPrevTabItemCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectPrevTabItem();
|
||||
});
|
||||
|
||||
SelectNextPageCommand = new RelayCommand(_ =>
|
||||
@@ -142,27 +152,33 @@ namespace Wox.ViewModel
|
||||
results.SelectedIndex = int.Parse(index.ToString());
|
||||
}
|
||||
|
||||
var result = results.SelectedItem?.Result;
|
||||
if (result != null) // SelectedItem returns null if selection is empty.
|
||||
{
|
||||
bool hideWindow = result.Action != null && result.Action(new ActionContext
|
||||
{
|
||||
SpecialKeyState = GlobalHotkey.Instance.CheckModifiers()
|
||||
});
|
||||
//If there is a context button selected fire the action for that button before the main command.
|
||||
bool didExecuteContextButton = results.SelectedItem?.ExecuteSelectedContextButton() ?? false;
|
||||
|
||||
if (hideWindow)
|
||||
if (!didExecuteContextButton)
|
||||
{
|
||||
var result = results.SelectedItem?.Result;
|
||||
if (result != null) // SelectedItem returns null if selection is empty.
|
||||
{
|
||||
MainWindowVisibility = Visibility.Collapsed;
|
||||
}
|
||||
bool hideWindow = result.Action != null && result.Action(new ActionContext
|
||||
{
|
||||
SpecialKeyState = GlobalHotkey.Instance.CheckModifiers()
|
||||
});
|
||||
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
_userSelectedRecord.Add(result);
|
||||
_history.Add(result.OriginQuery.RawQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedResults = Results;
|
||||
if (hideWindow)
|
||||
{
|
||||
MainWindowVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
_userSelectedRecord.Add(result);
|
||||
_history.Add(result.OriginQuery.RawQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedResults = Results;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -270,7 +286,11 @@ namespace Wox.ViewModel
|
||||
|
||||
public ICommand EscCommand { get; set; }
|
||||
public ICommand SelectNextItemCommand { get; set; }
|
||||
public ICommand SelectPrevItemCommand { get; set; }
|
||||
public ICommand SelectPrevItemCommand { get; set; }
|
||||
|
||||
public ICommand SelectNextTabItemCommand { get; set; }
|
||||
public ICommand SelectPrevTabItemCommand { get; set; }
|
||||
|
||||
public ICommand SelectNextPageCommand { get; set; }
|
||||
public ICommand SelectPrevPageCommand { get; set; }
|
||||
public ICommand SelectFirstResultCommand { get; set; }
|
||||
@@ -287,46 +307,12 @@ namespace Wox.ViewModel
|
||||
{
|
||||
QueryResults();
|
||||
}
|
||||
else if (ContextMenuSelected())
|
||||
{
|
||||
QueryContextMenu();
|
||||
}
|
||||
else if (HistorySelected())
|
||||
{
|
||||
QueryHistory();
|
||||
}
|
||||
}
|
||||
|
||||
private void QueryContextMenu()
|
||||
{
|
||||
const string id = "Context Menu ID";
|
||||
var query = QueryText.ToLower().Trim();
|
||||
ContextMenu.Clear();
|
||||
|
||||
var selected = Results.SelectedItem?.Result;
|
||||
|
||||
if (selected != null) // SelectedItem returns null if selection is empty.
|
||||
{
|
||||
var results = PluginManager.GetContextMenusForPlugin(selected);
|
||||
results.Add(ContextMenuTopMost(selected));
|
||||
results.Add(ContextMenuPluginInfo(selected.PluginID));
|
||||
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
var filtered = results.Where
|
||||
(
|
||||
r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet()
|
||||
|| StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
|
||||
).ToList();
|
||||
ContextMenu.AddResults(filtered, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContextMenu.AddResults(results, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void QueryHistory()
|
||||
{
|
||||
const string id = "Query History ID";
|
||||
@@ -387,9 +373,9 @@ namespace Wox.ViewModel
|
||||
// handle the exclusiveness of plugin using action keyword
|
||||
RemoveOldQueryResults(query);
|
||||
|
||||
_lastQuery = query;
|
||||
_lastQuery = query;
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
// so looping will stop once it was cancelled
|
||||
@@ -463,66 +449,6 @@ namespace Wox.ViewModel
|
||||
}
|
||||
|
||||
|
||||
private Result ContextMenuTopMost(Result result)
|
||||
{
|
||||
Result menu;
|
||||
if (_topMostRecord.IsTopMost(result))
|
||||
{
|
||||
menu = new Result
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("cancelTopMostInThisQuery"),
|
||||
IcoPath = "Images\\down.png",
|
||||
PluginDirectory = Constant.ProgramDirectory,
|
||||
Action = _ =>
|
||||
{
|
||||
_topMostRecord.Remove(result);
|
||||
App.API.ShowMsg("Success");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
menu = new Result
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("setAsTopMostInThisQuery"),
|
||||
IcoPath = "Images\\up.png",
|
||||
PluginDirectory = Constant.ProgramDirectory,
|
||||
Action = _ =>
|
||||
{
|
||||
_topMostRecord.AddOrUpdate(result);
|
||||
App.API.ShowMsg("Success");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
private Result ContextMenuPluginInfo(string id)
|
||||
{
|
||||
var metadata = PluginManager.GetPluginForId(id).Metadata;
|
||||
var translator = InternationalizationManager.Instance;
|
||||
|
||||
var author = translator.GetTranslation("author");
|
||||
var website = translator.GetTranslation("website");
|
||||
var version = translator.GetTranslation("version");
|
||||
var plugin = translator.GetTranslation("plugin");
|
||||
var title = $"{plugin}: {metadata.Name}";
|
||||
var icon = metadata.IcoPath;
|
||||
var subtitle = $"{author}: {metadata.Author}, {website}: {metadata.Website} {version}: {metadata.Version}";
|
||||
|
||||
var menu = new Result
|
||||
{
|
||||
Title = title,
|
||||
IcoPath = icon,
|
||||
SubTitle = subtitle,
|
||||
PluginDirectory = metadata.PluginDirectory,
|
||||
Action = _ => false
|
||||
};
|
||||
return menu;
|
||||
}
|
||||
|
||||
private bool SelectedIsFromQueryResults()
|
||||
{
|
||||
var selected = SelectedResults == Results;
|
||||
|
||||
@@ -1,69 +1,186 @@
|
||||
using System;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Infrastructure.Image;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
|
||||
namespace Wox.ViewModel
|
||||
{
|
||||
public class ResultViewModel : BaseModel
|
||||
{
|
||||
public ResultViewModel(Result result)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource Image
|
||||
{
|
||||
get
|
||||
{
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Result.Icon();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
imagePath = Constant.ErrorIcon;
|
||||
}
|
||||
}
|
||||
|
||||
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
|
||||
return ImageLoader.Load(imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
public Result Result { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var r = obj as ResultViewModel;
|
||||
if (r != null)
|
||||
{
|
||||
return Result.Equals(r.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Result.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Result.Title.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Controls.Ribbon;
|
||||
using System.Windows.Input;
|
||||
using Wox.Core.Plugin;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Infrastructure.Hotkey;
|
||||
using Wox.Infrastructure.Image;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
|
||||
namespace Wox.ViewModel
|
||||
{
|
||||
public class ResultViewModel : BaseModel
|
||||
{
|
||||
public List<ContextMenuItemViewModel> ContextMenuItems { get; set; }
|
||||
|
||||
public ICommand LoadContextMenuCommand { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
public int ContextMenuSelectedIndex { get; set; }
|
||||
|
||||
const int NoSelectionIndex = -1;
|
||||
|
||||
public ResultViewModel(Result result)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
ContextMenuSelectedIndex = NoSelectionIndex;
|
||||
LoadContextMenuCommand = new RelayCommand(LoadContextMenu);
|
||||
}
|
||||
public void LoadContextMenu(object sender=null)
|
||||
{
|
||||
var results = PluginManager.GetContextMenusForPlugin(Result);
|
||||
var newItems = new List<ContextMenuItemViewModel>();
|
||||
foreach (var r in results)
|
||||
{
|
||||
newItems.Add(new ContextMenuItemViewModel
|
||||
{
|
||||
Title = r.Title,
|
||||
Glyph = r.Glyph,
|
||||
FontFamily = r.FontFamily,
|
||||
AcceleratorKey = r.AcceleratorKey,
|
||||
AcceleratorModifiers = r.AcceleratorModifiers,
|
||||
Command = new RelayCommand(_ =>
|
||||
{
|
||||
bool hideWindow = r.Action != null && r.Action(new ActionContext
|
||||
{
|
||||
SpecialKeyState = GlobalHotkey.Instance.CheckModifiers()
|
||||
});
|
||||
|
||||
if (hideWindow)
|
||||
{
|
||||
//TODO - Do we hide the window
|
||||
// MainWindowVisibility = Visibility.Collapsed;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
ContextMenuItems = newItems;
|
||||
}
|
||||
|
||||
internal void EnableContextMenu()
|
||||
{
|
||||
foreach(var i in ContextMenuItems)
|
||||
{
|
||||
i.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void DisableContextMenu()
|
||||
{
|
||||
foreach (var i in ContextMenuItems)
|
||||
{
|
||||
i.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource Image
|
||||
{
|
||||
get
|
||||
{
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Result.Icon();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
imagePath = Constant.ErrorIcon;
|
||||
}
|
||||
}
|
||||
|
||||
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
|
||||
return ImageLoader.Load(imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
//Returns false if we've already reached the last item.
|
||||
public bool SelectNextContextButton()
|
||||
{
|
||||
if(ContextMenuSelectedIndex == (ContextMenuItems.Count -1))
|
||||
{
|
||||
ContextMenuSelectedIndex = NoSelectionIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
ContextMenuSelectedIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Returns false if we've already reached the first item.
|
||||
public bool SelectPrevContextButton()
|
||||
{
|
||||
if (ContextMenuSelectedIndex == NoSelectionIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ContextMenuSelectedIndex--;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SelectLastContextButton()
|
||||
{
|
||||
ContextMenuSelectedIndex = ContextMenuItems.Count - 1;
|
||||
}
|
||||
|
||||
public bool HasSelectedContextButton()
|
||||
{
|
||||
var isContextSelected = (ContextMenuSelectedIndex != NoSelectionIndex);
|
||||
return isContextSelected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the action on the selected context button
|
||||
/// </summary>
|
||||
/// <returns>False if there is nothing selected, oherwise true</returns>
|
||||
public bool ExecuteSelectedContextButton()
|
||||
{
|
||||
if (HasSelectedContextButton())
|
||||
{
|
||||
ContextMenuItems[ContextMenuSelectedIndex].Command.Execute(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Result Result { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var r = obj as ResultViewModel;
|
||||
if (r != null)
|
||||
{
|
||||
return Result.Equals(r.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Result.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Result.Title.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using Wox.Infrastructure.UserSettings;
|
||||
using Wox.Plugin;
|
||||
|
||||
@@ -47,9 +48,33 @@ namespace Wox.ViewModel
|
||||
|
||||
public int SelectedIndex { get; set; }
|
||||
|
||||
public ResultViewModel SelectedItem { get; set; }
|
||||
private ResultViewModel _selectedItem;
|
||||
public ResultViewModel SelectedItem
|
||||
{
|
||||
get { return _selectedItem; }
|
||||
set
|
||||
{
|
||||
//value can be null when selecting an item in a virtualized list
|
||||
if (value != null)
|
||||
{
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
_selectedItem.IsSelected = false;
|
||||
_selectedItem.DisableContextMenu();
|
||||
}
|
||||
|
||||
_selectedItem = value;
|
||||
_selectedItem.LoadContextMenu();
|
||||
_selectedItem.EnableContextMenu();
|
||||
_selectedItem.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Thickness Margin { get; set; }
|
||||
public Visibility Visbility { get; set; } = Visibility.Collapsed;
|
||||
public Visibility Visbility { get; set; } = Visibility.Hidden;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -127,8 +152,26 @@ namespace Wox.ViewModel
|
||||
public void RemoveResultsFor(PluginMetadata metadata)
|
||||
{
|
||||
Results.RemoveAll(r => r.Result.PluginID == metadata.ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SelectNextTabItem()
|
||||
{
|
||||
if(!SelectedItem.SelectNextContextButton())
|
||||
{
|
||||
SelectNextResult();
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectPrevTabItem()
|
||||
{
|
||||
if (!SelectedItem.SelectPrevContextButton())
|
||||
{
|
||||
//Tabbing backwards should highlight the last item of the previous row
|
||||
SelectPrevResult();
|
||||
SelectedItem.SelectLastContextButton();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
@@ -149,6 +192,7 @@ namespace Wox.ViewModel
|
||||
else
|
||||
{
|
||||
Margin = new Thickness { Top = 0 };
|
||||
Visbility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user