[Flyout][Hotfix]Appear next to the tray icon position (#23772)

* [Flyout]Appear next to the tray icon position

* fix spellchecker
This commit is contained in:
Jaime Bernardo
2023-02-03 15:10:14 +00:00
committed by GitHub
parent 2e047e04de
commit fc8ee435e6
8 changed files with 153 additions and 48 deletions

View File

@@ -316,7 +316,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args,
DWORD g_settings_process_id = 0; DWORD g_settings_process_id = 0;
void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window, bool show_flyout = false) void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position = std::nullopt)
{ {
g_isLaunchInProgress = true; g_isLaunchInProgress = true;
@@ -389,21 +389,31 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
// Arg 10: should flyout be shown // Arg 10: should flyout be shown
std::wstring settings_showFlyout = show_flyout ? L"true" : L"false"; std::wstring settings_showFlyout = show_flyout ? L"true" : L"false";
// Arg 11: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false";
// Arg 12: contains if there's flyout coordinates. If true, will add two extra arguments to the call containing the x and y coordinates.
std::wstring settings_containsFlyoutPosition = flyout_position.has_value() ? L"true" : L"false";
// Args 13, .... : Optional arguments depending on the options presented before. All by the same value.
// create general settings file to initialize the settings file with installation configurations like : // create general settings file to initialize the settings file with installation configurations like :
// 1. Run on start up. // 1. Run on start up.
PTSettingsHelper::save_general_settings(save_settings.to_json()); PTSettingsHelper::save_general_settings(save_settings.to_json());
std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}", std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}",
executable_path, executable_path,
powertoys_pipe_name, powertoys_pipe_name,
settings_pipe_name, settings_pipe_name,
std::to_wstring(powertoys_pid), std::to_wstring(powertoys_pid),
settings_theme, settings_theme,
settings_elevatedStatus, settings_elevatedStatus,
settings_isUserAnAdmin, settings_isUserAnAdmin,
settings_showOobe, settings_showOobe,
settings_showScoobe, settings_showScoobe,
settings_showFlyout); settings_showFlyout,
settings_containsSettingsWindow,
settings_containsFlyoutPosition);
if (settings_window.has_value()) if (settings_window.has_value())
{ {
@@ -411,6 +421,14 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
executable_args.append(settings_window.value()); executable_args.append(settings_window.value());
} }
if (flyout_position)
{
executable_args.append(L" ");
executable_args.append(std::to_wstring(flyout_position.value().x));
executable_args.append(L" ");
executable_args.append(std::to_wstring(flyout_position.value().y));
}
BOOL process_created = false; BOOL process_created = false;
// Commented out to fix #22659 // Commented out to fix #22659
@@ -550,7 +568,7 @@ void bring_settings_to_front()
EnumWindows(callback, 0); EnumWindows(callback, 0);
} }
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout = false) void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position)
{ {
if (g_settings_process_id != 0) if (g_settings_process_id != 0)
{ {
@@ -558,7 +576,14 @@ void open_settings_window(std::optional<std::wstring> settings_window, bool show
{ {
if (current_settings_ipc) if (current_settings_ipc)
{ {
current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}"); if (!flyout_position.has_value())
{
current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}");
}
else
{
current_settings_ipc->send(fmt::format(L"{{\"ShowYourself\":\"flyout\", \"x_position\":{}, \"y_position\":{} }}", std::to_wstring(flyout_position.value().x), std::to_wstring(flyout_position.value().y)));
}
} }
} }
else else
@@ -575,8 +600,8 @@ void open_settings_window(std::optional<std::wstring> settings_window, bool show
{ {
if (!g_isLaunchInProgress) if (!g_isLaunchInProgress)
{ {
std::thread([settings_window, show_flyout]() { std::thread([settings_window, show_flyout, flyout_position]() {
run_settings_window(false, false, settings_window, show_flyout); run_settings_window(false, false, settings_window, show_flyout, flyout_position);
}).detach(); }).detach();
} }
} }
@@ -608,13 +633,6 @@ void open_scoobe_window()
}).detach(); }).detach();
} }
void open_flyout()
{
std::thread([]() {
run_settings_window(false, false, std::nullopt, true);
}).detach();
}
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
{ {
switch (value) switch (value)

View File

@@ -22,7 +22,7 @@ enum class ESettingsWindowNames
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); std::string ESettingsWindowNames_to_string(ESettingsWindowNames value);
ESettingsWindowNames ESettingsWindowNames_from_string(std::string value); ESettingsWindowNames ESettingsWindowNames_from_string(std::string value);
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout); void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position);
void close_settings_window(); void close_settings_window();
void open_oobe_window(); void open_oobe_window();

View File

