Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ struct TickState {
double panelX = 0, panelY = 0; // its desktop position (sub-pixel)
RECT panelSavedClip{}; // the clip to give back when the panel closes
double viewVx = 0, viewVy = 0; // tracking spring velocity (trackGlideMode=1)
// trackLog diagnostics (#326): the last logged caret seq and the last tracking-enable reason.
unsigned diagSeq = 0;
int diagEnableBits = -1;
HCURSOR bodyCursor = nullptr; // edge mode: the cursor cursorBody was measured from
wind::CursorBody cursorBody; // its visible body around the hotspot (desktop px)
unsigned long long lastButtonMs = 0; // last tick a mouse button was down (click quiet period)
Expand Down Expand Up @@ -1776,6 +1779,16 @@ static void RunTick(TickState& t) {
const bool trackEnabled = lvl > 1.001 && !panel && !inspect && !t.detector.locked() && !fsCover &&
(t.cfg.trackCaret != 0 || t.cfg.trackFocus != 0);
g_track.setActive(trackEnabled, t.cfg.trackCaret != 0, t.cfg.trackFocus != 0, t.cfg.trackLog != 0);
if (t.cfg.trackLog) { // #326: why tracking is on or off, logged on every change
const int bits = (lvl > 1.001 ? 1 : 0) | (panel ? 2 : 0) | (inspect ? 4 : 0) |
(t.detector.locked() ? 8 : 0) | (fsCover ? 16 : 0);
if (bits != t.diagEnableBits) {
t.diagEnableBits = bits;
wind::Log(wind::LogLevel::Info, "track", "diag enabled=%d zoomed=%d panel=%d inspect=%d locked=%d fsCover=%d lvl=%.2f",
trackEnabled ? 1 : 0, bits & 1 ? 1 : 0, bits & 2 ? 1 : 0, bits & 4 ? 1 : 0,
bits & 8 ? 1 : 0, bits & 16 ? 1 : 0, lvl);
}
}
// Keyboard panning (#287): only presses the hook swallowed count (a key that went to the app
// at 1x never pans after a zoom-in mid-press); without the hook, the polled combo.
const bool panEnabled = panArmed && !panel;
Expand Down Expand Up @@ -1808,10 +1821,27 @@ static void RunTick(TickState& t) {
// hook no longer switches the gate off. No stamp at all and no hook: no information, no gate.
const unsigned long long lastKey = g_input.lastAnyKeyDownMs();
vi.msSinceKey = lastKey ? double(nowMs - lastKey) : (g_input.kbHookActive() ? 1e9 : 0.0);
// #328: a key after the last mouse button means typing, so the click quiet period ends early.
vi.keyAfterButton = lastKey && t.lastButtonMs && lastKey > t.lastButtonMs;
vi.dtMs = dt * 1000.0;
vi.snap = g_track.snapshot();
const wind::ViewOwner was = t.viewOwner.owner;
const unsigned diagTargetSeqBefore = t.viewOwner.target.seq;
const wind::ViewOwner owner = wind::StepViewOwner(t.viewOwner, vi);
if (t.cfg.trackLog && vi.snap.seq != t.diagSeq) { // #326: every caret/focus event, with the gate inputs
t.diagSeq = vi.snap.seq;
const bool latched = t.viewOwner.target.seq == vi.snap.seq && t.viewOwner.target.seq != diagTargetSeqBefore;
double ax = -1, ay = -1;
const wind::TrackRect drc{ vi.snap.l - t.mon.x, vi.snap.t - t.mon.y, vi.snap.r - t.mon.x, vi.snap.b - t.mon.y };
wind::TrackTargetCenter(drc, t.viewCx, t.viewCy, lvl, t.mon.w, t.mon.h, t.cfg.trackAlign, t.cfg.trackMarginPct, ax, ay);
static const char* kOwn[] = { "mouse", "caret", "focus", "keys" };
wind::Log(wind::LogLevel::Info, "track",
"diag evt seq=%u kind=%d rect=%.0f,%.0f-%.0f,%.0f owner=%s latched=%d msKey=%.0f msBtn=%.0f en=%d lvl=%.2f view=%.0f,%.0f (%s) aim=%.0f,%.0f viewW=%.0f",
vi.snap.seq, (int)vi.snap.kind, drc.l, drc.t, drc.r, drc.b, kOwn[(int)owner], latched ? 1 : 0,
vi.msSinceKey, vi.msSinceButton, vi.enabled ? 1 : 0, lvl,
t.viewDetached ? t.viewCx : r.centerX, t.viewDetached ? t.viewCy : r.centerY,
t.viewDetached ? "detached" : "mouse", ax, ay, t.mon.w / lvl);
}
if (owner != was && t.cfg.trackLog) {
static const char* kName[] = { "mouse", "caret", "focus", "keys" };
wind::Log(wind::LogLevel::Info, "track", "view %s -> %s%s", kName[(int)was], kName[(int)owner],
Expand Down
4 changes: 2 additions & 2 deletions src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#define WIND_VER_MAJOR 0
#define WIND_VER_MINOR 21
#define WIND_VER_PATCH 2
#define WIND_VER_PATCH 3

// String form for logs/snapshot/UI. Keep in sync with the numeric parts above.
#define WIND_VERSION_STR "0.21.2"
#define WIND_VERSION_STR "0.21.3"
7 changes: 5 additions & 2 deletions src/view_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
// would drag. (The old "glide the view back" chased a moving pointer and never arrived - the
// field-reported wobble.)
// - Caret/focus changes within kClickQuietMs of a mouse button are the click's own doing
// (opening a page, clicking into a field): they are consumed without taking the view.
// (opening a page, clicking into a field): they are consumed without taking the view. A key
// pressed AFTER the click ends that early (#328: clicking into Notepad and typing at once lost the
// first ~8 characters while the caret was off screen).
// - And they need a KEY: a caret/focus change takes the view only if a key went down or up within
// kKeyDrivenMs (issue #289; key-ups count so Alt+Tab released after a long look still counts). Scrolling a page moves a focused control's caret on screen with
// no key at all (field: the Settings page dragged the view while scrolling).
Expand Down Expand Up @@ -45,6 +47,7 @@ struct ViewOwnerInputs {
bool buttonDown = false;
double msSinceButton = 1e9; // time since a mouse button was last down
double msSinceKey = 0; // time since any key went down (0 when unknown: no gate)
bool keyAfterButton = false; // a key went down AFTER the last mouse button: typing, so the click quiet period is over (#328)
double dtMs = 0;
TrackSnapshot snap;
bool panning = false; // a pan key is held or its motion is still gliding (#287)
Expand Down Expand Up @@ -94,7 +97,7 @@ inline ViewOwner StepViewOwner(ViewOwnerState& s, const ViewOwnerInputs& in) {
// A new tracking event, unless a recent click caused it.
if (in.trackActive && in.snap.seq != s.lastSeq) { // tracking off: nothing takes the view
s.lastSeq = in.snap.seq;
if (in.msSinceButton >= kClickQuietMs && in.msSinceKey <= kKeyDrivenMs) {
if ((in.msSinceButton >= kClickQuietMs || in.keyAfterButton) && in.msSinceKey <= kKeyDrivenMs) {
if (in.snap.kind == TrackKind::Caret && in.trackCaret) { s.owner = ViewOwner::Caret; s.target = in.snap; }
else if (in.snap.kind == TrackKind::Focus && in.trackFocus) { s.owner = ViewOwner::Focus; s.target = in.snap; }
}
Expand Down
12 changes: 12 additions & 0 deletions tests/test_view_target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ TEST_CASE("caret or focus changes right after a click are consumed, never follow
in.snap = Snap(TrackKind::Caret, 2); // a NEW change after the quiet period does
CHECK(StepViewOwner(s, in) == ViewOwner::Caret);
}
TEST_CASE("typing right after a click is followed: a key AFTER the click ends the quiet period (#328)") {
// Field 2026-10-02 (Notepad): click into the text, type at once; the first 8 characters fell inside
// the 1 s click quiet period and the view sat on the pointer, the caret off screen.
ViewOwnerState s; auto in = Base();
in.msSinceButton = 300; in.msSinceKey = 20; in.keyAfterButton = true; in.snap = Snap(TrackKind::Caret, 1);
CHECK(StepViewOwner(s, in) == ViewOwner::Caret);
}
TEST_CASE("the click's own caret move is still consumed when no key followed the click (#328)") {
ViewOwnerState s; auto in = Base();
in.msSinceButton = 300; in.msSinceKey = 700; in.keyAfterButton = false; in.snap = Snap(TrackKind::Caret, 1);
CHECK(StepViewOwner(s, in) == ViewOwner::Mouse);
}
TEST_CASE("tracking turned off mid-caret goes straight back to the mouse, no warp") {
ViewOwnerState s; auto in = Base(); in.snap = Snap(TrackKind::Caret, 1);
StepViewOwner(s, in); in.enabled = false;
Expand Down
2 changes: 1 addition & 1 deletion ui/src/controls/Preview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
const visible = (r) => !r.showIf || String(values[r.showIf.key]) === String(r.showIf.eq);
const set = (key, val) => { values[key] = val; calls.push([key, val]); };
const extra = {
runningModel: 'hybrid', mpoNeedsRestart: false, version: '0.21.2',
runningModel: 'hybrid', mpoNeedsRestart: false, version: '0.21.3',
onRestart: () => calls.push('restart'), onAction: (a) => calls.push(['action', a]),
onRepo: () => calls.push('repo'),
pick: async () => 'RDR2.exe',
Expand Down
Loading