Skip to content

Commit a0c9dec

Browse files
authored
fix(caret): caret not resetting position on quick restart (@nadalaba) (#7038)
- quick restarting immediately after some input makes the caret fail to reset its position sometimes. https://github.com/user-attachments/assets/d9e1def5-d8f6-4af2-845c-778c89279849 this happens because the reset of caret's position that should happen on the callback inside `requestAnimationFrame` in `caret.goTo()` (triggered in `TestUI.updateWordsWrapperClasses()`) is cancelled by a later call to `caret.goTo()` (triggered by `TestUI.focusWords()`). However the second call sets the position directly without stopping previous animation `$('#caret').stop().animate()`. As a result, the earlier animation (caused by last input) continues after the reset, if the restart was done fast enough. - update some previous comments.
1 parent 4222766 commit a0c9dec

File tree

2 files changed

+11
-5
lines changed

2 files changed

+11
-5
lines changed

frontend/src/ts/test/test-logic.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ export function restart(options = {} as RestartOptions): void {
286286
TimerProgress.hide();
287287
Replay.pauseReplay();
288288
TestState.setBailedOut(false);
289+
Caret.resetPosition();
289290
PaceCaret.reset();
290291
Monkey.hide();
291292
TestInput.input.setKoreanStatus(false);

frontend/src/ts/utils/caret.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export class Caret {
102102
top: number;
103103
width?: number;
104104
}): void {
105+
$(this.element).stop("pos", true, false);
105106
this.element.style.left = `${options.left}px`;
106107
this.element.style.top = `${options.top}px`;
107108
if (options.width !== undefined) {
@@ -143,6 +144,8 @@ export class Caret {
143144
}
144145

145146
public handleTapeWordsRemoved(widthRemoved: number): void {
147+
// Removing tape words reduces the absolute value of options.newValue passed to handleTapeScroll().
148+
// To keep the target marginLeft accurate, we reduce the absolute value of cumulative correction by the same amount.
146149
this.cumulativeTapeMarginCorrection += widthRemoved;
147150
}
148151

@@ -154,12 +157,14 @@ export class Caret {
154157
this.readyToResetMarginLeft = false;
155158

156159
/**
157-
* If we didn't reset marginLeft, then options.newValue gives the correct caret
158-
* position by adding up the widths of all typed characters. But since we reset
159-
* caret.style.marginLeft during the test, the caret ends up too far left.
160+
* options.newValue is the total width of all previously typed characters, and assuming we didn't
161+
* reset marginLeft or remove any tape words, then it would be the correct marginLeft to go to.
160162
*
161-
* To fix this, we track how much marginLeft we've reset so far (cumulativeTapeMarginCorrection),
162-
* and subtract it from options.newValue to get the correct newMarginLeft.
163+
* Each time marginLeft is reset, the distance between options.newValue and the current
164+
* marginLeft-after-reset increases, making the caret shift too much left (in LTR).
165+
*
166+
* To correct this, we decrease the new target marginLeft by how much we've
167+
* reset the margin so far (cumulativeTapeMarginCorrection).
163168
*/
164169
const newMarginLeft =
165170
options.newValue - this.cumulativeTapeMarginCorrection;

0 commit comments

Comments
 (0)