Merge branch 'upstreamdev' into queryPluginsUpdates

This commit is contained in:
AT 2019-12-13 18:17:11 +02:00
commit be77bf94aa
27 changed files with 455 additions and 153 deletions

View File

@ -27,7 +27,6 @@ namespace Wox.Plugin.ControlPanel
Directory.CreateDirectory(iconFolder); Directory.CreateDirectory(iconFolder);
} }
foreach (ControlPanelItem item in controlPanelItems) foreach (ControlPanelItem item in controlPanelItems)
{ {
if (!File.Exists(iconFolder + item.GUID + fileType) && item.Icon != null) if (!File.Exists(iconFolder + item.GUID + fileType) && item.Icon != null)
@ -43,7 +42,10 @@ namespace Wox.Plugin.ControlPanel
foreach (var item in controlPanelItems) foreach (var item in controlPanelItems)
{ {
item.Score = Score(item, query.Search); var titleMatch = StringMatcher.FuzzySearch(query.Search, item.LocalizedString);
var subTitleMatch = StringMatcher.FuzzySearch(query.Search, item.InfoTip);
item.Score = Math.Max(titleMatch.Score, subTitleMatch.Score);
if (item.Score > 0) if (item.Score > 0)
{ {
var result = new Result var result = new Result
@ -66,6 +68,16 @@ namespace Wox.Plugin.ControlPanel
return true; return true;
} }
}; };
if (item.Score == titleMatch.Score)
{
result.TitleHighlightData = titleMatch.MatchData;
}
else
{
result.SubTitleHighlightData = subTitleMatch.MatchData;
}
results.Add(result); results.Add(result);
} }
} }
@ -74,26 +86,6 @@ namespace Wox.Plugin.ControlPanel
return panelItems; return panelItems;
} }
private int Score(ControlPanelItem item, string query)
{
var scores = new List<int> {0};
if (!string.IsNullOrEmpty(item.LocalizedString))
{
var score1 = StringMatcher.FuzzySearch(query, item.LocalizedString).ScoreAfterSearchPrecisionFilter();
var score2 = StringMatcher.ScoreForPinyin(item.LocalizedString, query);
scores.Add(score1);
scores.Add(score2);
}
if (!string.IsNullOrEmpty(item.InfoTip))
{
var score1 = StringMatcher.FuzzySearch(query, item.InfoTip).ScoreAfterSearchPrecisionFilter();
var score2 = StringMatcher.ScoreForPinyin(item.InfoTip, query);
scores.Add(score1);
scores.Add(score2);
}
return scores.Max();
}
public string GetTranslatedPluginTitle() public string GetTranslatedPluginTitle()
{ {
return context.API.GetTranslation("wox_plugin_controlpanel_plugin_name"); return context.API.GetTranslation("wox_plugin_controlpanel_plugin_name");

View File

@ -55,6 +55,7 @@ namespace Wox.Plugin.Everything
r.Title = Path.GetFileName(path); r.Title = Path.GetFileName(path);
r.SubTitle = path; r.SubTitle = path;
r.IcoPath = path; r.IcoPath = path;
r.TitleHighlightData = StringMatcher.FuzzySearch(keyword, Path.GetFileName(path)).MatchData;
r.Action = c => r.Action = c =>
{ {
bool hide; bool hide;
@ -78,6 +79,7 @@ namespace Wox.Plugin.Everything
return hide; return hide;
}; };
r.ContextData = s; r.ContextData = s;
r.SubTitleHighlightData = StringMatcher.FuzzySearch(keyword, path).MatchData;
results.Add(r); results.Add(r);
} }
} }

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Wox.Infrastructure;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
namespace Wox.Plugin.Folder namespace Wox.Plugin.Folder
@ -58,13 +59,14 @@ namespace Wox.Plugin.Folder
return results; return results;
} }
private Result CreateFolderResult(string title, string path, string queryActionKeyword) private Result CreateFolderResult(string title, string path, Query query)
{ {
return new Result return new Result
{ {
Title = title, Title = title,
IcoPath = path, IcoPath = path,
SubTitle = "Ctrl + Enter to open the directory", SubTitle = "Ctrl + Enter to open the directory",
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
Action = c => Action = c =>
{ {
if (c.SpecialKeyState.CtrlPressed) if (c.SpecialKeyState.CtrlPressed)
@ -82,7 +84,9 @@ namespace Wox.Plugin.Folder
} }
string changeTo = path.EndsWith("\\") ? path : path + "\\"; string changeTo = path.EndsWith("\\") ? path : path + "\\";
_context.API.ChangeQuery(string.IsNullOrEmpty(queryActionKeyword)? changeTo : queryActionKeyword + " " + changeTo); _context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ?
changeTo :
query.ActionKeyword + " " + changeTo);
return false; return false;
} }
}; };
@ -93,8 +97,8 @@ namespace Wox.Plugin.Folder
string search = query.Search.ToLower(); string search = query.Search.ToLower();
var userFolderLinks = _settings.FolderLinks.Where( var userFolderLinks = _settings.FolderLinks.Where(
x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
var results = userFolderLinks.Select(item => CreateFolderResult(item.Nickname, item.Path, query.ActionKeyword)) var results = userFolderLinks.Select(item =>
.ToList(); CreateFolderResult(item.Nickname, item.Path, query)).ToList();
return results; return results;
} }
@ -132,26 +136,31 @@ namespace Wox.Plugin.Folder
incompleteName = search.Substring(index + 1).ToLower(); incompleteName = search.Substring(index + 1).ToLower();
search = search.Substring(0, index + 1); search = search.Substring(0, index + 1);
if (!Directory.Exists(search)) if (!Directory.Exists(search))
{
return results; return results;
} }
else
return results;
} }
else else
{ // folder exist, add \ at the end of doesn't exist {
return results;
}
}
else
{
// folder exist, add \ at the end of doesn't exist
if (!search.EndsWith("\\")) if (!search.EndsWith("\\"))
{
search += "\\"; search += "\\";
} }
}
results.Add(CreateOpenCurrentFolderResult(incompleteName, search)); results.Add(CreateOpenCurrentFolderResult(incompleteName, search));
var searchOption = SearchOption.TopDirectoryOnly;
var directoryInfo = new DirectoryInfo(search);
var searchOption= SearchOption.TopDirectoryOnly;
incompleteName += "*"; incompleteName += "*";
if (incompleteName.StartsWith(">")) // give the ability to search all folder when starting with > // give the ability to search all folder when starting with >
if (incompleteName.StartsWith(">"))
{ {
searchOption = SearchOption.AllDirectories; searchOption = SearchOption.AllDirectories;
incompleteName = incompleteName.Substring(1); incompleteName = incompleteName.Substring(1);
@ -160,6 +169,7 @@ namespace Wox.Plugin.Folder
try try
{ {
// search folder and add results // search folder and add results
var directoryInfo = new DirectoryInfo(search);
var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption);
foreach (var fileSystemInfo in fileSystemInfos) foreach (var fileSystemInfo in fileSystemInfos)
@ -168,12 +178,12 @@ namespace Wox.Plugin.Folder
var result = var result =
fileSystemInfo is DirectoryInfo fileSystemInfo is DirectoryInfo
? CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, query.ActionKeyword) ? CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, query)
: CreateFileResult(fileSystemInfo.FullName); : CreateFileResult(fileSystemInfo.FullName, query);
results.Add(result); results.Add(result);
} }
} }
catch(Exception e) catch (Exception e)
{ {
if (e is UnauthorizedAccessException || e is ArgumentException) if (e is UnauthorizedAccessException || e is ArgumentException)
{ {
@ -188,12 +198,13 @@ namespace Wox.Plugin.Folder
return results; return results;
} }
private static Result CreateFileResult(string filePath) private static Result CreateFileResult(string filePath, Query query)
{ {
var result = new Result var result = new Result
{ {
Title = Path.GetFileName(filePath), Title = Path.GetFileName(filePath),
IcoPath = filePath, IcoPath = filePath,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData,
Action = c => Action = c =>
{ {
try try

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Infrastructure;
using Wox.Infrastructure.Http; using Wox.Infrastructure.Http;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
@ -142,6 +143,8 @@ namespace Wox.Plugin.PluginManagement
Title = r.name, Title = r.name,
SubTitle = r.description, SubTitle = r.description,
IcoPath = "Images\\plugin.png", IcoPath = "Images\\plugin.png",
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData,
Action = c => Action = c =>
{ {
MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin", MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin",
@ -191,6 +194,8 @@ namespace Wox.Plugin.PluginManagement
Title = plugin.Name, Title = plugin.Name,
SubTitle = plugin.Description, SubTitle = plugin.Description,
IcoPath = plugin.IcoPath, IcoPath = plugin.IcoPath,
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Description).MatchData,
Action = e => Action = e =>
{ {
UnInstallPlugin(plugin); UnInstallPlugin(plugin);

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -31,6 +31,7 @@
<system:String x:Key="wox_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String> <system:String x:Key="wox_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String>
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String> <system:String x:Key="wox_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String>
<system:String x:Key="wox_plugin_program_run_as_different_user">Run As Different User</system:String>
<system:String x:Key="wox_plugin_program_run_as_administrator">Run As Administrator</system:String> <system:String x:Key="wox_plugin_program_run_as_administrator">Run As Administrator</system:String>
<system:String x:Key="wox_plugin_program_open_containing_folder">Open containing folder</system:String> <system:String x:Key="wox_plugin_program_open_containing_folder">Open containing folder</system:String>
<system:String x:Key="wox_plugin_program_disable_program">Disable this program from displaying</system:String> <system:String x:Key="wox_plugin_program_disable_program">Disable this program from displaying</system:String>

View File

@ -191,12 +191,12 @@ namespace Wox.Plugin.Program
); );
} }
public static bool StartProcess(ProcessStartInfo info) public static bool StartProcess(Func<ProcessStartInfo, Process> runProcess, ProcessStartInfo info)
{ {
bool hide; bool hide;
try try
{ {
Process.Start(info); runProcess(info);
hide = true; hide = true;
} }
catch (Exception) catch (Exception)

View File

@ -35,7 +35,6 @@ namespace Wox.Plugin.Program.Programs
public UWP(Package package) public UWP(Package package)
{ {
Location = package.InstalledLocation.Path; Location = package.InstalledLocation.Path;
Name = package.Id.Name; Name = package.Id.Name;
FullName = package.Id.FullName; FullName = package.Id.FullName;
@ -266,11 +265,9 @@ namespace Wox.Plugin.Program.Programs
private int Score(string query) private int Score(string query)
{ {
var score1 = StringMatcher.FuzzySearch(query, DisplayName).ScoreAfterSearchPrecisionFilter(); var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName);
var score2 = StringMatcher.ScoreForPinyin(DisplayName, query); var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter(); var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max();
var score4 = StringMatcher.ScoreForPinyin(Description, query);
var score = new[] { score1, score2, score3, score4 }.Max();
return score; return score;
} }
@ -299,14 +296,18 @@ namespace Wox.Plugin.Program.Programs
Description.Substring(0, DisplayName.Length) == DisplayName) Description.Substring(0, DisplayName.Length) == DisplayName)
{ {
result.Title = Description; result.Title = Description;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
} }
else if (!string.IsNullOrEmpty(Description)) else if (!string.IsNullOrEmpty(Description))
{ {
result.Title = $"{DisplayName}: {Description}"; var title = $"{DisplayName}: {Description}";
result.Title = title;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
} }
else else
{ {
result.Title = DisplayName; result.Title = DisplayName;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData;
} }
return result; return result;
} }
@ -320,7 +321,7 @@ namespace Wox.Plugin.Program.Programs
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"), Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
Action = _ => Action = _ =>
{ {
var hide = Main.StartProcess(new ProcessStartInfo(Package.Location)); var hide = Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location));
return hide; return hide;
}, },
IcoPath = "Images/folder.png" IcoPath = "Images/folder.png"

View File

@ -6,10 +6,12 @@ using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32; using Microsoft.Win32;
using Shell; using Shell;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Plugin.Program.Logger; using Wox.Plugin.Program.Logger;
using Wox.Plugin.SharedCommands;
namespace Wox.Plugin.Program.Programs namespace Wox.Plugin.Program.Programs
{ {
@ -33,12 +35,10 @@ namespace Wox.Plugin.Program.Programs
private int Score(string query) private int Score(string query)
{ {
var score1 = StringMatcher.FuzzySearch(query, Name).ScoreAfterSearchPrecisionFilter(); var nameMatch = StringMatcher.FuzzySearch(query, Name);
var score2 = StringMatcher.ScoreForPinyin(Name, query); var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter(); var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName);
var score4 = StringMatcher.ScoreForPinyin(Description, query); var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max();
var score5 = StringMatcher.FuzzySearch(query, ExecutableName).ScoreAfterSearchPrecisionFilter();
var score = new[] { score1, score2, score3, score4, score5 }.Max();
return score; return score;
} }
@ -64,7 +64,7 @@ namespace Wox.Plugin.Program.Programs
FileName = FullPath, FileName = FullPath,
WorkingDirectory = ParentDirectory WorkingDirectory = ParentDirectory
}; };
var hide = Main.StartProcess(info); var hide = Main.StartProcess(Process.Start, info);
return hide; return hide;
} }
}; };
@ -73,14 +73,18 @@ namespace Wox.Plugin.Program.Programs
Description.Substring(0, Name.Length) == Name) Description.Substring(0, Name.Length) == Name)
{ {
result.Title = Description; result.Title = Description;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
} }
else if (!string.IsNullOrEmpty(Description)) else if (!string.IsNullOrEmpty(Description))
{ {
result.Title = $"{Name}: {Description}"; var title = $"{Name}: {Description}";
result.Title = title;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
} }
else else
{ {
result.Title = Name; result.Title = Name;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData;
} }
return result; return result;
@ -91,6 +95,19 @@ namespace Wox.Plugin.Program.Programs
{ {
var contextMenus = new List<Result> var contextMenus = new List<Result>
{ {
new Result
{
Title = api.GetTranslation("wox_plugin_program_run_as_different_user"),
Action = _ =>
{
var info = FullPath.SetProcessStartInfo(ParentDirectory);
Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info));
return true;
},
IcoPath = "Images/user.png"
},
new Result new Result
{ {
Title = api.GetTranslation("wox_plugin_program_run_as_administrator"), Title = api.GetTranslation("wox_plugin_program_run_as_administrator"),
@ -102,7 +119,7 @@ namespace Wox.Plugin.Program.Programs
WorkingDirectory = ParentDirectory, WorkingDirectory = ParentDirectory,
Verb = "runas" Verb = "runas"
}; };
var hide = Main.StartProcess(info); var hide = Main.StartProcess(Process.Start, info);
return hide; return hide;
}, },
IcoPath = "Images/cmd.png" IcoPath = "Images/cmd.png"
@ -112,7 +129,7 @@ namespace Wox.Plugin.Program.Programs
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"), Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
Action = _ => Action = _ =>
{ {
var hide = Main.StartProcess(new ProcessStartInfo(ParentDirectory)); var hide = Main.StartProcess(Process.Start, new ProcessStartInfo(ParentDirectory));
return hide; return hide;
}, },
IcoPath = "Images/folder.png" IcoPath = "Images/folder.png"

