Add build copy feature (#2084)

This commit is contained in:
Veronica Berglyd Olsen
2024-11-05 18:38:32 +01:00
committed by GitHub
9 changed files with 94 additions and 7 deletions
@@ -44,6 +44,7 @@ cls_template = mixed_document-new.svg
cls_timeline = typ_calendar.svg
cls_trash = typ_trash.svg
cls_world = typ_location.svg
copy = mixed_copy.svg
cross = typ_times.svg
document = typ_document.svg
down = typ_chevron-down.svg
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="m7.8824 2c-1.2988 0-2.3529 1.0541-2.3529 2.3529v2.3529h-1.1765c-1.2988 0-2.3529 1.0541-2.3529 2.3529v10.588c0 1.2988 1.0541 2.3529 2.3529 2.3529h10.588c1.2988 0 2.3529-1.0541 2.3529-2.3529v-1.1765h2.3529c1.2988 0 2.3529-1.0541 2.3529-2.3529v-11.765c0-1.2988-1.0541-2.3529-2.3529-2.3529zm0 2.3529h11.765v11.765h-2.3529v-7.0588c0-1.2988-1.0541-2.3529-2.3529-2.3529h-7.0588zm-3.5294 4.7059h10.588c0.32353 0 0-0.32353 0 0v10.588h-10.588v-9.8943z" fill="#69c" stroke-width="1.1765"/>
</svg>

After

Width:  |  Height:  |  Size: 633 B

@@ -44,6 +44,7 @@ cls_template = mixed_document-new.svg
cls_timeline = typ_calendar.svg
cls_trash = typ_trash.svg
cls_world = typ_location.svg
copy = mixed_copy.svg
cross = typ_times.svg
document = typ_document.svg
down = typ_chevron-down.svg
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="m7.8824 2c-1.2988 0-2.3529 1.0541-2.3529 2.3529v2.3529h-1.1765c-1.2988 0-2.3529 1.0541-2.3529 2.3529v10.588c0 1.2988 1.0541 2.3529 2.3529 2.3529h10.588c1.2988 0 2.3529-1.0541 2.3529-2.3529v-1.1765h2.3529c1.2988 0 2.3529-1.0541 2.3529-2.3529v-11.765c0-1.2988-1.0541-2.3529-2.3529-2.3529zm0 2.3529h11.765v11.765h-2.3529v-7.0588c0-1.2988-1.0541-2.3529-2.3529-2.3529h-7.0588zm-3.5294 4.7059h10.588c0.32353 0 0-0.32353 0 0v10.588h-10.588v-9.8943z" fill="#4271ae" stroke-width="1.1765"/>
</svg>

After

Width:  |  Height:  |  Size: 636 B

+9
View File
@@ -502,6 +502,15 @@ class BuildSettings:
return
@classmethod
def duplicate(cls, source: BuildSettings) -> BuildSettings:
"""Make a copy of another build."""
cls = BuildSettings()
cls.unpack(source.pack())
cls._uuid = str(uuid.uuid4())
cls._name = f"{source.name} 2"
return cls
class BuildCollection:
"""Core: Build Collection Class
+5 -4
View File
@@ -503,10 +503,11 @@ class GuiIcons:
"fmt_strike-md", "fmt_subscript", "fmt_superscript", "fmt_underline",
# General Button Icons
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
"document", "down", "edit", "export", "font", "forward", "import", "list", "maximise",
"menu", "minimise", "more", "noncheckable", "open", "panel", "quote", "refresh", "remove",
"revert", "search_replace", "search", "settings", "star", "unchecked", "up", "view",
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "copy",
"cross", "document", "down", "edit", "export", "font", "forward", "import", "list",
"maximise", "menu", "minimise", "more", "noncheckable", "open", "panel", "quote",
"refresh", "remove", "revert", "search_replace", "search", "settings", "star", "unchecked",
"up", "view",
# Switches
"sticky-on", "sticky-off",
+19 -3
View File
@@ -116,6 +116,11 @@ class GuiManuscript(NToolDialog):
self.tbDel.setStyleSheet(buttonStyle)
self.tbDel.clicked.connect(self._deleteSelectedBuild)
self.tbCopy = NIconToolButton(self, iSz, "copy")
self.tbCopy.setToolTip(self.tr("Duplicate Selected Build"))
self.tbCopy.setStyleSheet(buttonStyle)
self.tbCopy.clicked.connect(self._copySelectedBuild)
self.tbEdit = NIconToolButton(self, iSz, "edit")
self.tbEdit.setToolTip(self.tr("Edit Selected Build"))
self.tbEdit.setStyleSheet(buttonStyle)
@@ -128,6 +133,7 @@ class GuiManuscript(NToolDialog):
self.listToolBox.addStretch(1)
self.listToolBox.addWidget(self.tbAdd)
self.listToolBox.addWidget(self.tbDel)
self.listToolBox.addWidget(self.tbCopy)
self.listToolBox.addWidget(self.tbEdit)
self.listToolBox.setSpacing(0)
@@ -291,6 +297,17 @@ class GuiManuscript(NToolDialog):
self._openSettingsDialog(build)
return
@pyqtSlot()
def _copySelectedBuild(self) -> None:
"""Copy the currently selected build settings entry."""
if build := self._getSelectedBuild():
new = BuildSettings.duplicate(build)
self._builds.setBuild(new)
self._updateBuildsList()
if item := self._buildMap.get(new.buildID):
item.setSelected(True)
return
@pyqtSlot("QListWidgetItem*", "QListWidgetItem*")
def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetItem) -> None:
"""Process change of build selection to update the details."""
@@ -460,9 +477,8 @@ class GuiManuscript(NToolDialog):
def _updateBuildItem(self, build: BuildSettings) -> None:
"""Update the entry of a specific build item."""
bItem = self._buildMap.get(build.buildID, None)
if isinstance(bItem, QListWidgetItem):
bItem.setText(build.name)
if item := self._buildMap.get(build.buildID):
item.setText(build.name)
else: # Probably a new item
self._updateBuildsList()
return
@@ -465,3 +465,45 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
]
assert another.lastBuild == buildIDOne
assert another.defaultBuild == buildIDTwo
@pytest.mark.core
def testCoreBuildSettings_Duplicate(monkeypatch, mockGUI, fncPath: Path, mockRnd):
"""Test duplicating builds."""
project = NWProject()
buildTestProject(project, fncPath)
buildsFile = project.storage.getMetaFile(nwFiles.BUILDS_FILE)
assert isinstance(buildsFile, Path)
# No initial builds in a fresh project
builds = BuildCollection(project)
assert len(builds) == 0
assert not buildsFile.exists()
assert builds.lastBuild == ""
assert builds.defaultBuild == ""
# Create a default build
buildOne = BuildSettings()
buildOne.setName("Test Build")
buildOne.setValue("headings.fmtScene", nwHeadFmt.TITLE)
buildOne.setValue("headings.fmtAltScene", nwHeadFmt.TITLE)
buildOne.setValue("headings.fmtSection", nwHeadFmt.TITLE)
# Copy it
buildTwo = BuildSettings().duplicate(buildOne)
assert buildTwo.name == "Test Build 2"
# Raw data
dataOne = buildOne.pack()
dataTwo = buildTwo.pack()
# Name and UUID should be different
assert dataOne["name"] != dataTwo["name"]
assert dataOne["uuid"] != dataTwo["uuid"]
# The rest should be equal
assert dataOne["path"] == dataTwo["path"]
assert dataOne["build"] == dataTwo["build"]
assert dataOne["format"] == dataTwo["format"]
assert dataOne["settings"] == dataTwo["settings"]
assert dataOne["content"] == dataTwo["content"]
@@ -138,6 +138,15 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
assert build.name == "Test Build"
assert manus.buildList.count() == 1
# Copy the build
manus._buildMap[build.buildID].setSelected(True)
manus._copySelectedBuild()
assert manus.buildList.count() == 2
new = manus._getSelectedBuild()
assert new is not None
assert new.name == "Test Build 2"
# Close the dialog should also close the child dialogs
manus.btnClose.click()
if isinstance(bSettings, GuiBuildSettings):