From c6316f420e91c84c891b0cbdcfa27b003303a28a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Fri, 8 Jul 2022 10:21:46 +0200 Subject: [PATCH] beginnings of live reload of .gggls project file after external change --- Lib/fontgoggles/mac/document.py | 26 +++++++++++++++++++- Lib/fontgoggles/mac/mainWindow.py | 41 +++++++++++++++++++++++++++++++ Lib/fontgoggles/project.py | 7 +++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Lib/fontgoggles/mac/document.py b/Lib/fontgoggles/mac/document.py index 702aeca2..9207a672 100644 --- a/Lib/fontgoggles/mac/document.py +++ b/Lib/fontgoggles/mac/document.py @@ -1,9 +1,12 @@ +import json import pathlib +import objc import AppKit from ..project import Project from .mainWindow import FGMainWindowController from ..font import defaultSortSpec, sortedFontPathsAndNumbers +from .fileObserver import getFileObserver class FGDocument(AppKit.NSDocument): @@ -36,10 +39,24 @@ def dataOfType_error_(self, type, error): return AppKit.NSData.dataWithData_(self.project.asJSON(rootPath)), error def readFromData_ofType_error_(self, data, type, error): - rootPath = pathlib.Path(self.fileURL().path()).parent + documentPath = str(self.fileURL().path()) + rootPath = pathlib.Path(documentPath).parent self.project = Project.fromJSON(bytes(data), rootPath) + obs = getFileObserver() + obs.addObserver(documentPath, self._projectFileChangedOnDisk) return True, None + @objc.python_method + def _projectFileChangedOnDisk(self, oldPath, newPath, wasModified): + if not wasModified: + return + rootPath = pathlib.Path(newPath).parent + with open(newPath, "rb") as f: + dataDict = json.load(f) + self.project.updateFromDict(dataDict, rootPath) + for controller in self.windowControllers(): + controller.syncFromProject() + def revertToContentsOfURL_ofType_error_(self, url, type, error): for controller in list(self.windowControllers()): self.removeWindowController_(controller) @@ -49,3 +66,10 @@ def revertToContentsOfURL_ofType_error_(self, url, type, error): if success: self.makeWindowControllers() return True, error + + def fileModificationDate(self): + # This is a workaround to avoid the "The file has been changed by another application" + # message, if we have reloaded the project after an external change. + fileManager = AppKit.NSFileManager.defaultManager() + attrs, error = fileManager.attributesOfItemAtPath_error_(self.fileURL().path(), None) + return attrs[AppKit.NSFileModificationDate] diff --git a/Lib/fontgoggles/mac/mainWindow.py b/Lib/fontgoggles/mac/mainWindow.py index 114a1a5f..749ecd64 100644 --- a/Lib/fontgoggles/mac/mainWindow.py +++ b/Lib/fontgoggles/mac/mainWindow.py @@ -163,6 +163,31 @@ def syncUISettingsWithProject(self): uiSettings.feaVarTabSelection = feaVarTabValues[self.feaVarTabs.get()] uiSettings.showHiddenAxes = self.variationsGroup.showHiddenAxes + @suppressAndLogException + def syncFromProject(self): + print("sync!") + + textSettings = self.project.textSettings + uiSettings = self.project.uiSettings + + self.w.mainSplitView.showPaneReally("characterList", uiSettings.characterListVisible) + self.w.mainSplitView.setPaneSize("characterList", uiSettings.characterListSize) + + self.restoreWindowPosition(uiSettings.windowPosition) + self.textEntry.set(textSettings.text) + + self.fontList.itemSize = uiSettings.fontListItemSize + self.fontList.relativeFontSize = textSettings.relativeFontSize + self.fontList.relativeHBaseline = textSettings.relativeHBaseline + self.fontList.relativeVBaseline = textSettings.relativeVBaseline + self.fontList.relativeMargin = textSettings.relativeMargin + + self.fontList.purgeFontItems() + self.project.purgeFonts() + self._projectFontsChanged([]) + self.growOrShrinkFontList() + self.textEntryChangedCallback(self.textEntry) + @objc.python_method def restoreWindowPosition(self, windowPosition): if not windowPosition: @@ -1326,6 +1351,9 @@ class MySplitView(SplitView): def isPaneReallyVisible(self, paneIdentifier): return not self.isPaneVisible(paneIdentifier) + def showPaneReally(self, paneIdentifier, onOff): + self.showPane(paneIdentifier, not onOff) + def paneSize(self, paneIdentifier): view = self._identifierToPane[paneIdentifier]["view"] w, h = view._nsObject.frame().size @@ -1334,6 +1362,19 @@ def paneSize(self, paneIdentifier): else: return h + def setPaneSize(self, paneIdentifier, size): + view = self._identifierToPane[paneIdentifier]["view"] + (x, y), (w, h) = view._nsObject.frame() + print((x, y), (w, h)) + if self._nsObject.isVertical(): + w = size + else: + h = size + # XXXX not working + # print((x, y), (w, h)) + # view._nsObject.setFrame_(((x, y), (w, h))) + # self._nsObject.adjustSubviews() + _minimalSpaceBox = 12 diff --git a/Lib/fontgoggles/project.py b/Lib/fontgoggles/project.py index eec76674..68590b13 100644 --- a/Lib/fontgoggles/project.py +++ b/Lib/fontgoggles/project.py @@ -26,6 +26,12 @@ def fromJSON(cls, data, rootPath): @classmethod def fromDict(cls, root, rootPath): self = cls() + self.updateFromDict(root, rootPath) + return self + + def updateFromDict(self, root, rootPath): + self.fonts = [] + self.fontSelection = set() # not persistent for fontItemInfoDict in root["fonts"]: fontPath = pathlib.Path(os.path.normpath(os.path.join(rootPath, fontItemInfoDict["path"]))) self.addFont(fontPath, fontItemInfoDict.get("fontNumber", 0)) @@ -34,7 +40,6 @@ def fromDict(cls, root, rootPath): # relative path -> absolute path self.textSettings.textFilePath = os.path.normpath(os.path.join(rootPath, self.textSettings.textFilePath)) self.uiSettings.__dict__.update(root.get("uiSettings", {})) - return self def asJSON(self, rootPath): root = self.asDict(rootPath)