Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1
This commit is contained in:
@@ -41,6 +41,7 @@ import { isWaitingForSecondSelectPoint, selectingActor } from './selecting.ts';
|
||||
import { isMiddleDouble, zoomExtents } from './zoom-prompt.ts';
|
||||
import { attachTouch } from './input-touch.ts';
|
||||
import { NUMBER_REGEXP, type PointTextEvent } from './input-patterns.ts';
|
||||
import { commandCandidate, pushInput, recallInput } from './input-history.ts';
|
||||
import {
|
||||
angleLockHint,
|
||||
getAngleLock,
|
||||
@@ -74,6 +75,10 @@ const DRAG_SELECT_MIN_PX = 4;
|
||||
|
||||
export class InputController {
|
||||
private text = '';
|
||||
/** ↑↓ · Tab 돌림 — 기록인가 후보인가 · 후보의 처음 친 글 · 몇째 */
|
||||
private browsing: 'history' | 'candidates' | null = null;
|
||||
private candidateBase = '';
|
||||
private candidateIndex = 0;
|
||||
/** 왼쪽 버튼을 누른 화면 좌표 — 놓을 때 끌었는지 판단한다 */
|
||||
private leftPressLocation: Point | null = null;
|
||||
public readonly ime = new ImeSink((data) => {
|
||||
@@ -408,7 +413,12 @@ export class InputController {
|
||||
return;
|
||||
}
|
||||
if (evt.key === 'Tab') {
|
||||
// 점 단계(기준점 있음) = 동적 입력 칸 넘김 · 그 밖 = 브라우저 초점 이동
|
||||
// 쉼 + 친 글 = 명령 후보 돌림 · 점 단계(기준점 있음) = 동적 입력 칸 넘김 · 그 밖 = 브라우저 초점 이동
|
||||
if (isIdle() && this.text) {
|
||||
evt.preventDefault();
|
||||
this.cycleCandidate(1);
|
||||
return;
|
||||
}
|
||||
if (!dynamicInputApplies()) return;
|
||||
evt.preventDefault();
|
||||
const isNumber = NUMBER_REGEXP.test(this.text);
|
||||
@@ -445,6 +455,7 @@ export class InputController {
|
||||
// Remove the last character from the input field
|
||||
evt.preventDefault();
|
||||
this.text = this.text.slice(0, this.text.length - 1);
|
||||
this.browsing = null;
|
||||
} else if (evt.key === 'Delete') {
|
||||
// User wants to delete the current selection
|
||||
evt.preventDefault();
|
||||
@@ -464,6 +475,9 @@ export class InputController {
|
||||
this.handleEnterKey(true);
|
||||
this.ime.sync(activeToolWantsIme());
|
||||
}
|
||||
} else if ((evt.key === 'ArrowUp' || evt.key === 'ArrowDown') && !evt.shiftKey) {
|
||||
// ↑↓ = 기록 · 최근 값 · 후보(오토캐드) — 화면 옮김은 Shift+↑↓ · ← →
|
||||
this.recall(evt.key === 'ArrowUp' ? -1 : 1);
|
||||
} else if (evt.key === 'ArrowDown') {
|
||||
// Move the screen down
|
||||
getScreenCanvasDrawController().setScreenOffset(this.getScreenPanStep('down', evt.shiftKey));
|
||||
@@ -483,12 +497,40 @@ export class InputController {
|
||||
// 글 단계(TEXT·MTEXT)는 친 그대로 — 명령 입력일 때만 대문자로 바꾼다(보이는 글만 · 맞추기는 prompt.ts).
|
||||
// Ctrl · Alt · ⌘ 와 함께 누른 글자는 쌓지 않음(Ctrl+C → 원이 되던 것).
|
||||
this.text += activeToolWantsFreeText() ? evt.key : evt.key.toUpperCase();
|
||||
this.browsing = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** ↑↓ — 후보 보일 때 = 후보 돌림 · 아니면 기록(쉼 = 친 명령 · 명령 중 = 그 물음 최근 값) */
|
||||
private recall(dir: -1 | 1) {
|
||||
const candidatesShown = isIdle() && !!this.text && !!commandCandidate(this.text, 0);
|
||||
if (this.browsing === 'candidates' || (this.browsing === null && candidatesShown)) {
|
||||
this.cycleCandidate(dir);
|
||||
return;
|
||||
}
|
||||
const value = recallInput(dir, this.text);
|
||||
if (value === null) return;
|
||||
this.text = value;
|
||||
this.browsing = 'history';
|
||||
}
|
||||
|
||||
/** 명령 후보 돌림 — 처음 친 글로 시작하는 명령 이름을 차례로 친 글 자리에 */
|
||||
private cycleCandidate(dir: -1 | 1) {
|
||||
if (this.browsing !== 'candidates') {
|
||||
this.candidateBase = this.text;
|
||||
this.candidateIndex = dir > 0 ? -1 : 0;
|
||||
}
|
||||
const name = commandCandidate(this.candidateBase, this.candidateIndex + dir);
|
||||
if (!name) return;
|
||||
this.candidateIndex += dir;
|
||||
this.text = name;
|
||||
this.browsing = 'candidates';
|
||||
}
|
||||
|
||||
/** Esc 한 번 = 명령 끝(오토캐드) — 친 글 · 조합 · 각도 잠금 · 한 번 스냅 · 덧물음도 함께 풂 */
|
||||
public handleEscapeKey() {
|
||||
this.text = '';
|
||||
this.browsing = null;
|
||||
this.ime.composing = '';
|
||||
const boxing =
|
||||
this.isSelectToolActive() &&
|
||||
@@ -511,7 +553,12 @@ export class InputController {
|
||||
if (this.text === '' || !isIdle()) {
|
||||
return [];
|
||||
}
|
||||
return matchCommandPrefixes(this.text).slice(0, 6).map(describeCommand);
|
||||
const picking = this.browsing === 'candidates';
|
||||
const candidates = matchCommandPrefixes(picking ? this.candidateBase : this.text);
|
||||
const picked = picking ? candidates.findIndex((command) => command.id === this.text) : -1;
|
||||
return candidates
|
||||
.slice(0, 6)
|
||||
.map((command, index) => `${index === picked ? '▸ ' : ''}${describeCommand(command)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,6 +568,8 @@ export class InputController {
|
||||
public handleEnterKey(_repeatIfIdle = false) {
|
||||
const text = this.text;
|
||||
this.text = '';
|
||||
this.browsing = null;
|
||||
pushInput(text); // ↑↓ 기록(지금 물음 묶음 — 해석 전에)
|
||||
routeTyped(text);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user