mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Merge pull request #4634: REFAC(client, talking-ui): Stylable widget wrapper
It turned out that it is absolutely impossible to only overwrite
specific properties via a styleSheet without also overwriting all other
properties that have been set via styleSheet on that object before.
Therefore a myWidget->setStyleSheet("background: red") will not only set
the background color to red but also erase everything that has been
written to the StyleSheet before this point.
As this is very inconvenient and always appending the most recent
property to the existing StyleSheet is not an option (the StyleSheet
will just keep growing in size and also this doesn't work well once one
starts using selectors), this commit introduces a wrapper class that can
be used for fine-tuned control of a widget's style.
This works by storing the desired properties in a separate class and
re-generate and re-apply the StyleSheet every time one of these
properties changes.
Currently the only implemented properties are font-size and
background-color.
NOTE: The wrapper only works as long as no other calls to setStyleSheet
are made to the object the wrapper acts upon.
The mentioned wrapper was then incorporated into the UI elements of the
TalkingUI.
This commit is contained in:
commit
8c0f88c4f1
@ -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"
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "MainWindow.h"
|
||||
#include "TalkingUIComponent.h"
|
||||
#include "UserModel.h"
|
||||
#include "widgets/MultiStyleWidgetWrapper.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QGuiApplication>
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
///
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#include "TalkingUIComponent.h"
|
||||
#include "TalkingUIEntry.h"
|
||||
|
||||
#include "widgets/MultiStyleWidgetWrapper.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<int>(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<int>(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"));
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Settings.h"
|
||||
#include "TalkingUIComponent.h"
|
||||
#include "widgets/MultiStyleWidgetWrapper.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QLatin1String>
|
||||
@ -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;
|
||||
|
||||
|
||||
90
src/mumble/widgets/MultiStyleWidgetWrapper.cpp
Normal file
90
src/mumble/widgets/MultiStyleWidgetWrapper.cpp
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#include "MultiStyleWidgetWrapper.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
|
||||
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;
|
||||
}
|
||||
52
src/mumble/widgets/MultiStyleWidgetWrapper.h
Normal file
52
src/mumble/widgets/MultiStyleWidgetWrapper.h
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#ifndef MUMBLE_MUMBLE_WIDGETS_MULTISTYLEWIDGETWRAPPER_H_
|
||||
#define MUMBLE_MUMBLE_WIDGETS_MULTISTYLEWIDGETWRAPPER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <QString>
|
||||
|
||||
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_
|
||||
Loading…
Reference in New Issue
Block a user