Compare commits

...

8 Commits

Author SHA1 Message Date
Divyansh Srivastava
79eda1681b Added fix to update text on navigation using up/down arrow (#4626)
* Added fix to update text on navigation using up/down arrow

* Fix incorrect alignment with ghost text

* Added tests
2020-07-06 09:40:10 +02:00
Arjun
1360359bba Changed flags for newly pressed key after invoking shortcut 2020-07-03 20:23:15 +02:00
Alekhya
1a10c1b4f9 Removed the race condition (#4735) 2020-07-03 19:36:18 +02:00
Arjun Balgovind
ae08b810bb Rework the HotkeyManager and KeyboardHook interop classes (#4710)
* Use GetAsyncKeyState calls and remove additional thread usage

* Removed Environment.Exit
2020-07-03 17:24:01 +02:00
Alekhya
2baaa1f20e Fix for Memory issue with context menu items (#4597)
* Added the inotifyPropertyChanged to all the properties and that stops the memory for shooting up

* some more inotify properties added

(cherry picked from commit 26fa05d9b661dadc5ab0257d540ab838a07c43a6)

* Revert "some more inotify properties added"

This reverts commit 845a94c9b2.

* Removed unnecessary inotifypropertychanged interfaces and cleaned up the code

* removed the ctrl+c from folder plugin

* removed unnecessary init

* Added unit test to check if PropertyChanged is called

* renamed var

* refactored the tests

* formatting and adding comments

* changed access modifier in test

* Used observable collection instead of a list

* clearing the observable collection instead of setting it to a new one
2020-07-02 22:55:19 +02:00
Yevhenii Holovachov
25f0ba19ca [FancyZones] Fixed shift behavior (#4653) 2020-07-02 15:38:25 +02:00
Enrico Giordani
32d873f41d Now working on 0.19.1 (#4602) 2020-07-02 15:37:57 +02:00
Clint Rutkas
772387a27a 0.19 readme update (#4583)
* Update README.md

* Update README.md

* Update README.md

* Update README.md
2020-06-30 18:39:13 +02:00
23 changed files with 417 additions and 205 deletions

View File

@@ -72,9 +72,9 @@ PowerToys will now enable two types of files to be previewed: Markdown (.md) & S
- Windows 10 1803 (build 17134) or later.
- Have [.NET Core 3.1 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.4-windows-x64-installer). The installer will prompt this but we want to directly make people aware.
### Via Github with MSI [Recommended]
### Via GitHub with MSI [Recommended]
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.18.2-x64.msi` to download the PowerToys installer.
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.19.0-x64.msi` to download the PowerToys installer.
**Note:** After installing, you will have to start PowerToys for the first time. We will improve install experience this moving forward but due to a possible install dependency, we can't start after install currently.
@@ -107,8 +107,9 @@ choco upgrade powertoys
### Known issues
- [#2012 - Uninstalling with old control panel fails](https://github.com/microsoft/PowerToys/issues/2012): Please use the modern settings to uninstall. `Windows 10 Settings -> Apps -> Apps & features`
- [#3384 - PowerToys Settings window is empty](https://github.com/microsoft/PowerToys/issues/3384): Workaround appears to be run as admin. We are proactively looking into this as a hotfix.
- PT Run, Newly installed apps can't be found [#3553](https://github.com/microsoft/PowerToys/issues/3553). We will address this in 0.20.
- PT Run, CPU / Memory, still investigating [#3208](https://github.com/microsoft/PowerToys/issues/3208). We have 2 leads and fixed one item.
- WinKey remapping for PT Run can be quirky [#4578](https://github.com/microsoft/PowerToys/issues/4578)
### Processor support
@@ -120,26 +121,29 @@ We currently support the matrix below.
## What's Happening
### May 2020 Update
### June 2020 Update
Our goals for 0.18 release cycle was three big items, PowerToys Run, Keyboard manager, and migrating to the new settings system. This is also the first time we'll test out the auto-updating system.
Our goals for 0.19 release cycle had one big goal, add in stability / quality fixes. We've addressed over 100 issues across all our utilities. We've improved our installer experience and parts will start coming online in 0.19 and 0.20. In this release, it will be the last time during upgrade you'll see Windows Explorer flash on you. For 0.20, the .NET Core install experience much smoother.
Feedback is critical. We know there are areas for improvement on PT Run. We would love feedback so we can improve. We also would love to know if you want us to be more aggressive on auto-upgrading.
We'd also stress feedback is critical. We know there are areas for improvement on PowerToys Run. We would love feedback so we can improve. We also would love to know if you want us to be more aggressive on auto-upgrading.
- We shipped [v0.18][github-release-link]!
- New Utilities
- PowerToys Run, our new application launcher (use alt-space to activate)
- Keyboard manager, a quick easy way to remap your keyboard
- Fixed [#243](https://github.com/microsoft/PowerToys/issues/243)'s setting issue
- Improved performance on FancyZones [#1264](https://github.com/microsoft/PowerToys/issues/1264)
- Lots of bug fixes!
Lastly, we'd like to thank everyone who filed a bug, gave feedback or made a pull-request. The PowerToys team is extremely grateful to have the support of an amazing active community.
For [0.19](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F4), we are proactively working on:
- We shipped [v0.19][github-release-link]!
- Big push for PowerToys Run search quality fixes
- PowerToys Run can now remap to any key shortcut (minus restricted ones such as WinKey+L)
- Improved FancyZones on Virtual Desktops and multi-thread design
- Installer after 0.19 will no longer restart Windows Explorer
- Fixed [#2012 - Uninstalling with old control panel fails](https://github.com/microsoft/PowerToys/issues/2012)
- Fixed [#3384 - PowerToys Settings window is empty](https://github.com/microsoft/PowerToys/issues/3384)
- Over 100 bug fixes!
- Enable PT Run to be mapped to Win-Keys
- Stability / tech debt fixes
- Performance improvements with FancyZones
- A testing utility for FancyZones to be sure we can test different window configurations.
For [0.20](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F6), we are proactively working on:
- Stability
- Start work on FZ Editor V2
- Start work on OOBE improvements
- Keyboard manager improvements
### Version 1.0 plan

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Version>0.19.0</Version>
<Version>0.19.1</Version>
<DefineConstants>Version=$(Version);</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -5,15 +5,14 @@ using namespace interop;
HotkeyManager::HotkeyManager()
{
keyboardEventCallback = gcnew KeyboardEventCallback(this, &HotkeyManager::KeyboardEventProc);
keyboardEventCallback = gcnew KeyboardEventCallback(this, &HotkeyManager::KeyboardEventProc);
isActiveCallback = gcnew IsActiveCallback(this, &HotkeyManager::IsActiveProc);
filterKeyboardCallback = gcnew FilterKeyboardEvent(this, &HotkeyManager::FilterKeyboardProc);
keyboardHook = gcnew KeyboardHook(
keyboardEventCallback,
isActiveCallback,
filterKeyboardCallback
);
filterKeyboardCallback);
hotkeys = gcnew Dictionary<HOTKEY_HANDLE, HotkeyCallback ^>();
pressedKeys = gcnew Hotkey();
keyboardHook->Start();
@@ -25,8 +24,9 @@ HotkeyManager::~HotkeyManager()
}
// When all Shortcut keys are pressed, fire the HotkeyCallback event.
void HotkeyManager::KeyboardEventProc(KeyboardEvent^ ev)
void HotkeyManager::KeyboardEventProc(KeyboardEvent ^ ev)
{
// pressedKeys always stores the latest keyboard state
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
if (hotkeys->ContainsKey(pressedKeysHandle))
{
@@ -42,22 +42,24 @@ bool HotkeyManager::IsActiveProc()
}
// KeyboardEvent callback is only fired for relevant key events.
bool HotkeyManager::FilterKeyboardProc(KeyboardEvent^ ev)
bool HotkeyManager::FilterKeyboardProc(KeyboardEvent ^ ev)
{
auto oldHandle = GetHotkeyHandle(pressedKeys);
// Updating the pressed keys here so we know if the keypress event
// should be propagated or not.
UpdatePressedKeys(ev);
// Updating the pressed keys here so we know if the keypress event should be propagated or not.
pressedKeys->Win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
pressedKeys->Ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
pressedKeys->Alt = GetAsyncKeyState(VK_MENU) & 0x8000;
pressedKeys->Shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
pressedKeys->Key = ev->key;
// Convert to hotkey handle
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
// Check if the hotkey matches the pressed keys, and check if the pressed keys aren't duplicate
// (there shouldn't be auto repeating hotkeys)
if (hotkeys->ContainsKey(pressedKeysHandle) && oldHandle != pressedKeysHandle)
// Check if any hotkey matches the pressed keys if the current key event is a key down event
if ((ev->message == WM_KEYDOWN || ev->message == WM_SYSKEYDOWN) && hotkeys->ContainsKey(pressedKeysHandle))
{
return true;
}
return false;
}
@@ -83,51 +85,3 @@ HOTKEY_HANDLE HotkeyManager::GetHotkeyHandle(Hotkey ^ hotkey)
handle |= hotkey->Alt << 11;
return handle;
}
void HotkeyManager::UpdatePressedKey(DWORD code, bool replaceWith, unsigned char replaceWithKey)
{
switch (code)
{
case VK_LWIN:
case VK_RWIN:
pressedKeys->Win = replaceWith;
break;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
pressedKeys->Ctrl = replaceWith;
break;
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
pressedKeys->Shift = replaceWith;
break;
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
pressedKeys->Alt = replaceWith;
break;
default:
pressedKeys->Key = replaceWithKey;
break;
}
}
void HotkeyManager::UpdatePressedKeys(KeyboardEvent ^ ev)
{
switch (ev->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
{
UpdatePressedKey(ev->key, true, ev->key);
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
{
UpdatePressedKey(ev->key, false, 0);
}
break;
}
}

View File

@@ -26,7 +26,7 @@ public
public
delegate void HotkeyCallback();
typedef unsigned short HOTKEY_HANDLE;
typedef unsigned short HOTKEY_HANDLE;
public
ref class HotkeyManager
@@ -46,12 +46,9 @@ public
IsActiveCallback ^ isActiveCallback;
FilterKeyboardEvent ^ filterKeyboardCallback;
void KeyboardEventProc(KeyboardEvent ^ ev);
bool IsActiveProc();
bool FilterKeyboardProc(KeyboardEvent ^ ev);
HOTKEY_HANDLE GetHotkeyHandle(Hotkey ^ hotkey);
void UpdatePressedKeys(KeyboardEvent ^ ev);
void UpdatePressedKey(DWORD code, bool replaceWith, unsigned char replaceWithKey);
};
}

View File

@@ -15,8 +15,6 @@ KeyboardHook::KeyboardHook(
IsActiveCallback ^ isActiveCallback,
FilterKeyboardEvent ^ filterKeyboardEvent)
{
kbEventDispatch = gcnew Thread(gcnew ThreadStart(this, &KeyboardHook::DispatchProc));
queue = gcnew Queue<KeyboardEvent ^>();
this->keyboardEventCallback = keyboardEventCallback;
this->isActiveCallback = isActiveCallback;
this->filterKeyboardEvent = filterKeyboardEvent;
@@ -24,44 +22,10 @@ KeyboardHook::KeyboardHook(
KeyboardHook::~KeyboardHook()
{
quit = true;
// Notify the DispatchProc thread so that it isn't stuck at the Wait step
Monitor::Enter(queue);
Monitor::Pulse(queue);
Monitor::Exit(queue);
kbEventDispatch->Join();
// Unregister low level hook procedure
UnhookWindowsHookEx(hookHandle);
}
void KeyboardHook::DispatchProc()
{
Monitor::Enter(queue);
quit = false;
while (!quit)
{
if (queue->Count == 0)
{
Monitor::Wait(queue);
continue;
}
auto nextEv = queue->Dequeue();
// Release lock while callback is being invoked
Monitor::Exit(queue);
keyboardEventCallback->Invoke(nextEv);
// Re-aquire lock
Monitor::Enter(queue);
}
Monitor::Exit(queue);
}
void KeyboardHook::Start()
{
hookProc = gcnew HookProcDelegate(this, &KeyboardHook::HookProc);
@@ -85,8 +49,6 @@ void KeyboardHook::Start()
throw std::exception("SetWindowsHookEx failed.");
}
}
kbEventDispatch->Start();
}
LRESULT CALLBACK KeyboardHook::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
@@ -101,10 +63,7 @@ LRESULT CALLBACK KeyboardHook::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
}
Monitor::Enter(queue);
queue->Enqueue(ev);
Monitor::Pulse(queue);
Monitor::Exit(queue);
keyboardEventCallback->Invoke(ev);
return 1;
}
return CallNextHookEx(hookHandle, nCode, wParam, lParam);

View File

@@ -33,16 +33,12 @@ public
private:
delegate LRESULT HookProcDelegate(int nCode, WPARAM wParam, LPARAM lParam);
Thread ^ kbEventDispatch;
Queue<KeyboardEvent ^> ^ queue;
KeyboardEventCallback ^ keyboardEventCallback;
IsActiveCallback ^ isActiveCallback;
FilterKeyboardEvent ^ filterKeyboardEvent;
bool quit;
HHOOK hookHandle;
HookProcDelegate ^ hookProc;
void DispatchProc();
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
};

View File

@@ -9,13 +9,7 @@
template<uint16_t APIVersion>
bool IsAPIContractVxAvailable()
{
static bool isAPIContractVxAvailableInitialized = false;
static bool isAPIContractVxAvailable = false;
if (!isAPIContractVxAvailableInitialized)
{
isAPIContractVxAvailableInitialized = true;
isAPIContractVxAvailable = winrt::Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", APIVersion);
}
static bool isAPIContractVxAvailable = winrt::Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", APIVersion);
return isAPIContractVxAvailable;
}

View File

@@ -14,7 +14,7 @@ namespace Microsoft.PowerLauncher.Telemetry
/// <summary>
/// Gets The version string. TODO: This should be replaced by a P/Invoke call to get_product_version
/// </summary>
public string Version => "v0.19.0";
public string Version => "v0.19.1";
public double BootTimeMs { get; set; }

View File

@@ -74,9 +74,6 @@ namespace Microsoft.PowerToys.Settings.UI.Runner
MessageBoxButton.OK);
app.Shutdown();
}
// Terminate all threads of the process
Environment.Exit(0);
}
}

View File

@@ -9,7 +9,7 @@
<Identity
Name="f4f787a5-f0ae-47a9-be89-5408b1dd2b47"
Publisher="CN=lamotile"
Version="0.19.0.0" />
Version="0.19.1.0" />
<mp:PhoneIdentity PhoneProductId="f4f787a5-f0ae-47a9-be89-5408b1dd2b47" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -173,10 +173,7 @@ void WindowMoveHandlerPrivate::MoveSizeStart(HWND window, HMONITOR monitor, POIN
m_mouseHook->enable();
}
if (m_settings->GetSettings()->shiftDrag)
{
m_shiftHook->enable();
}
m_shiftHook->enable();
// This updates m_dragEnabled depending on if the shift key is being held down.
UpdateDragState(window);

View File

@@ -459,15 +459,15 @@ namespace KeyboardEventHandlers
i++;
}
// key down for original shortcut action key
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if (isActionKeyPressed)
{
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
// Send current key pressed
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++;
// Send dummy key since the current key pressed could be a modifier
@@ -541,15 +541,15 @@ namespace KeyboardEventHandlers
i++;
}
// key down for original shortcut action key
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if (isActionKeyPressed)
{
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
// Send current key pressed
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++;
// Send dummy key

View File

@@ -10,7 +10,7 @@ namespace Microsoft.PowerLauncher.Telemetry
/// <summary>
/// TODO: This should be replaced by a P/Invoke call to get_product_version
/// </summary>
public string Version => "v0.19.0";
public string Version => "v0.19.1";
public double BootTimeMs { get; set; }

View File

@@ -110,22 +110,31 @@ namespace PowerLauncher
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Visibility == System.Windows.Visibility.Visible)
if(e.PropertyName == nameof(MainViewModel.MainWindowVisibility))
{
// Not called on first launch
// Additionally called when deactivated by clicking on screen
UpdatePosition();
BringProcessToForeground();
if (!_viewModel.LastQuerySelected)
if (Visibility == System.Windows.Visibility.Visible)
{
_viewModel.LastQuerySelected = true;
// Not called on first launch
// Additionally called when deactivated by clicking on screen
UpdatePosition();
BringProcessToForeground();
if (!_viewModel.LastQuerySelected)
{
_viewModel.LastQuerySelected = true;
}
}
}
}
else if (e.PropertyName == nameof(MainViewModel.SystemQueryText))
{
this._isTextSetProgrammatically = true;
SearchBox.QueryTextBox.Text = _viewModel.SystemQueryText;
if (_viewModel.Results != null)
{
SearchBox.QueryTextBox.Text = MainViewModel.GetSearchText(
_viewModel.Results.SelectedIndex,
_viewModel.SystemQueryText,
_viewModel.QueryText);
}
}
}
@@ -261,32 +270,20 @@ namespace PowerLauncher
}
// 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
SearchBox.AutoCompleteTextBlock.Text = ListView_FirstItem(_viewModel.QueryText);
// Setting it here instead of when the text is changed as there is a delay in executing the query and populating the result
if (_viewModel.Results != null)
{
SearchBox.AutoCompleteTextBlock.Text = MainViewModel.GetAutoCompleteText(
_viewModel.Results.SelectedIndex,
_viewModel.Results.SelectedItem?.ToString(),
_viewModel.QueryText);
}
}
private const int millisecondsToWait = 100;
private static DateTime s_lastTimeOfTyping;
private bool disposedValue = false;
private string ListView_FirstItem(String input)
{
if (!string.IsNullOrEmpty(input))
{
string selectedItem = _viewModel.Results?.SelectedItem?.ToString();
int selectedIndex = _viewModel.Results.SelectedIndex;
if (selectedItem != null && selectedIndex == 0)
{
if (selectedItem.IndexOf(input, StringComparison.InvariantCultureIgnoreCase) == 0)
{
// Use the same case as the input query for the matched portion of the string
return input + selectedItem.Substring(input.Length);
}
}
}
return string.Empty;
}
private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_isTextSetProgrammatically)