@@ -32,6 +32,8 @@ namespace
HMENU h_sub_menu = nullptr; HMENU h_sub_menu = nullptr;
bool double_click_timer_running = false; bool double_click_timer_running = false;
bool double_clicked = false; bool double_clicked = false;
POINT tray_icon_click_point;
} }
// Struct to fill with callback and the data. The window_proc is responsible for cleaning it. // Struct to fill with callback and the data. The window_proc is responsible for cleaning it.
@@ -123,7 +125,7 @@ void click_timer_elapsed()
double_click_timer_running = false; double_click_timer_running = false;
if (!double_clicked) if (!double_clicked)
{ {
open_settings_window(std::nullopt, true); open_settings_window(std::nullopt, true, tray_icon_click_point);
} }
} }
@@ -212,6 +214,9 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam
// ignore event if this is the second click of a double click // ignore event if this is the second click of a double click
if (!double_click_timer_running) if (!double_click_timer_running)
{ {
// save the cursor position for sending where to show the popup.
GetCursorPos(&tray_icon_click_point);
// start timer for detecting single or double click // start timer for detecting single or double click
double_click_timer_running = true; double_click_timer_running = true;
double_clicked = false; double_clicked = false;

View File

@@ -7,7 +7,7 @@ void start_tray_icon();
// Stop the Tray Icon // Stop the Tray Icon
void stop_tray_icon(); void stop_tray_icon();
// Open the Settings Window // Open the Settings Window
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout); void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position = std::nullopt);
// Callback type to be called by the tray icon loop // Callback type to be called by the tray icon loop
typedef void (*main_loop_callback_function)(PVOID); typedef void (*main_loop_callback_function)(PVOID);
// Calls a callback in _callback // Calls a callback in _callback

View File

@@ -37,12 +37,12 @@ namespace Microsoft.PowerToys.Settings.UI
ShowOobeWindow, ShowOobeWindow,
ShowScoobeWindow, ShowScoobeWindow,
ShowFlyout, ShowFlyout,
SettingsWindow, ContainsSettingsWindow,
ContainsFlyoutPosition,
} }
// Quantity of arguments // Quantity of arguments
private const int RequiredArgumentsQty = 10; private const int RequiredArgumentsQty = 12;
private const int RequiredAndOptionalArgumentsQty = 11;
// Create an instance of the IPC wrapper. // Create an instance of the IPC wrapper.
private static TwoWayPipeMessageIPCManaged ipcmanager; private static TwoWayPipeMessageIPCManaged ipcmanager;
@@ -122,11 +122,16 @@ namespace Microsoft.PowerToys.Settings.UI
ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true"; ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true";
ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true"; ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true";
ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true"; ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true";
bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true";
bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true";
if (cmdArgs.Length == RequiredAndOptionalArgumentsQty) // To keep track of variable arguments
int currentArgumentIndex = RequiredArgumentsQty;
if (containsSettingsWindow)
{ {
// open specific window // open specific window
switch (cmdArgs[(int)Arguments.SettingsWindow]) switch (cmdArgs[currentArgumentIndex])
{ {
case "Overview": StartupPage = typeof(Views.GeneralPage); break; case "Overview": StartupPage = typeof(Views.GeneralPage); break;
case "AlwaysOnTop": StartupPage = typeof(Views.AlwaysOnTopPage); break; case "AlwaysOnTop": StartupPage = typeof(Views.AlwaysOnTopPage); break;
@@ -148,6 +153,17 @@ namespace Microsoft.PowerToys.Settings.UI
case "Hosts": StartupPage = typeof(Views.HostsPage); break; case "Hosts": StartupPage = typeof(Views.HostsPage); break;
default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break; default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break;
} }
currentArgumentIndex++;
}
int flyout_x = 0;
int flyout_y = 0;
if (containsFlyoutPosition)
{
// get the flyout position arguments
int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x);
int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y);
} }
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () => RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
@@ -193,7 +209,13 @@ namespace Microsoft.PowerToys.Settings.UI
} }
else if (ShowFlyout) else if (ShowFlyout)
{ {
ShellPage.OpenFlyoutCallback(); POINT? p = null;
if (containsFlyoutPosition)
{
p = new POINT(flyout_x, flyout_y);
}
ShellPage.OpenFlyoutCallback(p);
} }
} }
} }

View File

