Settings-Dialoge in echten Rhino-Fenstern (Eto.Form + WebView)
Statt Overlay-im-Panel oeffnet sich der Settings-Dialog jetzt als echtes Rhino-Fenster (verschiebbar, resizable, mehrere parallel). Infrastruktur in panel_base.py: - load_inline akzeptiert jetzt `params` (dict) und injiziert sie als window.PANEL_PARAMS — Satelliten-Apps lesen ihren initialen State daraus. - Neue Funktion open_satellite_window(mode, params, title, size, on_save, on_cancel): erstellt Eto.Forms.Form mit eingebetteter WebView, eigenem Inline-Bridge fuer SAVE/CANCEL-Messages, ruft Callbacks auf und schliesst das Fenster. Backend rhinopanel.py: - Neue Message-Handler OPEN_GESCHOSS_SETTINGS und OPEN_EBENEN_SETTINGS. - _open_geschoss_settings: oeffnet das Satelliten-Fenster mit dem Geschoss als Payload. on_save: replace im doc.Strings z-Liste + _apply(save_z=True). - _open_ebenen_settings: gleich, aber fuer Ebene + hatchPatterns. Neue React-Entries: - GeschossSettingsApp.jsx: wrappt GeschossSettingsDialog, liest window.PANEL_PARAMS, schickt SAVE/CANCEL direkt via document.title- Bridge. - EbenenSettingsApp.jsx: gleich fuer EbenenSettingsDialog. main.jsx-Switch erweitert um 'geschoss_settings' und 'ebenen_settings'. GeschossManager und EbenenManager: - Inline-Dialog-State und -Rendering entfernt. - onSettings ruft jetzt openGeschossSettings(z) / openEbenenSettings(e) in der Bridge auf → Backend oeffnet das Satelliten-Fenster. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { useEffect } from 'react'
|
||||
import EbenenSettingsDialog from './components/EbenenSettingsDialog'
|
||||
import { notifyReady } from './lib/rhinoBridge'
|
||||
|
||||
function bridgeSend(type, payload = {}) {
|
||||
if (!window.RHINO_MODE) { console.log('[Bridge] →', type, payload); return }
|
||||
const json = JSON.stringify({ type, payload })
|
||||
document.title = 'RHINOMSG::' + json
|
||||
}
|
||||
|
||||
export default function EbenenSettingsApp() {
|
||||
const initial = (typeof window !== 'undefined' && window.PANEL_PARAMS) || {}
|
||||
|
||||
useEffect(() => {
|
||||
notifyReady()
|
||||
const blockContext = (ev) => ev.preventDefault()
|
||||
document.addEventListener('contextmenu', blockContext)
|
||||
return () => document.removeEventListener('contextmenu', blockContext)
|
||||
}, [])
|
||||
|
||||
const ebene = initial.ebene || initial
|
||||
const hatchPatterns = initial.hatchPatterns || ['Solid']
|
||||
|
||||
if (!ebene || typeof ebene !== 'object' || !ebene.code) {
|
||||
return <div style={{ padding: 20, color: 'var(--text-muted)' }}>Keine Daten</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
width: '100vw', height: '100vh',
|
||||
background: 'var(--bg-base)',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<EbenenSettingsDialog
|
||||
ebene={ebene}
|
||||
hatchPatterns={hatchPatterns}
|
||||
onSave={(updated) => bridgeSend('SAVE', updated)}
|
||||
onClose={() => bridgeSend('CANCEL', {})}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { useEffect } from 'react'
|
||||
import GeschossSettingsDialog from './components/GeschossSettingsDialog'
|
||||
import { onMessage, notifyReady } from './lib/rhinoBridge'
|
||||
|
||||
// Inline send fuer SAVE/CANCEL — schickt direkt an Python-Bridge des
|
||||
// Satelliten-Fensters.
|
||||
function bridgeSend(type, payload = {}) {
|
||||
if (!window.RHINO_MODE) { console.log('[Bridge] →', type, payload); return }
|
||||
const json = JSON.stringify({ type, payload })
|
||||
document.title = 'RHINOMSG::' + json
|
||||
}
|
||||
|
||||
export default function GeschossSettingsApp() {
|
||||
// PANEL_PARAMS wurden von Python beim Window-Open injiziert.
|
||||
const initial = (typeof window !== 'undefined' && window.PANEL_PARAMS) || {}
|
||||
|
||||
useEffect(() => {
|
||||
notifyReady()
|
||||
// Native Browser-Context-Menu unterdruecken
|
||||
const blockContext = (ev) => ev.preventDefault()
|
||||
document.addEventListener('contextmenu', blockContext)
|
||||
return () => document.removeEventListener('contextmenu', blockContext)
|
||||
}, [])
|
||||
|
||||
// Wenn keine Daten da sind: leeres Frame
|
||||
if (!initial || typeof initial !== 'object') {
|
||||
return <div style={{ padding: 20, color: 'var(--text-muted)' }}>Keine Daten</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
width: '100vw', height: '100vh',
|
||||
background: 'var(--bg-base)',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<GeschossSettingsDialog
|
||||
geschoss={initial}
|
||||
onSave={(updated) => bridgeSend('SAVE', updated)}
|
||||
onClose={() => bridgeSend('CANCEL', {})}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,7 @@ import { useState, useRef, useMemo, useEffect } from 'react'
|
||||
import Icon from './Icon'
|
||||
import ConfirmDeleteEbene from './ConfirmDeleteEbene'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import EbenenSettingsDialog from './EbenenSettingsDialog'
|
||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene } from '../lib/rhinoBridge'
|
||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene, openEbenenSettings } from '../lib/rhinoBridge'
|
||||
|
||||
const MODES = [
|
||||
{ value: 'all', label: 'Alle anzeigen' },
|
||||
@@ -266,7 +265,8 @@ export default function EbenenManager({
|
||||
const [ctxMenu, setCtxMenu] = useState(null) // { x, y, code }
|
||||
const [clipboard, setClipboard] = useState(null) // { color, lw }
|
||||
const [autoEdit, setAutoEdit] = useState(null) // { code, field, token }
|
||||
const [settingsCode, setSettingsCode] = useState(null) // code der Ebene deren Einstellungen offen sind
|
||||
// Settings-Dialog laeuft jetzt in einem echten Rhino-Fenster (Satellite-
|
||||
// Window via Eto.Form + WebView). State hier nicht mehr noetig.
|
||||
|
||||
// Bei Auto-Edit: in View scrollen
|
||||
useEffect(() => {
|
||||
@@ -409,7 +409,10 @@ export default function EbenenManager({
|
||||
}
|
||||
|
||||
const ctxItems = (code) => [
|
||||
{ label: 'Ebeneneinstellungen…', icon: 'settings', onClick: () => setSettingsCode(code) },
|
||||
{ label: 'Ebeneneinstellungen…', icon: 'settings', onClick: () => {
|
||||
const target = ebenen.find(e => e.code === code)
|
||||
if (target) openEbenenSettings(target, hatchPatterns)
|
||||
} },
|
||||
{ divider: true },
|
||||
{ label: 'Selektion hierher übertragen', icon: 'move_down', onClick: () => moveSelectionToEbene(code) },
|
||||
{ divider: true },
|
||||
@@ -583,22 +586,6 @@ export default function EbenenManager({
|
||||
/>
|
||||
)}
|
||||
|
||||
{settingsCode && (() => {
|
||||
const target = ebenen.find(e => e.code === settingsCode)
|
||||
if (!target) { setSettingsCode(null); return null }
|
||||
return (
|
||||
<EbenenSettingsDialog
|
||||
ebene={target}
|
||||
hatchPatterns={hatchPatterns}
|
||||
onSave={(updated) => {
|
||||
// Code-Wechsel handhaben (eindeutiger key)
|
||||
onChange(ebenen.map(e => e.code === settingsCode ? updated : e))
|
||||
setSettingsCode(null)
|
||||
}}
|
||||
onClose={() => setSettingsCode(null)}
|
||||
/>
|
||||
)
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import Icon from './Icon'
|
||||
import GeschossDialog from './GeschossDialog'
|
||||
import GeschossSettingsDialog from './GeschossSettingsDialog'
|
||||
import { openGeschossSettings } from '../lib/rhinoBridge'
|
||||
|
||||
function GeschossBadge({ name }) {
|
||||
return <span className="chip chip-info">{name}</span>
|
||||
@@ -86,7 +86,6 @@ export default function GeschossManager({
|
||||
mode, onModeChange,
|
||||
}) {
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
const [settingsFor, setSettingsFor] = useState(null) // Geschoss-Objekt oder null
|
||||
|
||||
const sorted = [...zeichnungsebenen].reverse()
|
||||
const gesamthoehe = zeichnungsebenen
|
||||
@@ -162,7 +161,7 @@ export default function GeschossManager({
|
||||
mode={mode}
|
||||
onClick={() => onActiveChange(z.id)}
|
||||
onToggleVisible={() => toggleVisible(z.id)}
|
||||
onSettings={() => setSettingsFor(z)}
|
||||
onSettings={() => openGeschossSettings(z)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -176,16 +175,6 @@ export default function GeschossManager({
|
||||
/>
|
||||
)}
|
||||
|
||||
{settingsFor && (
|
||||
<GeschossSettingsDialog
|
||||
geschoss={settingsFor}
|
||||
onSave={(updated) => {
|
||||
onChange(zeichnungsebenen.map(z => z.id === updated.id ? updated : z))
|
||||
setSettingsFor(null)
|
||||
}}
|
||||
onClose={() => setSettingsFor(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -277,6 +277,15 @@ export function saveCombinationPreset(name, layers) { send('SAVE_PRESET', { na
|
||||
export function saveCurrentAsCombination(name) { send('SAVE_CURRENT_AS_PRESET', { name }) }
|
||||
export function deleteCombinationPreset(name) { send('DELETE_PRESET', { name }) }
|
||||
|
||||
// Satelliten-Fenster oeffnen — Python oeffnet ein echtes Rhino-Fenster
|
||||
// (Eto.Form mit eingebetteter WebView) mit dem Settings-Dialog.
|
||||
export function openGeschossSettings(geschoss) {
|
||||
send('OPEN_GESCHOSS_SETTINGS', { geschoss })
|
||||
}
|
||||
export function openEbenenSettings(ebene, hatchPatterns) {
|
||||
send('OPEN_EBENEN_SETTINGS', { ebene, hatchPatterns })
|
||||
}
|
||||
|
||||
export function applyVisibility(activeZ, zeichnungsebenen, activeCode, ebenen, zMode, eMode) {
|
||||
// Split-Panels koennen mit null/[] fuer fremde Slice aufrufen — Backend
|
||||
// fuellt aus doc.Strings. Hier robust gegen alles Falsy.
|
||||
|
||||
+14
-10
@@ -3,6 +3,8 @@ import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
import ZeichnungsebenenApp from './ZeichnungsebenenApp.jsx'
|
||||
import GeschossSettingsApp from './GeschossSettingsApp.jsx'
|
||||
import EbenenSettingsApp from './EbenenSettingsApp.jsx'
|
||||
import GestaltungApp from './GestaltungApp.jsx'
|
||||
import AusschnitteApp from './AusschnitteApp.jsx'
|
||||
import MassstabApp from './MassstabApp.jsx'
|
||||
@@ -14,16 +16,18 @@ import LayoutsApp from './LayoutsApp.jsx'
|
||||
import ElementeApp from './ElementeApp.jsx'
|
||||
|
||||
const mode = (typeof window !== 'undefined' && window.PANEL_MODE) || 'ebenen'
|
||||
const RootApp = mode === 'gestaltung' ? GestaltungApp
|
||||
: mode === 'ausschnitte' ? AusschnitteApp
|
||||
: mode === 'massstab' ? MassstabApp
|
||||
: mode === 'werkzeuge' ? WerkzeugeApp
|
||||
: mode === 'oberleiste' ? OberleisteApp
|
||||
: mode === 'overrides' ? OverridesApp
|
||||
: mode === 'dimensionen' ? DimensionenApp
|
||||
: mode === 'layouts' ? LayoutsApp
|
||||
: mode === 'elemente' ? ElementeApp
|
||||
: mode === 'zeichnungsebenen' ? ZeichnungsebenenApp
|
||||
const RootApp = mode === 'gestaltung' ? GestaltungApp
|
||||
: mode === 'ausschnitte' ? AusschnitteApp
|
||||
: mode === 'massstab' ? MassstabApp
|
||||
: mode === 'werkzeuge' ? WerkzeugeApp
|
||||
: mode === 'oberleiste' ? OberleisteApp
|
||||
: mode === 'overrides' ? OverridesApp
|
||||
: mode === 'dimensionen' ? DimensionenApp
|
||||
: mode === 'layouts' ? LayoutsApp
|
||||
: mode === 'elemente' ? ElementeApp
|
||||
: mode === 'zeichnungsebenen' ? ZeichnungsebenenApp
|
||||
: mode === 'geschoss_settings' ? GeschossSettingsApp
|
||||
: mode === 'ebenen_settings' ? EbenenSettingsApp
|
||||
: App
|
||||
|
||||
window.onerror = function (msg, src, line, col, err) {
|
||||
|
||||
Reference in New Issue
Block a user