mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
Add non-updating mode for Crop-And-Lock (#40720)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Adds a "screenshot" mode to Crop And Lock, which allows creating a window showing a freezed snapshot of the original window. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #31799, #33071 (also requested in the already closed duplicate issues #28633, #33812, #37337, ) - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass (crop-and-lock utility doesn't have any tests) - [x] **Localization:** All end-user-facing strings can be localized - [x] **Dev docs:** Added/updated - [x] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** https://github.com/MicrosoftDocs/windows-dev-docs/pull/5528 <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments It was asked why this feature is needed at all, because it could be done with snipping tool and just AoT that window as well. While this is true, PowerToys goal always was to improve and speed up workflows. Instead of capturing the screenshot, opening it, and then apply "Crop and Lock" or "Always on Top" on the screenshots window, this PR aims to provide this functionality in a single step. Example use cases: - _when I want to compare between two situations like previous output result and current output result._ (#31799) - _Allow cropping a section of a large code file (say top while working at the bottom) as reference while working elsewhere in the file._ (#33071) - _Can be useful for the work in the same document, like excel or word where you are actively checking the data from the same document._ (#28633) - _In lot's of older applications, if you need to get some information or data from one dialog do another, but because of dialog modality it's not possible to have both windows open at the same time._ (#33812) - _nowadays quite a lot is happening inside the browser. Quite often, I want to keep a small portion of the current website visible and switch to e.g. the writing tool also running in a different tab in the same browser window._ (#31799) I've used win+ctrl+shift+s as the default activation shortcut, as it's not yet used by other powertoys utilities, has similarity with the normal win+shift+s shortcut hotkey and is consistent with the other Crop and Lock shortcuts win+ctrl+shift+r (Reparent Mode) and win+ctrl+shift+t (Thumbnail Mode). <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Compatibility tested manually with a large set of applications I have installed on my computer. However, automated tests don't really make sense as there is not much business logic which could be tested. --------- Co-authored-by: Niels Laute <niels.laute@live.nl> Co-authored-by: vanzue <vanzue@outlook.com>
This commit is contained in:
@@ -223,6 +223,10 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
{
|
||||
return CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT;
|
||||
}
|
||||
hstring Constants::CropAndLockScreenshotEvent()
|
||||
{
|
||||
return CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT;
|
||||
}
|
||||
hstring Constants::ShowEnvironmentVariablesSharedEvent()
|
||||
{
|
||||
return CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT;
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring TerminateHostsSharedEvent();
|
||||
static hstring CropAndLockThumbnailEvent();
|
||||
static hstring CropAndLockReparentEvent();
|
||||
static hstring CropAndLockScreenshotEvent();
|
||||
static hstring ShowEnvironmentVariablesSharedEvent();
|
||||
static hstring ShowEnvironmentVariablesAdminSharedEvent();
|
||||
static hstring WorkspacesLaunchEditorEvent();
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace PowerToys
|
||||
static String TerminateHostsSharedEvent();
|
||||
static String CropAndLockThumbnailEvent();
|
||||
static String CropAndLockReparentEvent();
|
||||
static String CropAndLockScreenshotEvent();
|
||||
static String ShowEnvironmentVariablesSharedEvent();
|
||||
static String ShowEnvironmentVariablesAdminSharedEvent();
|
||||
static String WorkspacesLaunchEditorEvent();
|
||||
|
||||
@@ -132,6 +132,7 @@ namespace CommonSharedConstants
|
||||
// Path to the events used by CropAndLock
|
||||
const wchar_t CROP_AND_LOCK_REPARENT_EVENT[] = L"Local\\PowerToysCropAndLockReparentEvent-6060860a-76a1-44e8-8d0e-6355785e9c36";
|
||||
const wchar_t CROP_AND_LOCK_THUMBNAIL_EVENT[] = L"Local\\PowerToysCropAndLockThumbnailEvent-1637be50-da72-46b2-9220-b32b206b2434";
|
||||
const wchar_t CROP_AND_LOCK_SCREENSHOT_EVENT[] = L"Local\\PowerToysCropAndLockScreenshotEvent-ff077ab2-8360-4bd1-864a-637389d35593";
|
||||
const wchar_t CROP_AND_LOCK_EXIT_EVENT[] = L"Local\\PowerToysCropAndLockExitEvent-d995d409-7b70-482b-bad6-e7c8666f375a";
|
||||
|
||||
// Path to the events used by EnvironmentVariables
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
<ClCompile Include="ChildWindow.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="ReparentCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ScreenshotCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ThumbnailCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="OverlayWindow.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -126,6 +127,7 @@
|
||||
<ClInclude Include="DisplaysUtil.h" />
|
||||
<ClInclude Include="ModuleConstants.h" />
|
||||
<ClInclude Include="ReparentCropAndLockWindow.h" />
|
||||
<ClInclude Include="ScreenshotCropAndLockWindow.h" />
|
||||
<ClInclude Include="ThumbnailCropAndLockWindow.h" />
|
||||
<ClInclude Include="SettingsWindow.h" />
|
||||
<ClInclude Include="OverlayWindow.h" />
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ClCompile Include="ReparentCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ChildWindow.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="ScreenshotCropAndLockWindow.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -28,6 +29,7 @@
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="ModuleConstants.h" />
|
||||
<ClInclude Include="DispatcherQueue.desktop.interop.h" />
|
||||
<ClInclude Include="ScreenshotCropAndLockWindow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="CropAndLock.rc" />
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "pch.h"
|
||||
#include "ScreenshotCropAndLockWindow.h"
|
||||
|
||||
const std::wstring ScreenshotCropAndLockWindow::ClassName = L"CropAndLock.ScreenshotCropAndLockWindow";
|
||||
std::once_flag ScreenshotCropAndLockWindowClassRegistration;
|
||||
|
||||
void ScreenshotCropAndLockWindow::RegisterWindowClass()
|
||||
{
|
||||
auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
|
||||
WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.hInstance = instance;
|
||||
wcex.hIcon = LoadIconW(instance, IDI_APPLICATION);
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
|
||||
wcex.lpszClassName = ClassName.c_str();
|
||||
wcex.hIconSm = LoadIconW(wcex.hInstance, IDI_APPLICATION);
|
||||
winrt::check_bool(RegisterClassExW(&wcex));
|
||||
}
|
||||
|
||||
ScreenshotCropAndLockWindow::ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height)
|
||||
{
|
||||
auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
|
||||
|
||||
std::call_once(ScreenshotCropAndLockWindowClassRegistration, []() { RegisterWindowClass(); });
|
||||
|
||||
auto exStyle = 0;
|
||||
auto style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
|
||||
|
||||
RECT rect = { 0, 0, width, height };
|
||||
winrt::check_bool(AdjustWindowRectEx(&rect, style, false, exStyle));
|
||||
auto adjustedWidth = rect.right - rect.left;
|
||||
auto adjustedHeight = rect.bottom - rect.top;
|
||||
|
||||
winrt::check_bool(CreateWindowExW(exStyle, ClassName.c_str(), titleString.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, adjustedWidth, adjustedHeight, nullptr, nullptr, instance, this));
|
||||
WINRT_ASSERT(m_window);
|
||||
}
|
||||
|
||||
ScreenshotCropAndLockWindow::~ScreenshotCropAndLockWindow()
|
||||
{
|
||||
DestroyWindow(m_window);
|
||||
}
|
||||
|
||||
LRESULT ScreenshotCropAndLockWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
if (m_closedCallback != nullptr && !m_destroyed)
|
||||
{
|
||||
m_destroyed = true;
|
||||
m_closedCallback(m_window);
|
||||
}
|
||||
break;
|
||||
case WM_PAINT:
|
||||
if (m_captured && m_bitmap)
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(m_window, &ps);
|
||||
HDC memDC = CreateCompatibleDC(hdc);
|
||||
SelectObject(memDC, m_bitmap.get());
|
||||
|
||||
RECT clientRect = {};
|
||||
GetClientRect(m_window, &clientRect);
|
||||
int clientWidth = clientRect.right - clientRect.left;
|
||||
int clientHeight = clientRect.bottom - clientRect.top;
|
||||
|
||||
int srcWidth = m_destRect.right - m_destRect.left;
|
||||
int srcHeight = m_destRect.bottom - m_destRect.top;
|
||||
|
||||
float srcAspect = static_cast<float>(srcWidth) / srcHeight;
|
||||
float dstAspect = static_cast<float>(clientWidth) / clientHeight;
|
||||
|
||||
int drawWidth = clientWidth;
|
||||
int drawHeight = static_cast<int>(clientWidth / srcAspect);
|
||||
if (dstAspect > srcAspect)
|
||||
{
|
||||
drawHeight = clientHeight;
|
||||
drawWidth = static_cast<int>(clientHeight * srcAspect);
|
||||
}
|
||||
|
||||
int offsetX = (clientWidth - drawWidth) / 2;
|
||||
int offsetY = (clientHeight - drawHeight) / 2;
|
||||
|
||||
SetStretchBltMode(hdc, HALFTONE);
|
||||
StretchBlt(hdc, offsetX, offsetY, drawWidth, drawHeight, memDC, 0, 0, srcWidth, srcHeight, SRCCOPY);
|
||||
DeleteDC(memDC);
|
||||
EndPaint(m_window, &ps);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return base_type::MessageHandler(message, wparam, lparam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScreenshotCropAndLockWindow::CropAndLock(HWND windowToCrop, RECT cropRect)
|
||||
{
|
||||
if (m_captured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get full window bounds
|
||||
RECT windowRect{};
|
||||
winrt::check_hresult(DwmGetWindowAttribute(
|
||||
windowToCrop,
|
||||
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&windowRect,
|
||||
sizeof(windowRect)));
|
||||
|
||||
RECT clientRect = ClientAreaInScreenSpace(windowToCrop);
|
||||
auto offsetX = clientRect.left - windowRect.left;
|
||||
auto offsetY = clientRect.top - windowRect.top;
|
||||
|
||||
m_sourceRect = {
|
||||
cropRect.left + offsetX,
|
||||
cropRect.top + offsetY,
|
||||
cropRect.right + offsetX,
|
||||
cropRect.bottom + offsetY
|
||||
};
|
||||
|
||||
int fullWidth = windowRect.right - windowRect.left;
|
||||
int fullHeight = windowRect.bottom - windowRect.top;
|
||||
|
||||
HDC fullDC = CreateCompatibleDC(nullptr);
|
||||
HDC screenDC = GetDC(nullptr);
|
||||
HBITMAP fullBitmap = CreateCompatibleBitmap(screenDC, fullWidth, fullHeight);
|
||||
HGDIOBJ oldFullBitmap = SelectObject(fullDC, fullBitmap);
|
||||
|
||||
// Capture full window
|
||||
winrt::check_bool(PrintWindow(windowToCrop, fullDC, PW_RENDERFULLCONTENT));
|
||||
|
||||
|
||||
// Crop
|
||||
int cropWidth = m_sourceRect.right - m_sourceRect.left;
|
||||
int cropHeight = m_sourceRect.bottom - m_sourceRect.top;
|
||||
|
||||
HDC cropDC = CreateCompatibleDC(nullptr);
|
||||
HBITMAP cropBitmap = CreateCompatibleBitmap(screenDC, cropWidth, cropHeight);
|
||||
HGDIOBJ oldCropBitmap = SelectObject(cropDC, cropBitmap);
|
||||
ReleaseDC(nullptr, screenDC);
|
||||
|
||||
BitBlt(
|
||||
cropDC,
|
||||
0,
|
||||
0,
|
||||
cropWidth,
|
||||
cropHeight,
|
||||
fullDC,
|
||||
m_sourceRect.left,
|
||||
m_sourceRect.top,
|
||||
SRCCOPY);
|
||||
|
||||
SelectObject(fullDC, oldFullBitmap);
|
||||
DeleteObject(fullBitmap);
|
||||
DeleteDC(fullDC);
|
||||
|
||||
SelectObject(cropDC, oldCropBitmap);
|
||||
DeleteDC(cropDC);
|
||||
m_bitmap.reset(cropBitmap);
|
||||
|
||||
// Resize our window
|
||||
RECT dest{ 0, 0, cropWidth, cropHeight };
|
||||
LONG_PTR exStyle = GetWindowLongPtrW(m_window, GWL_EXSTYLE);
|
||||
LONG_PTR style = GetWindowLongPtrW(m_window, GWL_STYLE);
|
||||
|
||||
winrt::check_bool(AdjustWindowRectEx(&dest, static_cast<DWORD>(style), FALSE, static_cast<DWORD>(exStyle)));
|
||||
|
||||
winrt::check_bool(SetWindowPos(
|
||||
m_window, HWND_TOPMOST, 0, 0, dest.right - dest.left, dest.bottom - dest.top, SWP_NOMOVE | SWP_SHOWWINDOW));
|
||||
|
||||
m_destRect = { 0, 0, cropWidth, cropHeight };
|
||||
m_captured = true;
|
||||
InvalidateRect(m_window, nullptr, FALSE);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <robmikh.common/DesktopWindow.h>
|
||||
#include "CropAndLockWindow.h"
|
||||
|
||||
struct ScreenshotCropAndLockWindow : robmikh::common::desktop::DesktopWindow<ScreenshotCropAndLockWindow>, CropAndLockWindow
|
||||
{
|
||||
static const std::wstring ClassName;
|
||||
ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height);
|
||||
~ScreenshotCropAndLockWindow() override;
|
||||
LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam);
|
||||
|
||||
HWND Handle() override { return m_window; }
|
||||
void CropAndLock(HWND windowToCrop, RECT cropRect) override;
|
||||
void OnClosed(std::function<void(HWND)> callback) override { m_closedCallback = callback; }
|
||||
|
||||
private:
|
||||
static void RegisterWindowClass();
|
||||
|
||||
private:
|
||||
std::unique_ptr<void, decltype(&DeleteObject)> m_bitmap{ nullptr, &DeleteObject };
|
||||
RECT m_destRect = {};
|
||||
RECT m_sourceRect = {};
|
||||
|
||||
bool m_captured = false;
|
||||
bool m_destroyed = false;
|
||||
std::function<void(HWND)> m_closedCallback;
|
||||
};
|
||||
@@ -4,4 +4,5 @@ enum class CropAndLockType
|
||||
{
|
||||
Reparent,
|
||||
Thumbnail,
|
||||
Screenshot,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "SettingsWindow.h"
|
||||
#include "OverlayWindow.h"
|
||||
#include "CropAndLockWindow.h"
|
||||
#include "ScreenshotCropAndLockWindow.h"
|
||||
#include "ThumbnailCropAndLockWindow.h"
|
||||
#include "ReparentCropAndLockWindow.h"
|
||||
#include "ModuleConstants.h"
|
||||
@@ -133,6 +134,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
// Handles and thread for the events sent from runner
|
||||
HANDLE m_reparent_event_handle;
|
||||
HANDLE m_thumbnail_event_handle;
|
||||
HANDLE m_screenshot_event_handle;
|
||||
HANDLE m_exit_event_handle;
|
||||
std::thread m_event_triggers_thread;
|
||||
|
||||
@@ -181,6 +183,11 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
Logger::trace(L"Creating a thumbnail window");
|
||||
Trace::CropAndLock::CreateThumbnailWindow();
|
||||
break;
|
||||
case CropAndLockType::Screenshot:
|
||||
croppedWindow = std::make_shared<ScreenshotCropAndLockWindow>(title, 800, 600);
|
||||
Logger::trace(L"Creating a screenshot window");
|
||||
Trace::CropAndLock::CreateScreenshotWindow();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -215,8 +222,9 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
// Start a thread to listen on the events.
|
||||
m_reparent_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT);
|
||||
m_thumbnail_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT);
|
||||
m_screenshot_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT);
|
||||
m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_EXIT_EVENT);
|
||||
if (!m_reparent_event_handle || !m_thumbnail_event_handle || !m_exit_event_handle)
|
||||
if (!m_reparent_event_handle || !m_thumbnail_event_handle || !m_screenshot_event_handle || !m_exit_event_handle)
|
||||
{
|
||||
Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError()));
|
||||
return 1;
|
||||
@@ -224,10 +232,10 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
|
||||
m_event_triggers_thread = std::thread([&]() {
|
||||
MSG msg;
|
||||
HANDLE event_handles[3] = { m_reparent_event_handle, m_thumbnail_event_handle, m_exit_event_handle };
|
||||
HANDLE event_handles[4] = { m_reparent_event_handle, m_thumbnail_event_handle, m_screenshot_event_handle, m_exit_event_handle };
|
||||
while (m_running)
|
||||
{
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(3, event_handles, false, INFINITE, QS_ALLINPUT);
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(4, event_handles, false, INFINITE, QS_ALLINPUT);
|
||||
if (!m_running)
|
||||
{
|
||||
break;
|
||||
@@ -259,13 +267,25 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 2:
|
||||
{
|
||||
// Screenshot Event
|
||||
bool enqueueSucceeded = controller.DispatcherQueue().TryEnqueue([&]() {
|
||||
ProcessCommand(CropAndLockType::Screenshot);
|
||||
});
|
||||
if (!enqueueSucceeded)
|
||||
{
|
||||
Logger::error("Couldn't enqueue message to screenshot a window.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 3:
|
||||
{
|
||||
// Exit Event
|
||||
Logger::trace(L"Received an exit event.");
|
||||
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 3:
|
||||
case WAIT_OBJECT_0 + 4:
|
||||
if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
@@ -295,6 +315,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
SetEvent(m_reparent_event_handle);
|
||||
CloseHandle(m_reparent_event_handle);
|
||||
CloseHandle(m_thumbnail_event_handle);
|
||||
CloseHandle(m_screenshot_event_handle);
|
||||
CloseHandle(m_exit_event_handle);
|
||||
m_event_triggers_thread.join();
|
||||
|
||||
|
||||
@@ -41,6 +41,15 @@ void Trace::CropAndLock::ActivateThumbnail() noexcept
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::ActivateScreenshot() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_ActivateScreenshot",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::CreateReparentWindow() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
@@ -59,8 +68,17 @@ void Trace::CropAndLock::CreateThumbnailWindow() noexcept
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::CreateScreenshotWindow() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_CreateScreenshotWindow",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
// Event to send settings telemetry.
|
||||
void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparentHotkey, PowertoyModuleIface::Hotkey& thumbnailHotkey) noexcept
|
||||
void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparentHotkey, PowertoyModuleIface::Hotkey& thumbnailHotkey, PowertoyModuleIface::Hotkey& screenshotHotkey) noexcept
|
||||
{
|
||||
std::wstring hotKeyStrReparent =
|
||||
std::wstring(reparentHotkey.win ? L"Win + " : L"") +
|
||||
@@ -76,11 +94,19 @@ void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparent
|
||||
std::wstring(thumbnailHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(thumbnailHotkey.key);
|
||||
|
||||
std::wstring hotKeyStrScreenshot =
|
||||
std::wstring(screenshotHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(screenshotHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(screenshotHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(screenshotHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(screenshotHotkey.key);
|
||||
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_Settings",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(hotKeyStrReparent.c_str(), "ReparentHotKey"),
|
||||
TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey"));
|
||||
TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey"),
|
||||
TraceLoggingWideString(hotKeyStrScreenshot.c_str(), "ScreenshotHotkey"));
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ public:
|
||||
static void Enable(bool enabled) noexcept;
|
||||
static void ActivateReparent() noexcept;
|
||||
static void ActivateThumbnail() noexcept;
|
||||
static void ActivateScreenshot() noexcept;
|
||||
static void CreateReparentWindow() noexcept;
|
||||
static void CreateThumbnailWindow() noexcept;
|
||||
static void SettingsTelemetry(PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&) noexcept;
|
||||
static void CreateScreenshotWindow() noexcept;
|
||||
static void SettingsTelemetry(PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&) noexcept;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_REPARENT_HOTKEY[] = L"reparent-hotkey";
|
||||
const wchar_t JSON_KEY_THUMBNAIL_HOTKEY[] = L"thumbnail-hotkey";
|
||||
const wchar_t JSON_KEY_SCREENSHOT_HOTKEY[] = L"screenshot-hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
}
|
||||
|
||||
@@ -124,6 +125,10 @@ public:
|
||||
SetEvent(m_thumbnail_event_handle);
|
||||
Trace::CropAndLock::ActivateThumbnail();
|
||||
}
|
||||
if (hotkeyId == 2) { // Same order as set by get_hotkeys
|
||||
SetEvent(m_screenshot_event_handle);
|
||||
Trace::CropAndLock::ActivateScreenshot();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -133,12 +138,13 @@ public:
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
if (hotkeys && buffer_size >= 2)
|
||||
if (hotkeys && buffer_size >= 3)
|
||||
{
|
||||
hotkeys[0] = m_reparent_hotkey;
|
||||
hotkeys[1] = m_thumbnail_hotkey;
|
||||
hotkeys[2] = m_screenshot_hotkey;
|
||||
}
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Enable the powertoy
|
||||
@@ -171,7 +177,7 @@ public:
|
||||
virtual void send_settings_telemetry() override
|
||||
{
|
||||
Logger::info("Send settings telemetry");
|
||||
Trace::CropAndLock::SettingsTelemetry(m_reparent_hotkey, m_thumbnail_hotkey);
|
||||
Trace::CropAndLock::SettingsTelemetry(m_reparent_hotkey, m_thumbnail_hotkey, m_screenshot_hotkey);
|
||||
}
|
||||
|
||||
CropAndLockModuleInterface()
|
||||
@@ -182,6 +188,7 @@ public:
|
||||
|
||||
m_reparent_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT);
|
||||
m_thumbnail_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT);
|
||||
m_screenshot_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT);
|
||||
m_exit_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_EXIT_EVENT);
|
||||
|
||||
init_settings();
|
||||
@@ -202,6 +209,7 @@ private:
|
||||
|
||||
ResetEvent(m_reparent_event_handle);
|
||||
ResetEvent(m_thumbnail_event_handle);
|
||||
ResetEvent(m_screenshot_event_handle);
|
||||
ResetEvent(m_exit_event_handle);
|
||||
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
@@ -234,6 +242,7 @@ private:
|
||||
|
||||
ResetEvent(m_reparent_event_handle);
|
||||
ResetEvent(m_thumbnail_event_handle);
|
||||
ResetEvent(m_screenshot_event_handle);
|
||||
|
||||
// Log telemetry
|
||||
if (traceEvent)
|
||||
@@ -283,6 +292,21 @@ private:
|
||||
{
|
||||
Logger::error("Failed to initialize CropAndLock thumbnail shortcut from settings. Value will keep unchanged.");
|
||||
}
|
||||
try
|
||||
{
|
||||
Hotkey _temp_screenshot;
|
||||
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SCREENSHOT_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
|
||||
_temp_screenshot.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
|
||||
_temp_screenshot.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
|
||||
_temp_screenshot.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
_temp_screenshot.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
_temp_screenshot.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_screenshot_hotkey = _temp_screenshot;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("Failed to initialize CropAndLock screenshot shortcut from settings. Value will keep unchanged.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -321,9 +345,11 @@ private:
|
||||
// TODO: actual default hotkey setting in line with other PowerToys.
|
||||
Hotkey m_reparent_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'R' };
|
||||
Hotkey m_thumbnail_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'T' };
|
||||
Hotkey m_screenshot_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'S' };
|
||||
|
||||
HANDLE m_reparent_event_handle;
|
||||
HANDLE m_thumbnail_event_handle;
|
||||
HANDLE m_screenshot_event_handle;
|
||||
HANDLE m_exit_event_handle;
|
||||
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -21,15 +22,20 @@ internal sealed partial class CropAndLockReparentCommand : InvokableCommand
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
Task.Run(async () =>
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockReparentEvent());
|
||||
evt.Set();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return CommandResult.ShowToast($"Failed to start Crop and Lock (Reparent): {ex.Message}");
|
||||
}
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockReparentEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace PowerToysExtension.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers Crop and Lock screenshot mode via the shared event.
|
||||
/// </summary>
|
||||
internal sealed partial class CropAndLockScreenshotCommand : InvokableCommand
|
||||
{
|
||||
public CropAndLockScreenshotCommand()
|
||||
{
|
||||
Name = "Crop and Lock (Screenshot)";
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockScreenshotEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -21,15 +22,20 @@ internal sealed partial class CropAndLockThumbnailCommand : InvokableCommand
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
Task.Run(async () =>
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockThumbnailEvent());
|
||||
evt.Set();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return CommandResult.ShowToast($"Failed to start Crop and Lock (Thumbnail): {ex.Message}");
|
||||
}
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockThumbnailEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ internal sealed class CropAndLockModuleCommandProvider : ModuleCommandProvider
|
||||
Subtitle = Resources.CropAndLock_Thumbnail_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new CropAndLockScreenshotCommand())
|
||||
{
|
||||
Title = Resources.CropAndLock_Screenshot_Title,
|
||||
Subtitle = Resources.CropAndLock_Screenshot_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
|
||||
@@ -375,6 +375,24 @@ namespace PowerToysExtension.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Crop and Lock (Screenshot).
|
||||
/// </summary>
|
||||
internal static string CropAndLock_Screenshot_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("CropAndLock_Screenshot_Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create a cropped screenshot window.
|
||||
/// </summary>
|
||||
internal static string CropAndLock_Screenshot_Subtitle {
|
||||
get {
|
||||
return ResourceManager.GetString("CropAndLock_Screenshot_Subtitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch Environment Variables editor.
|
||||
/// </summary>
|
||||
|
||||
@@ -260,6 +260,12 @@
|
||||
<data name="CropAndLock_Thumbnail_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped thumbnail window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Screenshot_Title" xml:space="preserve">
|
||||
<value>Crop and Lock (Screenshot)</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Screenshot_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped screenshot window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Crop and Lock settings</value>
|
||||
</data>
|
||||
|
||||
@@ -11,11 +11,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public static readonly HotkeySettings DefaultReparentHotkeyValue = new HotkeySettings(true, true, false, true, 0x52); // Ctrl+Win+Shift+R
|
||||
public static readonly HotkeySettings DefaultThumbnailHotkeyValue = new HotkeySettings(true, true, false, true, 0x54); // Ctrl+Win+Shift+T
|
||||
public static readonly HotkeySettings DefaultScreenshotHotkeyValue = new HotkeySettings(true, true, false, true, 0x53); // Ctrl+Win+Shift+S
|
||||
|
||||
public CropAndLockProperties()
|
||||
{
|
||||
ReparentHotkey = new KeyboardKeysProperty(DefaultReparentHotkeyValue);
|
||||
ThumbnailHotkey = new KeyboardKeysProperty(DefaultThumbnailHotkeyValue);
|
||||
ScreenshotHotkey = new KeyboardKeysProperty(DefaultScreenshotHotkeyValue);
|
||||
}
|
||||
|
||||
[JsonPropertyName("reparent-hotkey")]
|
||||
@@ -23,5 +25,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
[JsonPropertyName("thumbnail-hotkey")]
|
||||
public KeyboardKeysProperty ThumbnailHotkey { get; set; }
|
||||
|
||||
[JsonPropertyName("screenshot-hotkey")]
|
||||
public KeyboardKeysProperty ScreenshotHotkey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
() => Properties.ThumbnailHotkey.Value,
|
||||
value => Properties.ThumbnailHotkey.Value = value ?? CropAndLockProperties.DefaultThumbnailHotkeyValue,
|
||||
"CropAndLock_ThumbnailActivation_Shortcut"),
|
||||
new HotkeyAccessor(
|
||||
() => Properties.ScreenshotHotkey.Value,
|
||||
value => Properties.ScreenshotHotkey.Value = value ?? CropAndLockProperties.DefaultScreenshotHotkeyValue,
|
||||
"CropAndLock_ScreenshotActivation_Shortcut"),
|
||||
};
|
||||
|
||||
return hotkeyAccessors.ToArray();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
<controls:ShortcutWithTextLabelControl x:Name="ReparentHotkeyControl" x:Uid="Oobe_CropAndLock_HowToUse_Reparent" />
|
||||
|
||||
<controls:ShortcutWithTextLabelControl x:Name="ScreenshotHotkeyControl" x:Uid="Oobe_CropAndLock_HowToUse_Screenshot" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Uid="OOBE_Settings" Click="SettingsLaunchButton_Click" />
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
ViewModel.LogOpeningModuleEvent();
|
||||
|
||||
ReparentHotkeyControl.Keys = SettingsRepository<CropAndLockSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ReparentHotkey.Value.GetKeysList();
|
||||
ThumbnailHotkeyControl.Keys = SettingsRepository<CropAndLockSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ThumbnailHotkey.Value.GetKeysList();
|
||||
ScreenshotHotkeyControl.Keys = SettingsRepository<CropAndLockSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ScreenshotHotkey.Value.GetKeysList();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
AllowDisable="True"
|
||||
HotkeySettings="{x:Bind Path=ViewModel.ReparentActivationShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="CropAndLock_ScreenshotActivation_Shortcut" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<controls:ShortcutControl
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AllowDisable="True"
|
||||
HotkeySettings="{x:Bind Path=ViewModel.ScreenshotActivationShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
</StackPanel>
|
||||
</controls:SettingsPageControl.ModuleContent>
|
||||
|
||||
@@ -3107,6 +3107,12 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="CropAndLock_ThumbnailActivation_Shortcut.Description" xml:space="preserve">
|
||||
<value>Creates a cropped, non-interactive thumbnail of another window. Improves app compatibility.</value>
|
||||
</data>
|
||||
<data name="CropAndLock_ScreenshotActivation_Shortcut.Header" xml:space="preserve">
|
||||
<value>Screenshot shortcut</value>
|
||||
</data>
|
||||
<data name="CropAndLock_ScreenshotActivation_Shortcut.Description" xml:space="preserve">
|
||||
<value>Creates a cropped, static screenshot of another window. The screenshot won't update with the original window's content.</value>
|
||||
</data>
|
||||
<data name="CropAndLock.SecondaryLinksHeader" xml:space="preserve">
|
||||
<value>Attribution</value>
|
||||
<comment>giving credit to the projects this utility was based on</comment>
|
||||
@@ -3125,6 +3131,9 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="Oobe_CropAndLock_HowToUse_Reparent.Text" xml:space="preserve">
|
||||
<value>to crop an application's window into a cropped window. This is experimental and can cause issues with some applications, since the cropped window will contain the original application window.</value>
|
||||
</data>
|
||||
<data name="Oobe_CropAndLock_HowToUse_Screenshot.Text" xml:space="preserve">
|
||||
<value>to crop an application's window into a screenshot window. The screenshot won't update with the original window's content.</value>
|
||||
</data>
|
||||
<data name="AlwaysOnTop.ModuleDescription" xml:space="preserve">
|
||||
<value>Always On Top is a quick and easy way to pin windows on top.</value>
|
||||
<comment>{Locked="Always On Top"}</comment>
|
||||
@@ -3382,6 +3391,9 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="CropAndLock_Reparent" xml:space="preserve">
|
||||
<value>Crop an app's window into a cropped window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Screenshot" xml:space="preserve">
|
||||
<value>Crop an app into a screenshot window</value>
|
||||
</data>
|
||||
<data name="FancyZones_OpenEditor" xml:space="preserve">
|
||||
<value>Open editor</value>
|
||||
</data>
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
_reparentHotkey = Settings.Properties.ReparentHotkey.Value;
|
||||
_thumbnailHotkey = Settings.Properties.ThumbnailHotkey.Value;
|
||||
_screenshotHotkey = Settings.Properties.ScreenshotHotkey.Value;
|
||||
|
||||
// set the callback functions value to handle outgoing IPC message.
|
||||
SendConfigMSG = ipcMSGCallBackFunc;
|
||||
@@ -73,7 +74,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
var hotkeysDict = new Dictionary<string, HotkeySettings[]>
|
||||
{
|
||||
[ModuleName] = [ReparentActivationShortcut, ThumbnailActivationShortcut],
|
||||
[ModuleName] = [ReparentActivationShortcut, ThumbnailActivationShortcut, ScreenshotActivationShortcut],
|
||||
};
|
||||
|
||||
return hotkeysDict;
|
||||
@@ -172,6 +173,36 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public HotkeySettings ScreenshotActivationShortcut
|
||||
{
|
||||
get => _screenshotHotkey;
|
||||
set
|
||||
{
|
||||
if (value != _screenshotHotkey)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
_screenshotHotkey = CropAndLockProperties.DefaultScreenshotHotkeyValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_screenshotHotkey = value;
|
||||
}
|
||||
|
||||
Settings.Properties.ScreenshotHotkey.Value = _screenshotHotkey;
|
||||
NotifyPropertyChanged();
|
||||
|
||||
// Using InvariantCulture as this is an IPC message
|
||||
SendConfigMSG(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
|
||||
CropAndLockSettings.ModuleName,
|
||||
JsonSerializer.Serialize(Settings, SourceGenerationContextContext.Default.CropAndLockSettings)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
@@ -189,5 +220,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private bool _isEnabled;
|
||||
private HotkeySettings _reparentHotkey;
|
||||
private HotkeySettings _thumbnailHotkey;
|
||||
private HotkeySettings _screenshotHotkey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +440,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Thumbnail"), Shortcut = settings.Properties.ThumbnailHotkey.Value.GetKeysList() },
|
||||
new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Reparent"), Shortcut = settings.Properties.ReparentHotkey.Value.GetKeysList() },
|
||||
new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Screenshot"), Shortcut = settings.Properties.ScreenshotHotkey.Value.GetKeysList() },
|
||||
};
|
||||
return new ObservableCollection<DashboardModuleItem>(list);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user