View File

@ -112,6 +112,9 @@
<None Include="Images\disable.png"> <None Include="Images\disable.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -5,6 +5,7 @@
<system:String x:Key="wox_plugin_cmd_relace_winr">Replace Win+R</system:String> <system:String x:Key="wox_plugin_cmd_relace_winr">Replace Win+R</system:String>
<system:String x:Key="wox_plugin_cmd_leave_cmd_open">Do not close Command Prompt after command execution</system:String> <system:String x:Key="wox_plugin_cmd_leave_cmd_open">Do not close Command Prompt after command execution</system:String>
<system:String x:Key="wox_plugin_cmd_always_run_as_administrator">Always run as administrator</system:String> <system:String x:Key="wox_plugin_cmd_always_run_as_administrator">Always run as administrator</system:String>
<system:String x:Key="wox_plugin_cmd_run_as_different_user">Run as different user</system:String>
<system:String x:Key="wox_plugin_cmd_plugin_name">Shell</system:String> <system:String x:Key="wox_plugin_cmd_plugin_name">Shell</system:String>
<system:String x:Key="wox_plugin_cmd_plugin_description">Allows to execute system commands from Wox. Commands should start with ></system:String> <system:String x:Key="wox_plugin_cmd_plugin_description">Allows to execute system commands from Wox. Commands should start with ></system:String>
<system:String x:Key="wox_plugin_cmd_cmd_has_been_executed_times">this command has been executed {0} times</system:String> <system:String x:Key="wox_plugin_cmd_cmd_has_been_executed_times">this command has been executed {0} times</system:String>

