From 802cf6bd90867276cf302b8399dcca8db146fa3d Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Wed, 15 Oct 2025 14:11:22 -0400 Subject: [PATCH] frontend: Create event filter for widget state styles --- frontend/data/themes/Yami.obt | 52 ++++++------- frontend/data/themes/Yami_Acri.ovt | 5 ++ frontend/data/themes/Yami_Light.ovt | 30 +++++++ shared/qt/idian/CMakeLists.txt | 3 + shared/qt/idian/components/CheckBox.cpp | 5 +- shared/qt/idian/components/ComboBox.cpp | 5 +- shared/qt/idian/components/ToggleSwitch.cpp | 2 + shared/qt/idian/include/Idian/CheckBox.hpp | 13 ---- shared/qt/idian/include/Idian/ComboBox.hpp | 12 --- shared/qt/idian/include/Idian/Row.hpp | 44 ++--------- .../idian/include/Idian/StateEventFilter.cpp | 78 +++++++++++++++++++ .../idian/include/Idian/StateEventFilter.hpp | 25 ++++++ .../qt/idian/include/Idian/ToggleSwitch.hpp | 12 --- shared/qt/idian/include/Idian/Utils.cpp | 10 +++ shared/qt/idian/include/Idian/Utils.hpp | 28 +++---- shared/qt/idian/widgets/Row.cpp | 14 ++-- 16 files changed, 208 insertions(+), 130 deletions(-) create mode 100644 shared/qt/idian/include/Idian/StateEventFilter.cpp create mode 100644 shared/qt/idian/include/Idian/StateEventFilter.hpp create mode 100644 shared/qt/idian/include/Idian/Utils.cpp diff --git a/frontend/data/themes/Yami.obt b/frontend/data/themes/Yami.obt index 752dc069e..a710f77ad 100644 --- a/frontend/data/themes/Yami.obt +++ b/frontend/data/themes/Yami.obt @@ -1760,7 +1760,6 @@ QTableView::indicator:unchecked { QCheckBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:hover, QTableView::indicator:unchecked:hover { - border: none; image: url(theme:Yami/checkbox_unchecked_focus.svg); } @@ -2321,11 +2320,11 @@ idian--ToggleSwitch { border: var(--highlight_width) solid transparent; } -idian--ToggleSwitch:hover { +idian--ToggleSwitch.hover { border-color: var(--grey4); } -idian--ToggleSwitch:checked:hover { +idian--ToggleSwitch.checked.hover { border-color: var(--white1); } @@ -2333,13 +2332,13 @@ idian--ToggleSwitch.keyFocus { border-color: var(--highlight_color); } -idian--Row idian--ToggleSwitch:hover, +idian--Row idian--ToggleSwitch.hover, idian--Row.hover > idian--ToggleSwitch.row-buddy { border-color: var(--grey1); } -idian--Row idian--ToggleSwitch:checked:hover, -idian--Row.hover idian--ToggleSwitch.row-buddy:checked { +idian--Row idian--ToggleSwitch.hover.checked, +idian--Row.hover > idian--ToggleSwitch.checked.row-buddy { border-color: var(--white1); } @@ -2358,7 +2357,7 @@ idian--Row QComboBox:focus { border-color: transparent; } -idian--Row QComboBox:hover { +idian--Row QComboBox.hover { border-color: var(--grey1); } @@ -2383,7 +2382,7 @@ idian--Row QComboBox QAbstractItemView::item { padding: var(--padding_base) var(--padding_large); } -idian--Row QComboBox QAbstractItemView::item:hover, +idian--Row QComboBox QAbstractItemView.hover::item, idian--Row QComboBox QAbstractItemView::item:selected { background-color: var(--list_item_bg_selected); padding: var(--padding_base) var(--padding_large); @@ -2406,32 +2405,30 @@ idian--Row idian--CheckBox { outline: none; } -idian--Row idian--CheckBox::indicator, -idian--Row idian--CheckBox::indicator:unchecked:hover { +idian--CheckBox::indicator { border: var(--highlight_width) solid transparent; border-radius: var(--border_radius); } -idian--Row.hover > idian--CheckBox.row-buddy::indicator, -idian--Row > idian--CheckBox::indicator:unchecked:hover, -idian--Row > idian--CheckBox::indicator:hover { - border-color: var(--grey1); -} - -idian--Row.hover > idian--CheckBox.row-buddy::indicator:unchecked, -idian--CheckBox.keyFocus::indicator:unchecked { +idian--CheckBox.keyFocus::indicator, +idian--Row.hover > idian--CheckBox.row-buddy::indicator { image: url(theme:Yami/checkbox_unchecked_focus.svg); } -idian--Row idian--CheckBox.keyFocus::indicator, -idian--Row.hover > idian--CheckBox::indicator { +idian--CheckBox.keyFocus.checked::indicator, +idian--Row.hover > idian--CheckBox.row-buddy.checked::indicator { image: url(theme:Yami/checkbox_checked_focus.svg); } -idian--Row idian--CheckBox.keyFocus::indicator, -idian--Row idian--CheckBox.keyFocus::indicator:unchecked, -idian--Row idian--CheckBox.keyFocus::indicator:hover, -idian--Row idian--CheckBox.keyFocus::indicator:unchecked:hover { +idian--CheckBox.hover::indicator, +idian--CheckBox.checked.hover::indicator, +idian--Row.hover > idian--CheckBox.row-buddy::indicator +idian--Row.hover > idian--CheckBox.row-buddy.checked::indicator { + border-color: var(--grey1); +} + +idian--CheckBox.keyFocus::indicator, +idian--CheckBox.keyFocus.checked::indicator { border-color: var(--highlight_color); } @@ -2503,16 +2500,15 @@ idian--RowFrame.hover .btn-frame { background: var(--grey4); } -idian--RowFrame.hover idian--Row, -idian--RowFrame.hover idian--Row.hover { +idian--RowFrame.hover idian--Row { background: var(--grey4); - border: 2px solid var(--grey1); + border: var(--highlight_width) solid var(--grey1); border-right: none; } idian--RowFrame.hover .row-buddy { background: var(--grey4); - border: 2px solid var(--grey1); + border: var(--highlight_width) solid var(--grey1); border-left: none; } diff --git a/frontend/data/themes/Yami_Acri.ovt b/frontend/data/themes/Yami_Acri.ovt index 6283a02e2..8d054a4d0 100644 --- a/frontend/data/themes/Yami_Acri.ovt +++ b/frontend/data/themes/Yami_Acri.ovt @@ -229,3 +229,8 @@ QCalendarWidget QToolButton:pressed { QCalendarWidget QSpinBox { background-color: var(--primary_dark); } + +idian--ToggleSwitch { + qproperty-background_checked: var(--button_bg); + qproperty-background_checked_hover: var(--primary_light); +} diff --git a/frontend/data/themes/Yami_Light.ovt b/frontend/data/themes/Yami_Light.ovt index 7f63f6cbd..2cbcbe2f7 100644 --- a/frontend/data/themes/Yami_Light.ovt +++ b/frontend/data/themes/Yami_Light.ovt @@ -324,3 +324,33 @@ QCalendarWidget #qt_calendar_prevmonth { QCalendarWidget #qt_calendar_nextmonth { qproperty-icon: url(theme:Light/right.svg); } + +/* Idian Widgets */ +idian--ToggleSwitch { + qproperty-background: var(--grey8); + qproperty-background_hover: var(--grey7); + qproperty-background_checked: var(--primary); + qproperty-background_checked_hover: var(--primary_light); +} + +idian--Row QComboBox::down-arrow { + image: url(theme:Light/collapse.svg); +} + +idian--CheckBox.keyFocus::indicator, +idian--Row.hover > idian--CheckBox.row-buddy::indicator { + image: url(theme:Light/checkbox_unchecked_focus.svg); +} + +idian--CheckBox.keyFocus.checked::indicator, +idian--Row.hover > idian--CheckBox.row-buddy.checked::indicator { + image: url(theme:Light/checkbox_checked_focus.svg); +} + +idian--ExpandButton::indicator { + image: url(theme:Light/down.svg); +} + +idian--ExpandButton::indicator:checked { + image: url(theme:Light/up.svg); +} diff --git a/shared/qt/idian/CMakeLists.txt b/shared/qt/idian/CMakeLists.txt index 743d155e3..943dbbb0e 100644 --- a/shared/qt/idian/CMakeLists.txt +++ b/shared/qt/idian/CMakeLists.txt @@ -22,7 +22,10 @@ target_sources( include/Idian/Row.hpp include/Idian/SpinBox.hpp include/Idian/ToggleSwitch.hpp + include/Idian/Utils.cpp include/Idian/Utils.hpp + include/Idian/StateEventFilter.cpp + include/Idian/StateEventFilter.hpp widgets/Group.cpp widgets/PropertiesList.cpp widgets/Row.cpp diff --git a/shared/qt/idian/components/CheckBox.cpp b/shared/qt/idian/components/CheckBox.cpp index 1f083b1c2..89484539a 100644 --- a/shared/qt/idian/components/CheckBox.cpp +++ b/shared/qt/idian/components/CheckBox.cpp @@ -21,4 +21,7 @@ using idian::CheckBox; -CheckBox::CheckBox(QWidget *parent) : QCheckBox(parent), Utils(this) {} +CheckBox::CheckBox(QWidget *parent) : QCheckBox(parent), Utils(this) +{ + Utils::applyStateStylingEventFilter(this); +} diff --git a/shared/qt/idian/components/ComboBox.cpp b/shared/qt/idian/components/ComboBox.cpp index af77f2892..d8117f311 100644 --- a/shared/qt/idian/components/ComboBox.cpp +++ b/shared/qt/idian/components/ComboBox.cpp @@ -25,7 +25,10 @@ using idian::ComboBox; -ComboBox::ComboBox(QWidget *parent) : QComboBox(parent), Utils(this) {} +ComboBox::ComboBox(QWidget *parent) : QComboBox(parent), Utils(this) +{ + Utils::applyStateStylingEventFilter(this); +} void ComboBox::showPopup() { diff --git a/shared/qt/idian/components/ToggleSwitch.cpp b/shared/qt/idian/components/ToggleSwitch.cpp index 5d3c3413a..29d478a49 100644 --- a/shared/qt/idian/components/ToggleSwitch.cpp +++ b/shared/qt/idian/components/ToggleSwitch.cpp @@ -38,6 +38,8 @@ ToggleSwitch::ToggleSwitch(QWidget *parent) animBgColor(new QPropertyAnimation(this, "blend", this)), Utils(this) { + Utils::applyStateStylingEventFilter(this); + offPos = rect().width() / 2 - 18; onPos = rect().width() / 2 + 18; xPos = offPos; diff --git a/shared/qt/idian/include/Idian/CheckBox.hpp b/shared/qt/idian/include/Idian/CheckBox.hpp index 8e2cd510b..ac3f6f0fe 100644 --- a/shared/qt/idian/include/Idian/CheckBox.hpp +++ b/shared/qt/idian/include/Idian/CheckBox.hpp @@ -28,19 +28,6 @@ class CheckBox : public QCheckBox, public Utils { public: CheckBox(QWidget *parent = nullptr); - -protected: - void focusInEvent(QFocusEvent *e) override - { - Utils::showKeyFocused(e); - QAbstractButton::focusInEvent(e); - } - - void focusOutEvent(QFocusEvent *e) override - { - Utils::hideKeyFocused(e); - QAbstractButton::focusOutEvent(e); - } }; } // namespace idian diff --git a/shared/qt/idian/include/Idian/ComboBox.hpp b/shared/qt/idian/include/Idian/ComboBox.hpp index 3eb8e94b9..74b20068e 100644 --- a/shared/qt/idian/include/Idian/ComboBox.hpp +++ b/shared/qt/idian/include/Idian/ComboBox.hpp @@ -41,18 +41,6 @@ protected: void hidePopup() override; void mousePressEvent(QMouseEvent *event) override; - - void focusInEvent(QFocusEvent *e) override - { - Utils::showKeyFocused(e); - QComboBox::focusInEvent(e); - } - - void focusOutEvent(QFocusEvent *e) override - { - Utils::hideKeyFocused(e); - QComboBox::focusOutEvent(e); - } }; } // namespace idian diff --git a/shared/qt/idian/include/Idian/Row.hpp b/shared/qt/idian/include/Idian/Row.hpp index 074dc42a1..4f324957d 100644 --- a/shared/qt/idian/include/Idian/Row.hpp +++ b/shared/qt/idian/include/Idian/Row.hpp @@ -39,7 +39,13 @@ class GenericRow : public QFrame, public Utils { Q_OBJECT public: - GenericRow(QWidget *parent = nullptr) : QFrame(parent), Utils(this) { setAccessibleName(""); }; + GenericRow(QWidget *parent = nullptr) : QFrame(parent), Utils(this) + { + setAttribute(Qt::WA_Hover, true); + + applyStateStylingEventFilter(this); + setAccessibleName(""); + }; virtual void setTitle(const QString &title) = 0; virtual void setDescription(const QString &description) = 0; @@ -84,18 +90,6 @@ protected: void keyReleaseEvent(QKeyEvent *) override; bool hasDescription() const { return descriptionLabel != nullptr; } - void focusInEvent(QFocusEvent *event) override - { - Utils::showKeyFocused(event); - QFrame::focusInEvent(event); - } - - void focusOutEvent(QFocusEvent *event) override - { - Utils::hideKeyFocused(event); - QFrame::focusOutEvent(event); - } - private: QGridLayout *layout; @@ -127,18 +121,6 @@ protected: explicit ExpandButton(QWidget *parent = nullptr); void paintEvent(QPaintEvent *) override; - - void focusInEvent(QFocusEvent *event) override - { - Utils::showKeyFocused(event); - QAbstractButton::focusInEvent(event); - } - - void focusOutEvent(QFocusEvent *event) override - { - Utils::hideKeyFocused(event); - QAbstractButton::focusOutEvent(event); - } }; class RowFrame : protected QFrame, protected Utils { @@ -153,18 +135,6 @@ protected: void enterEvent(QEnterEvent *) override; void leaveEvent(QEvent *) override; - void focusInEvent(QFocusEvent *event) override - { - Utils::showKeyFocused(event); - QWidget::focusInEvent(event); - } - - void focusOutEvent(QFocusEvent *event) override - { - Utils::hideKeyFocused(event); - QWidget::focusOutEvent(event); - } - private: friend class CollapsibleRow; }; diff --git a/shared/qt/idian/include/Idian/StateEventFilter.cpp b/shared/qt/idian/include/Idian/StateEventFilter.cpp new file mode 100644 index 000000000..0fb2ee19b --- /dev/null +++ b/shared/qt/idian/include/Idian/StateEventFilter.cpp @@ -0,0 +1,78 @@ +#include +#include + +#include +#include + +namespace idian { +StateEventFilter::StateEventFilter(idian::Utils *utils, QWidget *target) : QObject(target), target(target), utils(utils) +{ + QAbstractButton *button = qobject_cast(target); + if (button) { + connect(button, &QAbstractButton::toggled, this, &StateEventFilter::updateCheckedState); + } +} + +bool StateEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (!obj->isWidgetType()) { + return QObject::eventFilter(obj, event); + } + + QWidget *widget = qobject_cast(obj); + QFocusEvent *focusEvent = nullptr; + + switch (event->type()) { + case QEvent::FocusIn: + utils->toggleClass(widget, "focus", true); + + focusEvent = static_cast(event); + if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::PopupFocusReason) { + utils->toggleClass(widget, "keyFocus", true); + } else { + utils->toggleClass(widget, "keyFocus", false); + } + + utils->polishChildren(widget); + break; + case QEvent::FocusOut: + utils->toggleClass(widget, "focus", false); + + focusEvent = static_cast(event); + if (focusEvent->reason() != Qt::PopupFocusReason) { + utils->toggleClass(widget, "keyFocus", false); + utils->polishChildren(widget); + } + + break; + case QEvent::MouseButtonPress: + break; + case QEvent::HoverEnter: + if (widget->isEnabled()) { + utils->toggleClass(widget, "hover", true); + } + + utils->polishChildren(widget); + break; + case QEvent::HoverLeave: + utils->toggleClass(widget, "hover", false); + + utils->polishChildren(widget); + break; + case QEvent::EnabledChange: + bool widgetEnabled = widget->isEnabled(); + utils->toggleClass(widget, "disabled", !widgetEnabled); + + utils->polishChildren(widget); + break; + } + + return QObject::eventFilter(obj, event); +} + +void StateEventFilter::updateCheckedState(bool checked) +{ + utils->toggleClass(target, "checked", checked); +} + +} // namespace idian diff --git a/shared/qt/idian/include/Idian/StateEventFilter.hpp b/shared/qt/idian/include/Idian/StateEventFilter.hpp new file mode 100644 index 000000000..4724521a4 --- /dev/null +++ b/shared/qt/idian/include/Idian/StateEventFilter.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include +#include + +namespace idian { +class StateEventFilter : public QObject { + Q_OBJECT + +public: + explicit StateEventFilter(idian::Utils *utils, QWidget *parent); + + bool eventFilter(QObject *obj, QEvent *event); + +public slots: + void updateCheckedState(bool checked); + +private: + Utils *utils; + QWidget *target; +}; +} // namespace idian diff --git a/shared/qt/idian/include/Idian/ToggleSwitch.hpp b/shared/qt/idian/include/Idian/ToggleSwitch.hpp index 251cdb42b..150204ed9 100644 --- a/shared/qt/idian/include/Idian/ToggleSwitch.hpp +++ b/shared/qt/idian/include/Idian/ToggleSwitch.hpp @@ -82,18 +82,6 @@ protected: void keyReleaseEvent(QKeyEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; - void focusInEvent(QFocusEvent *e) override - { - Utils::showKeyFocused(e); - QAbstractButton::focusInEvent(e); - } - - void focusOutEvent(QFocusEvent *e) override - { - Utils::hideKeyFocused(e); - QAbstractButton::focusOutEvent(e); - } - private slots: void onClicked(bool checked); diff --git a/shared/qt/idian/include/Idian/Utils.cpp b/shared/qt/idian/include/Idian/Utils.cpp new file mode 100644 index 000000000..000236c97 --- /dev/null +++ b/shared/qt/idian/include/Idian/Utils.cpp @@ -0,0 +1,10 @@ +#include +#include + +namespace idian { + +void Utils::applyStateStylingEventFilter(QWidget *widget) +{ + widget->installEventFilter(new StateEventFilter(this, widget)); +} +} // namespace idian diff --git a/shared/qt/idian/include/Idian/Utils.hpp b/shared/qt/idian/include/Idian/Utils.hpp index a052a905b..4f77311ab 100644 --- a/shared/qt/idian/include/Idian/Utils.hpp +++ b/shared/qt/idian/include/Idian/Utils.hpp @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include #include @@ -39,23 +40,6 @@ public: Utils(QWidget *w) { parent = w; } - // Set a custom property whenever the widget has keyboard focus specifically - void showKeyFocused(QFocusEvent *e) - { - if (e->reason() != Qt::MouseFocusReason && e->reason() != Qt::PopupFocusReason) { - addClass("keyFocus"); - } else { - removeClass("keyFocus"); - } - } - - void hideKeyFocused(QFocusEvent *e) - { - if (e->reason() != Qt::PopupFocusReason) { - removeClass("keyFocus"); - } - } - // Force all children widgets to repaint void polishChildren() { polishChildren(parent); } @@ -91,9 +75,11 @@ public: } classList.removeDuplicates(); + classList.removeAll(""); classList.append(classname); - widget->setProperty("class", classList.join(" ")); + QString newClasses = classList.isEmpty() ? "" : classList.join(" "); + widget->setProperty("class", newClasses); repolish(widget); } @@ -118,9 +104,11 @@ public: } classList.removeDuplicates(); + classList.removeAll(""); classList.removeAll(classname); - widget->setProperty("class", classList.join(" ")); + QString newClasses = classList.isEmpty() ? "" : classList.join(" "); + widget->setProperty("class", newClasses); repolish(widget); } @@ -136,6 +124,8 @@ public: removeClass(widget, classname); } } + + void applyStateStylingEventFilter(QWidget *widget); }; } // namespace idian diff --git a/shared/qt/idian/widgets/Row.cpp b/shared/qt/idian/widgets/Row.cpp index e4628fcc2..6a5f83da8 100644 --- a/shared/qt/idian/widgets/Row.cpp +++ b/shared/qt/idian/widgets/Row.cpp @@ -157,8 +157,6 @@ void Row::enterEvent(QEnterEvent *event) setCursor(Qt::PointingHandCursor); } - Utils::addClass("hover"); - if (buddyWidget) Utils::repolish(buddyWidget); @@ -171,8 +169,6 @@ void Row::enterEvent(QEnterEvent *event) void Row::leaveEvent(QEvent *event) { - Utils::removeClass("hover"); - if (buddyWidget) Utils::repolish(buddyWidget); @@ -236,6 +232,7 @@ void Row::connectBuddyWidget(QWidget *widget) // Button for expanding a collapsible ActionRow ExpandButton::ExpandButton(QWidget *parent) : QAbstractButton(parent), Utils(this) { + Utils::applyStateStylingEventFilter(this); setCheckable(true); } @@ -358,13 +355,17 @@ void CollapsibleRow::addRow(GenericRow *actionRow) propertyList->addRow(actionRow); } -RowFrame::RowFrame(QWidget *parent) : QFrame(parent), Utils(this) {} +RowFrame::RowFrame(QWidget *parent) : QFrame(parent), Utils(this) +{ + setAttribute(Qt::WA_Hover, true); + + Utils::applyStateStylingEventFilter(this); +} void RowFrame::enterEvent(QEnterEvent *event) { setCursor(Qt::PointingHandCursor); - Utils::addClass("hover"); Utils::polishChildren(); QWidget::enterEvent(event); @@ -372,7 +373,6 @@ void RowFrame::enterEvent(QEnterEvent *event) void RowFrame::leaveEvent(QEvent *event) { - Utils::removeClass("hover"); Utils::polishChildren(); QWidget::leaveEvent(event);