Skip to content

Commit

Permalink
Make build settings re-orderable (#1542)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo committed Nov 10, 2023
1 parent f9fd351 commit 2c5947e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 24 deletions.
60 changes: 39 additions & 21 deletions novelwriter/core/buildsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def __init__(self) -> None:
self._uuid = str(uuid.uuid4())
self._path = Path.home()
self._build = ""
self._order = 0
self._format = nwBuildFmt.ODT
self._skipRoot = set()
self._excluded = set()
Expand All @@ -164,20 +165,32 @@ def __init__(self) -> None:
self._changed = False
return

@classmethod
def fromDict(cls, data: dict) -> BuildSettings:
"""Create a build settings object from a dict."""
cls = BuildSettings()
cls.unpack(data)
return cls

##
# Properties
##

@property
def name(self) -> str:
"""The build name."""
"""Return the build name."""
return self._name

@property
def buildID(self) -> str:
"""The build ID as a UUID."""
"""Return the build ID as a UUID."""
return self._uuid

@property
def order(self) -> int:
"""Return the build order."""
return self._order

@property
def lastPath(self) -> Path:
"""The last used build path."""
Expand Down Expand Up @@ -251,6 +264,12 @@ def setBuildID(self, value: str | uuid.UUID) -> None:
self._uuid = value
return

def setOrder(self, value: int) -> None:
"""Set the build order."""
if isinstance(value, int):
self._order = value
return

def setLastPath(self, path: Path | str | None) -> None:
"""Set the last used build path."""
if isinstance(path, str):
Expand Down Expand Up @@ -398,6 +417,7 @@ def pack(self) -> dict:
"uuid": self._uuid,
"path": str(self._path),
"build": self._build,
"order": self._order,
"format": self._format.name,
"settings": self._settings.copy(),
"content": {
Expand All @@ -409,14 +429,15 @@ def pack(self) -> dict:

def unpack(self, data: dict) -> None:
"""Unpack a dictionary and populate the class."""
content = data.get("content", {})
settings = data.get("settings", {})
content = data.get("content", {})
included = content.get("included", [])
excluded = content.get("excluded", [])
skipRoot = content.get("skipRoot", [])

self.setName(data.get("name", ""))
self.setBuildID(data.get("uuid", ""))
self.setOrder(data.get("order", 0))
self.setLastPath(data.get("path", None))
self.setLastBuildName(data.get("build", ""))

Expand Down Expand Up @@ -453,9 +474,9 @@ class BuildCollection:

def __init__(self, project: NWProject) -> None:
self._project = project
self._builds = {}
self._lastBuild = ""
self._defaultBuild = ""
self._builds: dict[str, BuildSettings] = {}
self._loadCollection()
return

Expand Down Expand Up @@ -483,21 +504,19 @@ def defaultBuild(self) -> str:

def getBuild(self, buildID: str) -> BuildSettings | None:
"""Get a specific build settings object."""
if buildID not in self._builds:
return None
build = BuildSettings()
build.unpack(self._builds[buildID])
return build
return self._builds.get(buildID, None)

##
# Setters
##

def setLastBuild(self, buildID: str) -> None:
def setBuildsState(self, lastBuild: str, order: list[str]) -> None:
"""Set the last active build id."""
if buildID != self._lastBuild:
self._lastBuild = buildID
self._saveCollection()
for i, key in enumerate(order):
if build := self._builds.get(key):
build.setOrder(i)
self._lastBuild = lastBuild
self._saveCollection()
return

def setDefaultBuild(self, buildID: str) -> None:
Expand All @@ -510,8 +529,7 @@ def setDefaultBuild(self, buildID: str) -> None:
def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection."""
if isinstance(build, BuildSettings):
buildID = build.buildID
self._builds[buildID] = build.pack()
self._builds[build.buildID] = build
self._saveCollection()
return

Expand All @@ -520,15 +538,15 @@ def setBuild(self, build: BuildSettings) -> None:
##

def removeBuild(self, buildID: str) -> None:
"""Remove the a build from the collection."""
"""Remove a build from the collection."""
self._builds.pop(buildID, None)
self._saveCollection()
return

def builds(self) -> Iterable[tuple[str, str]]:
"""Iterate over all available builds."""
for buildID in self._builds:
yield buildID, self._builds[buildID].get("name", "")
for buildID, build in sorted(self._builds.items(), key=lambda x: x[1].order):
yield buildID, build.name
return

##
Expand Down Expand Up @@ -567,7 +585,7 @@ def _loadCollection(self) -> bool:
elif key == "defaultBuild":
self._defaultBuild = str(entry)
elif isinstance(entry, dict):
self._builds[key] = entry
self._builds[key] = BuildSettings.fromDict(entry)

return True

Expand All @@ -579,11 +597,11 @@ def _saveCollection(self) -> bool:

logger.debug("Saving builds file")
try:
data = {
data: dict[str, str | dict] = {
"lastBuild": self._lastBuild,
"defaultBuild": self._defaultBuild,
}
data.update(self._builds)
data.update({k: b.pack() for k, b in self._builds.items()})
with open(buildsFile, mode="w+", encoding="utf-8") as outFile:
outFile.write(jsonEncode({"novelWriter.builds": data}, nmax=4))
except Exception:
Expand Down
12 changes: 10 additions & 2 deletions novelwriter/tools/manuscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ def __init__(self, mainGui: GuiMain):
self.buildList.setIconSize(QSize(iPx, iPx))
self.buildList.doubleClicked.connect(self._editSelectedBuild)
self.buildList.currentItemChanged.connect(self._updateBuildDetails)
self.buildList.setSelectionMode(QAbstractItemView.SingleSelection)
self.buildList.setDragDropMode(QAbstractItemView.InternalMove)

self.buildDetails = _DetailsWidget(self)
self.buildDetails.setColumnWidth(
Expand Down Expand Up @@ -417,9 +419,15 @@ def _saveSettings(self):
"""Save the user GUI settings."""
logger.debug("Saving GuiManuscript settings")

buildOrder = []
for i in range(self.buildList.count()):
if item := self.buildList.item(i):
buildOrder.append(item.data(self.D_KEY))

current = self.buildList.currentItem()
if isinstance(current, QListWidgetItem):
self._builds.setLastBuild(current.data(self.D_KEY))
lastBuild = current.data(self.D_KEY) if isinstance(current, QListWidgetItem) else ""

self._builds.setBuildsState(lastBuild, buildOrder)

winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
Expand Down
2 changes: 1 addition & 1 deletion tests/test_core/test_core_buildsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
(buildIDTwo, "Build Two"),
(buildIDOne, "Build One"),
]
builds.setLastBuild(buildIDOne)
builds.setBuildsState(buildIDOne, [buildIDTwo, buildIDOne])
builds.setDefaultBuild(buildIDTwo)

# Check errors: No valid path
Expand Down

0 comments on commit 2c5947e

Please sign in to comment.