View File

@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using WindowsInput; using WindowsInput;
using WindowsInput.Native; using WindowsInput.Native;
@ -84,7 +86,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m); Execute(Process.Start, PrepareProcessStartInfo(m));
return true; return true;
} }
})); }));
@ -117,7 +119,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m.Key); Execute(Process.Start, PrepareProcessStartInfo(m.Key));
return true; return true;
} }
}; };
@ -136,7 +138,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(cmd); Execute(Process.Start, PrepareProcessStartInfo(cmd));
return true; return true;
} }
}; };
@ -154,14 +156,14 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m.Key); Execute(Process.Start, PrepareProcessStartInfo(m.Key));
return true; return true;
} }
}).Take(5); }).Take(5);
return history.ToList(); return history.ToList();
} }
private void Execute(string command, bool runAsAdministrator = false) private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdministrator = false)
{ {
command = command.Trim(); command = command.Trim();
command = Environment.ExpandEnvironmentVariables(command); command = Environment.ExpandEnvironmentVariables(command);
@ -212,19 +214,33 @@ namespace Wox.Plugin.Shell
} }
else else
{ {
return; throw new NotImplementedException();
} }
info.UseShellExecute = true; info.UseShellExecute = true;
_settings.AddCmdHistory(command);
return info;
}
private void Execute(Func<ProcessStartInfo, Process> startProcess,ProcessStartInfo info)
{
try try
{ {
Process.Start(info); startProcess(info);
_settings.AddCmdHistory(command);
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
MessageBox.Show($"Command not found: {e.Message}"); var name = "Plugin: Shell";
var message = $"Command not found: {e.Message}";
_context.API.ShowMsg(name, message);
}
catch(Win32Exception e)
{
var name = "Plugin: Shell";
var message = $"Error running the command: {e.Message}";
_context.API.ShowMsg(name, message);
} }
} }
@ -306,19 +322,31 @@ namespace Wox.Plugin.Shell
public List<Result> LoadContextMenus(Result selectedResult) public List<Result> LoadContextMenus(Result selectedResult)
{ {
return new List<Result> var resultlist = new List<Result>
{ {
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 Result
{ {
Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"), Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"),
Action = c => Action = c =>
{ {
Execute(selectedResult.Title, true); Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
return true; return true;
}, },
IcoPath = Image IcoPath = Image
} }
}; };
return resultlist;
} }
} }
} }

