2D: Enter in der Befehlszeile bestätigt Punkt-Griff jetzt auch ohne neuen Wert

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).
This commit is contained in:
2026-08-21 23:38:05 +02:00
parent f9b202ac25
commit cdfef949bf
2 changed files with 29 additions and 0 deletions
+5
View File
@@ -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) {
+24
View File
@@ -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<PlanViewHandle, PlanViewProps>(
// 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;