From cdfef949bf741e5c9ebd9d26624e44499d2bc73a Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 21 Aug 2026 23:38:05 +0200 Subject: [PATCH] =?UTF-8?q?2D:=20Enter=20in=20der=20Befehlszeile=20best?= =?UTF-8?q?=C3=A4tigt=20Punkt-Griff=20jetzt=20auch=20ohne=20neuen=20Wert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nutzer-Report: Enter sollte den Punkt gemäss Maus-/Feld-Stand platzieren ("die Maus steuert bis man Werte eingibt, dann übernimmt es im Feld den aktuellen Wert, man kann eigene Werte per Tab eingeben") -- aber Enter tat nach dem Tippen eines Werts (oder auch ganz ohne Tippen) nichts. Ursache: die Befehlszeile fokussiert sich selbst, sobald man Tab drückt (Feld-Controller öffnen). Der GLOBALE Enter/Escape-Tastatur-Handler aus dem vorigen Commit überspringt bewusst fokussierte Eingabefelder (damit er sich nicht mit der Befehlszeilen-eigenen Enter-Behandlung beisst) -- aber die Befehlszeile selbst kannte für die Griff-Bearbeitung nur den Fall "Text eingetippt, als Zahl geparst" (submitGripEditValue). Bei LEERER Eingabe (Enter ohne neuen Wert -- der Normalfall, wenn man nur bestätigen will) geschah dadurch schlicht nichts. Fix: PlanViewHandle bekommt eine neue Methode confirmGripEdit() (liest den bewaffneten Griff aus der lokalen gripDrag-Ref, ruft gripHandlers.onGripEnd() -- dieselbe Aktion wie ein bestätigender Klick auf der Zeichenfläche). Die Befehlszeilen-Eingabe in App.tsx ruft sie jetzt bei leerem/nicht parsbarem Text während der Griff-Bearbeitung auf. Da das Imperativ-Handle mit leeren Deps memoisiert ist (stabil für die Oberleiste), braucht es einen frischen gripHandlersRef-Spiegel statt die gripHandlers-Prop direkt zu schließen (sonst stale closure vom ersten Render). --- src/App.tsx | 5 +++++ src/plan/PlanView.tsx | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index b8894a7..2d63bbc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2819,6 +2819,11 @@ export default function App() { if (gripEdit) { const v = parseFloat(text.replace(",", ".")); if (isFinite(v)) submitGripEditValue(v); + // Leere Eingabe: kein neuer Wert zu locken — Enter bestätigt + // stattdessen die aktuelle Position (Maus- oder bereits + // gelockte Werte) und schließt die Griff-Bearbeitung ab. Vorher + // ein Totdruck (Nutzer-Report: Enter tat nichts). + else planViewRef.current?.confirmGripEdit(); return; } if (activeTransform) { diff --git a/src/plan/PlanView.tsx b/src/plan/PlanView.tsx index bdddd8e..b23d3d5 100644 --- a/src/plan/PlanView.tsx +++ b/src/plan/PlanView.tsx @@ -111,6 +111,14 @@ export interface PlanViewHandle { * Leere Punktmenge → No-op (kein Fit, kein Crash). */ fitBounds: (pts: Vec2[]) => void; + /** + * Einen bewaffneten Punkt-Griff (s. gripDrag) an seiner AKTUELLEN Position + * abschließen — dieselbe Aktion wie ein bestätigender Klick/Enter auf der + * Zeichenfläche, aber von der Befehlszeile aus angestoßen (Enter dort MIT + * leerem Eingabetext: kein neuer Wert zu locken, nur „fertig, so lassen"). + * No-op ohne bewaffneten Griff. + */ + confirmGripEdit: () => void; } /** Ergebnis eines Links-Klicks (Auswahl) an einer Modellposition. */ @@ -707,12 +715,28 @@ export const PlanView = forwardRef( // eslint-disable-next-line react-hooks/exhaustive-deps }, [resetKey]); + // Frischer Spiegel der gripHandlers-Prop für das (unten stabil mit leeren + // Deps memoisierte) Imperativ-Handle — direkt referenzieren würde die Prop + // vom ERSTEN Render einfrieren (stale closure, s. Kommentar dort). + const gripHandlersRef = useRef(gripHandlers); + gripHandlersRef.current = gripHandlers; + // Imperatives Steuerhandle für die Oberleiste (Einpassen / Auswahl / Massstab). // Stabil (leere Deps); liest den frischen Ausschnitt + die Auswahl aus Refs. useImperativeHandle( ref, () => ({ fit: () => setView(defaultViewRef.current), + confirmGripEdit: () => { + // Von der Befehlszeile aus angestoßen (Enter mit leerem Eingabetext, + // s. App.tsx) — dieselbe Aktion wie ein bestätigender Klick auf der + // Zeichenfläche (siehe onPointerDown-Kopf unten), nur eben nicht über + // ein Pointer-Event. + if (gripDrag.current) { + gripDrag.current = null; + gripHandlersRef.current?.onGripEnd(); + } + }, fitSelection: () => { const sel = selectedRef.current; const prim = sel !== null ? planRef.current.primitives[sel] : undefined;