diff --git a/novelwriter/enum.py b/novelwriter/enum.py index 7ea60294..1bb85120 100644 --- a/novelwriter/enum.py +++ b/novelwriter/enum.py @@ -247,3 +247,18 @@ class nwStatusShape(Enum): BLOCK_2 = 17 BLOCK_3 = 18 BLOCK_4 = 19 + + +class nwStandardButton(Enum): + """Enum: Standard Dialog Buttons.""" + + OK = 0 + CANCEL = 1 + YES = 2 + NO = 3 + OPEN = 4 + CLOSE = 5 + BROWSE = 6 + LIST = 7 + NEW = 8 + CREATE = 9 diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py index bb256ff1..7e245d21 100644 --- a/novelwriter/extensions/modified.py +++ b/novelwriter/extensions/modified.py @@ -31,8 +31,8 @@ from typing import TYPE_CHECKING from PyQt6.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot from PyQt6.QtWidgets import ( - QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox, - QToolButton, QTreeView, QWidget + QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QPushButton, + QSpinBox, QToolButton, QTreeView, QWidget ) from novelwriter import CONFIG, SHARED @@ -199,6 +199,36 @@ class NDoubleSpinBox(QDoubleSpinBox): event.ignore() +class NPushButton(QPushButton): + """Custom: Modified QPushButton. + + A quicker way to create a push button using the app theme. + """ + + def __init__( + self, parent: QWidget, text: str, iconSize: QSize, + icon: str | None = None, color: str | None = None + ) -> None: + super().__init__(parent=parent) + self.setText(text) + self.setIconSize(iconSize) + self._icon = icon + self._color = color + if icon: + self.refreshIcon() + + def setThemeIcon(self, icon: str, color: str | None = None) -> None: + """Set an icon from the current theme.""" + self._icon = icon + self._color = color + self.refreshIcon() + + def refreshIcon(self) -> None: + """Reload the theme icon.""" + if self._icon: + self.setIcon(SHARED.theme.getIcon(self._icon, self._color)) + + class NIconToolButton(QToolButton): """Custom: Modified QToolButton. diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index f3a026f1..9b3c10d4 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -31,19 +31,20 @@ from dataclasses import dataclass from math import ceil from typing import TYPE_CHECKING, Final -from PyQt6.QtCore import QSize, Qt +from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication, QSize, Qt from PyQt6.QtGui import ( QColor, QFont, QFontDatabase, QFontMetrics, QGuiApplication, QIcon, QPainter, QPainterPath, QPalette, QPixmap ) -from PyQt6.QtWidgets import QApplication +from PyQt6.QtWidgets import QApplication, QWidget from novelwriter import CONFIG from novelwriter.common import checkInt, minmax from novelwriter.config import DEF_GUI_DARK, DEF_GUI_LIGHT, DEF_ICONS, DEF_TREECOL from novelwriter.constants import nwLabels -from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwTheme +from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwStandardButton, nwTheme from novelwriter.error import logException +from novelwriter.extensions.modified import NPushButton from novelwriter.types import QtBlack, QtHexArgb, QtPaintAntiAlias, QtTransparent if TYPE_CHECKING: @@ -55,6 +56,19 @@ STYLES_FLAT_TABS = "flatTabWidget" STYLES_MIN_TOOLBUTTON = "minimalToolButton" STYLES_BIG_TOOLBUTTON = "bigToolButton" +STANDARD_BUTTONS = { + nwStandardButton.OK: (QT_TRANSLATE_NOOP("Button", "OK"), "bullet-on", "blue"), + nwStandardButton.CANCEL: (QT_TRANSLATE_NOOP("Button", "Cancel"), "cancel", "red"), + nwStandardButton.YES: (QT_TRANSLATE_NOOP("Button", "Yes"), "bullet-on", "green"), + nwStandardButton.NO: (QT_TRANSLATE_NOOP("Button", "No"), "bullet-on", "red"), + nwStandardButton.OPEN: (QT_TRANSLATE_NOOP("Button", "Open"), "open", "blue"), + nwStandardButton.CLOSE: (QT_TRANSLATE_NOOP("Button", "Close"), "close", "default"), + nwStandardButton.BROWSE: (QT_TRANSLATE_NOOP("Button", "Browse"), "browse", "yellow"), + nwStandardButton.LIST: (QT_TRANSLATE_NOOP("Button", "List"), "list", "blue"), + nwStandardButton.NEW: (QT_TRANSLATE_NOOP("Button", "New"), "add", "green"), + nwStandardButton.CREATE: (QT_TRANSLATE_NOOP("Button", "Create"), "star", "yellow"), +} + @dataclass class ThemeEntry: @@ -120,9 +134,9 @@ class GuiTheme: "_qColors", "_styleSheets", "_svgColors", "_syntaxList", "accentCol", "baseButtonHeight", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText", "fadedText", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration", - "getHeaderDecorationNarrow", "getIcon", "getItemIcon", "getPixmap", "getToggleIcon", - "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall", "helpText", - "iconCache", "isDarkTheme", "syntaxTheme", "textNHeight", "textNWidth", + "getHeaderDecorationNarrow", "getIcon", "getItemIcon", "getPixmap", "getStandardButton", + "getToggleIcon", "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall", + "helpText", "iconCache", "isDarkTheme", "syntaxTheme", "textNHeight", "textNWidth", ) def __init__(self) -> None: @@ -153,6 +167,7 @@ class GuiTheme: self.getItemIcon = self.iconCache.getItemIcon self.getToggleIcon = self.iconCache.getToggleIcon self.getDecoration = self.iconCache.getDecoration + self.getStandardButton = self.iconCache.getStandardButton self.getHeaderDecoration = self.iconCache.getHeaderDecoration self.getHeaderDecorationNarrow = self.iconCache.getHeaderDecorationNarrow @@ -808,6 +823,14 @@ class GuiIcons: w, h = size return self.getIcon(name, color, w, h).pixmap(w, h, QIcon.Mode.Normal) + def getStandardButton(self, button: nwStandardButton, parent: QWidget) -> NPushButton: + """Return a standard button with icon and text.""" + text, icon, color = STANDARD_BUTTONS.get(button, ("", "", "")) + return NPushButton( + parent, QCoreApplication.translate("Button", text), + self._theme.buttonIconSize, icon, color + ) + def getDecoration(self, name: str, w: int | None = None, h: int | None = None) -> QPixmap: """Load graphical decoration element based on the decoration map or the icon map. This function always returns a QPixmap. diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py index 69d2e090..041a0031 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -35,15 +35,15 @@ from PyQt6.QtCore import ( from PyQt6.QtGui import QAction, QCloseEvent, QFont, QPainter, QPaintEvent, QPen, QShortcut from PyQt6.QtWidgets import ( QApplication, QFileDialog, QFormLayout, QHBoxLayout, QLabel, QLineEdit, - QListView, QMenu, QPushButton, QScrollArea, QStackedWidget, - QStyledItemDelegate, QStyleOptionViewItem, QVBoxLayout, QWidget + QListView, QMenu, QScrollArea, QStackedWidget, QStyledItemDelegate, + QStyleOptionViewItem, QVBoxLayout, QWidget ) from novelwriter import CONFIG, SHARED from novelwriter.common import formatInt, makeFileNameSafe, qtAddAction, qtLambda from novelwriter.constants import nwFiles from novelwriter.core.coretools import ProjectBuilder -from novelwriter.enum import nwItemClass +from novelwriter.enum import nwItemClass, nwStandardButton from novelwriter.extensions.configlayout import NWrappedWidgetBox from novelwriter.extensions.modified import NDialog, NIconToolButton, NSpinBox from novelwriter.extensions.switch import NSwitch @@ -75,8 +75,6 @@ class GuiWelcome(NDialog): self.setMinimumHeight(450) self.resize(*CONFIG.welcomeWinSize) - btnIconSize = SHARED.theme.buttonIconSize - # Elements # ======== @@ -104,34 +102,22 @@ class GuiWelcome(NDialog): # Buttons # ======= - self.btnList = QPushButton(self.tr("List"), self) - self.btnList.setIcon(SHARED.theme.getIcon("list", "blue")) - self.btnList.setIconSize(btnIconSize) + self.btnList = SHARED.theme.getStandardButton(nwStandardButton.LIST, self) self.btnList.clicked.connect(self._showOpenProjectPage) - self.btnNew = QPushButton(self.tr("New"), self) - self.btnNew.setIcon(SHARED.theme.getIcon("add", "green")) - self.btnNew.setIconSize(btnIconSize) + self.btnNew = SHARED.theme.getStandardButton(nwStandardButton.NEW, self) self.btnNew.clicked.connect(self._showNewProjectPage) - self.btnBrowse = QPushButton(self.tr("Browse"), self) - self.btnBrowse.setIcon(SHARED.theme.getIcon("browse", "yellow")) - self.btnBrowse.setIconSize(btnIconSize) + self.btnBrowse = SHARED.theme.getStandardButton(nwStandardButton.BROWSE, self) self.btnBrowse.clicked.connect(self._browseForProject) - self.btnCancel = QPushButton(self.tr("Cancel"), self) - self.btnCancel.setIcon(SHARED.theme.getIcon("cancel", "red")) - self.btnCancel.setIconSize(btnIconSize) + self.btnCancel = SHARED.theme.getStandardButton(nwStandardButton.CANCEL, self) self.btnCancel.clicked.connect(qtLambda(self.close)) - self.btnCreate = QPushButton(self.tr("Create"), self) - self.btnCreate.setIcon(SHARED.theme.getIcon("star", "yellow")) - self.btnCreate.setIconSize(btnIconSize) + self.btnCreate = SHARED.theme.getStandardButton(nwStandardButton.CREATE, self) self.btnCreate.clicked.connect(self.tabNew.createNewProject) - self.btnOpen = QPushButton(self.tr("Open"), self) - self.btnOpen.setIcon(SHARED.theme.getIcon("open", "blue")) - self.btnOpen.setIconSize(btnIconSize) + self.btnOpen = SHARED.theme.getStandardButton(nwStandardButton.OPEN, self) self.btnOpen.clicked.connect(self._openSelectedItem) self.btnBox = QHBoxLayout()