feat(설정): 332-1 휠 확대 방향 읽기 층 — input_prefs · 모든 휠 화면 한 설정(기본 당김) · 핀치는 늘 벌리면 확대
- 새 A00_Common/input_prefs.ts(INPUT_PREFS_KEY · DEFAULT_INPUT_PREFS · inputPrefs · wheelIn · wheelEventIn · 의존 없음) - 등록표 input-prefs(pref · global) 한 줄 - touch_bridge wheelZoomsIn · b_svg_zoom_pan · B04 3D 카메라 · M02 DF 그림 · M02 노무 그림 · 돋보기 · 시트 배선 - M02 그림 · 시트 · 돋보기 기본 방향 = 당김(지금과 반대) - 시험: test_input_prefs 새로 · 돋보기 · 266 · 244-8 헬퍼가 읽기 층을 불러옴 · 돋보기 부호 당김 기본 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
This commit is contained in:
@@ -103,6 +103,8 @@ export const STATE_REGISTRY = {
|
||||
"cross-columns": { bucket: "pref", scope: "global" },
|
||||
/** B05 · B06 종단 누가토량 알약 자리(309-2) — 그래프 영역 안 비율 `{"x":0~1,"y":0~1}` · 없으면 처음 자리. */
|
||||
"mass-haul-badge-pos": { bucket: "pref", scope: "global" },
|
||||
/** ☰ [설정] 마우스 · 단축키(332) — 읽기는 `input_prefs.ts`(같은 키 글자) · [저장] 때만 씀. */
|
||||
"input-prefs": { bucket: "pref", scope: "global" },
|
||||
/** B05 3D 영역 투영(316-2) `perspective · ortho` · 없으면 원근. */
|
||||
"b05-3d-projection": { bucket: "pref", scope: "global" },
|
||||
"section-panel-height": {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* 그리는 일은 안 한다 — 넘겨받은 `layer` 에 `transform` 만 적는다.
|
||||
* ========================================================================== */
|
||||
|
||||
import { wheelEventIn } from "./input_prefs";
|
||||
|
||||
/** 줌·팬 상태 — 다시 그릴 때 배율을 되살리는 데 쓴다(2026-08-22 사용자 ③). */
|
||||
export interface ZoomPanState {
|
||||
scale: number;
|
||||
@@ -213,8 +215,8 @@ export function attachSvgZoomPan(options: SvgZoomPanOptions): ZoomPanHandle {
|
||||
const rect = svg.getBoundingClientRect();
|
||||
const cx = ((event.clientX - rect.left) / Math.max(rect.width, 1)) * widthPx;
|
||||
const cy = ((event.clientY - rect.top) / Math.max(rect.height, 1)) * heightPx;
|
||||
// 노선 편집 지도와 같은 방향 — **당기면 확대**(`deltaY > 0`).
|
||||
zoomAt(event.deltaY > 0 ? WHEEL_IN : WHEEL_OUT, cx, cy);
|
||||
// ☰ [설정] 휠 방향(332 · 기본 당기면 확대) — 그림 면이 손가락을 받으므로 핀치는 트랙패드뿐.
|
||||
zoomAt(wheelEventIn(event) ? WHEEL_IN : WHEEL_OUT, cx, cy);
|
||||
},
|
||||
{ passive: false },
|
||||
);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/* =============================================================================
|
||||
* input_prefs.ts
|
||||
* 마우스 · 단축키 설정 읽기 층(PLAN 332) — ☰ [설정] 창이 [저장] 때 계정 pref 로 씀.
|
||||
*
|
||||
* - 값 = `aislo:pref:input-prefs`(등록표 `input-prefs`) JSON 한 벌 · 부를 때마다 읽음(새로고침 없이 바로 따름)
|
||||
* - 없음 · 깨짐 · Node(저장소 없음) = 기본값(= 지금 동작) · 의존 없음 · 불러올 때 window 안 만짐(CAD 도 import)
|
||||
* - 휠 확대 방향 하나로 모든 화면 일체 — 당김 = 휠을 몸 쪽으로(deltaY > 0) 굴리면 확대
|
||||
* - 핀치(트랙패드 · 손가락)는 설정과 상관없이 늘 벌리면 확대(deltaY < 0)
|
||||
* ========================================================================== */
|
||||
|
||||
export type WheelZoom = "pull" | "push";
|
||||
|
||||
export interface InputPrefs {
|
||||
/** 휠 확대 방향 — pull = 당기면 확대 · push = 밀면 확대 */
|
||||
wheelZoom: WheelZoom;
|
||||
/** CAD 내 별칭 `{명령: [별칭]}`(대문자 · acad.pgp 식 — 명령 이름 > 내 별칭 > 기본 별칭) */
|
||||
cadAliases: Record<string, string[]>;
|
||||
}
|
||||
|
||||
/** `stateKey("input-prefs")` 와 같은 키(시험이 맞춰 봄) — 등록표를 불러오면 window 가 딸려 와서 글자로 둠 */
|
||||
export const INPUT_PREFS_KEY = "aislo:pref:input-prefs";
|
||||
|
||||
export const DEFAULT_INPUT_PREFS: InputPrefs = { wheelZoom: "pull", cadAliases: {} };
|
||||
|
||||
const cleanAliases = (raw: unknown): Record<string, string[]> => {
|
||||
const out: Record<string, string[]> = {};
|
||||
if (!raw || typeof raw !== "object") return out;
|
||||
for (const [id, list] of Object.entries(raw as Record<string, unknown>)) {
|
||||
if (!Array.isArray(list)) continue;
|
||||
const aliases = list
|
||||
.filter((a): a is string => typeof a === "string")
|
||||
.map((a) => a.toUpperCase());
|
||||
if (aliases.length) out[id.toUpperCase()] = aliases;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/** 지금 설정 — 부를 때마다 저장소에서 읽음 */
|
||||
export function inputPrefs(): InputPrefs {
|
||||
try {
|
||||
const raw = globalThis.localStorage?.getItem(INPUT_PREFS_KEY);
|
||||
if (!raw) return { ...DEFAULT_INPUT_PREFS, cadAliases: {} };
|
||||
const value = JSON.parse(raw) as Partial<InputPrefs> | null;
|
||||
return {
|
||||
wheelZoom: value?.wheelZoom === "push" ? "push" : "pull",
|
||||
cadAliases: cleanAliases(value?.cadAliases),
|
||||
};
|
||||
} catch {
|
||||
return { ...DEFAULT_INPUT_PREFS, cadAliases: {} };
|
||||
}
|
||||
}
|
||||
|
||||
/** 휠 한 번이 확대인가 — 설정 방향을 따름 */
|
||||
export const wheelIn = (deltaY: number): boolean =>
|
||||
inputPrefs().wheelZoom === "push" ? deltaY < 0 : deltaY > 0;
|
||||
|
||||
/** 트랙패드 핀치 — 브라우저가 Ctrl+휠 · 잘게 쪼갠 값으로 냄
|
||||
* ponytail: |deltaY| < 50 로 마우스 Ctrl+휠(한 칸 ≥ 약 100)과 가름 · 잘게 도는 마우스는 핀치로 봄 */
|
||||
export const isTrackpadPinch = (e: Pick<WheelEvent, "ctrlKey" | "deltaY">): boolean =>
|
||||
e.ctrlKey && Math.abs(e.deltaY) < 50;
|
||||
|
||||
/** 휠 사건이 확대인가 — 핀치(`pinch` 손가락 벌림 · 트랙패드)는 늘 벌리면 확대 · 나머지는 설정 방향 */
|
||||
export const wheelEventIn = (e: Pick<WheelEvent, "ctrlKey" | "deltaY">, pinch = false): boolean =>
|
||||
pinch || isTrackpadPinch(e) ? e.deltaY < 0 : wheelIn(e.deltaY);
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
import "./spreadsheet_grid.css";
|
||||
import { colIndex, toA1 } from "./spreadsheet_address";
|
||||
import { wheelZoomsIn } from "../touch_bridge";
|
||||
import {
|
||||
buildColGeometry,
|
||||
buildOutlineRuns,
|
||||
@@ -271,7 +272,8 @@ export function createGrid(ctx: SpreadsheetContext): GridHandle {
|
||||
if (!ev.ctrlKey) return;
|
||||
ev.preventDefault();
|
||||
const before = zoom;
|
||||
const next = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom * Math.exp(-ev.deltaY * 0.0015)));
|
||||
const step = (wheelZoomsIn(ev) ? 1 : -1) * Math.abs(ev.deltaY) * 0.0015; // ☰ [설정] 방향(332)
|
||||
const next = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom * Math.exp(step)));
|
||||
if (next === before) return;
|
||||
zoom = next;
|
||||
colGeo = scaledGeometry(baseColGeo, zoom);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
* 마우스 · 펜 이벤트는 건드리지 않음.
|
||||
* ========================================================================== */
|
||||
|
||||
import { wheelEventIn } from "./input_prefs";
|
||||
|
||||
const LONG_MS = 500;
|
||||
const SLOP_PX = 10;
|
||||
const DOUBLE_MS = 300;
|
||||
@@ -31,9 +33,8 @@ let contactPx = 0;
|
||||
/** 고름 반경(px) — 손가락으로 눌렀으면 접촉 크기만큼(최소 24px) · 마우스 · 펜은 그대로(기기대응 4-4) */
|
||||
export const pickPx = (px: number): number => (contactPx ? Math.max(px, contactPx) : px);
|
||||
|
||||
/** 「당기면 확대」 휠 화면(B04 · B05 지도)의 확대 판정 — 손가락 벌림은 거꾸로라 뒤집음 */
|
||||
export const wheelZoomsIn = (e: WheelEvent): boolean =>
|
||||
isPinchWheel(e) ? e.deltaY < 0 : e.deltaY > 0;
|
||||
/** 휠 화면(B04 · B05 지도 · M02 그림 · 시트)의 확대 판정 — ☰ [설정] 방향(332) · 손가락 벌림은 늘 벌리면 확대 */
|
||||
export const wheelZoomsIn = (e: WheelEvent): boolean => wheelEventIn(e, isPinchWheel(e));
|
||||
|
||||
const ownsTouch = (t: EventTarget | null): boolean =>
|
||||
t instanceof Element && !!t.closest("[data-touch-own]");
|
||||
|
||||
Reference in New Issue
Block a user