View File

@ -70,6 +70,9 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@ -56,12 +56,21 @@ namespace Wox.Plugin.Sys
var results = new List<Result>(); var results = new List<Result>();
foreach (var c in commands) foreach (var c in commands)
{ {
var titleScore = StringMatcher.FuzzySearch(query.Search, c.Title).ScoreAfterSearchPrecisionFilter(); var titleMatch = StringMatcher.FuzzySearch(query.Search, c.Title);
var subTitleScore = StringMatcher.FuzzySearch(query.Search, c.SubTitle).ScoreAfterSearchPrecisionFilter(); var subTitleMatch = StringMatcher.FuzzySearch(query.Search, c.SubTitle);
var score = Math.Max(titleScore, subTitleScore);
var score = Math.Max(titleMatch.Score, subTitleMatch.Score);
if (score > 0) if (score > 0)
{ {
c.Score = score; c.Score = score;
if (score == titleMatch.Score)
{
c.TitleHighlightData = titleMatch.MatchData;
}
else
{
c.SubTitleHighlightData = subTitleMatch.MatchData;
}
results.Add(c); results.Add(c);
} }
} }

View File

@ -20,12 +20,18 @@ namespace Wox.Infrastructure
public static void Initialize(Settings settings) public static void Initialize(Settings settings)
{ {
_settings = settings; _settings = settings;
InitializePinyinHelpers();
}
private static void InitializePinyinHelpers()
{
Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
Stopwatch.Normal("|Wox.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () => Stopwatch.Normal("|Wox.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () =>
{ {
_pinyinStorage = new BinaryStorage<ConcurrentDictionary<string, string[][]>>("Pinyin"); _pinyinStorage = new BinaryStorage<ConcurrentDictionary<string, string[][]>>("Pinyin");
PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary<string, string[][]>()); PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary<string, string[][]>());
// force pinyin library static constructor initialize // force pinyin library static constructor initialize
PinyinHelper.toHanyuPinyinStringArray('T', Format); PinyinHelper.toHanyuPinyinStringArray('T', Format);
}); });
@ -34,6 +40,10 @@ namespace Wox.Infrastructure
public static void Save() public static void Save()
{ {
if (!_settings.ShouldUsePinyin)
{
return;
}
_pinyinStorage.Save(PinyinCache); _pinyinStorage.Save(PinyinCache);
} }
@ -68,11 +78,13 @@ namespace Wox.Infrastructure
/// </summmary> /// </summmary>
public static string[][] PinyinComination(string characters) public static string[][] PinyinComination(string characters)
{ {
if (_settings.ShouldUsePinyin && !string.IsNullOrEmpty(characters)) if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters))
{ {
return Empty2DStringArray;
}
if (!PinyinCache.ContainsKey(characters)) if (!PinyinCache.ContainsKey(characters))
{ {
var allPinyins = new List<string[]>(); var allPinyins = new List<string[]>();
foreach (var c in characters) foreach (var c in characters)
{ {
@ -98,11 +110,6 @@ namespace Wox.Infrastructure
return PinyinCache[characters]; return PinyinCache[characters];
} }
} }
else
{
return Empty2DStringArray;
}
}
public static string Acronym(string[] pinyin) public static string Acronym(string[] pinyin)
{ {
@ -142,7 +149,5 @@ namespace Wox.Infrastructure
).ToArray(); ).ToArray();
return combination; return combination;
} }
} }
} }