@@ -1,11 +1,13 @@
// Copyright (c) Microsoft Corporation // Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout; using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout;
using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry;
using Microsoft.UI; using Microsoft.UI;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Windows.Graphics;
using WinUIEx; using WinUIEx;
namespace Microsoft.PowerToys.Settings.UI namespace Microsoft.PowerToys.Settings.UI
@@ -21,23 +23,65 @@ namespace Microsoft.PowerToys.Settings.UI
public FlyoutViewModel ViewModel { get; set; } public FlyoutViewModel ViewModel { get; set; }
public FlyoutWindow() public POINT? FlyoutAppearPosition { get; set; }
public FlyoutWindow(POINT? initialPosition)
{ {
this.InitializeComponent(); this.InitializeComponent();
this.Activated += FlyoutWindow_Activated; this.Activated += FlyoutWindow_Activated;
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); FlyoutAppearPosition = initialPosition;
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest);
double dpiScale = (float)this.GetDpiForWindow() / 96;
double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin));
double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin));
this.MoveAndResize(x, y, WindowWidth, WindowHeight);
ViewModel = new FlyoutViewModel(); ViewModel = new FlyoutViewModel();
} }
private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
{ {
PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent()); PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent());
if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.CodeActivated)
{
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
if (!FlyoutAppearPosition.HasValue)
{
DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest);
double dpiScale = (float)this.GetDpiForWindow() / 96;
double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin));
double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin));
this.MoveAndResize(x, y, WindowWidth, WindowHeight);
}
else
{
DisplayArea displayArea = DisplayArea.GetFromPoint(new PointInt32(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y), DisplayAreaFallback.Nearest);
// Move the window to the correct screen as a little blob, so we can get the accurate dpi for the screen to calculate the best position to show it.
this.MoveAndResize(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y, 1, 1);
double dpiScale = (float)this.GetDpiForWindow() / 96;
// Position the window so that it's inside the display are closest to the point.
POINT newPosition = new POINT(FlyoutAppearPosition.Value.X - (int)(dpiScale * WindowWidth / 2), FlyoutAppearPosition.Value.Y - (int)(dpiScale * WindowHeight / 2));
if (newPosition.X < displayArea.WorkArea.X)
{
newPosition.X = displayArea.WorkArea.X;
}
if (newPosition.Y < displayArea.WorkArea.Y)
{
newPosition.Y = displayArea.WorkArea.Y;
}
if (newPosition.X + (dpiScale * WindowWidth) > displayArea.WorkArea.X + displayArea.WorkArea.Width)
{
newPosition.X = (int)(displayArea.WorkArea.X + displayArea.WorkArea.Width - (dpiScale * WindowWidth));
}
if (newPosition.Y + (dpiScale * WindowHeight) > displayArea.WorkArea.Y + displayArea.WorkArea.Height)
{
newPosition.Y = (int)(displayArea.WorkArea.Y + displayArea.WorkArea.Height - (dpiScale * WindowHeight));
}
this.MoveAndResize(newPosition.X, newPosition.Y, WindowWidth, WindowHeight);
}
}
if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated) if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated)
{ {
if (ViewModel.CanHide) if (ViewModel.CanHide)

View File

@@ -171,16 +171,17 @@ namespace Microsoft.PowerToys.Settings.UI
}); });
// open flyout // open flyout
ShellPage.SetOpenFlyoutCallback(() => ShellPage.SetOpenFlyoutCallback((POINT? p) =>
{ {
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
{ {
if (App.GetFlyoutWindow() == null) if (App.GetFlyoutWindow() == null)
{ {
App.SetFlyoutWindow(new FlyoutWindow()); App.SetFlyoutWindow(new FlyoutWindow(p));
} }
FlyoutWindow flyout = App.GetFlyoutWindow(); FlyoutWindow flyout = App.GetFlyoutWindow();
flyout.FlyoutAppearPosition = p;
flyout.Activate(); flyout.Activate();
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
@@ -196,7 +197,7 @@ namespace Microsoft.PowerToys.Settings.UI
{ {
if (App.GetFlyoutWindow() == null) if (App.GetFlyoutWindow() == null)
{ {
App.SetFlyoutWindow(new FlyoutWindow()); App.SetFlyoutWindow(new FlyoutWindow(null));
} }
App.GetFlyoutWindow().ViewModel.DisableHiding(); App.GetFlyoutWindow().ViewModel.DisableHiding();

View File

@@ -45,7 +45,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
/// <summary> /// <summary>
/// Declaration for the opening flyout window callback function. /// Declaration for the opening flyout window callback function.
/// </summary> /// </summary>
public delegate void FlyoutOpeningCallback(); public delegate void FlyoutOpeningCallback(POINT? point);
/// <summary> /// <summary>
/// Declaration for the disabling hide of flyout window callback function. /// Declaration for the disabling hide of flyout window callback function.
@@ -330,13 +330,28 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{ {
if (json != null) if (json != null)
{ {
if (json.ToString().StartsWith("{\"ShowYourself\":")) IJsonValue whatToShowJson;
if (json.TryGetValue("ShowYourself", out whatToShowJson))
{ {
if (json.ToString().EndsWith("\"flyout\"}")) if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("flyout"))
{ {
OpenFlyoutCallback(); POINT? p = null;
IJsonValue flyoutPointX;
IJsonValue flyoutPointY;
if (json.TryGetValue("x_position", out flyoutPointX) && json.TryGetValue("y_position", out flyoutPointY))
{
if (flyoutPointX.ValueType == JsonValueType.Number && flyoutPointY.ValueType == JsonValueType.Number)
{
int flyout_x = (int)flyoutPointX.GetNumber();
int flyout_y = (int)flyoutPointY.GetNumber();
p = new POINT(flyout_x, flyout_y);
}
}
OpenFlyoutCallback(p);
} }
else if (json.ToString().EndsWith("\"main_page\"}")) else if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("main_page"))
{ {
OpenMainWindowCallback(); OpenMainWindowCallback();
} }