View-Toggle: Active aus lastSetView + Stat-Box-Hoehe angeglichen
User-Feedback: 1. View-Bars sind hoeher als andere Elemente auf der Seite 2. Active-Highlight bleibt auf Top haengen — andere Views leuchten nicht 3. Glitch: Klick auf Top → Bar zeigt weiterhin Perspektive aktiv Fix 1 (Hoehe): Stat-Box Inhalts-Hoehe BAR_H*2+4 → BAR_H*2+6, der innere Trennstrich-Gap 4 → 6. Damit visual 50 → 52 = identisch mit den 2-row- Blocks (View, Preset, Massstab). Fix 2 + 3 (Active-Highlight): Backend trackt `self._last_set_view` ← gesetzt wenn handler in SET_VIEW erfolgreich war. Frontend matchView prueft zuerst `state.lastSetView === v` — kein Race-Condition zwischen ChangeProjection und Viewport-State-Lesen mehr. Fallback auf Viewport-State-Detection wenn lastSetView noch null (initial load). N/O/S/W kriegen jetzt auch Active-Highlight (vorher hartcoded false). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+15
-18
@@ -772,6 +772,7 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
self._last_state_sig = None # Fingerprint des letzten Push — dedupe
|
self._last_state_sig = None # Fingerprint des letzten Push — dedupe
|
||||||
self._cached_overrides = None # (enabled, count) — invalidiert bei Toggle/Update
|
self._cached_overrides = None # (enabled, count) — invalidiert bei Toggle/Update
|
||||||
self._cached_combinations = None # (names, active) — invalidiert bei jeder Comb-Aenderung
|
self._cached_combinations = None # (names, active) — invalidiert bei jeder Comb-Aenderung
|
||||||
|
self._last_set_view = None # Letzte ueber Topbar gesetzte Ansicht (fuer Active-Highlight)
|
||||||
# Command-Liste LAZY laden — die Enumeration durchlaeuft alle Plugins
|
# Command-Liste LAZY laden — die Enumeration durchlaeuft alle Plugins
|
||||||
# und ist teuer (~hundert ms). Wird erst beim ersten _send_state, oder
|
# und ist teuer (~hundert ms). Wird erst beim ersten _send_state, oder
|
||||||
# explizit bei Command-Input-Fokus, gebaut.
|
# explizit bei Command-Input-Fokus, gebaut.
|
||||||
@@ -880,35 +881,29 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
vp = kamera._active_viewport()
|
vp = kamera._active_viewport()
|
||||||
except Exception:
|
except Exception:
|
||||||
vp = None
|
vp = None
|
||||||
|
handled = False
|
||||||
if v == "Top":
|
if v == "Top":
|
||||||
# Plan rotiert mit Norden — Up-Vektor zeigt Norden
|
|
||||||
try:
|
try:
|
||||||
import kamera
|
import kamera
|
||||||
kamera.set_top_view(vp)
|
kamera.set_top_view(vp); handled = True
|
||||||
except Exception as ex:
|
except Exception as ex: print("[OBERLEISTE] top:", ex)
|
||||||
print("[OBERLEISTE] top:", ex)
|
|
||||||
self._send_state(force=True)
|
|
||||||
elif v == "Perspective":
|
elif v == "Perspective":
|
||||||
_run("_-{} _Enter".format(v))
|
_run("_-{} _Enter".format(v)); handled = True
|
||||||
self._send_state(force=True)
|
|
||||||
elif v == "Iso":
|
elif v == "Iso":
|
||||||
try:
|
try:
|
||||||
import kamera
|
import kamera
|
||||||
kamera._set_iso(vp, "NE")
|
kamera._set_iso(vp, "NE"); handled = True
|
||||||
except Exception as ex:
|
except Exception as ex: print("[OBERLEISTE] iso:", ex)
|
||||||
print("[OBERLEISTE] iso:", ex)
|
|
||||||
self._send_state(force=True)
|
|
||||||
elif v in ("N", "O", "S", "W"):
|
elif v in ("N", "O", "S", "W"):
|
||||||
try:
|
try:
|
||||||
import kamera
|
import kamera
|
||||||
kamera.set_cardinal_view(vp, v)
|
kamera.set_cardinal_view(vp, v); handled = True
|
||||||
except Exception as ex:
|
except Exception as ex: print("[OBERLEISTE] cardinal:", ex)
|
||||||
print("[OBERLEISTE] cardinal:", ex)
|
|
||||||
self._send_state(force=True)
|
|
||||||
elif v in ("Front", "Right", "Left", "Back", "Bottom"):
|
elif v in ("Front", "Right", "Left", "Back", "Bottom"):
|
||||||
# Legacy direkte Rhino-Views (falls noch wo benutzt)
|
_run("_-{} _Enter".format(v)); handled = True
|
||||||
_run("_-{} _Enter".format(v))
|
if handled:
|
||||||
self._send_state(force=True)
|
self._last_set_view = v
|
||||||
|
self._send_state(force=True)
|
||||||
|
|
||||||
# --- Kamera-Panel oeffnen ---------------------------------------
|
# --- Kamera-Panel oeffnen ---------------------------------------
|
||||||
elif t == "OPEN_KAMERA_PANEL":
|
elif t == "OPEN_KAMERA_PANEL":
|
||||||
@@ -1203,6 +1198,8 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
info["northAngle"] = kamera.get_north_angle(doc)
|
info["northAngle"] = kamera.get_north_angle(doc)
|
||||||
except Exception:
|
except Exception:
|
||||||
info["northAngle"] = 0
|
info["northAngle"] = 0
|
||||||
|
# Letzte ueber Topbar gesetzte Ansicht (fuer Active-Highlight)
|
||||||
|
info["lastSetView"] = self._last_set_view
|
||||||
# Command-Line State
|
# Command-Line State
|
||||||
prompt = _get_command_prompt()
|
prompt = _get_command_prompt()
|
||||||
info["cmdPrompt"] = prompt
|
info["cmdPrompt"] = prompt
|
||||||
|
|||||||
+22
-18
@@ -315,6 +315,7 @@ export default function OberleisteApp() {
|
|||||||
textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false },
|
textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false },
|
||||||
textFonts: [],
|
textFonts: [],
|
||||||
northAngle: 0,
|
northAngle: 0,
|
||||||
|
lastSetView: null,
|
||||||
})
|
})
|
||||||
const [appliedScale, setAppliedScale] = useState(null)
|
const [appliedScale, setAppliedScale] = useState(null)
|
||||||
const appliedScaleRef = useRef(null)
|
const appliedScaleRef = useRef(null)
|
||||||
@@ -373,11 +374,11 @@ export default function OberleisteApp() {
|
|||||||
if (appliedScale && appliedScale > 0) setMassstab(appliedScale)
|
if (appliedScale && appliedScale > 0) setMassstab(appliedScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aktuelles View-Match. Top/Persp/Iso werden via parallel-Flag und
|
// Active-Highlight aus lastSetView (vom Backend getrackt — vermeidet
|
||||||
// viewName unterschieden. N/O/S/W: kein zuverlaessiges View-Match
|
// Race-Conditions zwischen ChangeProjection und Viewport-State-Lesen).
|
||||||
// ueber den Viewport-Namen (wir setzen die Camera direkt) — daher
|
// Fallback wenn noch nie geklickt: Viewport-State raten.
|
||||||
// kein Active-State fuer Cardinals.
|
|
||||||
const matchView = (v) => {
|
const matchView = (v) => {
|
||||||
|
if (state.lastSetView) return state.lastSetView === v
|
||||||
const name = (state.viewName || '').toLowerCase()
|
const name = (state.viewName || '').toLowerCase()
|
||||||
if (v === 'Top') return name === 'top'
|
if (v === 'Top') return name === 'top'
|
||||||
if (v === 'Perspective') return state.parallel === false
|
if (v === 'Perspective') return state.parallel === false
|
||||||
@@ -508,18 +509,21 @@ export default function OberleisteApp() {
|
|||||||
border: '1px solid var(--border)', borderRadius: 999,
|
border: '1px solid var(--border)', borderRadius: 999,
|
||||||
overflow: 'hidden', flexShrink: 0,
|
overflow: 'hidden', flexShrink: 0,
|
||||||
}}>
|
}}>
|
||||||
{VIEWS_ROW2.map((v, idx) => (
|
{VIEWS_ROW2.map((v, idx) => {
|
||||||
<button key={v.value}
|
const isActive = matchView(v.value)
|
||||||
onClick={() => setView(v.value)}
|
return (
|
||||||
title={`Ansicht aus ${v.value} (Norden = ${(state.northAngle || 0).toFixed(0)}°)`}
|
<button key={v.value}
|
||||||
onMouseEnter={hoverIn(false)}
|
onClick={() => setView(v.value)}
|
||||||
onMouseLeave={hoverOut(false)}
|
title={`Ansicht aus ${v.value} (Norden = ${(state.northAngle || 0).toFixed(0)}°)`}
|
||||||
style={cellStyle(false, idx === 0)}>
|
onMouseEnter={hoverIn(isActive)}
|
||||||
<span style={{
|
onMouseLeave={hoverOut(isActive)}
|
||||||
fontFamily: 'DM Mono, monospace', fontSize: 11, fontWeight: 600,
|
style={cellStyle(isActive, idx === 0)}>
|
||||||
}}>{v.label}</span>
|
<span style={{
|
||||||
</button>
|
fontFamily: 'DM Mono, monospace', fontSize: 11, fontWeight: 600,
|
||||||
))}
|
}}>{v.label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -702,7 +706,7 @@ export default function OberleisteApp() {
|
|||||||
<div style={{
|
<div style={{
|
||||||
gridRow: '1 / span 2',
|
gridRow: '1 / span 2',
|
||||||
display: 'flex', flexDirection: 'column',
|
display: 'flex', flexDirection: 'column',
|
||||||
width: STAT_W, height: BAR_H * 2 + 4,
|
width: STAT_W, height: BAR_H * 2 + 6,
|
||||||
background: atScale ? 'var(--accent-dim)' : 'var(--bg-input)',
|
background: atScale ? 'var(--accent-dim)' : 'var(--bg-input)',
|
||||||
color: atScale ? 'var(--accent-light)' : 'var(--text-primary)',
|
color: atScale ? 'var(--accent-light)' : 'var(--text-primary)',
|
||||||
border: '1px solid ' + (atScale ? 'var(--accent)' : 'var(--border)'),
|
border: '1px solid ' + (atScale ? 'var(--accent)' : 'var(--border)'),
|
||||||
@@ -719,7 +723,7 @@ export default function OberleisteApp() {
|
|||||||
}} title={isPerspective ? 'Perspektive — kein Massstab' : 'Aktueller Live-Massstab'}>
|
}} title={isPerspective ? 'Perspektive — kein Massstab' : 'Aktueller Live-Massstab'}>
|
||||||
{isPerspective ? '—' : fmtScale(scaleVal)}
|
{isPerspective ? '—' : fmtScale(scaleVal)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: 4, position: 'relative' }}>
|
<div style={{ height: 6, position: 'relative' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', left: 6, right: 6,
|
position: 'absolute', left: 6, right: 6,
|
||||||
top: '50%', height: 1,
|
top: '50%', height: 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user