Library drag-in

This commit is contained in:
2026-08-17 17:52:12 +02:00
parent b1f453a6df
commit e88c58f095
14 changed files with 138 additions and 198 deletions

View File

@@ -182,6 +182,7 @@ class GraphEditorClipboardHandler(ClipboardHandler):
self.editor = editor
self.clipboard = clipboard
editor.scene.selectionChanged.connect(self.availability_changed)
editor.component_drop_requested.connect(self.drop_components)
def owns_focus(self, widget: QWidget) -> bool:
return widget is self.editor or self.editor.isAncestorOf(widget)
@@ -214,12 +215,19 @@ class GraphEditorClipboardHandler(ClipboardHandler):
payload = self.clipboard.get_json(ClipboardService.COMPONENTS_MIME)
if graph_component is None or not isinstance(graph_component.implementation, GraphImplementation) or payload is None:
return
x, y = self.editor.paste_position()
self._paste_payload(graph_component, payload, (x, y))
def drop_components(self, graph_component: Component, payload: dict, position: tuple[int, int]) -> None:
self._paste_payload(graph_component, payload, position)
def _paste_payload(self, graph_component: Component, payload: dict, position: tuple[int, int]) -> None:
try:
components, icons = import_components(payload)
except (TypeError, ValueError) as exc:
QMessageBox.critical(self.editor, "Could not paste components", str(exc))
return
x, y = self.editor.paste_position()
x, y = position
spacing = self.editor.snap_to_grid_size * 4
positions = {component_id: (x + index * spacing, y + index * spacing) for index, component_id in enumerate(components)}
self.document.paste_graph_components(graph_component, components, icons, positions)

View File

@@ -1,13 +1,14 @@
from PySide6.QtCore import QObject, QSize
from PySide6.QtCore import QObject, QSize, Qt
from PySide6.QtWidgets import QAbstractItemView, QHeaderView
from bedit_core.models import Component, ComponentID, GraphImplementation
from bedit_gui.models import Icon, IconDatabase
from bedit_gui.services.application_settings import ApplicationSettings
from bedit_gui.services.component_clipboard import export_component_data
from bedit_gui.services.libraries import load_library_documents
from bedit_gui.utils.icon import render_fitted_icon
from bedit_gui.views.main_window import MainWindow
from bedit_gui.views.models.document_tree_model import DocumentTreeModel
from bedit_gui.views.models.library_tree_model import LibraryTreeModel
ICON_SIZE = QSize(32, 32)
@@ -17,13 +18,17 @@ class LibraryController(QObject):
super().__init__(window)
self.window = window
self.settings = settings
self.model = DocumentTreeModel(editable=False)
self._component_sources: dict[int, tuple[ComponentID, dict[ComponentID, Icon]]] = {}
self.model = LibraryTreeModel(self._component_payload)
tree = window.ui.libraryTree
tree.setModel(self.model)
tree.setHeaderHidden(True)
tree.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
tree.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
tree.setDragEnabled(True)
tree.setDragDropMode(QAbstractItemView.DragDropMode.DragOnly)
tree.setDefaultDropAction(Qt.DropAction.CopyAction)
tree.setIconSize(QSize(48, 48))
tree.header().setStretchLastSection(False)
tree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
@@ -34,6 +39,11 @@ class LibraryController(QObject):
def reload(self) -> None:
libraries = load_library_documents(self.settings.library_paths)
self._component_sources = {}
for library in libraries:
database = library.document.metadata.get("icon_database") if library.document.metadata is not None else None
icons = database.icons if isinstance(database, IconDatabase) else {}
self._collect_component_sources(library.document.root, icons)
self.model.set_documents([library.document for library in libraries])
for library in libraries:
database = library.document.metadata.get("icon_database") if library.document.metadata is not None else None
@@ -41,6 +51,24 @@ class LibraryController(QObject):
self._set_component_icons(library.document.root, icons)
self.window.ui.libraryTree.expandAll()
def _component_payload(self, components: list[Component]) -> dict:
roots = {}
icons = {}
for component in components:
source = self._component_sources.get(id(component))
if source is None:
continue
component_id, source_icons = source
roots[component_id] = component
icons.update(source_icons)
return export_component_data(roots, icons)
def _collect_component_sources(self, components: dict[ComponentID, Component], icons: dict[ComponentID, Icon]) -> None:
for component_id, component in components.items():
self._component_sources[id(component)] = (component_id, icons)
if isinstance(component.implementation, GraphImplementation):
self._collect_component_sources(component.implementation.graph.components, icons)
def _set_component_icons(self, components: dict[ComponentID, Component], icons: dict[ComponentID, Icon]) -> None:
for component_id, component in components.items():
self.model.set_component_icon(component_id, render_fitted_icon(icons.get(component_id, Icon()), component.interface.ports, ICON_SIZE))