feat(hotkey): make the new-entry hotkey configurable + fix tray title capture
Part A — Ctrl+Shift+A (new entry from window title) is now editable like the other 3 global hotkeys, following the same pattern end to end: state.newEntryHotkey (synced), a capture button in Settings > Autofill, dup cross-check, reset-to- defaults, autofillPushHotkeys, and Bridge.setAutofillHotkeys carrying ne_mods/ ne_vk. Delphi gets SetNewEntryHotkey + stores the live combo (FNewEntryHotkeyMods/ Vk) so ExecuteAutofill's post-fill re-register uses the user's combo instead of resetting to Ctrl+Shift+A. UMainForm parses ne_* unconditionally (works with autofill off). Part B — new-entry from the tray pre-filled the title with "Password Manager": GetForegroundWindow resolved to our own hidden window. Only capture the title when the foreground window belongs to another process; otherwise pass '' (blank). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,10 @@ type
|
||||
FDebugHotkeyRegistered: Boolean;
|
||||
FOnDebugHotkey: TProc;
|
||||
FNewEntryHotkeyRegistered: Boolean;
|
||||
// Current new-entry combo (user-configurable, default Ctrl+Shift+A). Kept
|
||||
// so ExecuteAutofill can re-register it after unregistering during a fill.
|
||||
FNewEntryHotkeyMods: Word;
|
||||
FNewEntryHotkeyVk: Word;
|
||||
FOnNewEntryHotkey: TNewEntryHotkeyEvent;
|
||||
// Ctrl+Shift+Q — "Quick search + autofill": open the JS quick-search
|
||||
// modal, the user picks an entry, the password is SendInput'd into the
|
||||
@@ -163,6 +167,9 @@ type
|
||||
// Re-register the Ctrl+Shift+Q (default) quick-search hotkey with a
|
||||
// user-chosen combo. True on success.
|
||||
function SetQuickSearchHotkey(AMods, AVk: Word): Boolean;
|
||||
// Re-register the new-entry hotkey with a user-chosen combo (default
|
||||
// Ctrl+Shift+A). Stored so ExecuteAutofill can restore it after the fill.
|
||||
function SetNewEntryHotkey(AMods, AVk: Word): Boolean;
|
||||
// Convenience wrapper: register the historical defaults (Ctrl+Shift+L
|
||||
// and Ctrl+Shift+P). Used by the host on first start; runtime changes
|
||||
// go through SetAutofillHotkeys.
|
||||
@@ -483,8 +490,12 @@ begin
|
||||
|
||||
FDebugHotkeyRegistered := RegisterHotKey(FMsgWindow, DEBUG_HOTKEY_ID,
|
||||
AF_MOD_CONTROL or AF_MOD_SHIFT, Ord('D'));
|
||||
// Default combo; JS overrides it via cmd://autofill/hotkeys (SetNewEntryHotkey)
|
||||
// at unlock. Store it so ExecuteAutofill's re-register uses the live combo.
|
||||
FNewEntryHotkeyMods := AF_MOD_CONTROL or AF_MOD_SHIFT;
|
||||
FNewEntryHotkeyVk := Ord('A');
|
||||
FNewEntryHotkeyRegistered := RegisterHotKey(FMsgWindow, NEW_ENTRY_HOTKEY_ID,
|
||||
AF_MOD_CONTROL or AF_MOD_SHIFT, Ord('A'));
|
||||
FNewEntryHotkeyMods, FNewEntryHotkeyVk);
|
||||
FQuickSearchHotkeyRegistered := RegisterHotKey(FMsgWindow,
|
||||
QUICK_SEARCH_HOTKEY_ID, AF_MOD_CONTROL or AF_MOD_SHIFT, Ord('Q'));
|
||||
end;
|
||||
@@ -850,10 +861,18 @@ begin
|
||||
if Assigned(FOnNewEntryHotkey) then
|
||||
begin
|
||||
var LTarget := GetForegroundWindow;
|
||||
var LTitle: string;
|
||||
SetLength(LTitle, 512);
|
||||
var LLen := GetWindowTextW(LTarget, PChar(LTitle), 512);
|
||||
SetLength(LTitle, LLen);
|
||||
var LTitle: string := '';
|
||||
var LPid: DWORD := 0;
|
||||
GetWindowThreadProcessId(LTarget, LPid);
|
||||
// From the tray the foreground can resolve to OUR own window — don't
|
||||
// pre-fill the new entry with "Password Manager". Only capture a real
|
||||
// external window's title; otherwise pass '' (blank title).
|
||||
if (LTarget <> 0) and (LPid <> GetCurrentProcessId) then
|
||||
begin
|
||||
SetLength(LTitle, 512);
|
||||
var LLen := GetWindowTextW(LTarget, PChar(LTitle), 512);
|
||||
SetLength(LTitle, LLen);
|
||||
end;
|
||||
FOnNewEntryHotkey(LTitle);
|
||||
end;
|
||||
end
|
||||
@@ -942,6 +961,23 @@ begin
|
||||
Result := FQuickSearchHotkeyRegistered;
|
||||
end;
|
||||
|
||||
function TPMBridge.SetNewEntryHotkey(AMods, AVk: Word): Boolean;
|
||||
begin
|
||||
if FNewEntryHotkeyRegistered then
|
||||
begin
|
||||
UnregisterHotKey(FMsgWindow, NEW_ENTRY_HOTKEY_ID);
|
||||
FNewEntryHotkeyRegistered := False;
|
||||
end;
|
||||
if (AVk <> 0) and (AMods <> 0) then
|
||||
begin
|
||||
FNewEntryHotkeyMods := AMods;
|
||||
FNewEntryHotkeyVk := AVk;
|
||||
FNewEntryHotkeyRegistered := RegisterHotKey(FMsgWindow,
|
||||
NEW_ENTRY_HOTKEY_ID, AMods, AVk);
|
||||
end;
|
||||
Result := FNewEntryHotkeyRegistered;
|
||||
end;
|
||||
|
||||
procedure TPMBridge.ApplyTitleBarTheme(ADark: Boolean);
|
||||
const
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
||||
@@ -1350,9 +1386,11 @@ begin
|
||||
ClearFieldIfWanted;
|
||||
SendUnicodeString(APassword);
|
||||
finally
|
||||
// Re-register with the LIVE combo (user may have rebound it), not the
|
||||
// hardcoded default — otherwise a fill would reset new-entry to Ctrl+Shift+A.
|
||||
if FNewEntryHotkeyRegistered then
|
||||
RegisterHotKey(FMsgWindow, NEW_ENTRY_HOTKEY_ID,
|
||||
AF_MOD_CONTROL or AF_MOD_SHIFT, Ord('A'));
|
||||
FNewEntryHotkeyMods, FNewEntryHotkeyVk);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@@ -874,6 +874,16 @@ begin
|
||||
LogLine(Format('Quick-search hotkey set — mods:%d vk:%d (ok=%s)',
|
||||
[LQsMods, LQsVk, BoolToStr(LQsOk, True)]));
|
||||
end;
|
||||
// New-entry combo — also independent of the autofill enabled flag
|
||||
// (Ctrl+Shift+A works even with autofill turned off).
|
||||
if GetParam('ne_vk') <> '' then
|
||||
begin
|
||||
var LNeMods := Word(StrToIntDef(GetParam('ne_mods'), 6));
|
||||
var LNeVk := Word(StrToIntDef(GetParam('ne_vk'), Ord('A')));
|
||||
var LNeOk := FBridge.SetNewEntryHotkey(LNeMods, LNeVk);
|
||||
LogLine(Format('New-entry hotkey set — mods:%d vk:%d (ok=%s)',
|
||||
[LNeMods, LNeVk, BoolToStr(LNeOk, True)]));
|
||||
end;
|
||||
if GetParam('enabled') <> '1' then
|
||||
begin
|
||||
FBridge.UnregisterAutofillHotkey;
|
||||
|
||||
Reference in New Issue
Block a user