Document tree panel

This commit is contained in:
2026-07-26 15:25:51 +02:00
parent 9312ea544b
commit a96bde9642
3 changed files with 159 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ from bedit_gui.controllers.settings_controller import SettingsController
from bedit_gui.controllers.undo_controller import UndoController from bedit_gui.controllers.undo_controller import UndoController
from bedit_gui.controllers.view_menu_controller import ViewMenuController from bedit_gui.controllers.view_menu_controller import ViewMenuController
from bedit_gui.controllers.window_state_controller import WindowStateController from bedit_gui.controllers.window_state_controller import WindowStateController
from bedit_gui.controllers.document_tree_controller import DocumentTreeController
from bedit_gui.documents import Document from bedit_gui.documents import Document
from bedit_gui.services.application_settings import ApplicationSettings from bedit_gui.services.application_settings import ApplicationSettings
from bedit_gui.views.main_window import MainWindow from bedit_gui.views.main_window import MainWindow
@@ -29,6 +30,7 @@ def main() -> int:
SettingsController(window, settings) SettingsController(window, settings)
UndoController(document, window) UndoController(document, window)
ViewMenuController(window) ViewMenuController(window)
DocumentTreeController(document, window)
window_state_controller = WindowStateController(app, window) window_state_controller = WindowStateController(app, window)
window_state_controller.restore() window_state_controller.restore()
window.showMaximized() window.showMaximized()

View File

@@ -0,0 +1,34 @@
from PySide6.QtCore import QObject
from bedit_core.models import Document as CoreDocument
from bedit_gui.documents import Document
from bedit_gui.views.main_window import MainWindow
from bedit_gui.views.models.document_tree_model import DocumentTreeModel
class DocumentTreeController(QObject):
def __init__(
self,
document: Document,
window: MainWindow,
) -> None:
super().__init__(window)
self.document = document
self.window = window
self.model = DocumentTreeModel()
window.ui.documentTree.setModel(self.model)
document.model_changed.connect(self._on_document_changed)
window.ui.documentTree.setHeaderHidden(True)
self._on_document_changed(document.model)
def _on_document_changed(self, model: CoreDocument) -> None:
"""Rebuild the tree whenever New/Open replaces the core document."""
self.model.set_document(model)
# Optional presentation behavior. Later, you could instead remember
# expanded component IDs and restore only those nodes.
self.window.ui.documentTree.expandAll()

View File

@@ -0,0 +1,123 @@
from __future__ import annotations
from dataclasses import dataclass
from PySide6.QtCore import QAbstractItemModel, QModelIndex, Qt
from bedit_core.models import Document as CoreDocument, ComponentID, Component, GraphImplementation
@dataclass
class DocumentTreeNode:
name: str
value: object
parent: DocumentTreeNode | None
children: list[DocumentTreeNode]
class DocumentTreeModel(QAbstractItemModel):
def __init__(self) -> None:
super().__init__()
self._document: CoreDocument | None = None
self._root = DocumentTreeNode("Document", None, None, [])
def set_document(self, document: CoreDocument) -> None:
self.beginResetModel()
self._document = document
self._root = self._build_tree(document)
self.endResetModel()
def rowCount(self, parent: QModelIndex | None = None) -> int:
return len(self._node(parent).children)
def columnCount(self, _parent: QModelIndex | None = None) -> int:
return 1
def index(
self,
row: int,
column: int,
parent: QModelIndex | None = None,
) -> QModelIndex:
parent_node = self._node(parent)
if (
column != 0
or row < 0
or row >= len(parent_node.children)
):
return QModelIndex()
return self.createIndex(row, column, parent_node.children[row])
def parent(self, index: QModelIndex) -> QModelIndex:
if not index.isValid():
return QModelIndex()
node = index.internalPointer()
if not isinstance(node, DocumentTreeNode):
return QModelIndex()
parent_node = node.parent
if parent_node is None or parent_node is self._root:
return QModelIndex()
grandparent = parent_node.parent
if grandparent is None:
return QModelIndex()
row = grandparent.children.index(parent_node)
return self.createIndex(row, 0, parent_node)
def data(
self,
index: QModelIndex,
role: int = Qt.ItemDataRole.DisplayRole,
) -> object | None:
if not index.isValid():
return None
node = index.internalPointer()
if (
role == Qt.ItemDataRole.DisplayRole
and isinstance(node, DocumentTreeNode)
):
return node.name
return None
def _node(self, index: QModelIndex | None) -> DocumentTreeNode:
if index is None or not index.isValid():
return self._root
node = index.internalPointer()
return node if isinstance(node, DocumentTreeNode) else self._root
def _build_tree(self, document: CoreDocument) -> DocumentTreeNode:
# QT's invisible root
root = DocumentTreeNode(
name="",
value=None,
parent=None,
children=[],
)
# Add itself as a child so the document root is visible in the tree
document_root = DocumentTreeNode(
name = document.name,
value = document,
parent = root,
children = []
)
root.children.append(document_root)
def _list_children(root: DocumentTreeNode, components: dict[ComponentID, Component]) -> None:
for component_id, component in components.items():
component_node = DocumentTreeNode(
name = component.name,
value = (component_id, component),
parent = root,
children = []
)
root.children.append(component_node)
if isinstance(component.implementation, GraphImplementation):
_list_children(component_node, component.implementation.graph.components)
_list_children(document_root, document.root)
return root