new text mode editor

This commit is contained in:
2026-07-20 14:24:47 +02:00
parent bf2512995f
commit a067c85994
14 changed files with 685 additions and 392 deletions

View File

@@ -31,6 +31,7 @@ from bedit.core.model import (
Endpoint,
GraphDocument,
Icon,
Parameter,
Port,
clone_component,
)
@@ -47,6 +48,7 @@ class DocumentController(QObject):
componentMoved = Signal(str, QPointF)
componentRotated = Signal(str, float)
componentPropertiesChanged = Signal(str)
textDefinitionChanged = Signal(str)
connectionAdded = Signal(str)
connectionRemoved = Signal(str)
graphItemChanged = Signal(str, str)
@@ -153,12 +155,13 @@ class DocumentController(QObject):
if self.document is None:
raise ValueError("Open or create a document first")
number = len(self.document.roots) + 1
base_name = f"New {'Graph' if kind == 'graph' else 'Text'} Block "
component = Component(
id=str(uuid4()),
name=f"New {'Graph' if kind == 'graph' else 'Text'} Block {number}",
name=self._available_component_name(base_name, self.document.roots.values(), number),
implementation_kind=kind,
icon=Icon(text="Graph" if kind == "graph" else "Text"),
source={"equations": [], "parameters": {}} if kind == "text" else {},
source={"equations": "", "parameters": []} if kind == "text" else {},
)
self.undo_stack.push(AddComponentCommand(self, None, component))
self.activate_component(component.id)
@@ -171,12 +174,13 @@ class DocumentController(QObject):
if owner is None or owner.implementation_kind != "graph":
raise ValueError("Children can only be added to graph components")
number = len(owner.graph.blocks) + 1
base_name = f"New {'Graph' if kind == 'graph' else 'Text'} Block "
component = Component(
id=str(uuid4()),
name=f"New {'Graph' if kind == 'graph' else 'Text'} Block {number}",
name=self._available_component_name(base_name, owner.graph.blocks.values(), number),
implementation_kind=kind,
icon=Icon(text="Graph" if kind == "graph" else "Text"),
source={"equations": [], "parameters": {}} if kind == "text" else {},
source={"equations": "", "parameters": []} if kind == "text" else {},
)
self.undo_stack.push(AddComponentCommand(self, owner_id, component))
return component.id
@@ -211,6 +215,9 @@ class DocumentController(QObject):
if self.active_component is None or self.active_component.implementation_kind != "graph":
raise ValueError("Open a graph component before placing components")
component = clone_component(source)
component.name = self._available_component_name(
source.name, self.active_component.graph.blocks.values(), 0
)
component.x, component.y = position.x(), position.y()
self.undo_stack.push(AddComponentCommand(self, self.active_component_id, component))
return component.id
@@ -470,7 +477,8 @@ class DocumentController(QObject):
self,
inputs: list[Port],
outputs: list[Port],
source: dict,
equations: str,
parameters: list[Parameter],
) -> None:
component = self.active_component
if component is None or component.implementation_kind != "text":
@@ -479,6 +487,13 @@ class DocumentController(QObject):
output_ids = [port.id for port in outputs]
if len(set(input_ids)) != len(input_ids) or len(set(output_ids)) != len(output_ids):
raise ValueError("Input and output IDs must be unique")
if any(not port.name.strip() for port in (*inputs, *outputs)):
raise ValueError("Every port must have a name")
parameter_ids = [parameter.id for parameter in parameters]
if len(set(parameter_ids)) != len(parameter_ids):
raise ValueError("Parameter IDs must be unique")
if any(not parameter.name.strip() for parameter in parameters):
raise ValueError("Every parameter must have a name")
if self.document is not None:
parent = self.document.find_parent(component.id)
if parent is not None:
@@ -505,7 +520,10 @@ class DocumentController(QObject):
new = {
"inputs": [port.to_dict() for port in inputs],
"outputs": [port.to_dict() for port in outputs],
"source": deepcopy(source),
"source": {
"equations": equations,
"parameters": [parameter.to_dict() for parameter in parameters],
},
}
if old != new:
self.undo_stack.push(EditTextDefinitionCommand(self, component.id, old, new))
@@ -525,6 +543,9 @@ class DocumentController(QObject):
component = self.document.find_component(component_id)
if component is None:
return
siblings = self._component_siblings(component_id)
if any(item.id != component_id and item.name == name for item in siblings):
raise ValueError(f"A component named {name!r} already exists at this level")
old = {
"name": component.name,
"icon": component.icon.to_dict(),
@@ -726,10 +747,13 @@ class DocumentController(QObject):
pairs = [(source, clone_component(source)) for source in source_components]
id_map = {source.id: clone.id for source, clone in pairs}
blocks = {}
for _source, clone in pairs:
used = list(owner.graph.blocks.values())
for source, clone in pairs:
clone.name = self._available_component_name(source.name, used, 0)
clone.x += offset.x()
clone.y += offset.y()
blocks[clone.id] = clone
used.append(clone)
connections = {}
for source in source_connections:
if source.source.block not in id_map or source.target.block not in id_map:
@@ -759,6 +783,26 @@ class DocumentController(QObject):
raise ValueError("The containing component is no longer in the document")
return owner.graph
@staticmethod
def _available_component_name(
base: str, components, start: int = 0
) -> str:
used = {component.name for component in components}
number = start
while f"{base}{number}" in used:
number += 1
return f"{base}{number}"
def _component_siblings(self, component_id: str):
if self.document is None:
return ()
parent = self.document.find_parent(component_id)
return (
parent.graph.blocks.values()
if parent is not None
else self.document.roots.values()
)
def _insert_component(self, owner_id: str | None, component: Component) -> None:
if self.document is None:
raise ValueError("There is no open document")
@@ -1039,5 +1083,4 @@ class DocumentController(QObject):
component.source = deepcopy(values["source"])
self.interfaceChanged.emit()
self.documentReset.emit()
if component_id == self.active_component_id:
self.activeGraphChanged.emit()
self.textDefinitionChanged.emit(component_id)