From f754c803523ad7380719e5e055166cb802d8e1be Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:55:42 +0200 Subject: [PATCH] Redesign main sidebar --- novelwriter/gui/dochighlight.py | 2 +- novelwriter/gui/sidebar.py | 164 +++++++++++++++++++------------- novelwriter/guimain.py | 40 ++++---- 3 files changed, 122 insertions(+), 84 deletions(-) diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py index 0876a913..7cd67364 100644 --- a/novelwriter/gui/dochighlight.py +++ b/novelwriter/gui/dochighlight.py @@ -329,7 +329,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.setFormat(0, 4, self._hStyles["header4h"]) self.setFormat(4, len(text), self._hStyles["header4"]) - if text.startswith("#! "): # Title + elif text.startswith("#! "): # Title self.setFormat(0, 2, self._hStyles["header1h"]) self.setFormat(2, len(text), self._hStyles["header1"]) diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index 266d735a..d1e258d1 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -25,99 +25,108 @@ from __future__ import annotations import logging -from PyQt5.QtCore import Qt, QSize, pyqtSignal -from PyQt5.QtWidgets import ( - QToolBar, QWidget, QSizePolicy, QAction, QMenu, QToolButton -) +from typing import TYPE_CHECKING + +from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal +from PyQt5.QtGui import QPalette +from PyQt5.QtWidgets import QMenu, QToolButton, QVBoxLayout, QWidget from novelwriter import CONFIG, SHARED from novelwriter.enum import nwView +if TYPE_CHECKING: # pragma: no cover + from novelwriter.guimain import GuiMain + logger = logging.getLogger(__name__) -class GuiSideBar(QToolBar): +class PopRightMenu(QMenu): + + def event(self, event: QEvent): + """Overload the show event and move the menu popup location.""" + if event.type() == QEvent.Show: + parent = self.parent() + if isinstance(parent, QWidget): + offset = QPoint(parent.width(), parent.height() - self.height()) + self.move(parent.mapToGlobal(offset)) + return super(PopRightMenu, self).event(event) + + +class GuiSideBar(QWidget): viewChangeRequested = pyqtSignal(nwView) - def __init__(self, mainGui): + def __init__(self, mainGui: GuiMain) -> None: super().__init__(parent=mainGui) logger.debug("Create: GuiSideBar") self.mainGui = mainGui - # Style - iPx = CONFIG.pxInt(22) - mPx = CONFIG.pxInt(60) - - lblFont = SHARED.theme.guiFont - lblFont.setPointSizeF(0.65*SHARED.theme.fontPointSize) - - self.setMovable(False) - self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) - self.setIconSize(QSize(iPx, iPx)) - self.setMaximumWidth(mPx) + iPx = CONFIG.pxInt(24) + iconSize = QSize(iPx, iPx) self.setContentsMargins(0, 0, 0, 0) - stretch = QWidget(self) - stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - # Actions - self.aProject = QAction(self.tr("Project"), self) - self.aProject.setFont(lblFont) - self.aProject.setToolTip(self.tr("Project Tree View")) - self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT)) + self.tbProject = QToolButton(self) + self.tbProject.setToolTip(self.tr("Project Tree View")) + self.tbProject.setIconSize(iconSize) + self.tbProject.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT)) - self.aNovel = QAction(self.tr("Novel"), self) - self.aNovel.setFont(lblFont) - self.aNovel.setToolTip(self.tr("Novel Tree View")) - self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) + self.tbNovel = QToolButton(self) + self.tbNovel.setToolTip(self.tr("Novel Tree View")) + self.tbNovel.setIconSize(iconSize) + self.tbNovel.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) - self.aOutline = QAction(self.tr("Outline"), self) - self.aOutline.setFont(lblFont) - self.aOutline.setToolTip(self.tr("Novel Outline View")) - self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) + self.tbOutline = QToolButton(self) + self.tbOutline.setToolTip(self.tr("Novel Outline View")) + self.tbOutline.setIconSize(iconSize) + self.tbOutline.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) - self.aBuild = QAction(self.tr("Build"), self) - self.aBuild.setFont(lblFont) - self.aBuild.setToolTip(self.tr("Build Manuscript")) - self.aBuild.triggered.connect(lambda: self.mainGui.showBuildManuscriptDialog()) + self.tbBuild = QToolButton(self) + self.tbBuild.setToolTip(self.tr("Build Manuscript")) + self.tbBuild.setIconSize(iconSize) + self.tbBuild.clicked.connect(self.mainGui.showBuildManuscriptDialog) - self.aDetails = QAction(self.tr("Details"), self) - self.aDetails.setFont(lblFont) - self.aDetails.setToolTip(self.tr("Project Details")) - self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog()) + self.tbDetails = QToolButton(self) + self.tbDetails.setToolTip(self.tr("Project Details")) + self.tbDetails.setIconSize(iconSize) + self.tbDetails.clicked.connect(self.mainGui.showProjectDetailsDialog) - self.aStats = QAction(self.tr("Stats"), self) - self.aStats.setFont(lblFont) - self.aStats.setToolTip(self.tr("Writing Statistics")) - self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog()) + self.tbStats = QToolButton(self) + self.tbStats.setToolTip(self.tr("Writing Statistics")) + self.tbStats.setIconSize(iconSize) + self.tbStats.clicked.connect(self.mainGui.showWritingStatsDialog) # Settings Menu - self.mSettings = QMenu() + self.tbSettings = QToolButton(self) + self.tbSettings.setToolTip(self.tr("Settings")) + self.tbSettings.setIconSize(iconSize) + self.tbSettings.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly) + self.mSettings = PopRightMenu(self.tbSettings) self.mSettings.addAction(self.mainGui.mainMenu.aEditWordList) self.mSettings.addAction(self.mainGui.mainMenu.aProjectSettings) self.mSettings.addSeparator() self.mSettings.addAction(self.mainGui.mainMenu.aPreferences) - self.tbSettings = QToolButton(self) - self.tbSettings.setFont(lblFont) - self.tbSettings.setText(self.tr("Settings")) self.tbSettings.setMenu(self.mSettings) - self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.tbSettings.setPopupMode(QToolButton.InstantPopup) # Assemble - self.addAction(self.aProject) - self.addAction(self.aNovel) - self.addAction(self.aOutline) - self.addAction(self.aBuild) - self.addWidget(stretch) - self.addAction(self.aDetails) - self.addAction(self.aStats) - self.addWidget(self.tbSettings) + self.outerBox = QVBoxLayout() + self.outerBox.addWidget(self.tbProject) + self.outerBox.addWidget(self.tbNovel) + self.outerBox.addWidget(self.tbOutline) + self.outerBox.addWidget(self.tbBuild) + self.outerBox.addStretch(1) + self.outerBox.addWidget(self.tbDetails) + self.outerBox.addWidget(self.tbStats) + self.outerBox.addWidget(self.tbSettings) + self.outerBox.setContentsMargins(0, 0, CONFIG.pxInt(2), 0) + self.outerBox.setSpacing(CONFIG.pxInt(4)) + + self.setLayout(self.outerBox) self.updateTheme() @@ -125,18 +134,39 @@ class GuiSideBar(QToolBar): return - def updateTheme(self): - """Initialise GUI elements that depend on specific settings. - """ - self.setStyleSheet("QToolBar {border: 0px;}") + def updateTheme(self) -> None: + """Initialise GUI elements that depend on specific settings.""" + qPalette = self.palette() + qPalette.setBrush(QPalette.Window, qPalette.base()) + self.setPalette(qPalette) + + fadeCol = qPalette.text().color() + buttonStyle = ( + "QToolButton {{padding: {0}px; border: none; background: transparent;}} " + "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}" + ).format(CONFIG.pxInt(4), fadeCol.red(), fadeCol.green(), fadeCol.blue()) + buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}" + + self.tbProject.setIcon(SHARED.theme.getIcon("view_editor")) + self.tbProject.setStyleSheet(buttonStyle) + + self.tbNovel.setIcon(SHARED.theme.getIcon("view_novel")) + self.tbNovel.setStyleSheet(buttonStyle) + + self.tbOutline.setIcon(SHARED.theme.getIcon("view_outline")) + self.tbOutline.setStyleSheet(buttonStyle) + + self.tbBuild.setIcon(SHARED.theme.getIcon("view_build")) + self.tbBuild.setStyleSheet(buttonStyle) + + self.tbDetails.setIcon(SHARED.theme.getIcon("proj_details")) + self.tbDetails.setStyleSheet(buttonStyle) + + self.tbStats.setIcon(SHARED.theme.getIcon("proj_stats")) + self.tbStats.setStyleSheet(buttonStyle) - self.aProject.setIcon(SHARED.theme.getIcon("view_editor")) - self.aNovel.setIcon(SHARED.theme.getIcon("view_novel")) - self.aOutline.setIcon(SHARED.theme.getIcon("view_outline")) - self.aBuild.setIcon(SHARED.theme.getIcon("view_build")) - self.aDetails.setIcon(SHARED.theme.getIcon("proj_details")) - self.aStats.setIcon(SHARED.theme.getIcon("proj_stats")) self.tbSettings.setIcon(SHARED.theme.getIcon("settings")) + self.tbSettings.setStyleSheet(buttonStyleMenu) return diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 96806648..299ae5ab 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -33,8 +33,8 @@ from datetime import datetime from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence from PyQt5.QtWidgets import ( - qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter, - QStackedWidget, QVBoxLayout, QWidget + QDialog, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, + QSplitter, QStackedWidget, QVBoxLayout, QWidget, qApp ) from novelwriter import CONFIG, SHARED, __hexversion__ @@ -95,7 +95,7 @@ class GuiMain(QMainWindow): logger.debug("Create: GUI") self.setObjectName("GuiMain") - self.threadPool = QThreadPool() + self.threadPool = QThreadPool(self) # System Info # =========== @@ -146,14 +146,14 @@ class GuiMain(QMainWindow): self.viewsBar = GuiSideBar(self) # Project Tree Stack - self.projStack = QStackedWidget() + self.projStack = QStackedWidget(self) self.projStack.addWidget(self.projView) self.projStack.addWidget(self.novelView) self.projStack.currentChanged.connect(self._projStackChanged) # Project Tree View - self.treePane = QWidget() - self.treeBox = QVBoxLayout() + self.treePane = QWidget(self) + self.treeBox = QVBoxLayout(self) self.treeBox.setContentsMargins(0, 0, 0, 0) self.treeBox.setSpacing(mPx) self.treeBox.addWidget(self.projStack) @@ -161,7 +161,7 @@ class GuiMain(QMainWindow): self.treePane.setLayout(self.treeBox) # Splitter : Document Viewer / Document Meta - self.splitView = QSplitter(Qt.Vertical) + self.splitView = QSplitter(Qt.Vertical, self) self.splitView.addWidget(self.docViewer) self.splitView.addWidget(self.viewMeta) self.splitView.setHandleWidth(hWd) @@ -169,7 +169,7 @@ class GuiMain(QMainWindow): self.splitView.setSizes(CONFIG.viewPanePos) # Splitter : Document Editor / Document Viewer - self.splitDocs = QSplitter(Qt.Horizontal) + self.splitDocs = QSplitter(Qt.Horizontal, self) self.splitDocs.addWidget(self.docEditor) self.splitDocs.addWidget(self.splitView) self.splitDocs.setOpaqueResize(False) @@ -185,7 +185,7 @@ class GuiMain(QMainWindow): self.splitMain.setSizes(CONFIG.mainPanePos) # Main Stack : Editor / Outline - self.mainStack = QStackedWidget() + self.mainStack = QStackedWidget(self) self.mainStack.addWidget(self.splitMain) self.mainStack.addWidget(self.outlineView) self.mainStack.currentChanged.connect(self._mainStackChanged) @@ -222,11 +222,19 @@ class GuiMain(QMainWindow): # Initialise the Project Tree self.rebuildTrees() - # Set Main Window Elements + # Assemble Main Window Elements + self.mainBox = QHBoxLayout(self) + self.mainBox.addWidget(self.viewsBar) + self.mainBox.addWidget(self.mainStack) + self.mainBox.setContentsMargins(0, 0, 0, 0) + self.mainBox.setSpacing(0) + + self.mainWidget = QWidget(self) + self.mainWidget.setLayout(self.mainBox) + self.setMenuBar(self.mainMenu) - self.setCentralWidget(self.mainStack) + self.setCentralWidget(self.mainWidget) self.setStatusBar(self.mainStatus) - self.addToolBar(Qt.LeftToolBarArea, self.viewsBar) self.setContextMenuPolicy(Qt.NoContextMenu) # Issue #1147 # Connect Signals @@ -269,15 +277,15 @@ class GuiMain(QMainWindow): # ======================= # Set Up Auto-Save Project Timer - self.asProjTimer = QTimer() + self.asProjTimer = QTimer(self) self.asProjTimer.timeout.connect(self._autoSaveProject) # Set Up Auto-Save Document Timer - self.asDocTimer = QTimer() + self.asDocTimer = QTimer(self) self.asDocTimer.timeout.connect(self._autoSaveDocument) # Main Clock - self.mainTimer = QTimer() + self.mainTimer = QTimer(self) self.mainTimer.setInterval(1000) self.mainTimer.timeout.connect(self._timeTick) self.mainTimer.start() @@ -1022,7 +1030,7 @@ class GuiMain(QMainWindow): def showAboutQtDialog(self) -> None: """Show the about dialog for Qt.""" - msgBox = QMessageBox() + msgBox = QMessageBox(self) msgBox.aboutQt(self, "About Qt") return