diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 7fc604127..0da75187d 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -232,6 +232,9 @@ set(MUMBLE_SOURCES "widgets/MUComboBox.cpp" "widgets/MUComboBox.h" + "widgets/MultiStyleWidgetWrapper.cpp" + "widgets/MultiStyleWidgetWrapper.h" + "${SHARED_SOURCE_DIR}/ACL.cpp" "${SHARED_SOURCE_DIR}/ACL.h" diff --git a/src/mumble/TalkingUI.cpp b/src/mumble/TalkingUI.cpp index 16737f7d0..8725a80b0 100644 --- a/src/mumble/TalkingUI.cpp +++ b/src/mumble/TalkingUI.cpp @@ -10,6 +10,7 @@ #include "MainWindow.h" #include "TalkingUIComponent.h" #include "UserModel.h" +#include "widgets/MultiStyleWidgetWrapper.h" #include #include @@ -236,18 +237,18 @@ void TalkingUI::setupUI() { &TalkingUI::on_mainWindowSelectionChanged); } -void TalkingUI::setFontSize(QWidget *widget) { +void TalkingUI::setFontSize(MultiStyleWidgetWrapper &widgetWrapper) { const double fontFactor = g.s.iTalkingUI_RelativeFontSize / 100.0; const int origLineHeight = QFontMetrics(font()).height(); if (font().pixelSize() >= 0) { // font specified in pixels - widget->setStyleSheet(QString::fromLatin1("font-size: %1px;") - .arg(static_cast< int >(std::max(fontFactor * font().pixelSize(), 1.0)))); + uint32_t pixelSize = static_cast< uint32_t >(std::max(fontFactor * font().pixelSize(), 1.0)); + widgetWrapper.setFontSize(pixelSize, true); } else { // font specified in points - widget->setStyleSheet(QString::fromLatin1("font-size: %1pt;") - .arg(static_cast< int >(std::max(fontFactor * font().pointSize(), 1.0)))); + uint32_t pointSize = static_cast< uint32_t >(std::max(fontFactor * font().pointSize(), 1.0)); + widgetWrapper.setFontSize(pointSize, false); } m_currentLineHeight = static_cast< int >(std::max(origLineHeight * fontFactor, 1.0)); @@ -342,7 +343,7 @@ void TalkingUI::addChannel(const Channel *channel) { QWidget *channelWidget = channelContainer->getWidget(); - setFontSize(channelWidget); + setFontSize(channelContainer->getStylableWidget()); layout()->addWidget(channelWidget); @@ -713,7 +714,7 @@ void TalkingUI::on_settingsChanged() { // The font size might have changed as well -> update it // By the hierarchy in the UI the font-size should propagate to all // sub-elements (entries) as well. - setFontSize(currentContainer->getWidget()); + setFontSize(currentContainer->getStylableWidget()); if (currentContainer->getType() != ContainerType::CHANNEL) { continue; diff --git a/src/mumble/TalkingUI.h b/src/mumble/TalkingUI.h index 8ceeca435..cad6b631d 100644 --- a/src/mumble/TalkingUI.h +++ b/src/mumble/TalkingUI.h @@ -27,6 +27,7 @@ class QMouseEvent; class Channel; class ClientUser; class TalkingUIComponent; +class MultiStyleWidgetWrapper; /// The talking UI is a widget that will display the users you are currently /// hearing to you. @@ -87,8 +88,9 @@ private: /// Sets the font size according to the settings /// - /// @param widget a pointer to the widget to set the font size for - void setFontSize(QWidget *widget); + /// @param widgetWrapper An instance of MultiStyleWidgetWrapper that wraps the widget whose font size + /// shall be set + void setFontSize(MultiStyleWidgetWrapper &widgetWrapper); /// Updates the user's status icons (reflecting e.g. its mut-state) /// diff --git a/src/mumble/TalkingUIComponent.h b/src/mumble/TalkingUIComponent.h index e21105f67..7f0035343 100644 --- a/src/mumble/TalkingUIComponent.h +++ b/src/mumble/TalkingUIComponent.h @@ -7,11 +7,13 @@ #define MUMBLE_MUMBLE_TALKINGUICOMPONENT_H_ class QWidget; +class MultiStyleWidgetWrapper; class TalkingUIComponent { public: - virtual QWidget *getWidget() = 0; - virtual const QWidget *getWidget() const = 0; + virtual QWidget *getWidget() = 0; + virtual const QWidget *getWidget() const = 0; + virtual MultiStyleWidgetWrapper &getStylableWidget() = 0; }; #endif // MUMBLE_MUMBLE_TALKINGUICOMPONENT_H_ diff --git a/src/mumble/TalkingUIContainer.cpp b/src/mumble/TalkingUIContainer.cpp index 0bf36c748..8fbedef20 100644 --- a/src/mumble/TalkingUIContainer.cpp +++ b/src/mumble/TalkingUIContainer.cpp @@ -135,10 +135,14 @@ bool TalkingUIContainer::operator<=(const TalkingUIContainer &other) const { const int CHANNEL_LAYOUT_TOP_MARGIN = 10; TalkingUIChannel::TalkingUIChannel(int associatedChannelID, QString name, TalkingUI &talkingUI) - : TalkingUIContainer(associatedChannelID, talkingUI), m_channelBox(new QGroupBox()) { + : TalkingUIContainer(associatedChannelID, talkingUI), m_channelBox(new QGroupBox()), + m_channelBoxStyleWrapper(m_channelBox) { // Set name m_channelBox->setTitle(name); + // Don't inherit the background color set on the channel box to its children. + m_channelBoxStyleWrapper.setBackgroundColorSelector("TalkingUI > QGroupBox"); + // Set layout QVBoxLayout *layout = new QVBoxLayout(); layout->setContentsMargins(0, CHANNEL_LAYOUT_TOP_MARGIN, 0, 0); @@ -198,6 +202,10 @@ const QWidget *TalkingUIChannel::getWidget() const { return m_channelBox; } +MultiStyleWidgetWrapper &TalkingUIChannel::getStylableWidget() { + return m_channelBoxStyleWrapper; +} + ContainerType TalkingUIChannel::getType() const { return ContainerType::CHANNEL; } diff --git a/src/mumble/TalkingUIContainer.h b/src/mumble/TalkingUIContainer.h index 8c4c946be..55526755f 100644 --- a/src/mumble/TalkingUIContainer.h +++ b/src/mumble/TalkingUIContainer.h @@ -9,6 +9,8 @@ #include "TalkingUIComponent.h" #include "TalkingUIEntry.h" +#include "widgets/MultiStyleWidgetWrapper.h" + #include #include @@ -74,6 +76,7 @@ public: class TalkingUIChannel : public TalkingUIContainer { protected: QGroupBox *m_channelBox; + MultiStyleWidgetWrapper m_channelBoxStyleWrapper; EntryPriority m_highestUserPriority = EntryPriority::LOWEST; @@ -90,6 +93,7 @@ public: virtual QWidget *getWidget() override; virtual const QWidget *getWidget() const override; + virtual MultiStyleWidgetWrapper &getStylableWidget() override; virtual ContainerType getType() const override; diff --git a/src/mumble/TalkingUIEntry.cpp b/src/mumble/TalkingUIEntry.cpp index a55cf1e32..5945dccca 100644 --- a/src/mumble/TalkingUIEntry.cpp +++ b/src/mumble/TalkingUIEntry.cpp @@ -91,10 +91,11 @@ static const int USER_CONTAINER_HORIZONTAL_MARGIN = 2; static const int USER_CONTAINER_VERTICAL_MARGIN = 3; -TalkingUIUser::TalkingUIUser(const ClientUser &user) : TalkingUIEntry(user.uiSession), m_name(user.qsName), m_timer() { +TalkingUIUser::TalkingUIUser(const ClientUser &user) + : TalkingUIEntry(user.uiSession), m_backgroundWidget(new QWidget()), + m_backgroundWidgetStyleWrapper(m_backgroundWidget), m_name(user.qsName), m_timer() { // Create background widget and its layout that we'll use to place all other // components on - m_backgroundWidget = new QWidget(); m_backgroundWidget->setProperty("selected", false); QLayout *backgroundLayout = new QHBoxLayout(); backgroundLayout->setContentsMargins(USER_CONTAINER_HORIZONTAL_MARGIN, USER_CONTAINER_VERTICAL_MARGIN, @@ -115,6 +116,7 @@ TalkingUIUser::TalkingUIUser(const ClientUser &user) : TalkingUIEntry(user.uiSes // Create the label we'll use to display any status icons for the user m_statusIcons = new QLabel(m_backgroundWidget); m_statusIcons->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_statusIcons->hide(); // hide by default // Uncomment the line below if you want to debug the icons // m_statusIcons->setStyleSheet(QLatin1String("border: 1px solid black;")); @@ -184,6 +186,10 @@ const QWidget *TalkingUIUser::getWidget() const { return m_backgroundWidget; } +MultiStyleWidgetWrapper &TalkingUIUser::getStylableWidget() { + return m_backgroundWidgetStyleWrapper; +} + EntryType TalkingUIUser::getType() const { return EntryType::USER; } @@ -307,13 +313,13 @@ void TalkingUIUser::setStatus(UserStatus status) { m_backgroundWidget->layout()->removeWidget(m_statusIcons); } else { // Create a Pixmap that'll hold all icons - const QSize size(m_iconSize * static_cast(icons.size()), m_iconSize); + const QSize size(m_iconSize * static_cast< int >(icons.size()), m_iconSize); QPixmap pixmap(size); pixmap.fill(Qt::transparent); // Draw the icons to the Pixmap QPainter painter(&pixmap); - for (int i = 0; i < static_cast(icons.size()); i++) { + for (int i = 0; i < static_cast< int >(icons.size()); i++) { painter.drawPixmap(i * m_iconSize, 0, icons[i].get().pixmap(QSize(m_iconSize, m_iconSize), QIcon::Normal, QIcon::On)); } @@ -331,10 +337,10 @@ void TalkingUIUser::setStatus(UserStatus status) { TalkingUIChannelListener::TalkingUIChannelListener(const ClientUser &user, const Channel &channel) - : TalkingUIEntry(user.uiSession), m_channelID(channel.iId), m_name(user.qsName) { + : TalkingUIEntry(user.uiSession), m_backgroundWidget(new QWidget()), + m_backgroundWidgetStyleWrapper(m_backgroundWidget), m_channelID(channel.iId), m_name(user.qsName) { // Create background widget and its layout that we'll use to place all other // components on - m_backgroundWidget = new QWidget(); m_backgroundWidget->setProperty("selected", false); QLayout *backgroundLayout = new QHBoxLayout(); backgroundLayout->setContentsMargins(USER_CONTAINER_HORIZONTAL_MARGIN, USER_CONTAINER_VERTICAL_MARGIN, @@ -379,6 +385,10 @@ const QWidget *TalkingUIChannelListener::getWidget() const { return m_backgroundWidget; } +MultiStyleWidgetWrapper &TalkingUIChannelListener::getStylableWidget() { + return m_backgroundWidgetStyleWrapper; +} + void TalkingUIChannelListener::setIconSize(int size) { static const QIcon s_earIcon = QIcon(QLatin1String("skin:ear.svg")); diff --git a/src/mumble/TalkingUIEntry.h b/src/mumble/TalkingUIEntry.h index b30051cd6..e7eb723d3 100644 --- a/src/mumble/TalkingUIEntry.h +++ b/src/mumble/TalkingUIEntry.h @@ -8,6 +8,7 @@ #include "Settings.h" #include "TalkingUIComponent.h" +#include "widgets/MultiStyleWidgetWrapper.h" #include #include @@ -70,6 +71,7 @@ public: class TalkingUIUser : public TalkingUIEntry { protected: QWidget *m_backgroundWidget = nullptr; + MultiStyleWidgetWrapper m_backgroundWidgetStyleWrapper; QLabel *m_talkingIcon = nullptr; QLabel *m_nameLabel = nullptr; @@ -97,6 +99,7 @@ public: virtual QWidget *getWidget() override; virtual const QWidget *getWidget() const override; + virtual MultiStyleWidgetWrapper &getStylableWidget() override; virtual EntryType getType() const override; @@ -120,6 +123,7 @@ public: class TalkingUIChannelListener : public TalkingUIEntry { protected: QWidget *m_backgroundWidget = nullptr; + MultiStyleWidgetWrapper m_backgroundWidgetStyleWrapper; QLabel *m_icon = nullptr; QLabel *m_nameLabel = nullptr; @@ -135,6 +139,7 @@ public: virtual QWidget *getWidget() override; virtual const QWidget *getWidget() const override; + MultiStyleWidgetWrapper &getStylableWidget() override; virtual void setIconSize(int size) override; diff --git a/src/mumble/widgets/MultiStyleWidgetWrapper.cpp b/src/mumble/widgets/MultiStyleWidgetWrapper.cpp new file mode 100644 index 000000000..fd7795cbf --- /dev/null +++ b/src/mumble/widgets/MultiStyleWidgetWrapper.cpp @@ -0,0 +1,90 @@ +// Copyright 2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "MultiStyleWidgetWrapper.h" + +#include +#include +#include + +const uint32_t MultiStyleWidgetWrapper::UNSET_FONTSIZE = 0; +const QString MultiStyleWidgetWrapper::UNSET_COLOR = ""; +const QString MultiStyleWidgetWrapper::UNSET_SELECTOR = "*"; + +MultiStyleWidgetWrapper::MultiStyleWidgetWrapper(QWidget *widget) : m_widget(widget) { +} + +void MultiStyleWidgetWrapper::setFontSize(uint32_t fontSize, bool isPixels) { + if (!isPixels) { + // Convert the font size to pixels + QFont font; + font.setPixelSize(fontSize); + + fontSize = QFontMetrics(font).height(); + } + + if (fontSize != m_fontSize) { + m_fontSize = fontSize; + + updateStyleSheet(); + } +} + +void MultiStyleWidgetWrapper::setFontSizeSelector(const QString &selector) { + if (m_fontSizeSelector != selector) { + m_fontSizeSelector = selector; + + updateStyleSheet(); + } +} + +void MultiStyleWidgetWrapper::setBackgroundColor(const QString &color) { + if (m_backgroundColor != color) { + m_backgroundColor = color; + + updateStyleSheet(); + } +} + +void MultiStyleWidgetWrapper::setBackgroundColorSelector(const QString &selector) { + if (m_backgroundColorSelector != selector) { + m_backgroundColorSelector = selector; + + updateStyleSheet(); + } +} + +void MultiStyleWidgetWrapper::clearFontSize() { + setFontSize(UNSET_FONTSIZE); +} + +void MultiStyleWidgetWrapper::clearFontSizeSelector() { + setFontSizeSelector(UNSET_SELECTOR); +} + +void MultiStyleWidgetWrapper::clearBackgroundColor() { + setBackgroundColor(UNSET_COLOR); +} + +void MultiStyleWidgetWrapper::clearBackgroundColorSelector() { + setBackgroundColorSelector(UNSET_SELECTOR); +} + +void MultiStyleWidgetWrapper::updateStyleSheet() { + QString styleSheet; + + if (m_fontSize != UNSET_FONTSIZE) { + styleSheet += QString(" %1 { font-size: %2px; }").arg(m_fontSizeSelector).arg(m_fontSize); + } + if (m_backgroundColor != UNSET_COLOR) { + styleSheet += QString(" %1 { background-color: %2; }").arg(m_backgroundColorSelector).arg(m_backgroundColor); + } + + m_widget->setStyleSheet(styleSheet); +} + +QWidget *MultiStyleWidgetWrapper::operator->() { + return m_widget; +} diff --git a/src/mumble/widgets/MultiStyleWidgetWrapper.h b/src/mumble/widgets/MultiStyleWidgetWrapper.h new file mode 100644 index 000000000..3ca40af5e --- /dev/null +++ b/src/mumble/widgets/MultiStyleWidgetWrapper.h @@ -0,0 +1,52 @@ +// Copyright 2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_WIDGETS_MULTISTYLEWIDGETWRAPPER_H_ +#define MUMBLE_MUMBLE_WIDGETS_MULTISTYLEWIDGETWRAPPER_H_ + +#include + +#include + +class QWidget; + +/// Purpose of this class is to work around the fact that normally it is not possible to +/// set single properties within a widget's stylesheet without potentially overwriting other +/// properties set through it. +class MultiStyleWidgetWrapper { + public: + void setFontSize(uint32_t fontSize, bool isPixels = true); + void setFontSizeSelector(const QString &selector); + void setBackgroundColor(const QString &color); + void setBackgroundColorSelector(const QString &selector); + + void clearFontSize(); + void clearFontSizeSelector(); + void clearBackgroundColor(); + void clearBackgroundColorSelector(); + + QWidget *operator->(); + + MultiStyleWidgetWrapper(QWidget *widget); + + protected: + static const uint32_t UNSET_FONTSIZE; + static const QString UNSET_COLOR; + static const QString UNSET_SELECTOR; + /// The wrapped widget + QWidget *m_widget; + /// The current font size in pixels + uint32_t m_fontSize = UNSET_FONTSIZE; + /// The CSS selector to be applied to the font size + QString m_fontSizeSelector = UNSET_SELECTOR; + /// The current background color + QString m_backgroundColor = UNSET_COLOR; + /// The CSS selector to be applied to the background color + QString m_backgroundColorSelector = UNSET_SELECTOR; + + void updateStyleSheet(); +}; + +#endif // MUMBLE_MUMBLE_WIDGETS_MULTISTYLEWIDGETWRAPPER_H_