Make status and importance labels sortable again (#1040)

This commit is contained in:
Veronica Berglyd Olsen
2022-04-16 18:40:10 +02:00
committed by GitHub
13 changed files with 282 additions and 71 deletions
@@ -42,6 +42,7 @@ doc_h2 = mixed_heading2.svg
doc_h3 = mixed_heading3.svg doc_h3 = mixed_heading3.svg
doc_h4 = mixed_heading4.svg doc_h4 = mixed_heading4.svg
done = typ_input-checked.svg done = typ_input-checked.svg
down = typ_chevron-down.svg
edit = typ_pencil.svg edit = typ_pencil.svg
forward = typ_chevron-right.svg forward = typ_chevron-right.svg
hash = typ_hash.svg hash = typ_hash.svg
@@ -74,3 +75,4 @@ status_stats = typ_chart-bar-grey.svg
status_time = typ_stopwatch-grey.svg status_time = typ_stopwatch-grey.svg
sticky-off = typ_pin-outline.svg sticky-off = typ_pin-outline.svg
sticky-on = typ_pin.svg sticky-on = typ_pin.svg
up = typ_chevron-up.svg
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8980"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8986">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8984" />
<path
style="fill:#6699cc;fill-opacity:1;stroke-width:1.28574"
id="path8978"
d="m 3.7531209,6.4345258 c -1.004161,1.002875 -1.004161,2.63319 0,3.6360652 l 8.2467181,8.248004 8.246719,-8.248004 C 20.749281,9.5691528 21,8.9108558 21,8.2525578 c 0,-0.658297 -0.250719,-1.316595 -0.753442,-1.818032 -1.004161,-1.004161 -2.631904,-1.004161 -3.636065,0 L 11.999839,11.043894 7.3891859,6.4345258 c -1.004161,-1.004161 -2.631905,-1.004161 -3.636065,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8980"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8986">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8984" />
<path
style="fill:#6699cc;fill-opacity:1;stroke-width:1.28574"
id="path8978"
d="m 20.246879,17.565474 c 1.004161,-1.002875 1.004161,-2.63319 0,-3.636065 L 12.000161,5.6814052 3.7534421,13.929409 c -0.502723,0.501438 -0.753442,1.159735 -0.753442,1.818033 0,0.658297 0.250719,1.316595 0.753442,1.818032 1.004161,1.004161 2.631904,1.004161 3.636065,0 l 4.6106539,-4.609368 4.610653,4.609368 c 1.004161,1.004161 2.631905,1.004161 3.636065,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -42,6 +42,7 @@ doc_h2 = mixed_heading2.svg
doc_h3 = mixed_heading3.svg doc_h3 = mixed_heading3.svg
doc_h4 = mixed_heading4.svg doc_h4 = mixed_heading4.svg
done = typ_input-checked.svg done = typ_input-checked.svg
down = typ_chevron-down.svg
edit = typ_pencil.svg edit = typ_pencil.svg
forward = typ_chevron-right.svg forward = typ_chevron-right.svg
hash = typ_hash.svg hash = typ_hash.svg
@@ -74,3 +75,4 @@ status_stats = typ_chart-bar-grey.svg
status_time = typ_stopwatch-grey.svg status_time = typ_stopwatch-grey.svg
sticky-off = typ_pin-outline.svg sticky-off = typ_pin-outline.svg
sticky-on = typ_pin.svg sticky-on = typ_pin.svg
up = typ_chevron-up.svg
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8980"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8986">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8984" />
<path
style="fill:#4271ae;fill-opacity:1;stroke-width:1.28574"
id="path8978"
d="m 3.7531209,6.4345258 c -1.004161,1.002875 -1.004161,2.63319 0,3.6360652 l 8.2467181,8.248004 8.246719,-8.248004 C 20.749281,9.5691528 21,8.9108558 21,8.2525578 c 0,-0.658297 -0.250719,-1.316595 -0.753442,-1.818032 -1.004161,-1.004161 -2.631904,-1.004161 -3.636065,0 L 11.999839,11.043894 7.3891859,6.4345258 c -1.004161,-1.004161 -2.631905,-1.004161 -3.636065,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8980"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8986">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8984" />
<path
style="fill:#4271ae;fill-opacity:1;stroke-width:1.28574"
id="path8978"
d="m 20.246879,17.565474 c 1.004161,-1.002875 1.004161,-2.63319 0,-3.636065 L 12.000161,5.6814052 3.7534421,13.929409 c -0.502723,0.501438 -0.753442,1.159735 -0.753442,1.818033 0,0.658297 0.250719,1.316595 0.753442,1.818032 1.004161,1.004161 2.631904,1.004161 3.636065,0 l 4.6106539,-4.609368 4.610653,4.609368 c 1.004161,1.004161 2.631905,1.004161 3.636065,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+26 -36
View File
@@ -1072,46 +1072,14 @@ class NWProject():
return True return True
def setStatusColours(self, newCols, delCols): def setStatusColours(self, newCols, delCols):
"""Update the list of novel file status flags. Also iterate """Update the list of novel file status flags.
through the project and replace keys that have been renamed.
""" """
if not (newCols or delCols): return self._setStatusImport(newCols, delCols, self.statusItems)
return False
for entry in newCols:
key = entry.get("key", None)
name = entry.get("name", "")
cols = entry.get("cols", (100, 100, 100))
if name:
self.statusItems.write(key, name, cols)
for key in delCols:
self.statusItems.remove(key)
self.setProjectChanged(True)
return True
def setImportColours(self, newCols, delCols): def setImportColours(self, newCols, delCols):
"""Update the list of note file importance flags. Also iterate """Update the list of note file importance flags.
through the project and replace keys that have been renamed.
""" """
if not (newCols or delCols): return self._setStatusImport(newCols, delCols, self.importItems)
return False
for entry in newCols:
key = entry.get("key", None)
name = entry.get("name", "")
cols = entry.get("cols", (100, 100, 100))
if name:
self.importItems.write(key, name, cols)
for key in delCols:
self.importItems.remove(key)
self.setProjectChanged(True)
return True
def setAutoReplace(self, autoReplace): def setAutoReplace(self, autoReplace):
"""Update the auto-replace dictionary. """Update the auto-replace dictionary.
@@ -1251,6 +1219,28 @@ class NWProject():
# Internal Functions # Internal Functions
## ##
def _setStatusImport(self, new, delete, target):
"""Update the list of novel file status or importance flags, and
delete those that have been requested deleted.
"""
if not (new or delete):
return False
order = []
for entry in new:
key = entry.get("key", None)
name = entry.get("name", "")
cols = entry.get("cols", (100, 100, 100))
if name:
order.append(target.write(key, name, cols))
for key in delete:
target.remove(key)
target.reorder(order)
return True
def _loadProjectLocalisation(self): def _loadProjectLocalisation(self):
"""Load the language data for the current project language. """Load the language data for the current project language.
""" """
+22
View File
@@ -167,6 +167,28 @@ class NWStatus():
else: else:
return self._defaultIcon return self._defaultIcon
def reorder(self, order):
"""Reorder the items according to list.
"""
if len(order) != len(self._store):
logger.error("Length mismatch between new and old order")
return False
if order == list(self._store.keys()):
return False
store = {}
for key in order:
if key in self._store:
store[key] = self._store[key]
else:
logger.error("Unknown key '%s' in order", key)
return False
self._store = store
return True
def resetCounts(self): def resetCounts(self):
"""Clear the counts of references to the status entries. """Clear the counts of references to the status entries.
""" """
+45 -19
View File
@@ -306,6 +306,12 @@ class GuiProjectEditStatus(QWidget):
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.delButton.clicked.connect(self._delItem) self.delButton.clicked.connect(self._delItem)
self.upButton = QPushButton(self.theTheme.getIcon("up"), "")
self.upButton.clicked.connect(lambda: self._moveItem(-1))
self.dnButton = QPushButton(self.theTheme.getIcon("down"), "")
self.dnButton.clicked.connect(lambda: self._moveItem(1))
# Edit Form # Edit Form
# ========= # =========
@@ -315,7 +321,7 @@ class GuiProjectEditStatus(QWidget):
self.editName.setPlaceholderText(self.tr("Select item to edit")) self.editName.setPlaceholderText(self.tr("Select item to edit"))
self.colPixmap = QPixmap(self.iPx, self.iPx) self.colPixmap = QPixmap(self.iPx, self.iPx)
self.colPixmap.fill(QColor(120, 120, 120)) self.colPixmap.fill(QColor(100, 100, 100))
self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour")) self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour"))
self.colButton.setIconSize(self.colPixmap.rect().size()) self.colButton.setIconSize(self.colPixmap.rect().size())
self.colButton.clicked.connect(self._selectColour) self.colButton.clicked.connect(self._selectColour)
@@ -329,6 +335,8 @@ class GuiProjectEditStatus(QWidget):
self.listControls = QVBoxLayout() self.listControls = QVBoxLayout()
self.listControls.addWidget(self.addButton) self.listControls.addWidget(self.addButton)
self.listControls.addWidget(self.delButton) self.listControls.addWidget(self.delButton)
self.listControls.addWidget(self.upButton)
self.listControls.addWidget(self.dnButton)
self.listControls.addStretch(1) self.listControls.addStretch(1)
self.editBox = QHBoxLayout() self.editBox = QHBoxLayout()
@@ -390,7 +398,7 @@ class GuiProjectEditStatus(QWidget):
def _newItem(self): def _newItem(self):
"""Create a new status item. """Create a new status item.
""" """
newItem = self._addItem(None, self.tr("New Item"), (0, 0, 0), 0) newItem = self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
newItem.setBackground(self.COL_LABEL, QBrush(QColor(0, 255, 0, 70))) newItem.setBackground(self.COL_LABEL, QBrush(QColor(0, 255, 0, 70)))
newItem.setBackground(self.COL_USAGE, QBrush(QColor(0, 255, 0, 70))) newItem.setBackground(self.COL_USAGE, QBrush(QColor(0, 255, 0, 70)))
self.colChanged = True self.colChanged = True
@@ -445,23 +453,47 @@ class GuiProjectEditStatus(QWidget):
return item return item
def _moveItem(self, step):
"""Move and item up or down step.
"""
selItem = self._getSelectedItem()
if selItem is None:
return
tIndex = self.listBox.indexOfTopLevelItem(selItem)
nChild = self.listBox.topLevelItemCount()
nIndex = tIndex + step
if nIndex < 0 or nIndex >= nChild:
return
cItem = self.listBox.takeTopLevelItem(tIndex)
self.listBox.insertTopLevelItem(nIndex, cItem)
self.listBox.clearSelection()
cItem.setSelected(True)
self.colChanged = True
return
def _selectedItem(self): def _selectedItem(self):
"""Extract the info of a selected item and populate the settings """Extract the info of a selected item and populate the settings
boxes and button. boxes and button.
""" """
selItem = self._getSelectedItem() selItem = self._getSelectedItem()
if selItem is not None: if selItem is None:
cols = selItem.data(self.COL_LABEL, self.COL_ROLE) return
name = selItem.text(self.COL_LABEL)
pixmap = QPixmap(self.iPx, self.iPx) cols = selItem.data(self.COL_LABEL, self.COL_ROLE)
pixmap.fill(QColor(*cols)) name = selItem.text(self.COL_LABEL)
self.selColour = QColor(*cols)
self.editName.setText(name) pixmap = QPixmap(self.iPx, self.iPx)
self.colButton.setIcon(QIcon(pixmap)) pixmap.fill(QColor(*cols))
self.editName.setEnabled(True) self.selColour = QColor(*cols)
self.editName.selectAll() self.editName.setText(name)
self.editName.setFocus() self.colButton.setIcon(QIcon(pixmap))
self.editName.setEnabled(True)
self.editName.selectAll()
self.editName.setFocus()
return return
@@ -477,12 +509,6 @@ class GuiProjectEditStatus(QWidget):
return selItem[0] return selItem[0]
return None return None
def _rowsMoved(self):
"""A row has been moved, so set the changed flag.
"""
self.colChanged = True
return
def _usageString(self, nUse): def _usageString(self, nUse):
"""Generate usage string. """Generate usage string.
""" """
+1 -1
View File
@@ -469,7 +469,7 @@ class GuiIcons:
"delete", "close", "done", "clear", "save", "add", "remove", "delete", "close", "done", "clear", "save", "add", "remove",
"search", "search_replace", "edit", "check", "cross", "hash", "search", "search_replace", "edit", "check", "cross", "hash",
"maximise", "minimise", "refresh", "reference", "backward", "maximise", "minimise", "refresh", "reference", "backward",
"forward", "settings", "forward", "settings", "up", "down",
# Switches # Switches
"sticky-on", "sticky-off", "sticky-on", "sticky-off",
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 16:54:15"> <novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 18:15:12">
<project> <project>
<name>Project Name</name> <name>Project Name</name>
<title>Project Title</title> <title>Project Title</title>
@@ -42,7 +42,7 @@
<entry key="i466852" count="0" red="100" green="100" blue="100">New</entry> <entry key="i466852" count="0" red="100" green="100" blue="100">New</entry>
<entry key="i3eb13b" count="0" red="200" green="50" blue="0">Minor</entry> <entry key="i3eb13b" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i392456" count="0" red="200" green="150" blue="0">Major</entry> <entry key="i392456" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i1a3d1f" count="0" red="0" green="0" blue="0">Final</entry> <entry key="i1a3d1f" count="0" red="100" green="100" blue="100">Final</entry>
</importance> </importance>
</settings> </settings>
<content count="8"> <content count="8">
+32
View File
@@ -229,6 +229,38 @@ def testCoreStatus_Entries(constData):
assert theStatus.count(constData.statusKeys[2]) == 0 assert theStatus.count(constData.statusKeys[2]) == 0
assert theStatus.count(constData.statusKeys[3]) == 0 assert theStatus.count(constData.statusKeys[3]) == 0
# Reorder
# =======
cOrder = list(theStatus.keys())
assert cOrder == constData.statusKeys
# Wrong length
assert theStatus.reorder([]) is False
# No change
assert theStatus.reorder(cOrder) is False
# Actual reaorder
nOrder = [
constData.statusKeys[0],
constData.statusKeys[2],
constData.statusKeys[1],
constData.statusKeys[3],
]
assert theStatus.reorder(nOrder) is True
assert list(theStatus.keys()) == nOrder
# Add an unknown key
wOrder = nOrder.copy()
wOrder[3] = theStatus._newKey()
assert theStatus.reorder(wOrder) is False
assert list(theStatus.keys()) == nOrder
# Put it back
assert theStatus.reorder(cOrder) is True
assert list(theStatus.keys()) == cOrder
# Default # Default
# ======= # =======
+26 -13
View File
@@ -28,9 +28,7 @@ from tools import cmpFiles, getGuiItem
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
QDialog, QAction, QMessageBox, QColorDialog, QTreeWidgetItem
)
from novelwriter.dialogs import GuiProjectSettings from novelwriter.dialogs import GuiProjectSettings
@@ -118,16 +116,6 @@ def testDlgProjSettings_Dialog(
assert projEdit.tabStatus.getNewList() == ([], []) assert projEdit.tabStatus.getNewList() == ([], [])
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4 assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
# Fake drag'n'drop should change changed status
projEdit.tabStatus._rowsMoved()
assert projEdit.tabStatus.colChanged is True
projEdit.tabStatus.colChanged = False
projEdit.tabStatus.listBox.clearSelection()
assert projEdit.tabStatus._getSelectedItem() is None
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
assert isinstance(projEdit.tabStatus._getSelectedItem(), QTreeWidgetItem)
# Can't delete the first item (it's in use) # Can't delete the first item (it's in use)
projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True) projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
@@ -178,6 +166,31 @@ def testDlgProjSettings_Dialog(
] ]
) )
# Move items
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus._moveItem(1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], None, constData.statusKeys[3]
]
projEdit.tabStatus._moveItem(1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
]
# Importance Tab # Importance Tab
# ============== # ==============