From 86b78256f74135afbff41f8f83907428fac1f406 Mon Sep 17 00:00:00 2001 From: Matthias Metelka <62722460+kleinerpirat@users.noreply.github.com> Date: Sun, 4 Sep 2022 11:34:03 +0200 Subject: [PATCH] Create stylesheet overrides for various Qt widgets Including QPushButton, QComboBox, QSpinBox, QLineEdit, QListWidget, QTabWidget, QTreeWidget, QToolTip, QTableView, QScrollBar and sub-widgets. --- qt/aqt/data/qt/icons/BUILD.bazel | 5 +- qt/aqt/stylesheets.py | 326 +++++++++++++++++++++++++++++++ qt/aqt/theme.py | 193 +++--------------- sass/_button-mixins.scss | 38 +--- sass/_fusion-vars.scss | 12 +- sass/_vars.scss | 20 +- sass/buttons.scss | 1 - ts/editor/legacy.scss | 14 +- 8 files changed, 395 insertions(+), 214 deletions(-) create mode 100644 qt/aqt/stylesheets.py diff --git a/qt/aqt/data/qt/icons/BUILD.bazel b/qt/aqt/data/qt/icons/BUILD.bazel index b3f131a17..70593727c 100644 --- a/qt/aqt/data/qt/icons/BUILD.bazel +++ b/qt/aqt/data/qt/icons/BUILD.bazel @@ -34,8 +34,9 @@ copy_mdi_icons( "tag-outline.svg", "tag-off-outline.svg", - # QComboBox arrow - "chevron-down.svg", + # QComboBox and QSpinBox arrows + "menu-up.svg", + "menu-down.svg", ], ) diff --git a/qt/aqt/stylesheets.py b/qt/aqt/stylesheets.py new file mode 100644 index 000000000..d61fdb456 --- /dev/null +++ b/qt/aqt/stylesheets.py @@ -0,0 +1,326 @@ +from aqt import colors +from aqt.theme import ThemeManager + + +def general_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QFrame {{ + background: none; +}} +QPushButton, +QComboBox, +QSpinBox, +QLineEdit, +QListWidget, +QTreeWidget, +QListView {{ + border: 1px solid {tm.color(colors.BUTTON_BORDER)}; + border-radius: 5px; +}} +QComboBox, +QLineEdit {{ + padding: 2px; +}} +QComboBox:focus, +QComboBox:on, +QLineEdit:focus {{ + border-color: {tm.color(colors.FOCUS_BORDER)}; +}} +QPushButton {{ + margin-top: 1px; +}} +QPushButton, +QComboBox, +QSpinBox {{ + padding: 2px 6px; +}} +QToolTip {{ + background: {tm.color(colors.TOOLTIP_BG)}; +}} + """ + return buf + + +def button_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QPushButton:pressed, +QHeaderView::section:pressed, +QSpinBox::up-button:pressed, +QSpinBox::down-button:pressed {{ + border: 1px solid {tm.color(colors.BUTTON_PRESSED_BORDER)}; + background: qlineargradient( + spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 {tm.color(colors.BUTTON_PRESSED_SHADOW)}, + stop:0.1 {tm.color(colors.BUTTON_GRADIENT_START)}, + stop:0.9 {tm.color(colors.BUTTON_GRADIENT_END)} + stop:1 {tm.color(colors.BUTTON_PRESSED_SHADOW)}, + ); +}} +QPushButton, +QHeaderView::section, +QSpinBox::up-button, +QSpinBox::down-button, +QComboBox:!editable, +QComboBox::drop-down:editable {{ + background: qlineargradient( + spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 {tm.color(colors.BUTTON_GRADIENT_START)}, + stop:1 {tm.color(colors.BUTTON_GRADIENT_END)} + ); + +}} +QPushButton:hover, +QHeaderView::section:hover, +QSpinBox::up-button:hover, +QSpinBox::down-button:hover, +QComboBox:!editable:hover, +QComboBox::drop-down:editable:hover {{ + background: qlineargradient( + spread:pad, x1:0.5, y1:0, x2:0.5, y2:1.25, + stop:0 {tm.color(colors.BUTTON_HOVER_GRADIENT_START)}, + stop:1 {tm.color(colors.BUTTON_HOVER_GRADIENT_END)} + ); +}} + """ + return buf + + +def combobox_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QComboBox:on {{ + border-bottom: none; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +}} +QComboBox::drop-down {{ + border: 0px; + subcontrol-origin: padding; + padding: 4px; + subcontrol-position: top right; + width: 18px; +}} +QComboBox::drop-down:editable {{ + margin: 1px; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + border-left: 1px solid {tm.color(colors.BUTTON_BORDER)}; +}} +QComboBox::down-arrow {{ + image: url(icons:menu-down.svg); +}} + """ + return buf + + +def tabwidget_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QTabWidget {{ + border-radius: 5px; + border: none; + background: none; +}} +QTabWidget::pane {{ + border: 1px solid {tm.color(colors.FRAME_BG)}; + border-radius: 5px; + background: {tm.color(colors.FRAME_BG)}; +}} +QTabWidget::tab-bar {{ + alignment: center; +}} +QTabBar::tab {{ + background: none; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + padding: 5px 10px; + margin-bottom: 0px; +}} +QTabBar::tab:!selected:hover, +QTabBar::tab:selected {{ + background: {tm.color(colors.FRAME_BG)}; +}} +QTabBar::tab:selected {{ + margin-bottom: -1px; +}} +QTabBar::tab:!selected {{ + margin-top: 5px; + background: {tm.color(colors.WINDOW_BG)}; +}} +QTabBar::tab {{ + min-width: 8ex; + padding: 5px 10px 5px 10px; +}} +QTabBar::tab:selected {{ + border-bottom-color: none; +}} +QTabBar::tab:bottom:selected {{ + border-top-color: none; +}} +QTabBar::tab:previous-selected {{ + border-top-left-radius: 0; +}} +QTabBar::tab:next-selected {{ + border-top-right-radius: 0; +}} + """ + return buf + + +def table_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QTableView {{ + margin: -1px -1px 1px -1px; + background: none; + border: 2px solid {tm.color(colors.WINDOW_BG)}; + border-radius: 5px; +}} +QHeaderView::section {{ + border: 2px solid {tm.color(colors.WINDOW_BG)}; + margin: -1px; +}} +QHeaderView::section:first {{ + border-top: 2px solid {tm.color(colors.WINDOW_BG)}; + border-left: 2px solid {tm.color(colors.WINDOW_BG)}; + border-top-left-radius: 5px; +}} +QHeaderView::section:!first {{ + border-left: none; +}} +QHeaderView::section:last {{ + border-top: 2px solid {tm.color(colors.WINDOW_BG)}; + border-right: 2px solid {tm.color(colors.WINDOW_BG)}; + border-top-right-radius: 5px; +}} +QHeaderView::section:next-selected {{ + border-right: none; +}} +QHeaderView::section:previous-selected {{ + border-left: none; +}} +QHeaderView::section:only-one {{ + border-left: 2px solid {tm.color(colors.WINDOW_BG)}; + border-top: 2px solid {tm.color(colors.WINDOW_BG)}; + border-right: 2px solid {tm.color(colors.WINDOW_BG)}; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +}} +QHeaderView::up-arrow, +QHeaderView::down-arrow {{ + width: 20px; + height: 20px; +}} +QHeaderView::up-arrow {{ + image: url(icons:menu-up.svg); +}} +QHeaderView::down-arrow {{ + image: url(icons:menu-down.svg); +}} + """ + return buf + + +def spinbox_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QSpinBox::up-button, +QSpinBox::down-button {{ + subcontrol-origin: border; + width: 16px; + border: 1px solid {tm.color(colors.BUTTON_BORDER)}; +}} +QSpinBox::up-button {{ + margin-bottom: -1px; + subcontrol-position: top right; + border-top-right-radius: 5px; +}} +QSpinBox::down-button {{ + margin-top: -1px; + subcontrol-position: bottom right; + border-bottom-right-radius: 5px; +}} +QSpinBox::up-arrow {{ + image: url(icons:menu-up.svg); +}} +QSpinBox::down-arrow {{ + image: url(icons:menu-down.svg); +}} +QSpinBox::up-arrow, +QSpinBox::down-arrow, +QSpinBox::up-arrow:pressed, +QSpinBox::down-arrow:pressed {{ + width: 16px; + height: 16px; +}} +QSpinBox::up-arrow:hover, +QSpinBox::down-arrow:hover {{ + width: 20px; + height: 20px; +}} + """ + return buf + + +def scrollbar_styles(tm: ThemeManager, buf: str) -> str: + buf += f""" +QAbstractScrollArea::corner {{ + background: none; + border: none; +}} +QScrollBar {{ + background-color: {tm.color(colors.WINDOW_BG)}; +}} +QScrollBar::handle {{ + border-radius: 5px; + background-color: {tm.color(colors.SCROLLBAR_BG)}; +}} +QScrollBar::handle:hover {{ + background-color: {tm.color(colors.SCROLLBAR_HOVER_BG)}; +}} +QScrollBar:horizontal {{ + height: 12px; +}} +QScrollBar::handle:horizontal {{ + min-width: 50px; +}} +QScrollBar:vertical {{ + width: 12px; +}} +QScrollBar::handle:vertical {{ + min-height: 50px; +}} +QScrollBar::add-line {{ + border: none; + background: none; +}} +QScrollBar::sub-line {{ + border: none; + background: none; +}} + """ + return buf + + +def win10_styles(tm: ThemeManager, buf: str) -> str: + + # day mode is missing a bottom border; background must be + # also set for border to apply + buf += f""" +QMenuBar {{ + border-bottom: 1px solid {tm.color(colors.BORDER)}; + background: {tm.color(colors.WINDOW_BG) if tm.night_mode else "white"}; +}} + """ + + # qt bug? setting the above changes the browser sidebar + # to white as well, so set it back + buf += f""" +QTreeWidget {{ + background: {tm.color(colors.WINDOW_BG)}; +}} + """ + + if tm.night_mode: + buf += """ +QToolTip { + border: 0; +} + """ + return buf diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py index 23d6a403d..0a9e6a64f 100644 --- a/qt/aqt/theme.py +++ b/qt/aqt/theme.py @@ -186,180 +186,37 @@ class ThemeManager: gui_hooks.theme_did_change() def _apply_style(self, app: QApplication) -> None: + from aqt.stylesheets import ( + button_styles, + combobox_styles, + general_styles, + scrollbar_styles, + spinbox_styles, + table_styles, + tabwidget_styles, + win10_styles, + ) + buf = "" + if not is_mac: - buf += f""" -QComboBox, -QLineEdit {{ - border: 1px solid {self.color(colors.BORDER)}; - border-radius: 5px; - padding: 2px; -}} -QComboBox:focus, -QLineEdit:focus {{ - border: 1px solid {self.color(colors.FOCUS_BORDER)}; -}} -QComboBox:on {{ - border-bottom: none; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -}} -QComboBox::drop-down {{ - border: 0px; /* This resets the arrow styles */ - subcontrol-origin: padding; - padding: 4px; - subcontrol-position: top right; - width: 18px; -}} -QComboBox::down-arrow {{ - image: url(icons:chevron-down.svg); -}} -QPushButton {{ - padding: 2px; - border-radius: 5px; - border: 1px solid #cfcbcb; - background: #fff; -}} -QPushButton:hover {{ - background: #f9f8f8; - border-color: #afabac; -}} -QToolTip {{ - border-radius: 5px; - border: 1px solid {self.color(colors.BORDER)}; -}} -QFrame {{ - border: none; - background: none; -}} -QLabel {{ - background: transparent; - border-color: transparent; -}} -QToolTip {{ - background: {self.color(colors.TOOLTIP_BG)}; -}} -QTabWidget {{ - border-radius: 5px; - background: none; - border: none; -}} - -QTabWidget::pane {{ - border: 1px solid {self.color(colors.BORDER)}; - border-top-left-radius: 0px; - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; - border-bottom-left-radius: 5px; - background: {self.color(colors.FRAME_BG)}; -}} - -QTabBar::tab {{ - background: none; - border: 1px solid {self.color(colors.BORDER)}; - border-radius: 0px; - padding: 5px 10px; - margin-bottom: 0px; -}} -QTabBar::tab:selected {{ - background: {self.color(colors.FRAME_BG)}; - margin-bottom: -1px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -}} - - -QTabWidget::tab-bar {{ - top: 1px; -}} - - -QTabBar::tab:!selected:hover {{ - background: {self.color(colors.FRAME_BG)}; -}} - -QTabBar::tab:!selected {{ - margin-top: 5px; - background: {self.color(colors.WINDOW_BG)}; -}} - - -QTabBar::tab {{ - min-width: 8ex; - margin-right: -1px; - padding: 5px 10px 5px 10px; -}} - -QTabBar::tab:selected {{ - border-bottom-color: none; -}} - -QTabBar::tab:bottom:selected {{ - border-top-color: none; -}} - -QTabBar::tab:first, -QTabBar::tab:only-one {{ - border-top-left-radius: 5px; -}} -QTabBar::tab:last, -QTabBar::tab:only-one {{ - border-top-right-radius: 5px; -}} - """ + buf += "".join( + [ + general_styles(self, buf), + button_styles(self, buf), + combobox_styles(self, buf), + tabwidget_styles(self, buf), + table_styles(self, buf), + spinbox_styles(self, buf), + scrollbar_styles(self, buf), + ] + ) if is_win and platform.release() == "10": - # day mode is missing a bottom border; background must be - # also set for border to apply - buf += f""" -QMenuBar {{ - border-bottom: 1px solid {self.color(colors.BORDER)}; - background: {self.color(colors.WINDOW_BG) if self.night_mode else "white"}; -}} -""" - # qt bug? setting the above changes the browser sidebar - # to white as well, so set it back - buf += f""" -QTreeWidget {{ - background: {self.color(colors.WINDOW_BG)}; -}} - """ - - if self.night_mode: - buf += """ -QToolTip { - border: 0; -} - """ + buf += win10_styles(self, buf) if not self.macos_dark_mode(): - buf += """ -QScrollBar {{ background-color: {}; }} -QScrollBar::handle {{ background-color: {}; border-radius: 5px; }} - -QScrollBar:horizontal {{ height: 12px; }} -QScrollBar::handle:horizontal {{ min-width: 50px; }} - -QScrollBar:vertical {{ width: 12px; }} -QScrollBar::handle:vertical {{ min-height: 50px; }} - -QScrollBar::add-line {{ - border: none; - background: none; -}} - -QScrollBar::sub-line {{ - border: none; - background: none; -}} - -QTabWidget {{ background-color: {}; }} -""".format( - self.color(colors.WINDOW_BG), - # fushion-button-hover-bg - "#656565", - self.color(colors.WINDOW_BG), - ) + buf += scrollbar_styles(self, buf) # allow addons to modify the styling buf = gui_hooks.style_did_init(buf) diff --git a/sass/_button-mixins.scss b/sass/_button-mixins.scss index aa7f74961..6dc2e186f 100644 --- a/sass/_button-mixins.scss +++ b/sass/_button-mixins.scss @@ -1,6 +1,7 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ @use "fusion-vars"; +@use "sass:color"; @import "bootstrap/scss/functions"; @import "bootstrap/scss/variables"; @@ -26,12 +27,7 @@ $btn-base-color-day: white; border-color: var(--medium-border) !important; } -@mixin btn-day( - $with-hover: true, - $with-active: true, - $with-disabled: true, - $with-margin: true -) { +@mixin btn-day($with-hover: true, $with-active: true, $with-disabled: true) { .btn-day { @include btn-day-base; @content ($btn-base-color-day); @@ -39,7 +35,7 @@ $btn-base-color-day: white; @if ($with-hover) { &:hover, &.hover { - background-color: darken($btn-base-color-day, 8%); + background-color: color.scale($btn-base-color-day, $lightness: -20%); } } @@ -60,10 +56,6 @@ $btn-base-color-day: white; box-shadow: none !important; } } - - @if ($with-margin) { - margin-left: -1px; - } } } @@ -72,23 +64,17 @@ $btn-base-color-night: fusion-vars.$button-border; @mixin btn-night-base { color: var(--text-fg); background: linear-gradient( - 0deg, + 180deg, fusion-vars.$button-gradient-start 0%, fusion-vars.$button-gradient-end 100% ); } -@mixin btn-night( - $with-hover: true, - $with-active: true, - $with-disabled: true, - $with-margin: true -) { +@mixin btn-night($with-hover: true, $with-active: true, $with-disabled: true) { .btn-night { @include btn-night-base; @content ($btn-base-color-night); - box-shadow: 0 0 3px fusion-vars.$button-outline; border: 1px solid fusion-vars.$button-border; -webkit-appearance: none; @@ -96,11 +82,11 @@ $btn-base-color-night: fusion-vars.$button-border; &:hover, &.hover { background: linear-gradient( - 0deg, - lighten(fusion-vars.$button-gradient-start, 8%) 0%, - lighten(fusion-vars.$button-gradient-end, 8%) 100% + 180deg, + color.scale(fusion-vars.$button-gradient-start, $lightness: 20%) 0%, + color.scale(fusion-vars.$button-gradient-end, $lightness: 20%) 100% ); - border-color: lighten(fusion-vars.$button-border, 8%); + border-color: color.scale(fusion-vars.$button-border, $lightness: 20%); } } @@ -108,7 +94,7 @@ $btn-base-color-night: fusion-vars.$button-border; &:active, &.active { @include impressed-shadow(0.35); - border-color: darken($btn-base-color-night, 8%); + border-color: color.scale($btn-base-color-night, $lightness: -20%); } &:active.active { @@ -124,10 +110,6 @@ $btn-base-color-night: fusion-vars.$button-border; border-color: $btn-base-color-night !important; } } - - @if ($with-margin) { - margin-left: 1px; - } } } diff --git a/sass/_fusion-vars.scss b/sass/_fusion-vars.scss index 9b91fb531..e19f28bb4 100644 --- a/sass/_fusion-vars.scss +++ b/sass/_fusion-vars.scss @@ -1,7 +1,7 @@ /* night-mode-specific colours */ -$button-gradient-start: #555555; -$button-gradient-end: #656565; -$button-outline: #222222; -$button-hover-bg: #656565; -$button-border: #646464; -$button-base-bg: #454545; +$button-gradient-start: #3f3f3f; +$button-gradient-end: #363636; +$button-outline: #212121; +$button-hover-bg: #404040; +$button-border: #212121; +$button-base-bg: #343434; diff --git a/sass/_vars.scss b/sass/_vars.scss index 1a2efce62..93032dae9 100644 --- a/sass/_vars.scss +++ b/sass/_vars.scss @@ -39,13 +39,22 @@ --focus-border: #0969da; --focus-shadow: rgba(9 105 218 / 0.3); --code-bg: white; + --button-gradient-start: white; + --button-gradient-end: #f6f6f6; + --button-hover-gradient-start: #f4f4f4; + --button-hover-gradient-end: #efefef; + --button-pressed-shadow: #555; + --button-border: #aaa; + --button-pressed-border: #8f8f8f; + --scrollbar-bg: #d8d8d8; + --scrollbar-hover-bg: #d0d0d0; } :root[class*="night-mode"] { --text-fg: white; --window-bg: #2f2f31; --frame-bg: #3a3a3a; - --border: #777; + --border: #4a4a4a; --medium-border: #444; --faint-border: #29292b; --link: #77ccff; @@ -79,5 +88,14 @@ --focus-border: #316dca; --focus-shadow: #194380; --code-bg: #272822; + --button-gradient-start: #3f3f3f; + --button-gradient-end: #363636; + --button-hover-gradient-start: #434343; + --button-hover-gradient-end: #404040; + --button-pressed-shadow: #232323; + --button-border: #2f2f31; + --button-pressed-border: #0a0a0a; + --scrollbar-bg: #434343; + --scrollbar-hover-bg: #4e4e4e; color-scheme: dark; } diff --git a/sass/buttons.scss b/sass/buttons.scss index 413bdc7e4..e79b02310 100644 --- a/sass/buttons.scss +++ b/sass/buttons.scss @@ -42,7 +42,6 @@ fusion-vars.$button-gradient-start 0%, fusion-vars.$button-gradient-end 100% ); - box-shadow: 0 0 3px fusion-vars.$button-outline; border: 1px solid fusion-vars.$button-border; border-radius: 5px; diff --git a/ts/editor/legacy.scss b/ts/editor/legacy.scss index 1bf6d591b..817256259 100644 --- a/ts/editor/legacy.scss +++ b/ts/editor/legacy.scss @@ -1,6 +1,7 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ @use "fusion-vars"; +@use "sass:color"; @use "sass/button-mixins" as button; $btn-base-color-day: white; @@ -17,10 +18,8 @@ $padding: 2px; background-color: $btn-base-color-day; border: 1px solid var(--medium-border); @include button.btn-border-radius; - margin-left: -1px; - &:hover { - background-color: darken($btn-base-color-day, 8%); + background-color: color.scale($btn-base-color-day, $lightness: -20%); } &:active, @@ -29,7 +28,6 @@ $padding: 2px; } .nightMode & { - box-shadow: 0 0 3px fusion-vars.$button-outline; border: 1px solid fusion-vars.$button-border; -webkit-appearance: none; background: linear-gradient( @@ -43,15 +41,15 @@ $padding: 2px; &:hover { background: linear-gradient( 0deg, - lighten(fusion-vars.$button-gradient-start, 8%) 0%, - lighten(fusion-vars.$button-gradient-end, 8%) 100% + color.scale(fusion-vars.$button-gradient-start, $lightness: 20%) 0%, + color.scale(fusion-vars.$button-gradient-end, $lightness: 20%) 100% ); - border-color: lighten(fusion-vars.$button-border, 8%); + border-color: color.scale(fusion-vars.$button-border, $lightness: 20%); } &:active { @include button.impressed-shadow(0.35); - border-color: darken($btn-base-color-night, 8%); + border-color: color.scale($btn-base-color-night, $lightness: -20%); } } }