fix(B07-CAD): 한글 IME 조합 입력 — 문자 단계 숨은 받개 · 조합 중 단축키 건너뜀
- 문자를 기다리는 단계(TEXT · MTEXT · 표 칸 · 지시선)에서 숨은 input 에 초점 · compositionupdate/end 로 글자 받음 - 조합 중 키(isComposing · 229 · Process)는 단축키 처리에서 건너뜀 · 명령줄도 같음 - 명령 단계는 초점을 풀어 한글 자판이어도 영문 명령 그대로 - 입력 해석 패턴을 input-patterns.ts 로 나눔(700줄 상한) - 검증 창구 entities 에 label · 시나리오 ui_checks/44-8_CAD_한글입력.js Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
This commit is contained in:
@@ -65,21 +65,15 @@ import {
|
||||
type RelativePointInputEvent,
|
||||
type TextInputEvent,
|
||||
} from '../tools/tool.types.ts';
|
||||
|
||||
const NUMBER_REGEXP = /^[0-9]+([.][0-9]+)?$/;
|
||||
/** 부호 있는 실수 한 개 (좌표는 음수가 될 수 있다) */
|
||||
const SIGNED = '(-?[0-9]+(?:[.][0-9]+)?)';
|
||||
const ABSOLUTE_POINT_REGEXP = new RegExp(`^${SIGNED}[ ]*,[ ]*${SIGNED}$`);
|
||||
const RELATIVE_POINT_REGEXP = new RegExp(`^@${SIGNED}[ ]*,[ ]*${SIGNED}$`);
|
||||
/** 극좌표 — 거리<각도(도). `@`가 붙으면 직전 점 기준 */
|
||||
const ABSOLUTE_POLAR_REGEXP = new RegExp(`^${SIGNED}[ ]*<[ ]*${SIGNED}$`);
|
||||
const RELATIVE_POLAR_REGEXP = new RegExp(`^@${SIGNED}[ ]*<[ ]*${SIGNED}$`);
|
||||
|
||||
/** 거리·각도(도)를 x·y 변위로 바꾼다 */
|
||||
function polarToPoint(distance: number, degrees: number): Point {
|
||||
const radians = (degrees * Math.PI) / 180;
|
||||
return new Point(distance * Math.cos(radians), distance * Math.sin(radians));
|
||||
}
|
||||
import { activeToolAcceptsText, ImeSink, isImeKey } from './ime-sink.ts';
|
||||
import {
|
||||
ABSOLUTE_POINT_REGEXP,
|
||||
ABSOLUTE_POLAR_REGEXP,
|
||||
NUMBER_REGEXP,
|
||||
polarToPoint,
|
||||
RELATIVE_POINT_REGEXP,
|
||||
RELATIVE_POLAR_REGEXP,
|
||||
} from './input-patterns.ts';
|
||||
|
||||
/** 이만큼 넘게 끌면 클릭이 아니라 드래그로 본다 (화면 픽셀) */
|
||||
const DRAG_SELECT_MIN_PX = 4;
|
||||
@@ -88,11 +82,15 @@ export class InputController {
|
||||
private text = '';
|
||||
/** 왼쪽 버튼을 누른 화면 좌표 — 놓을 때 끌었는지 판단한다 */
|
||||
private leftPressLocation: Point | null = null;
|
||||
public readonly ime = new ImeSink((data) => {
|
||||
this.text += data;
|
||||
});
|
||||
|
||||
constructor() {
|
||||
if (typeof process === 'object' && process?.env?.NODE_ENV === 'test') {
|
||||
return; // used during unit testing
|
||||
}
|
||||
this.ime.attach();
|
||||
// Listen for keystrokes
|
||||
document.addEventListener('keydown', (evt) => {
|
||||
this.handleKeyStroke(evt);
|
||||
@@ -141,10 +139,11 @@ export class InputController {
|
||||
CANVAS_INPUT_FIELD_HEIGHT,
|
||||
CANVAS_INPUT_FIELD_BACKGROUND_COLOR
|
||||
);
|
||||
// Draw text in input field
|
||||
if (this.text) {
|
||||
this.ime.sync(activeToolAcceptsText());
|
||||
// Draw text in input field (조합 중인 한글도 함께)
|
||||
if (this.text || this.ime.composing) {
|
||||
drawController.drawTextScreen(
|
||||
this.text,
|
||||
this.text + this.ime.composing,
|
||||
new Point(
|
||||
screenMouseLocation.x + CANVAS_INPUT_FIELD_MOUSE_OFFSET + 2,
|
||||
screenMouseLocation.y - CANVAS_INPUT_FIELD_MOUSE_OFFSET - CANVAS_INPUT_FIELD_HEIGHT - 2
|
||||
@@ -217,6 +216,7 @@ export class InputController {
|
||||
return;
|
||||
}
|
||||
this.sendMouseClick(evt);
|
||||
this.ime.sync(activeToolAcceptsText()); // 클릭으로 문자 단계에 들면 바로 한글 받개로
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,9 +392,11 @@ export class InputController {
|
||||
}
|
||||
|
||||
public handleKeyStroke(evt: KeyboardEvent) {
|
||||
if ((evt.target as HTMLElement | null)?.closest('input, textarea, select')) {
|
||||
const target = evt.target as HTMLElement | null;
|
||||
if (target !== this.ime.element && target?.closest('input, textarea, select')) {
|
||||
return;
|
||||
}
|
||||
if (isImeKey(evt)) return; // 한글 조합 중 — 단축키가 먹지 않게 IME 에 넘김
|
||||
console.log(`key pressed: ${evt.key}`);
|
||||
if (evt.key === 'F12') {
|
||||
// F12 => open developer tools
|
||||
@@ -472,6 +474,7 @@ export class InputController {
|
||||
} else if (evt.key === 'Enter') {
|
||||
// User wants to submit the input or submit the action
|
||||
this.handleEnterKey();
|
||||
this.ime.sync(activeToolAcceptsText());
|
||||
} else if (evt.key === 'ArrowDown') {
|
||||
// Move the screen down
|
||||
getScreenCanvasDrawController().setScreenOffset(this.getScreenPanStep('down', evt.shiftKey));
|
||||
@@ -517,11 +520,7 @@ export class InputController {
|
||||
|
||||
public handleEnterKey() {
|
||||
// submit the text as input to the active tool and clear the input field
|
||||
const activeTool = getActiveToolActor();
|
||||
const activeToolSnapshot = activeTool?.getSnapshot();
|
||||
const activeToolState = activeToolSnapshot?.value;
|
||||
const activeToolCanHandleTextInput =
|
||||
!!activeToolSnapshot?.machine?.states?.[activeToolState]?.config?.on?.TEXT_INPUT;
|
||||
const activeToolCanHandleTextInput = activeToolAcceptsText();
|
||||
|
||||
if (this.text === '') {
|
||||
console.log('ENTER: ', {
|
||||
|
||||
Reference in New Issue
Block a user