View File

@@ -11,7 +11,7 @@ namespace Wox.Plugin
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

View File

@@ -0,0 +1,211 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using Wox.Plugin;
using Wox.ViewModel;
namespace Wox.Test
{
[TestFixture]
class MainViewModelTest
{
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenInputIsNull()
{
// Arrange
int index = 0;
string input = null;
String query = "M";
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenInputIsEmpty()
{
// Arrange
int index = 0;
string input = string.Empty;
String query = "M";
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenQueryIsNull()
{
// Arrange
int index = 0;
string input = "M";
String query = null;
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenQueryIsEmpty()
{
// Arrange
int index = 0;
string input = "M";
String query = string.Empty;
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenIndexIsNonZero()
{
// Arrange
int index = 2;
string input = "Visual";
String query = "Vis";
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsMatchingString_WhenIndexIsZeroAndMatch()
{
// Arrange
int index = 0;
string input = "VISUAL";
String query = "VIs";
string ExpectedAutoCompleteText = "VIsUAL";
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, ExpectedAutoCompleteText);
}
[Test]
public void MainViewModel_GetAutoCompleteTextReturnsEmptyString_WhenIndexIsZeroAndNoMatch()
{
// Arrange
int index = 0;
string input = "VISUAL";
String query = "Vim";
string ExpectedAutoCompleteText = string.Empty;
// Act
string autoCompleteText = MainViewModel.GetAutoCompleteText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, ExpectedAutoCompleteText);
}
[Test]
public void MainViewModel_GetSearchTextReturnsEmptyString_WhenInputIsNull()
{
// Arrange
int index = 0;
string input = null;
String query = "M";
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetSearchTextReturnsEmptyString_WhenInputIsEmpty()
{
// Arrange
int index = 0;
string input = string.Empty;
String query = "M";
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, string.Empty);
}
[Test]
public void MainViewModel_GetSearchTextReturnsInputString_WhenQueryIsNull()
{
// Arrange
int index = 0;
string input = "Visual";
String query = null;
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, input);
}
[Test]
public void MainViewModel_GetSearchTextReturnsInputString_WhenQueryIsEmpty()
{
// Arrange
int index = 0;
string input = "Visual";
String query = string.Empty;
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, input);
}
[Test]
public void MainViewModel_GetSearchTextReturnsMatchingStringWithCase_WhenIndexIsZeroAndMatch()
{
// Arrange
int index = 0;
string input = "VISUAL";
String query = "VIs";
string ExpectedAutoCompleteText = "VIsUAL";
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, ExpectedAutoCompleteText);
}
[Test]
public void MainViewModel_GetSearchTextReturnsInput_WhenIndexIsZeroAndNoMatch()
{
// Arrange
int index = 0;
string input = "VISUAL";
String query = "Vim";
// Act
string autoCompleteText = MainViewModel.GetSearchText(index, input, query);
// Assert
Assert.AreEqual(autoCompleteText, input);
}
}
}

