diff --git a/novelwriter/assets/icons/typicons_dark/icons.conf b/novelwriter/assets/icons/typicons_dark/icons.conf index 521e9ee8..fa8b8619 100644 --- a/novelwriter/assets/icons/typicons_dark/icons.conf +++ b/novelwriter/assets/icons/typicons_dark/icons.conf @@ -21,7 +21,7 @@ backward = typ_chevron-left.svg bookmark = typ_bookmark.svg bullet-off = typ_media-record-outline.svg bullet-on = typ_media-record.svg -check = typ_tick.svg +checked = mixed_input-checked.svg close = typ_times.svg cls_archive = typ_delete.svg cls_character = typ_user.svg @@ -41,13 +41,14 @@ forward = typ_chevron-right.svg maximise = typ_arrow-maximise.svg menu = typ_th-menu.svg minimise = typ_arrow-minimise.svg +noncheckable = mixed_input-none.svg proj_chapter = mixed_document-chapter.svg proj_details = typ_th-list-grey.svg proj_document = typ_document-text.svg proj_folder = typ_folder.svg proj_note = mixed_document-note.svg -proj_section = mixed_document-section.svg proj_scene = mixed_document-scene.svg +proj_section = mixed_document-section.svg proj_stats = typ_chart-bar-grey.svg proj_title = mixed_document-title.svg reference = typ_at.svg @@ -70,6 +71,7 @@ status_stats = typ_chart-bar-grey.svg status_time = typ_stopwatch-grey.svg sticky-off = typ_pin-outline.svg sticky-on = typ_pin.svg +unchecked = mixed_input-unchecked.svg up = typ_chevron-up.svg view_build = typ_export.svg view_editor = mixed_edit.svg diff --git a/novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg b/novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg new file mode 100644 index 00000000..a044fbaa --- /dev/null +++ b/novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_dark/mixed_input-none.svg b/novelwriter/assets/icons/typicons_dark/mixed_input-none.svg new file mode 100644 index 00000000..105e1d8f --- /dev/null +++ b/novelwriter/assets/icons/typicons_dark/mixed_input-none.svg @@ -0,0 +1,44 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg b/novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg new file mode 100644 index 00000000..b5ee8b37 --- /dev/null +++ b/novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_dark/typ_tick.svg b/novelwriter/assets/icons/typicons_dark/typ_tick.svg deleted file mode 100644 index 84383114..00000000 --- a/novelwriter/assets/icons/typicons_dark/typ_tick.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/novelwriter/assets/icons/typicons_light/icons.conf b/novelwriter/assets/icons/typicons_light/icons.conf index 1b6b1348..5212b685 100644 --- a/novelwriter/assets/icons/typicons_light/icons.conf +++ b/novelwriter/assets/icons/typicons_light/icons.conf @@ -21,7 +21,7 @@ backward = typ_chevron-left.svg bookmark = typ_bookmark.svg bullet-off = typ_media-record-outline.svg bullet-on = typ_media-record.svg -check = typ_tick.svg +checked = mixed_input-checked.svg close = typ_times.svg cls_archive = typ_delete.svg cls_character = typ_user.svg @@ -41,13 +41,14 @@ forward = typ_chevron-right.svg maximise = typ_arrow-maximise.svg menu = typ_th-menu.svg minimise = typ_arrow-minimise.svg +noncheckable = mixed_input-none.svg proj_chapter = mixed_document-chapter.svg proj_details = typ_th-list-grey.svg proj_document = typ_document-text.svg proj_folder = typ_folder.svg proj_note = mixed_document-note.svg -proj_section = mixed_document-section.svg proj_scene = mixed_document-scene.svg +proj_section = mixed_document-section.svg proj_stats = typ_chart-bar-grey.svg proj_title = mixed_document-title.svg reference = typ_at.svg @@ -70,6 +71,7 @@ status_stats = typ_chart-bar-grey.svg status_time = typ_stopwatch-grey.svg sticky-off = typ_pin-outline.svg sticky-on = typ_pin.svg +unchecked = mixed_input-unchecked.svg up = typ_chevron-up.svg view_build = typ_export.svg view_editor = mixed_edit.svg diff --git a/novelwriter/assets/icons/typicons_light/mixed_input-checked.svg b/novelwriter/assets/icons/typicons_light/mixed_input-checked.svg new file mode 100644 index 00000000..8e42a8b6 --- /dev/null +++ b/novelwriter/assets/icons/typicons_light/mixed_input-checked.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_light/mixed_input-none.svg b/novelwriter/assets/icons/typicons_light/mixed_input-none.svg new file mode 100644 index 00000000..f80dd7ff --- /dev/null +++ b/novelwriter/assets/icons/typicons_light/mixed_input-none.svg @@ -0,0 +1,44 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg b/novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg new file mode 100644 index 00000000..0bd5f6dc --- /dev/null +++ b/novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/novelwriter/assets/icons/typicons_light/typ_tick.svg b/novelwriter/assets/icons/typicons_light/typ_tick.svg deleted file mode 100644 index 1c24677a..00000000 --- a/novelwriter/assets/icons/typicons_light/typ_tick.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py index a379f911..c466461e 100644 --- a/novelwriter/core/status.py +++ b/novelwriter/core/status.py @@ -30,7 +30,8 @@ import novelwriter from lxml import etree -from PyQt5.QtGui import QIcon, QPixmap, QColor +from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor +from PyQt5.QtCore import QRectF, Qt from novelwriter.common import checkInt, minmax, simplified @@ -49,10 +50,15 @@ class NWStatus: self._reverse = {} self._default = None - self._iconSize = novelwriter.CONFIG.pxInt(32) - pixmap = QPixmap(self._iconSize, self._iconSize) - pixmap.fill(QColor(100, 100, 100)) - self._defaultIcon = QIcon(pixmap) + self._iPX = novelwriter.CONFIG.pxInt(24) + + pA = novelwriter.CONFIG.pxInt(2) + pB = novelwriter.CONFIG.pxInt(20) + pR = float(novelwriter.CONFIG.pxInt(4)) + self._iconPath = QPainterPath() + self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR) + + self._defaultIcon = self._createIcon([100, 100, 100]) if self._type == self.STATUS: self._prefix = "s" @@ -63,19 +69,16 @@ class NWStatus: return - def write(self, key, name, cols, count=None): + def write(self, key, name, col, count=None): """Add or update a status entry. If the key is invalid, a new key is generated. """ if not self._isKey(key): key = self._newKey() - if not isinstance(cols, tuple): - cols = (100, 100, 100) - if len(cols) != 3: - cols = (100, 100, 100) - - pixmap = QPixmap(self._iconSize, self._iconSize) - pixmap.fill(QColor(*cols)) + if not isinstance(col, tuple): + col = (100, 100, 100) + if len(col) != 3: + col = (100, 100, 100) name = simplified(name) if count is None: @@ -83,8 +86,8 @@ class NWStatus: self._store[key] = { "name": name, - "icon": QIcon(pixmap), - "cols": cols, + "icon": self._createIcon(col), + "cols": col, "count": count, } self._reverse[name] = key @@ -267,6 +270,19 @@ class NWStatus: return False return True + def _createIcon(self, col): + """Generate an icon for a status label. + """ + pixmap = QPixmap(self._iPX, self._iPX) + pixmap.fill(Qt.transparent) + + painter = QPainter(pixmap) + painter.setRenderHint(QPainter.Antialiasing) + painter.fillPath(self._iconPath, QColor(*col)) + painter.end() + + return QIcon(pixmap) + ## # Iterator Bits ## diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 19d8af38..0055340a 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -417,11 +417,15 @@ class GuiProjectToolBar(QWidget): class GuiProjectTree(QTreeWidget): + C_DATA = 0 C_NAME = 0 C_COUNT = 1 C_ACTIVE = 2 C_STATUS = 3 + D_HANDLE = Qt.UserRole + D_WORDS = Qt.UserRole + 1 + def __init__(self, projView): super().__init__(parent=projView) @@ -947,10 +951,13 @@ class GuiProjectTree(QTreeWidget): trItem.setToolTip(self.C_STATUS, itemStatus) if nwItem.isFileType(): - iconName = "check" if nwItem.isActive else "cross" + iconName = "checked" if nwItem.isActive else "unchecked" toolTip = self._lblActive if nwItem.isActive else self._lblInactive - trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) trItem.setToolTip(self.C_ACTIVE, toolTip) + else: + iconName = "noncheckable" + + trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) if self.mainConf.emphLabels and nwItem.isDocumentLayout(): trFont = trItem.font(self.C_NAME) @@ -974,10 +981,10 @@ class GuiProjectTree(QTreeWidget): if countChildren: for i in range(tItem.childCount()): - newCount += int(tItem.child(i).data(self.C_COUNT, Qt.UserRole)) + newCount += int(tItem.child(i).data(self.C_DATA, self.D_WORDS)) tItem.setText(self.C_COUNT, f"{newCount:n}") - tItem.setData(self.C_COUNT, Qt.UserRole, int(newCount)) + tItem.setData(self.C_DATA, self.D_WORDS, int(newCount)) pItem = tItem.parent() if pItem is None: @@ -986,8 +993,8 @@ class GuiProjectTree(QTreeWidget): pCount = 0 pHandle = None for i in range(pItem.childCount()): - pCount += int(pItem.child(i).data(self.C_COUNT, Qt.UserRole)) - pHandle = pItem.data(self.C_NAME, Qt.UserRole) + pCount += int(pItem.child(i).data(self.C_DATA, self.D_WORDS)) + pHandle = pItem.data(self.C_DATA, self.D_HANDLE) if pHandle: if self.theProject.tree.checkType(pHandle, nwItemType.FILE): @@ -1038,8 +1045,8 @@ class GuiProjectTree(QTreeWidget): return False dstIndex = min(max(0, dstIndex), dstItem.childCount()) - sHandle = srcItem.data(self.C_NAME, Qt.UserRole) - dHandle = dstItem.data(self.C_NAME, Qt.UserRole) + sHandle = srcItem.data(self.C_DATA, self.D_HANDLE) + dHandle = dstItem.data(self.C_DATA, self.D_HANDLE) logger.debug("Moving item '%s' back to '%s', index %d", sHandle, dHandle, dstIndex) wCount = self._getItemWordCount(sHandle) @@ -1063,7 +1070,7 @@ class GuiProjectTree(QTreeWidget): """ selItem = self.selectedItems() if selItem: - return selItem[0].data(self.C_NAME, Qt.UserRole) + return selItem[0].data(self.C_DATA, self.D_HANDLE) return None @@ -1149,7 +1156,7 @@ class GuiProjectTree(QTreeWidget): hasChild = False selItem = self.itemAt(clickPos) if isinstance(selItem, QTreeWidgetItem): - tHandle = selItem.data(self.C_NAME, Qt.UserRole) + tHandle = selItem.data(self.C_DATA, self.D_HANDLE) tItem = self.theProject.tree[tHandle] hasChild = selItem.childCount() > 0 @@ -1308,7 +1315,7 @@ class GuiProjectTree(QTreeWidget): if not isinstance(selItem, QTreeWidgetItem): return - tHandle = selItem.data(self.C_NAME, Qt.UserRole) + tHandle = selItem.data(self.C_DATA, self.D_HANDLE) tItem = self.theProject.tree[tHandle] if tItem is None: return @@ -1367,7 +1374,7 @@ class GuiProjectTree(QTreeWidget): # Update item parent handle in the project, make sure meta data # is updated accordingly, and update word count - pHandle = trItemP.data(self.C_NAME, Qt.UserRole) + pHandle = trItemP.data(self.C_DATA, self.D_HANDLE) nwItemS.setParent(pHandle) trItemP.setExpanded(True) logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle) @@ -1397,7 +1404,7 @@ class GuiProjectTree(QTreeWidget): tItem = self._getTreeItem(tHandle) if tItem is None: return 0 - return int(tItem.data(self.C_COUNT, Qt.UserRole)) + return int(tItem.data(self.C_DATA, self.D_WORDS)) def _getTreeItem(self, tHandle): """Return the QTreeWidgetItem of a given item handle. @@ -1617,7 +1624,7 @@ class GuiProjectTree(QTreeWidget): """This is a recursive function returning all items in a tree starting at a given QTreeWidgetItem. """ - tHandle = tItem.data(self.C_NAME, Qt.UserRole) + tHandle = tItem.data(self.C_DATA, self.D_HANDLE) cCount = tItem.childCount() # Update tree-related meta data @@ -1650,8 +1657,8 @@ class GuiProjectTree(QTreeWidget): newItem.setTextAlignment(self.C_ACTIVE, Qt.AlignLeft) newItem.setTextAlignment(self.C_STATUS, Qt.AlignLeft) - newItem.setData(self.C_NAME, Qt.UserRole, tHandle) - newItem.setData(self.C_COUNT, Qt.UserRole, 0) + newItem.setData(self.C_DATA, self.D_HANDLE, tHandle) + newItem.setData(self.C_DATA, self.D_WORDS, 0) self._treeMap[tHandle] = newItem if pHandle is None: diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index 93c26440..570b06b2 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -448,9 +448,9 @@ class GuiIcons: "view_editor", "view_novel", "view_outline", # General Button Icons - "add", "backward", "bookmark", "check", "close", "cross", "down", "edit", "forward", - "maximise", "menu", "minimise", "reference", "refresh", "remove", "search_replace", - "search", "settings", "up", + "add", "backward", "bookmark", "checked", "close", "cross", "down", "edit", "forward", + "maximise", "menu", "minimise", "noncheckable", "reference", "refresh", "remove", + "search_replace", "search", "settings", "unchecked", "up", # Switches "sticky-on", "sticky-off", diff --git a/setup.py b/setup.py index a0bb9b03..675a29c8 100755 --- a/setup.py +++ b/setup.py @@ -131,6 +131,7 @@ def makeCheckSum(sumFile, cwd=None): except Exception as exc: print("Could not generate sha256 file") print(str(exc)) + return "" return shaFile