Document tree panel
This commit is contained in:
@@ -10,6 +10,7 @@ from bedit_gui.controllers.settings_controller import SettingsController
|
||||
from bedit_gui.controllers.undo_controller import UndoController
|
||||
from bedit_gui.controllers.view_menu_controller import ViewMenuController
|
||||
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.services.application_settings import ApplicationSettings
|
||||
from bedit_gui.views.main_window import MainWindow
|
||||
@@ -29,6 +30,7 @@ def main() -> int:
|
||||
SettingsController(window, settings)
|
||||
UndoController(document, window)
|
||||
ViewMenuController(window)
|
||||
DocumentTreeController(document, window)
|
||||
window_state_controller = WindowStateController(app, window)
|
||||
window_state_controller.restore()
|
||||
window.showMaximized()
|
||||
|
||||
34
src/bedit_gui/controllers/document_tree_controller.py
Normal file
34
src/bedit_gui/controllers/document_tree_controller.py
Normal 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()
|
||||
123
src/bedit_gui/views/models/document_tree_model.py
Normal file
123
src/bedit_gui/views/models/document_tree_model.py
Normal 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
|
||||
Reference in New Issue
Block a user