View File

@@ -44,6 +44,7 @@
<ProjectReference Include="..\Wox.Core\Wox.Core.csproj" />
<ProjectReference Include="..\Wox.Infrastructure\Wox.Infrastructure.csproj" />
<ProjectReference Include="..\Wox.Plugin\Wox.Plugin.csproj" />
<ProjectReference Include="..\Wox\Wox.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,58 @@
using System.Windows.Input;
using NUnit.Framework;
using Wox.Plugin;
using Wox.ViewModel;
using System.Runtime.CompilerServices;
namespace Wox.Test
{
[TestFixture]
public class Wox
{
// A Dummy class to test that OnPropertyChanged() is called while we set the variable
public class DummyTestClass : BaseModel
{
public bool isFunctionCalled = false;
private ICommand _item;
public ICommand Item
{
get
{
return this._item;
}
set
{
if (value != this._item)
{
this._item = value;
OnPropertyChanged();
}
}
}
// Overriding the OnPropertyChanged() function to test if it is being called
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
isFunctionCalled = true;
}
}
[Test]
public void AnyVariable_MustCallOnPropertyChanged_WhenSet()
{
// Arrange
DummyTestClass testClass = new DummyTestClass();
// Act
testClass.Item = new RelayCommand(null);
// Assert
Assert.IsTrue(testClass.isFunctionCalled);
}
}
}