View File

@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
using static Wox.Infrastructure.StringMatcher;
namespace Wox.Infrastructure namespace Wox.Infrastructure
{ {
@ -11,6 +13,7 @@ namespace Wox.Infrastructure
public static MatchOption DefaultMatchOption = new MatchOption(); public static MatchOption DefaultMatchOption = new MatchOption();
public static string UserSettingSearchPrecision { get; set; } public static string UserSettingSearchPrecision { get; set; }
public static bool ShouldUsePinyin { get; set; }
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] [Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
public static int Score(string source, string target) public static int Score(string source, string target)
@ -54,6 +57,9 @@ namespace Wox.Infrastructure
var firstMatchIndex = -1; var firstMatchIndex = -1;
var lastMatchIndex = 0; var lastMatchIndex = 0;
char ch; char ch;
var indexList = new List<int>();
for (var idx = 0; idx < len; idx++) for (var idx = 0; idx < len; idx++)
{ {
ch = stringToCompare[idx]; ch = stringToCompare[idx];
@ -63,6 +69,7 @@ namespace Wox.Infrastructure
firstMatchIndex = idx; firstMatchIndex = idx;
lastMatchIndex = idx + 1; lastMatchIndex = idx + 1;
indexList.Add(idx);
sb.Append(opt.Prefix + ch + opt.Suffix); sb.Append(opt.Prefix + ch + opt.Suffix);
patternIdx += 1; patternIdx += 1;
} }
@ -82,27 +89,38 @@ namespace Wox.Infrastructure
// return rendered string if we have a match for every char // return rendered string if we have a match for every char
if (patternIdx == pattern.Length) if (patternIdx == pattern.Length)
{ {
return new MatchResult var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex);
var pinyinScore = ScoreForPinyin(stringToCompare, query);
var result = new MatchResult
{ {
Success = true, Success = true,
Value = sb.ToString(), MatchData = indexList,
Score = CalScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex) RawScore = Math.Max(score, pinyinScore)
}; };
return result;
} }
return new MatchResult { Success = false }; return new MatchResult { Success = false };
} }
private static int CalScore(string query, string stringToCompare, int firstIndex, int matchLen) private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen)
{ {
//a match found near the beginning of a string is scored more than a match found near the end // A match found near the beginning of a string is scored more than a match found near the end
//a match is scored more if the characters in the patterns are closer to each other, while the score is lower if they are more spread out // A match is scored more if the characters in the patterns are closer to each other,
// while the score is lower if they are more spread out
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1)); var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
//a match with less characters assigning more weights
// A match with less characters assigning more weights
if (stringToCompare.Length - query.Length < 5) if (stringToCompare.Length - query.Length < 5)
{
score += 20; score += 20;
}
else if (stringToCompare.Length - query.Length < 10) else if (stringToCompare.Length - query.Length < 10)
{
score += 10; score += 10;
}
return score; return score;
} }
@ -114,21 +132,13 @@ namespace Wox.Infrastructure
None = 0 None = 0
} }
public static bool IsSearchPrecisionScoreMet(this MatchResult matchResult)
{
var precisionScore = (SearchPrecisionScore)Enum.Parse(typeof(SearchPrecisionScore),
UserSettingSearchPrecision ?? SearchPrecisionScore.Regular.ToString());
return matchResult.Score >= (int)precisionScore;
}
public static int ScoreAfterSearchPrecisionFilter(this MatchResult matchResult)
{
return matchResult.IsSearchPrecisionScoreMet() ? matchResult.Score : 0;
}
public static int ScoreForPinyin(string source, string target) public static int ScoreForPinyin(string source, string target)
{ {
if (!ShouldUsePinyin)
{
return 0;
}
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target)) if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target))
{ {
if (Alphabet.ContainsChinese(source)) if (Alphabet.ContainsChinese(source))
@ -158,12 +168,48 @@ namespace Wox.Infrastructure
public class MatchResult public class MatchResult
{ {
public bool Success { get; set; } public bool Success { get; set; }
public int Score { get; set; }
/// <summary> /// <summary>
/// highlight string /// The final score of the match result with all search precision filters applied.
/// </summary> /// </summary>
public string Value { get; set; } public int Score { get; private set; }
/// <summary>
/// The raw calculated search score without any search precision filtering applied.
/// </summary>
private int _rawScore;
public int RawScore
{
get { return _rawScore; }
set
{
_rawScore = value;
Score = ApplySearchPrecisionFilter(_rawScore);
}
}
/// <summary>
/// Matched data to highlight.
/// </summary>
public List<int> MatchData { get; set; }
public bool IsSearchPrecisionScoreMet()
{
return IsSearchPrecisionScoreMet(Score);
}
private bool IsSearchPrecisionScoreMet(int score)
{
var precisionScore = (SearchPrecisionScore)Enum.Parse(
typeof(SearchPrecisionScore),
UserSettingSearchPrecision ?? SearchPrecisionScore.Regular.ToString());
return score >= (int)precisionScore;
}
private int ApplySearchPrecisionFilter(int score)
{
return IsSearchPrecisionScoreMet(score) ? score : 0;
}
} }
public class MatchOption public class MatchOption

View File

@ -24,7 +24,17 @@ namespace Wox.Infrastructure.UserSettings
/// <summary> /// <summary>
/// when false Alphabet static service will always return empty results /// when false Alphabet static service will always return empty results
/// </summary> /// </summary>
public bool ShouldUsePinyin { get; set; } = true; private bool _shouldUsePinyin = true;
public bool ShouldUsePinyin
{
get { return _shouldUsePinyin; }
set
{
_shouldUsePinyin = value;
StringMatcher.ShouldUsePinyin = value;
}
}
private string _querySearchPrecision { get; set; } = StringMatcher.SearchPrecisionScore.Regular.ToString(); private string _querySearchPrecision { get; set; } = StringMatcher.SearchPrecisionScore.Regular.ToString();
public string QuerySearchPrecision public string QuerySearchPrecision

View File

@ -42,6 +42,16 @@ namespace Wox.Plugin
public int Score { get; set; } public int Score { get; set; }
/// <summary>
/// A list of indexes for the characters to be highlighted in Title
/// </summary>
public IList<int> TitleHighlightData { get; set; }
/// <summary>
/// A list of indexes for the characters to be highlighted in SubTitle
/// </summary>
public IList<int> SubTitleHighlightData { get; set; }
/// <summary> /// <summary>
/// Only resulsts that originQuery match with curren query will be displayed in the panel /// Only resulsts that originQuery match with curren query will be displayed in the panel
/// </summary> /// </summary>
@ -69,7 +79,9 @@ namespace Wox.Plugin
var equality = string.Equals(r?.Title, Title) && var equality = string.Equals(r?.Title, Title) &&
string.Equals(r?.SubTitle, SubTitle) && string.Equals(r?.SubTitle, SubTitle) &&
string.Equals(r?.IcoPath, IcoPath); string.Equals(r?.IcoPath, IcoPath) &&
TitleHighlightData == r.TitleHighlightData &&
SubTitleHighlightData == r.SubTitleHighlightData;
return equality; return equality;
} }

