mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-20 19:16:55 +01:00
Compare commits
9 Commits
main
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7c457b77f | ||
|
|
9d3daf09fc | ||
|
|
2fca036abd | ||
|
|
d2ba093209 | ||
|
|
ee14ee65c5 | ||
|
|
c572a49295 | ||
|
|
8fb98380e9 | ||
|
|
95a78c3ee5 | ||
|
|
d015b96eea |
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1520,6 +1520,7 @@ RKey
|
||||
RNumber
|
||||
rop
|
||||
rollups
|
||||
ROOTOWNER
|
||||
ROUNDSMALL
|
||||
ROWSETEXT
|
||||
rpcrt
|
||||
|
||||
@@ -72,6 +72,10 @@ namespace CommonSharedConstants
|
||||
|
||||
const wchar_t ALWAYS_ON_TOP_TERMINATE_EVENT[] = L"Local\\AlwaysOnTopTerminateEvent-cfdf1eae-791f-4953-8021-2f18f3837eae";
|
||||
|
||||
const wchar_t ALWAYS_ON_TOP_INCREASE_OPACITY_EVENT[] = L"Local\\AlwaysOnTopIncreaseOpacityEvent-a1b2c3d4-e5f6-7890-abcd-ef1234567890";
|
||||
|
||||
const wchar_t ALWAYS_ON_TOP_DECREASE_OPACITY_EVENT[] = L"Local\\AlwaysOnTopDecreaseOpacityEvent-b2c3d4e5-f6a7-8901-bcde-f12345678901";
|
||||
|
||||
// Path to the event used by PowerAccent
|
||||
const wchar_t POWERACCENT_EXIT_EVENT[] = L"Local\\PowerToysPowerAccentExitEvent-53e93389-d19a-4fbb-9b36-1981c8965e17";
|
||||
|
||||
|
||||
@@ -153,9 +153,21 @@ LRESULT AlwaysOnTop::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lp
|
||||
{
|
||||
if (message == WM_HOTKEY)
|
||||
{
|
||||
int hotkeyId = static_cast<int>(wparam);
|
||||
if (HWND fw{ GetForegroundWindow() })
|
||||
{
|
||||
ProcessCommand(fw);
|
||||
if (hotkeyId == static_cast<int>(HotkeyId::Pin))
|
||||
{
|
||||
ProcessCommand(fw);
|
||||
}
|
||||
else if (hotkeyId == static_cast<int>(HotkeyId::IncreaseOpacity))
|
||||
{
|
||||
StepWindowTransparency(fw, Settings::transparencyStep);
|
||||
}
|
||||
else if (hotkeyId == static_cast<int>(HotkeyId::DecreaseOpacity))
|
||||
{
|
||||
StepWindowTransparency(fw, -Settings::transparencyStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (message == WM_PRIV_SETTINGS_CHANGED)
|
||||
@@ -191,6 +203,10 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
||||
m_topmostWindows.erase(iter);
|
||||
}
|
||||
|
||||
// Restore transparency when unpinning
|
||||
RestoreWindowAlpha(window);
|
||||
m_windowOriginalLayeredState.erase(window);
|
||||
|
||||
Trace::AlwaysOnTop::UnpinWindow();
|
||||
}
|
||||
}
|
||||
@@ -200,6 +216,7 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
||||
{
|
||||
soundType = Sound::Type::On;
|
||||
AssignBorder(window);
|
||||
|
||||
Trace::AlwaysOnTop::PinWindow();
|
||||
}
|
||||
}
|
||||
@@ -269,11 +286,22 @@ void AlwaysOnTop::RegisterHotkey() const
|
||||
{
|
||||
if (m_useCentralizedLLKH)
|
||||
{
|
||||
// All hotkeys are handled by centralized LLKH
|
||||
return;
|
||||
}
|
||||
|
||||
// Register hotkeys only when not using centralized LLKH
|
||||
UnregisterHotKey(m_window, static_cast<int>(HotkeyId::Pin));
|
||||
UnregisterHotKey(m_window, static_cast<int>(HotkeyId::IncreaseOpacity));
|
||||
UnregisterHotKey(m_window, static_cast<int>(HotkeyId::DecreaseOpacity));
|
||||
|
||||
// Register pin hotkey
|
||||
RegisterHotKey(m_window, static_cast<int>(HotkeyId::Pin), AlwaysOnTopSettings::settings().hotkey.get_modifiers(), AlwaysOnTopSettings::settings().hotkey.get_code());
|
||||
|
||||
// Register transparency hotkeys using the same modifiers as the pin hotkey
|
||||
UINT modifiers = AlwaysOnTopSettings::settings().hotkey.get_modifiers();
|
||||
RegisterHotKey(m_window, static_cast<int>(HotkeyId::IncreaseOpacity), modifiers, VK_OEM_PLUS);
|
||||
RegisterHotKey(m_window, static_cast<int>(HotkeyId::DecreaseOpacity), modifiers, VK_OEM_MINUS);
|
||||
}
|
||||
|
||||
void AlwaysOnTop::RegisterLLKH()
|
||||
@@ -285,6 +313,8 @@ void AlwaysOnTop::RegisterLLKH()
|
||||
|
||||
m_hPinEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_PIN_EVENT);
|
||||
m_hTerminateEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_TERMINATE_EVENT);
|
||||
m_hIncreaseOpacityEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_INCREASE_OPACITY_EVENT);
|
||||
m_hDecreaseOpacityEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_DECREASE_OPACITY_EVENT);
|
||||
|
||||
if (!m_hPinEvent)
|
||||
{
|
||||
@@ -298,30 +328,54 @@ void AlwaysOnTop::RegisterLLKH()
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE handles[2] = { m_hPinEvent,
|
||||
m_hTerminateEvent };
|
||||
if (!m_hIncreaseOpacityEvent)
|
||||
{
|
||||
Logger::warn(L"Failed to create increaseOpacityEvent. {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
if (!m_hDecreaseOpacityEvent)
|
||||
{
|
||||
Logger::warn(L"Failed to create decreaseOpacityEvent. {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
HANDLE handles[4] = { m_hPinEvent,
|
||||
m_hTerminateEvent,
|
||||
m_hIncreaseOpacityEvent,
|
||||
m_hDecreaseOpacityEvent };
|
||||
|
||||
m_thread = std::thread([this, handles]() {
|
||||
MSG msg;
|
||||
while (m_running)
|
||||
{
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(2, handles, false, INFINITE, QS_ALLINPUT);
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(4, handles, false, INFINITE, QS_ALLINPUT);
|
||||
if (!m_running)
|
||||
{
|
||||
break;
|
||||
}
|
||||
switch (dwEvt)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
case WAIT_OBJECT_0: // Pin event
|
||||
if (HWND fw{ GetForegroundWindow() })
|
||||
{
|
||||
ProcessCommand(fw);
|
||||
}
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_OBJECT_0 + 1: // Terminate event
|
||||
PostThreadMessage(m_mainThreadId, WM_QUIT, 0, 0);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 2:
|
||||
case WAIT_OBJECT_0 + 2: // Increase opacity event
|
||||
if (HWND fw{ GetForegroundWindow() })
|
||||
{
|
||||
StepWindowTransparency(fw, Settings::transparencyStep);
|
||||
}
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 3: // Decrease opacity event
|
||||
if (HWND fw{ GetForegroundWindow() })
|
||||
{
|
||||
StepWindowTransparency(fw, -Settings::transparencyStep);
|
||||
}
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 4: // Message queue
|
||||
if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
@@ -370,9 +424,12 @@ void AlwaysOnTop::UnpinAll()
|
||||
{
|
||||
Logger::error(L"Unpinning topmost window failed");
|
||||
}
|
||||
// Restore transparency when unpinning all
|
||||
RestoreWindowAlpha(topWindow);
|
||||
}
|
||||
|
||||
m_topmostWindows.clear();
|
||||
m_windowOriginalLayeredState.clear();
|
||||
}
|
||||
|
||||
void AlwaysOnTop::CleanUp()
|
||||
@@ -456,6 +513,7 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
||||
for (const auto window : toErase)
|
||||
{
|
||||
m_topmostWindows.erase(window);
|
||||
m_windowOriginalLayeredState.erase(window);
|
||||
}
|
||||
|
||||
switch (data->event)
|
||||
@@ -556,4 +614,199 @@ void AlwaysOnTop::RefreshBorders()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HWND AlwaysOnTop::ResolveTransparencyTargetWindow(HWND window)
|
||||
{
|
||||
if (!window || !IsWindow(window))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Authorization: only allow if the ORIGINAL selection is tracked/pinned
|
||||
if (!IsTracked(window) && !IsPinned(window))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const HWND desktop = GetDesktopWindow();
|
||||
const HWND shell = GetShellWindow();
|
||||
|
||||
auto isBad = [&](HWND h) {
|
||||
return !h || !IsWindow(h) || h == desktop || h == shell;
|
||||
};
|
||||
|
||||
// Candidates
|
||||
HWND rootOwner = GetAncestor(window, GA_ROOTOWNER);
|
||||
HWND root = GetAncestor(window, GA_ROOT);
|
||||
|
||||
// Prefer a non-bad candidate. Visible is a preference, not a hard gate.
|
||||
if (!isBad(rootOwner) && IsWindowVisible(rootOwner))
|
||||
{
|
||||
return rootOwner;
|
||||
}
|
||||
|
||||
if (!isBad(root) && IsWindowVisible(root))
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
// If both are not visible but still valid, prefer rootOwner/root over the original.
|
||||
if (!isBad(rootOwner))
|
||||
{
|
||||
return rootOwner;
|
||||
}
|
||||
|
||||
if (!isBad(root))
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
||||
void AlwaysOnTop::StepWindowTransparency(HWND window, int delta)
|
||||
{
|
||||
HWND targetWindow = ResolveTransparencyTargetWindow(window);
|
||||
if (!targetWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int currentTransparency = Settings::maxTransparencyPercentage;
|
||||
LONG exStyle = GetWindowLong(targetWindow, GWL_EXSTYLE);
|
||||
if (exStyle & WS_EX_LAYERED)
|
||||
{
|
||||
BYTE alpha = 255;
|
||||
if (GetLayeredWindowAttributes(targetWindow, nullptr, &alpha, nullptr))
|
||||
{
|
||||
currentTransparency = (alpha * 100) / 255;
|
||||
}
|
||||
}
|
||||
|
||||
int newTransparency = (std::max)(Settings::minTransparencyPercentage,
|
||||
(std::min)(Settings::maxTransparencyPercentage, currentTransparency + delta));
|
||||
|
||||
if (newTransparency != currentTransparency)
|
||||
{
|
||||
ApplyWindowAlpha(targetWindow, newTransparency);
|
||||
|
||||
if (AlwaysOnTopSettings::settings().enableSound)
|
||||
{
|
||||
m_sound.Play(delta > 0 ? Sound::Type::IncreaseOpacity : Sound::Type::DecreaseOpacity);
|
||||
}
|
||||
|
||||
Logger::debug(L"Transparency adjusted to {}%", newTransparency);
|
||||
}
|
||||
}
|
||||
|
||||
void AlwaysOnTop::ApplyWindowAlpha(HWND window, int percentage)
|
||||
{
|
||||
if (!window || !IsWindow(window))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
percentage = (std::max)(Settings::minTransparencyPercentage,
|
||||
(std::min)(Settings::maxTransparencyPercentage, percentage));
|
||||
|
||||
LONG exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
bool isCurrentlyLayered = (exStyle & WS_EX_LAYERED) != 0;
|
||||
|
||||
// Cache original state on first transparency application
|
||||
if (m_windowOriginalLayeredState.find(window) == m_windowOriginalLayeredState.end())
|
||||
{
|
||||
WindowLayeredState state;
|
||||
state.hadLayeredStyle = isCurrentlyLayered;
|
||||
|
||||
if (isCurrentlyLayered)
|
||||
{
|
||||
BYTE alpha = 255;
|
||||
COLORREF colorKey = 0;
|
||||
DWORD flags = 0;
|
||||
if (GetLayeredWindowAttributes(window, &colorKey, &alpha, &flags))
|
||||
{
|
||||
state.originalAlpha = alpha;
|
||||
state.usedColorKey = (flags & LWA_COLORKEY) != 0;
|
||||
state.colorKey = colorKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::warn(L"GetLayeredWindowAttributes failed for layered window, skipping");
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_windowOriginalLayeredState[window] = state;
|
||||
}
|
||||
|
||||
// Clear WS_EX_LAYERED first to ensure SetLayeredWindowAttributes works
|
||||
if (isCurrentlyLayered)
|
||||
{
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
||||
SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
}
|
||||
|
||||
BYTE alphaValue = static_cast<BYTE>((255 * percentage) / 100);
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
|
||||
SetLayeredWindowAttributes(window, 0, alphaValue, LWA_ALPHA);
|
||||
}
|
||||
|
||||
void AlwaysOnTop::RestoreWindowAlpha(HWND window)
|
||||
{
|
||||
if (!window || !IsWindow(window))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LONG exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
auto it = m_windowOriginalLayeredState.find(window);
|
||||
|
||||
if (it != m_windowOriginalLayeredState.end())
|
||||
{
|
||||
const auto& originalState = it->second;
|
||||
|
||||
if (originalState.hadLayeredStyle)
|
||||
{
|
||||
// Window originally had WS_EX_LAYERED - restore original attributes
|
||||
// Clear and re-add to ensure clean state
|
||||
if (exStyle & WS_EX_LAYERED)
|
||||
{
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
||||
exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
}
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
|
||||
|
||||
// Restore original alpha and/or color key
|
||||
DWORD flags = LWA_ALPHA;
|
||||
if (originalState.usedColorKey)
|
||||
{
|
||||
flags |= LWA_COLORKEY;
|
||||
}
|
||||
SetLayeredWindowAttributes(window, originalState.colorKey, originalState.originalAlpha, flags);
|
||||
SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Window originally didn't have WS_EX_LAYERED - remove it completely
|
||||
if (exStyle & WS_EX_LAYERED)
|
||||
{
|
||||
SetLayeredWindowAttributes(window, 0, 255, LWA_ALPHA);
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
||||
SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
m_windowOriginalLayeredState.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: no cached state, just remove layered style
|
||||
if (exStyle & WS_EX_LAYERED)
|
||||
{
|
||||
SetLayeredWindowAttributes(window, 0, 255, LWA_ALPHA);
|
||||
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <common/hooks/WinHookEvent.h>
|
||||
#include <common/notifications/NotificationUtil.h>
|
||||
#include <common/utils/window.h>
|
||||
|
||||
class AlwaysOnTop : public SettingsObserver
|
||||
{
|
||||
@@ -38,6 +39,8 @@ private:
|
||||
enum class HotkeyId : int
|
||||
{
|
||||
Pin = 1,
|
||||
IncreaseOpacity = 2,
|
||||
DecreaseOpacity = 3,
|
||||
};
|
||||
|
||||
static inline AlwaysOnTop* s_instance = nullptr;
|
||||
@@ -48,8 +51,20 @@ private:
|
||||
HWND m_window{ nullptr };
|
||||
HINSTANCE m_hinstance;
|
||||
std::map<HWND, std::unique_ptr<WindowBorder>> m_topmostWindows{};
|
||||
|
||||
// Store original window layered state for proper restoration
|
||||
struct WindowLayeredState {
|
||||
bool hadLayeredStyle = false;
|
||||
BYTE originalAlpha = 255;
|
||||
bool usedColorKey = false;
|
||||
COLORREF colorKey = 0;
|
||||
};
|
||||
std::map<HWND, WindowLayeredState> m_windowOriginalLayeredState{};
|
||||
|
||||
HANDLE m_hPinEvent;
|
||||
HANDLE m_hTerminateEvent;
|
||||
HANDLE m_hIncreaseOpacityEvent;
|
||||
HANDLE m_hDecreaseOpacityEvent;
|
||||
DWORD m_mainThreadId;
|
||||
std::thread m_thread;
|
||||
const bool m_useCentralizedLLKH;
|
||||
@@ -78,6 +93,12 @@ private:
|
||||
bool AssignBorder(HWND window);
|
||||
void RefreshBorders();
|
||||
|
||||
// Transparency methods
|
||||
HWND ResolveTransparencyTargetWindow(HWND window);
|
||||
void StepWindowTransparency(HWND window, int delta);
|
||||
void ApplyWindowAlpha(HWND window, int percentage);
|
||||
void RestoreWindowAlpha(HWND window);
|
||||
|
||||
virtual void SettingsUpdate(SettingId type) override;
|
||||
|
||||
static void CALLBACK WinHookProc(HWINEVENTHOOK winEventHook,
|
||||
|
||||
@@ -15,6 +15,9 @@ class SettingsObserver;
|
||||
struct Settings
|
||||
{
|
||||
PowerToysSettings::HotkeyObject hotkey = PowerToysSettings::HotkeyObject::from_settings(true, true, false, false, 84); // win + ctrl + T
|
||||
static constexpr int minTransparencyPercentage = 20; // minimum transparency (can't go below 20%)
|
||||
static constexpr int maxTransparencyPercentage = 100; // maximum (fully opaque)
|
||||
static constexpr int transparencyStep = 10; // step size for +/- adjustment
|
||||
bool enableFrame = true;
|
||||
bool enableSound = true;
|
||||
bool roundCornersEnabled = true;
|
||||
|
||||
@@ -11,5 +11,5 @@ enum class SettingId
|
||||
BlockInGameMode,
|
||||
ExcludeApps,
|
||||
FrameAccentColor,
|
||||
RoundCornersEnabled
|
||||
RoundCornersEnabled,
|
||||
};
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mmsystem.h> // sound
|
||||
|
||||
class Sound
|
||||
@@ -12,12 +11,10 @@ public:
|
||||
{
|
||||
On,
|
||||
Off,
|
||||
IncreaseOpacity,
|
||||
DecreaseOpacity,
|
||||
};
|
||||
|
||||
Sound()
|
||||
: isPlaying(false)
|
||||
{}
|
||||
|
||||
void Play(Type type)
|
||||
{
|
||||
BOOL success = false;
|
||||
@@ -29,6 +26,12 @@ public:
|
||||
case Type::Off:
|
||||
success = PlaySound(TEXT("Media\\Speech Sleep.wav"), NULL, SND_FILENAME | SND_ASYNC);
|
||||
break;
|
||||
case Type::IncreaseOpacity:
|
||||
success = PlaySound(TEXT("Media\\Windows Hardware Insert.wav"), NULL, SND_FILENAME | SND_ASYNC);
|
||||
break;
|
||||
case Type::DecreaseOpacity:
|
||||
success = PlaySound(TEXT("Media\\Windows Hardware Remove.wav"), NULL, SND_FILENAME | SND_ASYNC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -38,7 +41,4 @@ public:
|
||||
Logger::error(L"Sound playing error");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> isPlaying;
|
||||
};
|
||||
@@ -105,17 +105,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool on_hotkey(size_t /*hotkeyId*/) override
|
||||
virtual bool on_hotkey(size_t hotkeyId) override
|
||||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
Logger::trace(L"AlwaysOnTop hotkey pressed");
|
||||
Logger::trace(L"AlwaysOnTop hotkey pressed, id={}", hotkeyId);
|
||||
if (!is_process_running())
|
||||
{
|
||||
Enable();
|
||||
}
|
||||
|
||||
SetEvent(m_hPinEvent);
|
||||
if (hotkeyId == 0)
|
||||
{
|
||||
SetEvent(m_hPinEvent);
|
||||
}
|
||||
else if (hotkeyId == 1)
|
||||
{
|
||||
SetEvent(m_hIncreaseOpacityEvent);
|
||||
}
|
||||
else if (hotkeyId == 2)
|
||||
{
|
||||
SetEvent(m_hDecreaseOpacityEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -125,19 +136,48 @@ public:
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
// Hotkey 0: Pin/Unpin (e.g., Win+Ctrl+T)
|
||||
if (m_hotkey.key)
|
||||
{
|
||||
if (hotkeys && buffer_size >= 1)
|
||||
if (hotkeys && buffer_size > count)
|
||||
{
|
||||
hotkeys[0] = m_hotkey;
|
||||
hotkeys[count] = m_hotkey;
|
||||
Logger::trace(L"AlwaysOnTop hotkey[0]: win={}, ctrl={}, shift={}, alt={}, key={}",
|
||||
m_hotkey.win, m_hotkey.ctrl, m_hotkey.shift, m_hotkey.alt, m_hotkey.key);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
// Hotkey 1: Increase opacity (same modifiers + VK_OEM_PLUS '=')
|
||||
if (m_hotkey.key)
|
||||
{
|
||||
return 0;
|
||||
if (hotkeys && buffer_size > count)
|
||||
{
|
||||
hotkeys[count] = m_hotkey;
|
||||
hotkeys[count].key = VK_OEM_PLUS; // '=' key
|
||||
Logger::trace(L"AlwaysOnTop hotkey[1] (increase opacity): win={}, ctrl={}, shift={}, alt={}, key={}",
|
||||
hotkeys[count].win, hotkeys[count].ctrl, hotkeys[count].shift, hotkeys[count].alt, hotkeys[count].key);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
// Hotkey 2: Decrease opacity (same modifiers + VK_OEM_MINUS '-')
|
||||
if (m_hotkey.key)
|
||||
{
|
||||
if (hotkeys && buffer_size > count)
|
||||
{
|
||||
hotkeys[count] = m_hotkey;
|
||||
hotkeys[count].key = VK_OEM_MINUS; // '-' key
|
||||
Logger::trace(L"AlwaysOnTop hotkey[2] (decrease opacity): win={}, ctrl={}, shift={}, alt={}, key={}",
|
||||
hotkeys[count].win, hotkeys[count].ctrl, hotkeys[count].shift, hotkeys[count].alt, hotkeys[count].key);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
Logger::trace(L"AlwaysOnTop get_hotkeys returning count={}", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
// Enable the powertoy
|
||||
@@ -175,6 +215,8 @@ public:
|
||||
app_key = NonLocalizable::ModuleKey;
|
||||
m_hPinEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_PIN_EVENT);
|
||||
m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_TERMINATE_EVENT);
|
||||
m_hIncreaseOpacityEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_INCREASE_OPACITY_EVENT);
|
||||
m_hDecreaseOpacityEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_DECREASE_OPACITY_EVENT);
|
||||
init_settings();
|
||||
}
|
||||
|
||||
@@ -292,6 +334,8 @@ private:
|
||||
// Handle to event used to pin/unpin windows
|
||||
HANDLE m_hPinEvent;
|
||||
HANDLE m_hTerminateEvent;
|
||||
HANDLE m_hIncreaseOpacityEvent;
|
||||
HANDLE m_hDecreaseOpacityEvent;
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
<tkcontrols:SettingsCard x:Uid="AlwaysOnTop_TransparencyInfo" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<TextBlock x:Uid="AlwaysOnTop_TransparencyInfo_Value" Style="{StaticResource SecondaryTextStyle}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="AlwaysOnTop_Behavior_GroupSettings" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
|
||||
@@ -3228,7 +3228,13 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<value>Activation shortcut</value>
|
||||
</data>
|
||||
<data name="AlwaysOnTop_ActivationShortcut.Description" xml:space="preserve">
|
||||
<value>Customize the shortcut to pin or unpin an app window</value>
|
||||
<value>Customize the shortcut to pin or unpin an app window. Use the same modifier keys with + or - to adjust window transparency.</value>
|
||||
</data>
|
||||
<data name="AlwaysOnTop_TransparencyInfo.Header" xml:space="preserve">
|
||||
<value>Transparency adjustment</value>
|
||||
</data>
|
||||
<data name="AlwaysOnTop_TransparencyInfo_Value.Text" xml:space="preserve">
|
||||
<value>Use modifier keys + (+) to increase opacity or modifier keys + (-) to decrease. For example: Win+Ctrl+(+) or Win+Ctrl+(-). Range: 20%-100%</value>
|
||||
</data>
|
||||
<data name="Oobe_AlwaysOnTop.Title" xml:space="preserve">
|
||||
<value>Always On Top</value>
|
||||
|
||||
Reference in New Issue
Block a user