View File

@@ -9,11 +9,29 @@ namespace Wox.ViewModel
{
public class ContextMenuItemViewModel : BaseModel
{
private ICommand _command;
public string PluginName { get; set; }
public string Title { get; set; }
public string Glyph { get; set; }
public string FontFamily { get; set; }
public ICommand Command { get; set; }
public ICommand Command {
get
{
return this._command;
}
set
{
// ICommand does not implement the INotifyPropertyChanged interface and must call OnPropertyChanged() to prevent memory leaks
if (value != this._command)
{
this._command = value;
OnPropertyChanged();
}
}
}
public Key AcceleratorKey { get; set; }
public ModifierKeys AcceleratorModifiers { get; set; }
public bool IsAcceleratorKeyEnabled { get; set; }

View File

@@ -709,6 +709,40 @@ namespace Wox.ViewModel
}
}
public static string GetAutoCompleteText(int index, string input, String query)
{
if (!string.IsNullOrEmpty(input) && !string.IsNullOrEmpty(query))
{
if (index == 0)
{
if (input.IndexOf(query, StringComparison.InvariantCultureIgnoreCase) == 0)
{
// Use the same case as the input query for the matched portion of the string
return query + input.Substring(query.Length);
}
}
}
return string.Empty;
}
public static string GetSearchText(int index, String input, string query)
{
if (!string.IsNullOrEmpty(input))
{
if (index == 0 && !string.IsNullOrEmpty(query))
{
if (input.IndexOf(query, StringComparison.InvariantCultureIgnoreCase) == 0)
{
return query + input.Substring(query.Length);
}
}
return input;
}
return string.Empty;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)