View File

@ -2,14 +2,65 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Wox.Plugin.SharedCommands namespace Wox.Plugin.SharedCommands
{ {
public static class ShellCommand public static class ShellCommand
{ {
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory="", string arguments = "", string verb = "") public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")] static extern bool EnumThreadWindows(uint threadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")] static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")] static extern int GetWindowTextLength(IntPtr hwnd);
private static bool containsSecurityWindow;
public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo)
{
processStartInfo.Verb = "RunAsUser";
var process = Process.Start(processStartInfo);
containsSecurityWindow = false;
while (!containsSecurityWindow) // wait for windows to bring up the "Windows Security" dialog
{
CheckSecurityWindow();
Thread.Sleep(25);
}
while (containsSecurityWindow) // while this process contains a "Windows Security" dialog, stay open
{
containsSecurityWindow = false;
CheckSecurityWindow();
Thread.Sleep(50);
}
return process;
}
private static void CheckSecurityWindow()
{
ProcessThreadCollection ptc = Process.GetCurrentProcess().Threads;
for (int i = 0; i < ptc.Count; i++)
EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero);
}
private static bool CheckSecurityThread(IntPtr hwnd, IntPtr lParam)
{
if (GetWindowTitle(hwnd) == "Windows Security")
containsSecurityWindow = true;
return true;
}
private static string GetWindowTitle(IntPtr hwnd)
{
StringBuilder sb = new StringBuilder(GetWindowTextLength(hwnd) + 1);
GetWindowText(hwnd, sb, sb.Capacity);
return sb.ToString();
}
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "")
{ {
var info = new ProcessStartInfo var info = new ProcessStartInfo
{ {

View File

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Infrastructure.UserSettings;
using Wox.Plugin; using Wox.Plugin;
namespace Wox.Test namespace Wox.Test
@ -48,14 +49,13 @@ namespace Wox.Test
"aac" "aac"
}; };
var results = new List<Result>(); var results = new List<Result>();
foreach (var str in sources) foreach (var str in sources)
{ {
results.Add(new Result results.Add(new Result
{ {
Title = str, Title = str,
Score = StringMatcher.FuzzySearch("inst", str).Score Score = StringMatcher.FuzzySearch("inst", str).RawScore
}); });
} }
@ -72,7 +72,7 @@ namespace Wox.Test
{ {
var compareString = "Can have rum only in my glass"; var compareString = "Can have rum only in my glass";
var scoreResult = StringMatcher.FuzzySearch(searchString, compareString).Score; var scoreResult = StringMatcher.FuzzySearch(searchString, compareString).RawScore;
Assert.True(scoreResult == 0); Assert.True(scoreResult == 0);
} }
@ -129,13 +129,12 @@ namespace Wox.Test
.ToList(); .ToList();
var results = new List<Result>(); var results = new List<Result>();
foreach (var str in searchStrings) foreach (var str in searchStrings)
{ {
results.Add(new Result results.Add(new Result
{ {
Title = str, Title = str,
Score = StringMatcher.FuzzySearch(searchTerm, str).Score Score = StringMatcher.FuzzySearch(searchTerm, str).RawScore
}); });
} }
@ -168,8 +167,11 @@ namespace Wox.Test
[TestCase("ccs", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("ccs", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("cand", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("cand", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("cand", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("cand", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)]
public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(string queryString, string compareString, public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(
int expectedPrecisionScore, bool expectedPrecisionResult) string queryString,
string compareString,
int expectedPrecisionScore,
bool expectedPrecisionResult)
{ {
var expectedPrecisionString = (StringMatcher.SearchPrecisionScore)expectedPrecisionScore; var expectedPrecisionString = (StringMatcher.SearchPrecisionScore)expectedPrecisionScore;
StringMatcher.UserSettingSearchPrecision = expectedPrecisionString.ToString(); StringMatcher.UserSettingSearchPrecision = expectedPrecisionString.ToString();

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
@ -56,6 +56,7 @@ namespace Wox
Alphabet.Initialize(_settings); Alphabet.Initialize(_settings);
StringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; StringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
StringMatcher.ShouldUsePinyin = _settings.ShouldUsePinyin;
PluginManager.LoadPlugins(_settings.PluginSettings); PluginManager.LoadPlugins(_settings.PluginSettings);
_mainVM = new MainViewModel(_settings); _mainVM = new MainViewModel(_settings);

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
namespace Wox.Converters
{
public class HighlightTextConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var text = value[0] as string;
var highlightData = value[1] as List<int>;
var textBlock = new Span();
if (highlightData == null || !highlightData.Any())
{
// No highlight data, just return the text
return new Run(text);
}
for (var i = 0; i < text.Length; i++)
{
var currentCharacter = text.Substring(i, 1);
if (this.ShouldHighlight(highlightData, i))
{
textBlock.Inlines.Add(new Bold(new Run(currentCharacter)));
}
else
{
textBlock.Inlines.Add(new Run(currentCharacter));
}
}
return textBlock;
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue };
}
private bool ShouldHighlight(List<int> highlightData, int index)
{
return highlightData.Contains(index);
}
}
}

View File

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Wox.ViewModel" xmlns:vm="clr-namespace:Wox.ViewModel"
xmlns:converter="clr-namespace:Wox.Converters"
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100" mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100"
d:DataContext="{d:DesignInstance vm:ResultsViewModel}" d:DataContext="{d:DesignInstance vm:ResultsViewModel}"
MaxHeight="{Binding MaxHeight}" MaxHeight="{Binding MaxHeight}"
@ -30,6 +31,9 @@
<Button.Content> <Button.Content>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5"
Cursor="Hand" UseLayoutRounding="False"> Cursor="Hand" UseLayoutRounding="False">
<Grid.Resources>
<converter:HighlightTextConverter x:Key="HighlightTextConverter"/>
</Grid.Resources>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="32" /> <ColumnDefinition Width="32" />
<ColumnDefinition /> <ColumnDefinition />
@ -44,9 +48,23 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Style="{DynamicResource ItemTitleStyle}" DockPanel.Dock="Left" <TextBlock Style="{DynamicResource ItemTitleStyle}" DockPanel.Dock="Left"
VerticalAlignment="Center" ToolTip="{Binding Result.Title}" x:Name="Title" VerticalAlignment="Center" ToolTip="{Binding Result.Title}" x:Name="Title"
Text="{Binding Result.Title}" /> Text="{Binding Result.Title}">
<vm:ResultsViewModel.FormattedText>
<MultiBinding Converter="{StaticResource HighlightTextConverter}">
<Binding Path="Result.Title" />
<Binding Path="Result.TitleHighlightData" />
</MultiBinding>
</vm:ResultsViewModel.FormattedText>
</TextBlock>
<TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding Result.SubTitle}" <TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding Result.SubTitle}"
Grid.Row="1" x:Name="SubTitle" Text="{Binding Result.SubTitle}" /> Grid.Row="1" x:Name="SubTitle" Text="{Binding Result.SubTitle}">
<vm:ResultsViewModel.FormattedText>
<MultiBinding Converter="{StaticResource HighlightTextConverter}">
<Binding Path="Result.SubTitle" />
<Binding Path="Result.SubTitleHighlightData" />
</MultiBinding>
</vm:ResultsViewModel.FormattedText>
</TextBlock>
</Grid> </Grid>
</Grid> </Grid>
</Button.Content> </Button.Content>

View File

@ -5,7 +5,6 @@ namespace Wox.ViewModel
{ {
public class RelayCommand : ICommand public class RelayCommand : ICommand
{ {
private Action<object> _action; private Action<object> _action;
public RelayCommand(Action<object> action) public RelayCommand(Action<object> action)

View File

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
using Wox.Plugin; using Wox.Plugin;
@ -197,8 +199,37 @@ namespace Wox.ViewModel
return results; return results;
} }
#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 Inline GetFormattedText(DependencyObject textBlock)
{
return (Inline)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null) return;
var inline = (Inline)e.NewValue;
textBlock.Inlines.Clear();
if (inline == null) return;
textBlock.Inlines.Add(inline);
}
#endregion #endregion
public class ResultCollection : ObservableCollection<ResultViewModel> public class ResultCollection : ObservableCollection<ResultViewModel>

View File

@ -158,6 +158,7 @@
<Compile Include="..\SolutionAssemblyInfo.cs"> <Compile Include="..\SolutionAssemblyInfo.cs">
<Link>Properties\SolutionAssemblyInfo.cs</Link> <Link>Properties\SolutionAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="Converters\HighlightTextConverter.cs" />
<Compile Include="Helper\SingletonWindowOpener.cs" /> <Compile Include="Helper\SingletonWindowOpener.cs" />
<Compile Include="PublicAPIInstance.cs" /> <Compile Include="PublicAPIInstance.cs" />
<Compile Include="ReportWindow.xaml.cs" /> <Compile Include="ReportWindow.xaml.cs" />