[Shortcut Guide] Support delayed display of shortcuts except for taskbar shortcuts (#21762)

* Implement the delayed rendering

* Impelement settings UI

* Rename

* Set the minimum of ShortcutGuide_PressTimeForTaskbarIconShortcuts to 100ms

* Separate the animations of the global windows shortcuts and the taskbar icon shortcuts

* Amend

* Handle the case when the shortcut guide is not activated by win key long press

* Remove .vscode

* Keep the user's original setting of the win key press time

* Revert the default press time to 900 ms

* Restore default as 900
This commit is contained in:
Okami Wong 2022-11-18 22:22:40 +08:00 committed by GitHub
parent 6767ff736b
commit 89330986f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 424 additions and 275 deletions

View File

@ -8,5 +8,6 @@ struct ShortcutGuideSettings
std::wstring theme = L"system";
std::wstring disabledApps = L"";
bool shouldReactToPressedWinKey = false;
int windowsKeyPressTime = 900;
int windowsKeyPressTimeForGlobalWindowsShortcuts = 900;
int windowsKeyPressTimeForTaskbarIconShortcuts = 900;
};

View File

@ -268,7 +268,11 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_right() const
}
D2DOverlayWindow::D2DOverlayWindow() :
total_screen({}), animation(0.3), D2DWindow()
total_screen({}),
background_animation(0.3),
global_windows_shortcuts_animation(0.3),
taskbar_icon_shortcuts_animation(0.3),
D2DWindow()
{
tasklist_thread = std::thread([&] {
while (running)
@ -356,7 +360,29 @@ void D2DOverlayWindow::show(HWND active_window, bool snappable)
// Ignore errors, if this fails we will just not show the thumbnail
DwmRegisterThumbnail(hwnd, active_window, &thumbnail);
}
animation.reset();
background_animation.reset();
if (milliseconds_press_time_for_global_windows_shortcuts < milliseconds_press_time_for_taskbar_icon_shortcuts)
{
global_windows_shortcuts_shown = true;
taskbar_icon_shortcuts_shown = false;
global_windows_shortcuts_animation.reset();
}
else if (milliseconds_press_time_for_global_windows_shortcuts > milliseconds_press_time_for_taskbar_icon_shortcuts)
{
global_windows_shortcuts_shown = false;
taskbar_icon_shortcuts_shown = true;
taskbar_icon_shortcuts_animation.reset();
}
else
{
global_windows_shortcuts_shown = true;
taskbar_icon_shortcuts_shown = true;
global_windows_shortcuts_animation.reset();
taskbar_icon_shortcuts_animation.reset();
}
auto primary_size = MonitorInfo::GetPrimaryMonitor().GetScreenSize(false);
shown_start_time = std::chrono::steady_clock::now();
lock.unlock();
@ -422,6 +448,16 @@ void D2DOverlayWindow::apply_overlay_opacity(float opacity)
overlay_opacity = opacity;
}
void D2DOverlayWindow::apply_press_time_for_global_windows_shortcuts(int press_time)
{
milliseconds_press_time_for_global_windows_shortcuts = std::max(press_time, 0);
}
void D2DOverlayWindow::apply_press_time_for_taskbar_icon_shortcuts(int press_time)
{
milliseconds_press_time_for_taskbar_icon_shortcuts = std::max(press_time, 0);
}
void D2DOverlayWindow::set_theme(const std::wstring& theme)
{
if (theme == L"light")
@ -524,7 +560,7 @@ void D2DOverlayWindow::resize()
text.resize(font, use_overlay->get_scale());
}
void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_scale, ID2D1DeviceContext5* d2d_dc)
void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_scale, ID2D1DeviceContext5* d2d_dc, int x_offset, int y_offset)
{
int dx = 0, dy = 0;
// Calculate taskbar orientation
@ -558,8 +594,8 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_
// assume button is 25% wider than taller, +10% to make room for each of the arrows that are hidden
auto render_arrow_width = (int)(button.height * 1.25f * 1.2f);
auto render_arrow_height = (int)(render_arrow_width * arrow_ratio);
arrow.resize(button.x + (button.width - render_arrow_width) / 2,
dy == -1 ? button.y - render_arrow_height : 0,
arrow.resize((button.x + (button.width - render_arrow_width) / 2) + x_offset,
(dy == -1 ? button.y - render_arrow_height : 0) + y_offset,
render_arrow_width,
render_arrow_height,
0.95f,
@ -571,8 +607,8 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_
// same as above - make room for the hidden arrow
auto render_arrow_height = (int)(button.height * 1.2f);
auto render_arrow_width = (int)(render_arrow_height / arrow_ratio);
arrow.resize(dx == -1 ? button.x - render_arrow_width : 0,
button.y + (button.height - render_arrow_height) / 2,
arrow.resize((dx == -1 ? button.x - render_arrow_width : 0) + x_offset,
(button.y + (button.height - render_arrow_height) / 2) + y_offset,
render_arrow_width,
render_arrow_height,
0.95f,
@ -617,35 +653,15 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_dc)
}
d2d_dc->Clear();
int x_offset = 0, y_offset = 0;
auto current_anim_value = (float)animation.value(Animation::AnimFunctions::LINEAR);
SetLayeredWindowAttributes(hwnd, 0, static_cast<byte>(255 * current_anim_value), LWA_ALPHA);
double pos_anim_value = 1 - animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
if (!tasklist_buttons.empty())
{
if (tasklist_buttons[0].x <= window_rect.left)
{ // taskbar on left
x_offset = (int)(-pos_anim_value * use_overlay->width() * use_overlay->get_scale());
}
if (tasklist_buttons[0].x >= window_rect.right)
{ // taskbar on right
x_offset = (int)(pos_anim_value * use_overlay->width() * use_overlay->get_scale());
}
if (tasklist_buttons[0].y <= window_rect.top)
{ // taskbar on top
y_offset = (int)(-pos_anim_value * use_overlay->height() * use_overlay->get_scale());
}
if (tasklist_buttons[0].y >= window_rect.bottom)
{ // taskbar on bottom
y_offset = (int)(pos_anim_value * use_overlay->height() * use_overlay->get_scale());
}
}
else
{
x_offset = 0;
y_offset = (int)(pos_anim_value * use_overlay->height() * use_overlay->get_scale());
}
int taskbar_icon_shortcuts_x_offset = 0, taskbar_icon_shortcuts_y_offset = 0;
float current_background_anim_value = (float)background_animation.value(Animation::AnimFunctions::LINEAR);
float current_global_windows_shortcuts_anim_value = (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR);
float pos_global_windows_shortcuts_anim_value = 1 - (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
float pos_taskbar_icon_shortcuts_anim_value = 1 - (float)taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
// Draw background
SetLayeredWindowAttributes(hwnd, 0, static_cast<byte>(255 * current_background_anim_value), LWA_ALPHA);
winrt::com_ptr<ID2D1SolidColorBrush> brush;
float brush_opacity = get_overlay_opacity();
D2D1_COLOR_F brushColor = light_mode ? D2D1::ColorF(1.0f, 1.0f, 1.0f, brush_opacity) : D2D1::ColorF(0, 0, 0, brush_opacity);
@ -656,225 +672,274 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_dc)
d2d_dc->SetTransform(D2D1::Matrix3x2F::Identity());
d2d_dc->FillRectangle(background_rect, brush.get());
// Thumbnail logic:
auto window_state = get_window_state(active_window);
auto thumb_window = get_window_pos(active_window);
if (!thumb_window.has_value())
// Draw the taskbar shortcuts (the arrows with numbers)
if (taskbar_icon_shortcuts_shown)
{
thumb_window = RECT();
}
bool miniature_shown = active_window != nullptr && thumbnail != nullptr && thumb_window && window_state != MINIMIZED;
RECT client_rect;
if (thumb_window && GetClientRect(active_window, &client_rect))
{
int dx = ((thumb_window->right - thumb_window->left) - (client_rect.right - client_rect.left)) / 2;
int dy = ((thumb_window->bottom - thumb_window->top) - (client_rect.bottom - client_rect.top)) / 2;
thumb_window->left += dx;
thumb_window->right -= dx;
thumb_window->top += dy;
thumb_window->bottom -= dy;
}
if (miniature_shown && thumb_window->right - thumb_window->left <= 0 || thumb_window->bottom - thumb_window->top <= 0)
{
miniature_shown = false;
}
bool render_monitors = true;
auto total_monitor_with_screen = total_screen;
if (thumb_window)
{
total_monitor_with_screen.rect.left = std::min(total_monitor_with_screen.rect.left, thumb_window->left + monitor_dx);
total_monitor_with_screen.rect.top = std::min(total_monitor_with_screen.rect.top, thumb_window->top + monitor_dy);
total_monitor_with_screen.rect.right = std::max(total_monitor_with_screen.rect.right, thumb_window->right + monitor_dx);
total_monitor_with_screen.rect.bottom = std::max(total_monitor_with_screen.rect.bottom, thumb_window->bottom + monitor_dy);
}
// Only allow the new rect being slight bigger.
if (total_monitor_with_screen.width() - total_screen.width() > (thumb_window->right - thumb_window->left) / 2 ||
total_monitor_with_screen.height() - total_screen.height() > (thumb_window->bottom - thumb_window->top) / 2)
{
render_monitors = false;
}
if (window_state == MINIMIZED)
{
total_monitor_with_screen = total_screen;
}
auto rect_and_scale = use_overlay->get_thumbnail_rect_and_scale(0, 0, total_monitor_with_screen.width(), total_monitor_with_screen.height(), 1);
if (miniature_shown)
{
RECT thumbnail_pos;
if (render_monitors)
if (!tasklist_buttons.empty())
{
thumbnail_pos.left = (int)((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
thumbnail_pos.top = (int)((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
thumbnail_pos.right = (int)((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
thumbnail_pos.bottom = (int)((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
if (tasklist_buttons[0].x <= window_rect.left)
{
// taskbar on left
taskbar_icon_shortcuts_x_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale());
}
if (tasklist_buttons[0].x >= window_rect.right)
{
// taskbar on right
taskbar_icon_shortcuts_x_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale());
}
if (tasklist_buttons[0].y <= window_rect.top)
{
// taskbar on top
taskbar_icon_shortcuts_y_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale());
}
if (tasklist_buttons[0].y >= window_rect.bottom)
{
// taskbar on bottom
taskbar_icon_shortcuts_y_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale());
}
for (auto&& button : tasklist_buttons)
{
if ((size_t)(button.keynum) - 1 >= arrows.size())
{
continue;
}
render_arrow(arrows[(size_t)(button.keynum) - 1], button, window_rect, use_overlay->get_scale(), d2d_dc, taskbar_icon_shortcuts_x_offset, taskbar_icon_shortcuts_y_offset);
}
}
else
{
thumbnail_pos = use_overlay->get_thumbnail_rect_and_scale(0, 0, thumb_window->right - thumb_window->left, thumb_window->bottom - thumb_window->top, 1).rect;
}
// If the animation is done show the thumbnail
// we cannot animate the thumbnail, the animation lags behind
miniature_shown = show_thumbnail(thumbnail_pos, current_anim_value);
}
else
{
hide_thumbnail();
}
if (window_state == MINIMIZED)
{
render_monitors = true;
}
// render the monitors
if (render_monitors)
{
brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? current_anim_value * 0.9f : current_anim_value * 0.3f);
brush = nullptr;
winrt::check_hresult(d2d_dc->CreateSolidColorBrush(brushColor, brush.put()));
for (auto& monitor : monitors)
auto time_since_start = std::chrono::high_resolution_clock::now() - shown_start_time;
if (time_since_start.count() / 1000000 > milliseconds_press_time_for_taskbar_icon_shortcuts - milliseconds_press_time_for_global_windows_shortcuts)
{
D2D1_RECT_F monitor_rect;
const auto monitor_size = monitor.GetScreenSize(true);
monitor_rect.left = (float)((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
monitor_rect.top = (float)((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
monitor_rect.right = (float)((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
monitor_rect.bottom = (float)((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
d2d_dc->SetTransform(D2D1::Matrix3x2F::Identity());
d2d_dc->FillRectangle(monitor_rect, brush.get());
taskbar_icon_shortcuts_shown = true;
taskbar_icon_shortcuts_animation.reset();
}
}
// Finalize the overlay - dimm the buttons if no thumbnail is present and show "No active window"
use_overlay->toggle_window_group(miniature_shown || window_state == MINIMIZED);
if (!miniature_shown && window_state != MINIMIZED)
{
no_active.render(d2d_dc);
window_state = UNKNOWN;
}
// Set the animation - move the draw window according to animation step
auto popIn = D2D1::Matrix3x2F::Translation((float)x_offset, (float)y_offset);
d2d_dc->SetTransform(popIn);
// Animate keys
for (unsigned id = 0; id < key_animations.size();)
if (global_windows_shortcuts_shown)
{
auto& animation = key_animations[id];
D2D1_COLOR_F color;
auto value = (float)animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
color.a = 1.0f;
color.r = animation.original.r + (1.0f - animation.original.r) * value;
color.g = animation.original.g + (1.0f - animation.original.g) * value;
color.b = animation.original.b + (1.0f - animation.original.b) * value;
animation.button->SetAttributeValue(L"fill", color);
if (animation.animation.done())
// Thumbnail logic:
auto window_state = get_window_state(active_window);
auto thumb_window = get_window_pos(active_window);
if (!thumb_window.has_value())
{
if (value == 1)
thumb_window = RECT();
}
bool miniature_shown = active_window != nullptr && thumbnail != nullptr && thumb_window && window_state != MINIMIZED;
RECT client_rect;
if (thumb_window && GetClientRect(active_window, &client_rect))
{
int dx = ((thumb_window->right - thumb_window->left) - (client_rect.right - client_rect.left)) / 2;
int dy = ((thumb_window->bottom - thumb_window->top) - (client_rect.bottom - client_rect.top)) / 2;
thumb_window->left += dx;
thumb_window->right -= dx;
thumb_window->top += dy;
thumb_window->bottom -= dy;
}
if (miniature_shown && thumb_window->right - thumb_window->left <= 0 || thumb_window->bottom - thumb_window->top <= 0)
{
miniature_shown = false;
}
bool render_monitors = true;
auto total_monitor_with_screen = total_screen;
if (thumb_window)
{
total_monitor_with_screen.rect.left = std::min(total_monitor_with_screen.rect.left, thumb_window->left + monitor_dx);
total_monitor_with_screen.rect.top = std::min(total_monitor_with_screen.rect.top, thumb_window->top + monitor_dy);
total_monitor_with_screen.rect.right = std::max(total_monitor_with_screen.rect.right, thumb_window->right + monitor_dx);
total_monitor_with_screen.rect.bottom = std::max(total_monitor_with_screen.rect.bottom, thumb_window->bottom + monitor_dy);
}
// Only allow the new rect being slight bigger.
if (total_monitor_with_screen.width() - total_screen.width() > (thumb_window->right - thumb_window->left) / 2 ||
total_monitor_with_screen.height() - total_screen.height() > (thumb_window->bottom - thumb_window->top) / 2)
{
render_monitors = false;
}
if (window_state == MINIMIZED)
{
total_monitor_with_screen = total_screen;
}
auto rect_and_scale = use_overlay->get_thumbnail_rect_and_scale(0, 0, total_monitor_with_screen.width(), total_monitor_with_screen.height(), 1);
if (miniature_shown)
{
RECT thumbnail_pos;
if (render_monitors)
{
animation.animation.reset(0.05, 1, 0);
animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
thumbnail_pos.left = (int)((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
thumbnail_pos.top = (int)((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
thumbnail_pos.right = (int)((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
thumbnail_pos.bottom = (int)((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
}
else
{
key_animations.erase(key_animations.begin() + id);
continue;
thumbnail_pos = use_overlay->get_thumbnail_rect_and_scale(0, 0, thumb_window->right - thumb_window->left, thumb_window->bottom - thumb_window->top, 1).rect;
}
// If the animation is done show the thumbnail
// we cannot animate the thumbnail, the animation lags behind
miniature_shown = show_thumbnail(thumbnail_pos, current_global_windows_shortcuts_anim_value);
}
else
{
hide_thumbnail();
}
if (window_state == MINIMIZED)
{
render_monitors = true;
}
// render the monitors
if (render_monitors)
{
brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? current_global_windows_shortcuts_anim_value * 0.9f : current_global_windows_shortcuts_anim_value * 0.3f);
brush = nullptr;
winrt::check_hresult(d2d_dc->CreateSolidColorBrush(brushColor, brush.put()));
for (auto& monitor : monitors)
{
D2D1_RECT_F monitor_rect;
const auto monitor_size = monitor.GetScreenSize(true);
monitor_rect.left = (float)((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
monitor_rect.top = (float)((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
monitor_rect.right = (float)((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left);
monitor_rect.bottom = (float)((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top);
d2d_dc->SetTransform(D2D1::Matrix3x2F::Identity());
d2d_dc->FillRectangle(monitor_rect, brush.get());
}
}
++id;
}
// Finally: render the overlay...
use_overlay->render(d2d_dc);
// ... window arrows texts ...
std::wstring left, right, up, down;
bool left_disabled = false;
bool right_disabled = false;
bool up_disabled = false;
bool down_disabled = false;
switch (window_state)
{
case MINIMIZED:
left = GET_RESOURCE_STRING(IDS_NO_ACTION);
left_disabled = true;
right = GET_RESOURCE_STRING(IDS_NO_ACTION);
right_disabled = true;
up = GET_RESOURCE_STRING(IDS_RESTORE);
down = GET_RESOURCE_STRING(IDS_NO_ACTION);
down_disabled = true;
break;
case MAXIMIZED:
left = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
up = GET_RESOURCE_STRING(IDS_NO_ACTION);
up_disabled = true;
down = GET_RESOURCE_STRING(IDS_RESTORE);
break;
case SNAPPED_TOP_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
right = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
break;
case SNAPPED_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
right = GET_RESOURCE_STRING(IDS_RESTORE);
up = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
down = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
break;
case SNAPPED_BOTTOM_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
right = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
up = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
case SNAPPED_TOP_RIGHT:
left = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
break;
case SNAPPED_RIGHT:
left = GET_RESOURCE_STRING(IDS_RESTORE);
right = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
up = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
down = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
break;
case SNAPPED_BOTTOM_RIGHT:
left = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
up = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
case RESTORED:
left = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
default:
left = GET_RESOURCE_STRING(IDS_NO_ACTION);
left_disabled = true;
right = GET_RESOURCE_STRING(IDS_NO_ACTION);
right_disabled = true;
up = GET_RESOURCE_STRING(IDS_NO_ACTION);
up_disabled = true;
down = GET_RESOURCE_STRING(IDS_NO_ACTION);
down_disabled = true;
}
auto text_color = D2D1::ColorF(light_mode ? 0x222222 : 0xDDDDDD, active_window_snappable && (miniature_shown || window_state == MINIMIZED) ? 1.0f : 0.3f);
use_overlay->find_element(L"KeyUpGroup")->SetAttributeValue(L"fill-opacity", up_disabled ? 0.3f : 1.0f);
text.set_alignment_center().write(d2d_dc, text_color, use_overlay->get_maximize_label(), up);
use_overlay->find_element(L"KeyDownGroup")->SetAttributeValue(L"fill-opacity", down_disabled ? 0.3f : 1.0f);
text.write(d2d_dc, text_color, use_overlay->get_minimize_label(), down);
use_overlay->find_element(L"KeyLeftGroup")->SetAttributeValue(L"fill-opacity", left_disabled ? 0.3f : 1.0f);
text.set_alignment_right().write(d2d_dc, text_color, use_overlay->get_snap_left(), left);
use_overlay->find_element(L"KeyRightGroup")->SetAttributeValue(L"fill-opacity", right_disabled ? 0.3f : 1.0f);
text.set_alignment_left().write(d2d_dc, text_color, use_overlay->get_snap_right(), right);
// ... and the arrows with numbers
for (auto&& button : tasklist_buttons)
{
if ((size_t)(button.keynum) - 1 >= arrows.size())
// Finalize the overlay - dimm the buttons if no thumbnail is present and show "No active window"
use_overlay->toggle_window_group(miniature_shown || window_state == MINIMIZED);
if (!miniature_shown && window_state != MINIMIZED)
{
continue;
no_active.render(d2d_dc);
window_state = UNKNOWN;
}
// Set the animation - move the draw window according to animation step
int global_windows_shortcuts_y_offset = (int)(pos_global_windows_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale());
auto popIn = D2D1::Matrix3x2F::Translation(0, (float)global_windows_shortcuts_y_offset);
d2d_dc->SetTransform(popIn);
// Animate keys
for (unsigned id = 0; id < key_animations.size();)
{
auto& animation = key_animations[id];
D2D1_COLOR_F color;
auto value = (float)animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
color.a = 1.0f;
color.r = animation.original.r + (1.0f - animation.original.r) * value;
color.g = animation.original.g + (1.0f - animation.original.g) * value;
color.b = animation.original.b + (1.0f - animation.original.b) * value;
animation.button->SetAttributeValue(L"fill", color);
if (animation.animation.done())
{
if (value == 1)
{
animation.animation.reset(0.05, 1, 0);
animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
}
else
{
key_animations.erase(key_animations.begin() + id);
continue;
}
}
++id;
}
// Finally: render the overlay...
use_overlay->render(d2d_dc);
// ... window arrows texts ...
std::wstring left, right, up, down;
bool left_disabled = false;
bool right_disabled = false;
bool up_disabled = false;
bool down_disabled = false;
switch (window_state)
{
case MINIMIZED:
left = GET_RESOURCE_STRING(IDS_NO_ACTION);
left_disabled = true;
right = GET_RESOURCE_STRING(IDS_NO_ACTION);
right_disabled = true;
up = GET_RESOURCE_STRING(IDS_RESTORE);
down = GET_RESOURCE_STRING(IDS_NO_ACTION);
down_disabled = true;
break;
case MAXIMIZED:
left = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
up = GET_RESOURCE_STRING(IDS_NO_ACTION);
up_disabled = true;
down = GET_RESOURCE_STRING(IDS_RESTORE);
break;
case SNAPPED_TOP_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
right = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
break;
case SNAPPED_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
right = GET_RESOURCE_STRING(IDS_RESTORE);
up = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
down = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
break;
case SNAPPED_BOTTOM_LEFT:
left = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
right = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
up = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
case SNAPPED_TOP_RIGHT:
left = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_UPPER_LEFT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
break;
case SNAPPED_RIGHT:
left = GET_RESOURCE_STRING(IDS_RESTORE);
right = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
up = GET_RESOURCE_STRING(IDS_SNAP_UPPER_RIGHT);
down = GET_RESOURCE_STRING(IDS_SNAP_LOWER_RIGHT);
break;
case SNAPPED_BOTTOM_RIGHT:
left = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_LOWER_LEFT);
up = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
case RESTORED:
left = GET_RESOURCE_STRING(IDS_SNAP_LEFT);
right = GET_RESOURCE_STRING(IDS_SNAP_RIGHT);
up = GET_RESOURCE_STRING(IDS_MAXIMIZE);
down = GET_RESOURCE_STRING(IDS_MINIMIZE);
break;
default:
left = GET_RESOURCE_STRING(IDS_NO_ACTION);
left_disabled = true;
right = GET_RESOURCE_STRING(IDS_NO_ACTION);
right_disabled = true;
up = GET_RESOURCE_STRING(IDS_NO_ACTION);
up_disabled = true;
down = GET_RESOURCE_STRING(IDS_NO_ACTION);
down_disabled = true;
}
auto text_color = D2D1::ColorF(light_mode ? 0x222222 : 0xDDDDDD, active_window_snappable && (miniature_shown || window_state == MINIMIZED) ? 1.0f : 0.3f);
use_overlay->find_element(L"KeyUpGroup")->SetAttributeValue(L"fill-opacity", up_disabled ? 0.3f : 1.0f);
text.set_alignment_center().write(d2d_dc, text_color, use_overlay->get_maximize_label(), up);
use_overlay->find_element(L"KeyDownGroup")->SetAttributeValue(L"fill-opacity", down_disabled ? 0.3f : 1.0f);
text.write(d2d_dc, text_color, use_overlay->get_minimize_label(), down);
use_overlay->find_element(L"KeyLeftGroup")->SetAttributeValue(L"fill-opacity", left_disabled ? 0.3f : 1.0f);
text.set_alignment_right().write(d2d_dc, text_color, use_overlay->get_snap_left(), left);
use_overlay->find_element(L"KeyRightGroup")->SetAttributeValue(L"fill-opacity", right_disabled ? 0.3f : 1.0f);
text.set_alignment_left().write(d2d_dc, text_color, use_overlay->get_snap_right(), right);
}
else
{
auto time_since_start = std::chrono::high_resolution_clock::now() - shown_start_time;
if (time_since_start.count() / 1000000 > milliseconds_press_time_for_global_windows_shortcuts - milliseconds_press_time_for_taskbar_icon_shortcuts)
{
global_windows_shortcuts_shown = true;
global_windows_shortcuts_animation.reset();
}
render_arrow(arrows[(size_t)(button.keynum) - 1], button, window_rect, use_overlay->get_scale(), d2d_dc);
}
}

View File

@ -51,6 +51,8 @@ public:
void show(HWND active_window, bool snappable);
~D2DOverlayWindow();
void apply_overlay_opacity(float opacity);
void apply_press_time_for_global_windows_shortcuts(int press_time);
void apply_press_time_for_taskbar_icon_shortcuts(int press_time);
void set_theme(const std::wstring& theme);
void quick_hide();
@ -78,7 +80,11 @@ private:
int monitor_dx = 0, monitor_dy = 0;
D2DText text;
WindowsColors colors;
Animation animation;
Animation background_animation;
Animation global_windows_shortcuts_animation;
Animation taskbar_icon_shortcuts_animation;
bool global_windows_shortcuts_shown = false;
bool taskbar_icon_shortcuts_shown = false;
RECT window_rect = {};
Tasklist tasklist;
std::vector<TasklistButton> tasklist_buttons;
@ -103,4 +109,6 @@ private:
System
} theme_setting = System;
bool light_mode = true;
UINT milliseconds_press_time_for_global_windows_shortcuts = 900;
UINT milliseconds_press_time_for_taskbar_icon_shortcuts = 900;
};

View File

@ -86,7 +86,7 @@ namespace
}
const LPARAM eventActivateWindow = 1;
bool wasWinPressed = false;
bool isWinPressed()
{
@ -132,7 +132,7 @@ namespace
{
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (event.lParam->vkCode == VK_ESCAPE)
{
Logger::trace(L"ESC key was pressed");
@ -164,14 +164,14 @@ namespace
{
switch (type)
{
case HideWindowType::ESC_PRESSED:
return L"ESC_PRESSED";
case HideWindowType::WIN_RELEASED:
return L"WIN_RELEASED";
case HideWindowType::WIN_SHORTCUT_PRESSED:
return L"WIN_SHORTCUT_PRESSED";
case HideWindowType::THE_SHORTCUT_PRESSED:
return L"THE_SHORTCUT_PRESSED";
case HideWindowType::ESC_PRESSED:
return L"ESC_PRESSED";
case HideWindowType::WIN_RELEASED:
return L"WIN_RELEASED";
case HideWindowType::WIN_SHORTCUT_PRESSED:
return L"WIN_SHORTCUT_PRESSED";
case HideWindowType::THE_SHORTCUT_PRESSED:
return L"THE_SHORTCUT_PRESSED";
}
return L"";
@ -181,7 +181,7 @@ namespace
OverlayWindow::OverlayWindow(HWND activeWindow)
{
instance = this;
this -> activeWindow = activeWindow;
this->activeWindow = activeWindow;
app_name = GET_RESOURCE_STRING(IDS_SHORTCUT_GUIDE);
Logger::info("Overlay Window is creating");
@ -198,6 +198,19 @@ void OverlayWindow::ShowWindow()
winkey_popup = std::make_unique<D2DOverlayWindow>();
winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f);
winkey_popup->set_theme(theme.value);
// The press time only takes effect when the shortcut guide is activated by pressing the win key.
if (shouldReactToPressedWinKey.value)
{
winkey_popup->apply_press_time_for_global_windows_shortcuts(windowsKeyPressTimeForGlobalWindowsShortcuts.value);
winkey_popup->apply_press_time_for_taskbar_icon_shortcuts(windowsKeyPressTimeForTaskbarIconShortcuts.value);
}
else
{
winkey_popup->apply_press_time_for_global_windows_shortcuts(0);
winkey_popup->apply_press_time_for_taskbar_icon_shortcuts(0);
}
target_state = std::make_unique<TargetState>();
try
{
@ -254,12 +267,12 @@ OverlayWindow::~OverlayWindow()
{
event_waiter.reset();
}
if (winkey_popup)
{
winkey_popup->hide();
}
if (target_state)
{
target_state->exit();
@ -270,7 +283,7 @@ OverlayWindow::~OverlayWindow()
{
winkey_popup.reset();
}
if (keyboardHook)
{
UnhookWindowsHookEx(keyboardHook);
@ -310,6 +323,8 @@ void OverlayWindow::init_settings()
theme.value = settings.theme;
disabledApps.value = settings.disabledApps;
shouldReactToPressedWinKey.value = settings.shouldReactToPressedWinKey;
windowsKeyPressTimeForGlobalWindowsShortcuts.value = settings.windowsKeyPressTimeForGlobalWindowsShortcuts;
windowsKeyPressTimeForTaskbarIconShortcuts.value = settings.windowsKeyPressTimeForTaskbarIconShortcuts;
update_disabled_apps();
}
@ -419,7 +434,15 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept
try
{
settings.windowsKeyPressTime = (int)properties.GetNamedObject(WindowsKeyPressTime::name).GetNamedNumber(L"value");
settings.windowsKeyPressTimeForGlobalWindowsShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForGlobalWindowsShortcuts::name).GetNamedNumber(L"value");
}
catch (...)
{
}
try
{
settings.windowsKeyPressTimeForTaskbarIconShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForTaskbarIconShortcuts::name).GetNamedNumber(L"value");
}
catch (...)
{

View File

@ -84,10 +84,17 @@ private:
bool value;
} shouldReactToPressedWinKey;
struct WindowsKeyPressTime
struct WindowsKeyPressTimeForGlobalWindowsShortcuts
{
static inline PCWSTR name = L"press_time";
} windowsKeyPressTime;
int value;
} windowsKeyPressTimeForGlobalWindowsShortcuts;
struct WindowsKeyPressTimeForTaskbarIconShortcuts
{
static inline PCWSTR name = L"press_time_for_taskbar_icon_shortcuts";
int value;
} windowsKeyPressTimeForTaskbarIconShortcuts;
struct OpenShortcut
{

View File

@ -40,7 +40,8 @@ void Trace::SendSettings(ShortcutGuideSettings settings) noexcept
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
TraceLoggingWideString(settings.disabledApps.c_str(), "DisabledApps"),
TraceLoggingBoolean(settings.shouldReactToPressedWinKey, "ShouldReactToPressedWinKey"),
TraceLoggingInt32(settings.windowsKeyPressTime, "WindowsKeyPressTime"),
TraceLoggingInt32(settings.windowsKeyPressTimeForGlobalWindowsShortcuts, "WindowsKeyPressTimeForGlobalWindowsShortcuts"),
TraceLoggingInt32(settings.windowsKeyPressTimeForTaskbarIconShortcuts, "WindowsKeyPressTimeForTaskbarIconShortcuts"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));

View File

@ -171,7 +171,7 @@ public:
virtual UINT milliseconds_win_key_must_be_pressed() override
{
return m_millisecondsWinKeyShouldBePressed;
return std::min(m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts, m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts);
}
private:
@ -185,9 +185,11 @@ private:
HotkeyEx m_hotkey;
// If the module should be activated through the legacy pressing windows key behavior.
const UINT DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED = 900;
const UINT DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_GLOBAL_WINDOWS_SHORTCUTS = 900;
const UINT DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_TASKBAR_ICON_SHORTCUTS = 900;
bool m_shouldReactToPressedWinKey = false;
UINT m_millisecondsWinKeyShouldBePressed = DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED;
UINT m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_GLOBAL_WINDOWS_SHORTCUTS;
UINT m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_TASKBAR_ICON_SHORTCUTS;
HANDLE exitEvent;
@ -280,7 +282,8 @@ private:
void ParseSettings(PowerToysSettings::PowerToyValues& settings)
{
m_shouldReactToPressedWinKey = false;
m_millisecondsWinKeyShouldBePressed = DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED;
m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_GLOBAL_WINDOWS_SHORTCUTS;
m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_TASKBAR_ICON_SHORTCUTS;
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
@ -322,8 +325,10 @@ private:
// Parse Legacy windows key press behavior settings
auto jsonUseLegacyWinKeyBehaviorObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"use_legacy_press_win_key_behavior");
m_shouldReactToPressedWinKey = (bool)jsonUseLegacyWinKeyBehaviorObject.GetNamedBoolean(L"value");
auto jsonPressTimeObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time");
m_millisecondsWinKeyShouldBePressed = (UINT)jsonPressTimeObject.GetNamedNumber(L"value");
auto jsonPressTimeForGlobalWindowsShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time");
auto jsonPressTimeForTaskbarIconShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time_for_taskbar_icon_shortcuts");
m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = (UINT)jsonPressTimeForGlobalWindowsShortcutsObject.GetNamedNumber(L"value");
m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = (UINT)jsonPressTimeForTaskbarIconShortcutsObject.GetNamedNumber(L"value");
}
catch (...)
{

View File

@ -12,7 +12,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
OverlayOpacity = new IntProperty(90);
UseLegacyPressWinKeyBehavior = new BoolProperty(false);
PressTime = new IntProperty(900);
PressTimeForGlobalWindowsShortcuts = new IntProperty(900);
PressTimeForTaskbarIconShortcuts = new IntProperty(900);
Theme = new StringProperty("system");
DisabledApps = new StringProperty();
OpenShortcutGuide = new HotkeySettings(true, false, false, true, 0xBF);
@ -28,7 +29,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public BoolProperty UseLegacyPressWinKeyBehavior { get; set; }
[JsonPropertyName("press_time")]
public IntProperty PressTime { get; set; }
public IntProperty PressTimeForGlobalWindowsShortcuts { get; set; }
[JsonPropertyName("press_time_for_taskbar_icon_shortcuts")]
public IntProperty PressTimeForTaskbarIconShortcuts { get; set; }
[JsonPropertyName("theme")]
public StringProperty Theme { get; set; }

View File

@ -814,13 +814,10 @@
<data name="ShortcutGuide.ModuleDescription" xml:space="preserve">
<value>Shows a help overlay with Windows shortcuts.</value>
</data>
<data name="ShortcutGuide_PressTime.Header" xml:space="preserve">
<value>Press duration before showing (ms)</value>
<data name="ShortcutGuide_PressTimeForGlobalWindowsShortcuts.Header" xml:space="preserve">
<value>Press duration before showing global Windows shortcuts (ms)</value>
<comment>ms = milliseconds</comment>
</data>
<data name="ShortcutGuide_PressTime.Description" xml:space="preserve">
<value>How long to press the Windows key to activate the module</value>
</data>
<data name="ShortcutGuide_ActivationMethod.Header" xml:space="preserve">
<value>Activation method</value>
</data>
@ -2685,6 +2682,10 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value>
</data>
<data name="ShortcutGuide_PressTimeForTaskbarIconShortcuts.Header" xml:space="preserve">
<value>Press duration before showing taskbar icon shortcuts (ms)</value>
<comment>ms = milliseconds</comment>
</data>
<data name="FileLocksmith.ModuleDescription" xml:space="preserve">
<value>A Windows shell extension to find out which processes are using the selected files and directories.</value>
</data>

View File

@ -66,7 +66,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
_useLegacyPressWinKeyBehavior = Settings.Properties.UseLegacyPressWinKeyBehavior.Value;
_pressTime = Settings.Properties.PressTime.Value;
_pressTimeForGlobalWindowsShortcuts = Settings.Properties.PressTimeForGlobalWindowsShortcuts.Value;
_pressTimeForTaskbarIconShortcuts = Settings.Properties.PressTimeForTaskbarIconShortcuts.Value;
_opacity = Settings.Properties.OverlayOpacity.Value;
_disabledApps = Settings.Properties.DisabledApps.Value;
@ -83,7 +84,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private bool _isEnabled;
private int _themeIndex;
private bool _useLegacyPressWinKeyBehavior;
private int _pressTime;
private int _pressTimeForGlobalWindowsShortcuts;
private int _pressTimeForTaskbarIconShortcuts;
private int _opacity;
public bool IsEnabled
@ -201,15 +203,33 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
get
{
return _pressTime;
return _pressTimeForGlobalWindowsShortcuts;
}
set
{
if (_pressTime != value)
if (_pressTimeForGlobalWindowsShortcuts != value)
{
_pressTime = value;
Settings.Properties.PressTime.Value = value;
_pressTimeForGlobalWindowsShortcuts = value;
Settings.Properties.PressTimeForGlobalWindowsShortcuts.Value = value;
NotifyPropertyChanged();
}
}
}
public int DelayTime
{
get
{
return _pressTimeForTaskbarIconShortcuts;
}
set
{
if (_pressTimeForTaskbarIconShortcuts != value)
{
_pressTimeForTaskbarIconShortcuts = value;
Settings.Properties.PressTimeForTaskbarIconShortcuts.Value = value;
NotifyPropertyChanged();
}
}

View File

@ -53,7 +53,7 @@
</controls:Setting.ActionContent>
</controls:Setting>
<controls:Setting x:Uid="ShortcutGuide_PressTime" Icon="&#xE916;" Visibility="{x:Bind Mode=OneWay, Path=ViewModel.UseLegacyPressWinKeyBehavior, Converter={StaticResource TrueToVisibleConverter}}">
<controls:Setting x:Uid="ShortcutGuide_PressTimeForGlobalWindowsShortcuts" Icon="&#xE916;" Visibility="{x:Bind Mode=OneWay, Path=ViewModel.UseLegacyPressWinKeyBehavior, Converter={StaticResource TrueToVisibleConverter}}">
<controls:Setting.ActionContent>
<NumberBox
Minimum="100"
@ -67,6 +67,20 @@
</controls:Setting.ActionContent>
</controls:Setting>
<controls:Setting x:Uid="ShortcutGuide_PressTimeForTaskbarIconShortcuts" Icon="&#xE916;" Visibility="{x:Bind Mode=OneWay, Path=ViewModel.UseLegacyPressWinKeyBehavior, Converter={StaticResource TrueToVisibleConverter}}">
<controls:Setting.ActionContent>
<NumberBox
Minimum="100"
Value="{x:Bind Mode=TwoWay, Path=ViewModel.DelayTime}"
MinWidth="{StaticResource SettingActionControlMinWidth}"
SpinButtonPlacementMode="Compact"
HorizontalAlignment="Left"
SmallChange="50"
LargeChange="100"
/>
</controls:Setting.ActionContent>
</controls:Setting>
<InfoBar
x:Uid="ShortcutGuide_PressWinKeyWarning"
Severity="Warning"