View File

@@ -22,7 +22,7 @@ namespace Wox.ViewModel
Hover
};
public List<ContextMenuItemViewModel> ContextMenuItems { get; set; }
public ObservableCollection<ContextMenuItemViewModel> ContextMenuItems { get; set; } = new ObservableCollection<ContextMenuItemViewModel>();
public ICommand ActivateContextButtonsHoverCommand { get; set; }
public ICommand ActivateContextButtonsSelectionCommand { get; set; }
@@ -46,7 +46,10 @@ namespace Wox.ViewModel
{
Result = result;
}
ContextMenuSelectedIndex = NoSelectionIndex;
LoadContextMenu();
ActivateContextButtonsHoverCommand = new RelayCommand(ActivateContextButtonsHoverAction);
ActivateContextButtonsSelectionCommand = new RelayCommand(ActivateContextButtonsSelectionAction);
DeactivateContextButtonsHoverCommand = new RelayCommand(DeactivateContextButtonsHoverAction);
@@ -64,11 +67,6 @@ namespace Wox.ViewModel
}
public void ActivateContextButtons(ActivationType activationType)
{
if (ContextMenuItems == null)
{
LoadContextMenu();
}
// Result does not contain any context menu items - we don't need to show the context menu ListView at all.
if (ContextMenuItems.Count > 0)
{
@@ -78,14 +76,13 @@ namespace Wox.ViewModel
{
AreContextButtonsActive = false;
}
if (activationType == ActivationType.Selection)
{
IsSelected = true;
EnableContextMenuAcceleratorKeys();
}
else if(activationType == ActivationType.Hover)
else if (activationType == ActivationType.Hover)
{
IsHovered = true;
}
@@ -122,17 +119,17 @@ namespace Wox.ViewModel
else
{
AreContextButtonsActive = false;
}
}
}
public void LoadContextMenu()
{
var results = PluginManager.GetContextMenusForPlugin(Result);
var newItems = new List<ContextMenuItemViewModel>();
ContextMenuItems.Clear();
foreach (var r in results)
{
newItems.Add(new ContextMenuItemViewModel
ContextMenuItems.Add(new ContextMenuItemViewModel()
{
PluginName = r.PluginName,
Title = r.Title,
@@ -155,13 +152,11 @@ namespace Wox.ViewModel
})
});
}
ContextMenuItems = newItems;
}
private void EnableContextMenuAcceleratorKeys()
{
foreach(var i in ContextMenuItems)
foreach (var i in ContextMenuItems)
{
i.IsAcceleratorKeyEnabled = true;
}
@@ -192,7 +187,7 @@ namespace Wox.ViewModel
imagePath = ImageLoader.ErrorIconPath;
}
}
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
return ImageLoader.Load(imagePath);
}
@@ -201,10 +196,10 @@ namespace Wox.ViewModel
//Returns false if we've already reached the last item.
public bool SelectNextContextButton()
{
if(ContextMenuSelectedIndex == (ContextMenuItems.Count -1))
if (ContextMenuSelectedIndex == (ContextMenuItems.Count - 1))
{
ContextMenuSelectedIndex = NoSelectionIndex;
return false;
return false;
}
ContextMenuSelectedIndex++;

View File

@@ -17,7 +17,7 @@ namespace MarkdownPreviewHandler.Telemetry.Events
/// <summary>
/// Gets The version string. TODO: This should be replaced by a P/Invoke call to get_product_version.
/// </summary>
public string Version => "v0.19.0";
public string Version => "v0.19.1";
/// <inheritdoc/>
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;

View File

@@ -17,7 +17,7 @@ namespace SvgPreviewHandler.Telemetry.Events
/// <summary>
/// Gets The version string. TODO: This should be replaced by a P/Invoke call to get_product_version.
/// </summary>
public string Version => "v0.19.0";
public string Version => "v0.19.1";
/// <inheritdoc/>
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;