from __future__ import annotations from pathlib import Path from PySide6.QtCore import QObject, Signal from PySide6.QtGui import QUndoStack from bedit_core.models import ID from bedit_core.models import Document as CoreDocument from bedit_gui.services import document_files class Document(QObject): """The editable document currently owned by the GUI application.""" model_changed = Signal(object) path_changed = Signal(object) modified_changed = Signal(bool) def __init__(self, parent: QObject | None = None) -> None: super().__init__(parent) self.undo_stack = QUndoStack(self) self.undo_stack.cleanChanged.connect(self._on_clean_changed) self._model = self._new_model() self._path: Path | None = None self.undo_stack.setClean() @property def model(self) -> CoreDocument: return self._model @property def path(self) -> Path | None: return self._path @property def modified(self) -> bool: return not self.undo_stack.isClean() def new(self) -> None: self._replace(self._new_model(), None) def open(self, path: str | Path) -> None: file_path = Path(path) model = document_files.load(file_path) self._replace(model, file_path) def save(self) -> None: if self._path is None: raise ValueError("the document does not have a file path") document_files.save(self._model, self._path) self.undo_stack.setClean() def save_as(self, path: str | Path) -> None: file_path = Path(path) document_files.save(self._model, file_path) if file_path != self._path: self._path = file_path self.path_changed.emit(file_path) self.undo_stack.setClean() def _replace(self, model: CoreDocument, path: Path | None) -> None: self.undo_stack.clear() self._model = model self._path = path self.model_changed.emit(model) self.path_changed.emit(path) self.undo_stack.setClean() def _on_clean_changed(self, clean: bool) -> None: self.modified_changed.emit(not clean) @staticmethod def _new_model() -> CoreDocument: return CoreDocument( format_version=1, id=ID(), name="Untitled", root={}, )