refactor: buttons and friends (#6102)

This commit is contained in:
nerix
2025-05-25 11:17:06 +02:00
committed by GitHub
parent deed1061b5
commit 84c0b39fde
49 changed files with 1083 additions and 691 deletions
+1
View File
@@ -52,6 +52,7 @@
- Dev: Simplified string literals to be a re-export of Qt functions. (#6175) - Dev: Simplified string literals to be a re-export of Qt functions. (#6175)
- Dev: Fixed incorrect lua generation of static methods for typescript plugins. (#6190, #6223) - Dev: Fixed incorrect lua generation of static methods for typescript plugins. (#6190, #6223)
- Dev: Merged top/bottom and left/right notebook layouts. (#6215) - Dev: Merged top/bottom and left/right notebook layouts. (#6215)
- Dev: Refactored `Button` and friends. (#6102)
## 2.5.3 ## 2.5.3
+21 -14
View File
@@ -523,8 +523,6 @@ set(SOURCE_FILES
util/Helpers.hpp util/Helpers.hpp
util/IncognitoBrowser.cpp util/IncognitoBrowser.cpp
util/IncognitoBrowser.hpp util/IncognitoBrowser.hpp
util/InitUpdateButton.cpp
util/InitUpdateButton.hpp
util/IpcQueue.cpp util/IpcQueue.cpp
util/IpcQueue.hpp util/IpcQueue.hpp
util/IrcHelpers.cpp util/IrcHelpers.cpp
@@ -602,6 +600,27 @@ set(SOURCE_FILES
widgets/Window.cpp widgets/Window.cpp
widgets/Window.hpp widgets/Window.hpp
widgets/buttons/Button.cpp
widgets/buttons/Button.hpp
widgets/buttons/DimButton.cpp
widgets/buttons/DimButton.hpp
widgets/buttons/InitUpdateButton.cpp
widgets/buttons/InitUpdateButton.hpp
widgets/buttons/LabelButton.cpp
widgets/buttons/LabelButton.hpp
widgets/buttons/NotebookButton.cpp
widgets/buttons/NotebookButton.hpp
widgets/buttons/PixmapButton.cpp
widgets/buttons/PixmapButton.hpp
widgets/buttons/SignalLabel.cpp
widgets/buttons/SignalLabel.hpp
widgets/buttons/SvgButton.cpp
widgets/buttons/SvgButton.hpp
widgets/buttons/TitlebarButton.cpp
widgets/buttons/TitlebarButton.hpp
widgets/buttons/TitlebarButtons.cpp
widgets/buttons/TitlebarButtons.hpp
widgets/dialogs/BadgePickerDialog.cpp widgets/dialogs/BadgePickerDialog.cpp
widgets/dialogs/BadgePickerDialog.hpp widgets/dialogs/BadgePickerDialog.hpp
widgets/dialogs/ChannelFilterEditorDialog.cpp widgets/dialogs/ChannelFilterEditorDialog.cpp
@@ -660,8 +679,6 @@ set(SOURCE_FILES
widgets/helper/color/SBCanvas.cpp widgets/helper/color/SBCanvas.cpp
widgets/helper/color/SBCanvas.hpp widgets/helper/color/SBCanvas.hpp
widgets/helper/Button.cpp
widgets/helper/Button.hpp
widgets/helper/ChannelView.cpp widgets/helper/ChannelView.cpp
widgets/helper/ChannelView.hpp widgets/helper/ChannelView.hpp
widgets/helper/ComboBoxItemDelegate.cpp widgets/helper/ComboBoxItemDelegate.cpp
@@ -670,16 +687,12 @@ set(SOURCE_FILES
widgets/helper/DebugPopup.hpp widgets/helper/DebugPopup.hpp
widgets/helper/EditableModelView.cpp widgets/helper/EditableModelView.cpp
widgets/helper/EditableModelView.hpp widgets/helper/EditableModelView.hpp
widgets/helper/EffectLabel.cpp
widgets/helper/EffectLabel.hpp
widgets/helper/IconDelegate.cpp widgets/helper/IconDelegate.cpp
widgets/helper/IconDelegate.hpp widgets/helper/IconDelegate.hpp
widgets/helper/InvisibleSizeGrip.cpp widgets/helper/InvisibleSizeGrip.cpp
widgets/helper/InvisibleSizeGrip.hpp widgets/helper/InvisibleSizeGrip.hpp
widgets/helper/MessageView.cpp widgets/helper/MessageView.cpp
widgets/helper/MessageView.hpp widgets/helper/MessageView.hpp
widgets/helper/NotebookButton.cpp
widgets/helper/NotebookButton.hpp
widgets/helper/NotebookTab.cpp widgets/helper/NotebookTab.cpp
widgets/helper/NotebookTab.hpp widgets/helper/NotebookTab.hpp
widgets/helper/OverlayInteraction.cpp widgets/helper/OverlayInteraction.cpp
@@ -694,14 +707,8 @@ set(SOURCE_FILES
widgets/helper/SearchPopup.hpp widgets/helper/SearchPopup.hpp
widgets/helper/SettingsDialogTab.cpp widgets/helper/SettingsDialogTab.cpp
widgets/helper/SettingsDialogTab.hpp widgets/helper/SettingsDialogTab.hpp
widgets/helper/SignalLabel.cpp
widgets/helper/SignalLabel.hpp
widgets/helper/TableStyles.cpp widgets/helper/TableStyles.cpp
widgets/helper/TableStyles.hpp widgets/helper/TableStyles.hpp
widgets/helper/TitlebarButton.cpp
widgets/helper/TitlebarButton.hpp
widgets/helper/TitlebarButtons.cpp
widgets/helper/TitlebarButtons.hpp
widgets/helper/TrimRegExpValidator.cpp widgets/helper/TrimRegExpValidator.cpp
widgets/helper/TrimRegExpValidator.hpp widgets/helper/TrimRegExpValidator.hpp
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include <memory>
namespace pajlada {
namespace Signals {
class SignalHolder;
}
} // namespace pajlada
namespace chatterino {
class Button;
class UpdateDialog;
void initUpdateButton(Button &button,
pajlada::Signals::SignalHolder &signalHolder);
} // namespace chatterino
+9 -23
View File
@@ -8,8 +8,9 @@
#include "util/DebugCount.hpp" #include "util/DebugCount.hpp"
#include "util/PostToThread.hpp" #include "util/PostToThread.hpp"
#include "util/WindowsHelper.hpp" #include "util/WindowsHelper.hpp"
#include "widgets/helper/EffectLabel.hpp" #include "widgets/buttons/LabelButton.hpp"
#include "widgets/helper/TitlebarButtons.hpp" #include "widgets/buttons/TitlebarButton.hpp"
#include "widgets/buttons/TitlebarButtons.hpp"
#include "widgets/Label.hpp" #include "widgets/Label.hpp"
#include "widgets/Window.hpp" #include "widgets/Window.hpp"
@@ -35,8 +36,6 @@
# include <QOperatingSystemVersion> # include <QOperatingSystemVersion>
#endif #endif
#include "widgets/helper/TitlebarButton.hpp"
namespace { namespace {
using namespace chatterino; using namespace chatterino;
@@ -672,34 +671,21 @@ void BaseWindow::focusOutEvent(QFocusEvent *event)
BaseWidget::focusOutEvent(event); BaseWidget::focusOutEvent(event);
} }
TitleBarButton *BaseWindow::addTitleBarButton(const TitleBarButtonStyle &style, void BaseWindow::appendTitlebarButton(Button *button)
std::function<void()> onClicked)
{ {
TitleBarButton *button = new TitleBarButton;
button->setScaleIndependentSize(30, 30);
this->ui_.buttons.push_back(button); this->ui_.buttons.push_back(button);
this->ui_.titlebarBox->insertWidget(1, button); this->ui_.titlebarBox->insertWidget(1, button);
button->setButtonStyle(style);
QObject::connect(button, &TitleBarButton::leftClicked, this, [onClicked] {
onClicked();
});
return button;
} }
EffectLabel *BaseWindow::addTitleBarLabel(std::function<void()> onClicked) LabelButton *BaseWindow::addTitleBarLabel(std::function<void()> onClicked)
{ {
EffectLabel *button = new EffectLabel; auto *button = new LabelButton;
button->setScaleIndependentHeight(30); button->setScaleIndependentHeight(30);
this->ui_.buttons.push_back(button); this->appendTitlebarButton(button);
this->ui_.titlebarBox->insertWidget(1, button);
QObject::connect(button, &EffectLabel::leftClicked, this, [onClicked] { QObject::connect(button, &LabelButton::leftClicked, this,
onClicked(); std::move(onClicked));
});
return button; return button;
} }
+19 -5
View File
@@ -16,10 +16,11 @@ typedef struct tagMSG MSG;
namespace chatterino { namespace chatterino {
class Button; class Button;
class EffectLabel; class LabelButton;
class PixmapButton;
class TitleBarButton; class TitleBarButton;
class TitleBarButtons; class TitleBarButtons;
enum class TitleBarButtonStyle; enum class TitleBarButtonStyle : std::uint8_t;
class BaseWindow : public BaseWidget class BaseWindow : public BaseWidget
{ {
@@ -52,9 +53,20 @@ public:
QWidget *getLayoutContainer(); QWidget *getLayoutContainer();
bool hasCustomWindowFrame() const; bool hasCustomWindowFrame() const;
TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style,
std::function<void()> onClicked); template <typename T>
EffectLabel *addTitleBarLabel(std::function<void()> onClicked); T *addTitleBarButton(std::function<void()> onClicked, auto &&...args)
{
auto *button = new T(std::forward<decltype(args)>(args)...);
button->setScaleIndependentSize(30, 30);
this->appendTitlebarButton(button);
QObject::connect(button, &T::leftClicked, this, std::move(onClicked));
return button;
}
LabelButton *addTitleBarLabel(std::function<void()> onClicked);
void moveTo(QPoint point, widgets::BoundsChecking mode); void moveTo(QPoint point, widgets::BoundsChecking mode);
@@ -169,6 +181,8 @@ private:
bool handleNCHITTEST(MSG *msg, long *result); bool handleNCHITTEST(MSG *msg, long *result);
#endif #endif
void appendTitlebarButton(Button *button);
bool enableCustomFrame_; bool enableCustomFrame_;
bool frameless_; bool frameless_;
bool shown_ = false; bool shown_ = false;
+2 -2
View File
@@ -2,7 +2,7 @@
#include "singletons/Resources.hpp" #include "singletons/Resources.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "widgets/helper/Button.hpp" #include "widgets/buttons/PixmapButton.hpp"
#include <QMouseEvent> #include <QMouseEvent>
@@ -119,7 +119,7 @@ void DraggablePopup::togglePinned()
} }
Button *DraggablePopup::createPinButton() Button *DraggablePopup::createPinButton()
{ {
this->pinButton_ = new Button(this); this->pinButton_ = new PixmapButton(this);
this->pinButton_->setPixmap(getTheme()->buttons.pin); this->pinButton_->setPixmap(getTheme()->buttons.pin);
this->pinButton_->setScaleIndependentSize(18, 18); this->pinButton_->setScaleIndependentSize(18, 18);
this->pinButton_->setToolTip("Pin Window"); this->pinButton_->setToolTip("Pin Window");
+3 -1
View File
@@ -9,6 +9,8 @@
namespace chatterino { namespace chatterino {
class PixmapButton;
class DraggablePopup : public BaseWindow class DraggablePopup : public BaseWindow
{ {
Q_OBJECT Q_OBJECT
@@ -60,7 +62,7 @@ private:
// dragTimer_ is called ~60 times per second once the user has initiated dragging // dragTimer_ is called ~60 times per second once the user has initiated dragging
QTimer dragTimer_; QTimer dragTimer_;
Button *pinButton_ = nullptr; PixmapButton *pinButton_ = nullptr;
bool isPinned_ = false; bool isPinned_ = false;
}; };
+11 -21
View File
@@ -10,10 +10,11 @@
#include "singletons/StreamerMode.hpp" #include "singletons/StreamerMode.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/InitUpdateButton.hpp" #include "widgets/buttons/InitUpdateButton.hpp"
#include "widgets/buttons/NotebookButton.hpp"
#include "widgets/buttons/PixmapButton.hpp"
#include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/NotebookButton.hpp"
#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/NotebookTab.hpp"
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp" #include "widgets/splits/SplitContainer.hpp"
@@ -1162,16 +1163,6 @@ NotebookButton *Notebook::getAddButton()
return this->addButton_; return this->addButton_;
} }
NotebookButton *Notebook::addCustomButton()
{
NotebookButton *btn = new NotebookButton(this);
this->customButtons_.push_back(btn);
this->performLayout();
return btn;
}
NotebookTab *Notebook::getTabFromPage(QWidget *page) NotebookTab *Notebook::getTabFromPage(QWidget *page)
{ {
for (auto &it : this->items_) for (auto &it : this->items_)
@@ -1416,7 +1407,7 @@ void SplitNotebook::showEvent(QShowEvent * /*event*/)
void SplitNotebook::addCustomButtons() void SplitNotebook::addCustomButtons()
{ {
// settings // settings
auto *settingsBtn = this->addCustomButton(); auto *settingsBtn = this->addCustomButton<NotebookButton>();
// This is to ensure you can't lock yourself out of the settings // This is to ensure you can't lock yourself out of the settings
if (getApp()->getArgs().safeMode) if (getApp()->getArgs().safeMode)
@@ -1442,7 +1433,7 @@ void SplitNotebook::addCustomButtons()
}); });
// account // account
auto *userBtn = this->addCustomButton(); auto *userBtn = this->addCustomButton<NotebookButton>();
userBtn->setVisible(!getSettings()->hideUserButton.getValue()); userBtn->setVisible(!getSettings()->hideUserButton.getValue());
getSettings()->hideUserButton.connect( getSettings()->hideUserButton.connect(
[userBtn](bool hide, auto) { [userBtn](bool hide, auto) {
@@ -1457,17 +1448,16 @@ void SplitNotebook::addCustomButtons()
}); });
// updates // updates
auto *updateBtn = this->addCustomButton(); auto *updateBtn = this->addCustomButton<PixmapButton>();
initUpdateButton(*updateBtn, this->signalHolder_); initUpdateButton(*updateBtn, this->signalHolder_);
// streamer mode // streamer mode
this->streamerModeIcon_ = this->addCustomButton(); this->streamerModeIcon_ = this->addCustomButton<PixmapButton>();
QObject::connect(this->streamerModeIcon_, &NotebookButton::leftClicked, QObject::connect(this->streamerModeIcon_, &Button::leftClicked, [this] {
[this] { getApp()->getWindows()->showSettingsDialog(
getApp()->getWindows()->showSettingsDialog( this, SettingsDialogPreference::StreamerMode);
this, SettingsDialogPreference::StreamerMode); });
});
QObject::connect(getApp()->getStreamerMode(), &IStreamerMode::changed, this, QObject::connect(getApp()->getStreamerMode(), &IStreamerMode::changed, this,
&SplitNotebook::updateStreamerModeIcon); &SplitNotebook::updateStreamerModeIcon);
this->updateStreamerModeIcon(); this->updateStreamerModeIcon();
+15 -3
View File
@@ -15,6 +15,9 @@
namespace chatterino { namespace chatterino {
class Button;
class PixmapButton;
class SvgButton;
class Window; class Window;
class UpdateDialog; class UpdateDialog;
class NotebookButton; class NotebookButton;
@@ -131,7 +134,16 @@ protected:
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
NotebookButton *getAddButton(); NotebookButton *getAddButton();
NotebookButton *addCustomButton();
template <typename T>
T *addCustomButton(auto &&...args)
{
auto *btn = new T(std::forward<decltype(args)>(args)..., this);
this->customButtons_.push_back(btn);
this->performLayout();
return btn;
}
struct Item { struct Item {
NotebookTab *tab{}; NotebookTab *tab{};
@@ -206,7 +218,7 @@ private:
QWidget *selectedPage_ = nullptr; QWidget *selectedPage_ = nullptr;
NotebookButton *addButton_; NotebookButton *addButton_;
std::vector<NotebookButton *> customButtons_; std::vector<Button *> customButtons_;
bool allowUserTabManagement_ = false; bool allowUserTabManagement_ = false;
bool showTabs_ = true; bool showTabs_ = true;
@@ -261,7 +273,7 @@ private:
pajlada::Signals::SignalHolder signalHolder_; pajlada::Signals::SignalHolder signalHolder_;
// Main window on Windows has basically a duplicate of this in Window // Main window on Windows has basically a duplicate of this in Window
NotebookButton *streamerModeIcon_{}; PixmapButton *streamerModeIcon_{};
void updateStreamerModeIcon(); void updateStreamerModeIcon();
}; };
+13 -10
View File
@@ -17,16 +17,17 @@
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "singletons/Updates.hpp" #include "singletons/Updates.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/InitUpdateButton.hpp"
#include "util/RapidJsonSerializeQSize.hpp" #include "util/RapidJsonSerializeQSize.hpp"
#include "widgets/AccountSwitchPopup.hpp" #include "widgets/AccountSwitchPopup.hpp"
#include "widgets/buttons/InitUpdateButton.hpp"
#include "widgets/buttons/LabelButton.hpp"
#include "widgets/buttons/PixmapButton.hpp"
#include "widgets/buttons/TitlebarButton.hpp"
#include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/dialogs/switcher/QuickSwitcherPopup.hpp" #include "widgets/dialogs/switcher/QuickSwitcherPopup.hpp"
#include "widgets/dialogs/UpdateDialog.hpp" #include "widgets/dialogs/UpdateDialog.hpp"
#include "widgets/dialogs/WelcomeDialog.hpp" #include "widgets/dialogs/WelcomeDialog.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/NotebookTab.hpp"
#include "widgets/helper/TitlebarButton.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
#include "widgets/splits/ClosedSplits.hpp" #include "widgets/splits/ClosedSplits.hpp"
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
@@ -202,12 +203,14 @@ void Window::addCustomTitlebarButtons()
} }
// settings // settings
this->addTitleBarButton(TitleBarButtonStyle::Settings, [this] { this->addTitleBarButton<TitleBarButton>(
getApp()->getWindows()->showSettingsDialog(this); [this] {
}); getApp()->getWindows()->showSettingsDialog(this);
},
TitleBarButtonStyle::Settings);
// updates // updates
auto *update = this->addTitleBarButton(TitleBarButtonStyle::None, [] {}); auto *update = this->addTitleBarButton<PixmapButton>([] {});
initUpdateButton(*update, this->signalHolder_); initUpdateButton(*update, this->signalHolder_);
@@ -221,7 +224,7 @@ void Window::addCustomTitlebarButtons()
// streamer mode // streamer mode
this->streamerModeTitlebarIcon_ = this->streamerModeTitlebarIcon_ =
this->addTitleBarButton(TitleBarButtonStyle::StreamerMode, [this] { this->addTitleBarButton<PixmapButton>([this] {
getApp()->getWindows()->showSettingsDialog( getApp()->getWindows()->showSettingsDialog(
this, SettingsDialogPreference::StreamerMode); this, SettingsDialogPreference::StreamerMode);
}); });
@@ -796,11 +799,11 @@ void Window::onAccountSelected()
{ {
if (user->isAnon()) if (user->isAnon())
{ {
this->userLabel_->getLabel().setText("anonymous"); this->userLabel_->setText("anonymous");
} }
else else
{ {
this->userLabel_->getLabel().setText(user->getUserName()); this->userLabel_->setText(user->getUserName());
} }
} }
} }
+4 -2
View File
@@ -9,6 +9,8 @@
namespace chatterino { namespace chatterino {
class PixmapButton;
class LabelButton;
class Theme; class Theme;
class UpdateDialog; class UpdateDialog;
class SplitNotebook; class SplitNotebook;
@@ -46,14 +48,14 @@ private:
WindowType type_; WindowType type_;
SplitNotebook *notebook_; SplitNotebook *notebook_;
EffectLabel *userLabel_ = nullptr; LabelButton *userLabel_ = nullptr;
std::shared_ptr<UpdateDialog> updateDialogHandle_; std::shared_ptr<UpdateDialog> updateDialogHandle_;
pajlada::Signals::SignalHolder signalHolder_; pajlada::Signals::SignalHolder signalHolder_;
std::vector<boost::signals2::scoped_connection> bSignals_; std::vector<boost::signals2::scoped_connection> bSignals_;
// this is only used on Windows and only on the main window, for the one used otherwise, see SplitNotebook in Notebook.hpp // this is only used on Windows and only on the main window, for the one used otherwise, see SplitNotebook in Notebook.hpp
TitleBarButton *streamerModeTitlebarIcon_ = nullptr; PixmapButton *streamerModeTitlebarIcon_ = nullptr;
void updateStreamerModeIcon(); void updateStreamerModeIcon();
friend class Notebook; friend class Notebook;
@@ -1,4 +1,4 @@
#include "widgets/helper/Button.hpp" #include "widgets/buttons/Button.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "util/FunctionEventFilter.hpp" #include "util/FunctionEventFilter.hpp"
@@ -8,42 +8,6 @@
#include <QPainter> #include <QPainter>
#include <QScreen> #include <QScreen>
namespace {
QSizeF deviceIndependentSize(const QPixmap &pixmap)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
return QSizeF(pixmap.width(), pixmap.height()) / pixmap.devicePixelRatio();
#else
return pixmap.deviceIndependentSize();
#endif
}
/**
* Resizes a pixmap to a desired size.
* Does nothing if the target pixmap is already sized correctly.
*
* @param target The target pixmap.
* @param source The unscaled pixmap.
* @param size The desired device independent size.
* @param dpr The device pixel ratio of the target area. The size of the target in pixels will be `size * dpr`.
*/
void resizePixmap(QPixmap &target, const QPixmap &source, const QSize &size,
qreal dpr)
{
if (deviceIndependentSize(target) == size)
{
return;
}
QPixmap resized = source;
resized.setDevicePixelRatio(dpr);
target = resized.scaled(size * dpr, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
} // namespace
namespace chatterino { namespace chatterino {
Button::Button(BaseWidget *parent) Button::Button(BaseWidget *parent)
@@ -58,98 +22,36 @@ Button::Button(BaseWidget *parent)
this->setMouseTracking(true); this->setMouseTracking(true);
} }
void Button::setMouseEffectColor(std::optional<QColor> color) bool Button::enabled() const noexcept
{
this->mouseEffectColor_ = std::move(color);
}
void Button::setPixmap(const QPixmap &_pixmap)
{
// Avoid updates if the pixmap didn't change
if (_pixmap.cacheKey() == this->pixmap_.cacheKey())
{
return;
}
this->pixmap_ = _pixmap;
this->resizedPixmap_ = {};
this->update();
}
void Button::setSvgResource(const QString &resourcePath)
{
if (resourcePath == this->svgResourcePath)
{
// Same resource path as before - nothing changed
return;
}
if (this->svgRenderer)
{
this->svgRenderer->deleteLater();
}
this->svgRenderer = new QSvgRenderer(resourcePath, this);
this->svgRenderer->setAspectRatioMode(Qt::KeepAspectRatio);
this->svgResourcePath = resourcePath;
this->update();
}
const QPixmap &Button::getPixmap() const
{
return this->pixmap_;
}
void Button::setDim(Dim value)
{
this->dimPixmap_ = value;
this->update();
}
Button::Dim Button::getDim() const
{
return this->dimPixmap_;
}
void Button::setEnable(bool value)
{
this->enabled_ = value;
this->update();
}
bool Button::getEnable() const
{ {
return this->enabled_; return this->enabled_;
} }
void Button::setEnableMargin(bool value) void Button::setEnabled(bool enabled)
{ {
this->enableMargin_ = value; this->enabled_ = enabled;
this->update(); this->update();
} }
bool Button::getEnableMargin() const bool Button::mouseOver() const noexcept
{ {
return this->enableMargin_; return this->mouseOver_;
} }
qreal Button::getCurrentDimAmount() const bool Button::mouseDown() const noexcept
{ {
if (this->dimPixmap_ == Dim::None || this->mouseOver_) return this->mouseDown_;
{ }
return 1;
} bool Button::menuVisible() const noexcept
else if (this->dimPixmap_ == Dim::Some) {
{ return this->menuVisible_;
return 0.7; }
}
else QColor Button::borderColor() const noexcept
{ {
return 0.15; return this->borderColor_;
}
} }
void Button::setBorderColor(const QColor &color) void Button::setBorderColor(const QColor &color)
@@ -159,9 +61,19 @@ void Button::setBorderColor(const QColor &color)
this->update(); this->update();
} }
const QColor &Button::getBorderColor() const std::optional<QColor> Button::mouseEffectColor() const
{ {
return this->borderColor_; return this->mouseEffectColor_;
}
void Button::setMouseEffectColor(std::optional<QColor> color)
{
this->mouseEffectColor_ = color;
}
QMenu *Button::menu() const
{
return this->menu_.get();
} }
void Button::setMenu(std::unique_ptr<QMenu> menu) void Button::setMenu(std::unique_ptr<QMenu> menu)
@@ -185,104 +97,45 @@ void Button::setMenu(std::unique_ptr<QMenu> menu)
})); }));
} }
void Button::paintEvent(QPaintEvent *) void Button::paintEvent(QPaintEvent * /*event*/)
{ {
QPainter painter(this); QPainter painter(this);
this->paintButton(painter); this->paintButton(painter);
} }
void Button::paintButton(QPainter &painter) void Button::invalidateContent()
{ {
painter.setRenderHint(QPainter::SmoothPixmapTransform); this->pixmapValid_ = false;
this->update();
if (this->svgRenderer != nullptr)
{
painter.setOpacity(this->getCurrentDimAmount());
auto rect = this->rect();
if (this->enableMargin_)
{
auto s = this->getMargin();
rect = {{s, s}, rect.size() - QSize{2 * s, 2 * s}};
}
this->svgRenderer->render(&painter, rect);
}
else if (!this->pixmap_.isNull())
{
painter.setOpacity(this->getCurrentDimAmount());
QRect rect = this->rect();
resizePixmap(this->resizedPixmap_, this->pixmap_, rect.size(),
this->devicePixelRatio());
if (this->enableMargin_)
{
auto s = this->getMargin();
rect.moveLeft(s);
rect.setRight(rect.right() - s - s);
rect.moveTop(s);
rect.setBottom(rect.bottom() - s - s);
}
painter.drawPixmap(rect, this->resizedPixmap_);
painter.setOpacity(1);
}
this->fancyPaint(painter);
if (this->borderColor_.isValid())
{
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setPen(this->borderColor_);
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
} }
void Button::fancyPaint(QPainter &painter) bool Button::contentCacheEnabled() const noexcept
{ {
if (!this->enabled_) return this->cachePixmap_;
}
void Button::setContentCacheEnabled(bool enabled)
{
if (this->cachePixmap_ == enabled)
{ {
return; return;
} }
painter.setRenderHint(QPainter::Antialiasing); if (!enabled)
QColor c;
if (this->mouseEffectColor_)
{ {
c = *this->mouseEffectColor_; this->cachedPixmap_ = {};
}
else
{
c = this->theme->isLightTheme() ? QColor(0, 0, 0)
: QColor(255, 255, 255);
} }
this->cachePixmap_ = enabled;
}
if (this->hoverMultiplier_ > 0) bool Button::opaqueContent() const noexcept
{ {
QRadialGradient gradient(QPointF(mousePos_), this->width() / 2); return this->opaqueContent_;
}
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(), void Button::setOpaqueContent(bool opaqueContent)
int(50 * this->hoverMultiplier_))); {
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), this->opaqueContent_ = opaqueContent;
int(40 * this->hoverMultiplier_)));
painter.fillRect(this->rect(), gradient);
}
for (auto effect : this->clickEffects_)
{
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(c.red(), c.green(), c.blue(),
int((1 - effect.progress) * 95)));
painter.drawEllipse(QPointF(effect.position),
effect.progress * qreal(width()) * 2,
effect.progress * qreal(width()) * 2);
}
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -319,7 +172,7 @@ void Button::mousePressEvent(QMouseEvent *event)
return; return;
} }
this->clickEffects_.push_back(ClickEffect(event->pos())); this->clickEffects_.emplace_back(event->pos());
this->mouseDown_ = true; this->mouseDown_ = true;
@@ -374,16 +227,7 @@ void Button::onMouseEffectTimeout()
{ {
bool performUpdate = false; bool performUpdate = false;
if (selected_) if (mouseOver_)
{
if (this->hoverMultiplier_ != 0)
{
this->hoverMultiplier_ =
std::max(0.0, this->hoverMultiplier_ - 0.1);
performUpdate = true;
}
}
else if (mouseOver_)
{ {
if (this->hoverMultiplier_ != 1) if (this->hoverMultiplier_ != 1)
{ {
@@ -402,7 +246,7 @@ void Button::onMouseEffectTimeout()
} }
} }
if (this->clickEffects_.size() != 0) if (!this->clickEffects_.empty())
{ {
performUpdate = true; performUpdate = true;
@@ -456,13 +300,93 @@ void Button::showMenu()
this->menuVisible_ = true; this->menuVisible_ = true;
} }
int Button::getMargin() const void Button::paintButton(QPainter &painter)
{ {
assert(this->enableMargin_); painter.setRenderHint(QPainter::SmoothPixmapTransform);
int baseMargin = this->height() < 22 * this->scale() ? 3 : 6; if (this->cachePixmap_)
{
if (this->cachedPixmap_.size() != this->size())
{
this->cachedPixmap_ = QPixmap(this->size());
this->cachedPixmap_.setDevicePixelRatio(this->devicePixelRatio());
this->pixmapValid_ = false;
}
return static_cast<int>(baseMargin * this->scale()); if (!this->pixmapValid_)
{
if (!this->opaqueContent_)
{
this->cachedPixmap_.fill(Qt::transparent);
}
{
QPainter pixmapPainter(&this->cachedPixmap_);
this->paintContent(pixmapPainter);
}
this->pixmapValid_ = true;
}
painter.drawPixmap(this->rect(), this->cachedPixmap_,
{{}, this->cachedPixmap_.size()});
}
else
{
this->paintContent(painter);
}
this->fancyPaint(painter);
if (this->borderColor_.isValid())
{
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setPen(this->borderColor_);
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
}
void Button::fancyPaint(QPainter &painter)
{
if (!this->enabled_)
{
return;
}
painter.setRenderHint(QPainter::Antialiasing);
QColor c;
if (this->mouseEffectColor_)
{
c = *this->mouseEffectColor_;
}
else
{
c = this->theme->isLightTheme() ? QColor(0, 0, 0)
: QColor(255, 255, 255);
}
if (this->hoverMultiplier_ > 0)
{
QRadialGradient gradient(QPointF(mousePos_),
static_cast<qreal>(this->width()) / 2.0);
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(),
int(50 * this->hoverMultiplier_)));
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(),
int(40 * this->hoverMultiplier_)));
painter.fillRect(this->rect(), gradient);
}
for (auto effect : this->clickEffects_)
{
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(c.red(), c.green(), c.blue(),
int((1 - effect.progress) * 95)));
painter.drawEllipse(QPointF(effect.position),
effect.progress * qreal(width()) * 2,
effect.progress * qreal(width()) * 2);
}
} }
} // namespace chatterino } // namespace chatterino
+211
View File
@@ -0,0 +1,211 @@
#pragma once
#include "widgets/BaseWidget.hpp"
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPoint>
#include <QTimer>
#include <QWidget>
#include <optional>
namespace chatterino {
/// @brief A generic button with click and hover effects.
///
/// This button doesn't display anything - subclasses add content to the button.
///
/// To add content in a derived class, implement #paintContent(). This gets
/// called before the click and hover effects are drawn.+
///
/// Subclasses can enable caching of the content drawn in #paintContent(), as
/// the button often repaints due to mouse movement (and the hover effects).
/// When updating the button, subclasses can invalidate the cache with
/// #invalidateContent(). Alternatively, a subclass opt out of this by calling
/// #setContentCacheEnabled().
class Button : public BaseWidget
{
Q_OBJECT
struct ClickEffect {
double progress = 0.0;
QPoint position;
ClickEffect(QPoint _position)
: position(_position)
{
}
};
public:
Button(BaseWidget *parent = nullptr);
/// @brief Returns true if the button is enabled
///
/// An enabled button will emit interaction events and show mouse effects.
/// When disabled, only the content and border is shown.
///
/// By default, a button is enabled.
[[nodiscard]] bool enabled() const noexcept;
/// Setter for #enabled()
void setEnabled(bool enabled);
/// Returns true if the user is hovering over the button
[[nodiscard]] bool mouseOver() const noexcept;
/// Returns true if the left mouse button is held down
[[nodiscard]] bool mouseDown() const noexcept;
/// @brief Returns true if the menu is visible
///
/// @sa #menu(), #setMenu()
[[nodiscard]] bool menuVisible() const noexcept;
/// @brief Returns the current border color
///
/// If no color is set, this will return an invalid color.
///
/// The border is shown with a width of 1 above all other content if the
/// border color is valid.
///
/// By default, no border color is set.
[[nodiscard]] QColor borderColor() const noexcept;
/// Setter for #borderColor()
void setBorderColor(const QColor &color);
/// @brief Returns the current mouse effect color (if set)
///
/// The mouse effect color is used for hover and click effects.
///
/// By default, the color is based on the selected theme.
[[nodiscard]] std::optional<QColor> mouseEffectColor() const;
/// Setter for #mouseEffectColor()
void setMouseEffectColor(std::optional<QColor> color);
/// @brief Returns the menu associated with this button.
///
/// The menu is shown when pressing the left mouse button.
///
/// The return value is non-owned.
/// If no menu is associated, `nullptr` is returned.
///
/// @sa #menuVisible(), #leftMousePress()
[[nodiscard]] QMenu *menu() const;
/// Setter for #menu()
void setMenu(std::unique_ptr<QMenu> menu);
Q_SIGNALS:
/// @brief Emitted after the user left-clicked the button.
///
/// A click is only emitted if the user released the mouse above the button
/// and the button is enabled.
void leftClicked();
/// @brief Emitted after the user clicked the button with any mouse-button
///
/// A click is only emitted if the user released the mouse above the button
/// and the button is enabled.
///
/// @sa #leftClicked()
void clicked(Qt::MouseButton button);
/// @brief Emitted when the user presses the left mouse button.
///
/// Avoid using this event where possible and use #leftClicked().
void leftMousePress();
protected:
void paintEvent(QPaintEvent * /*event*/) override;
/// @brief Paint the contents to be shown below the button
///
/// First, the content is painted, then hover and click effects and finally
/// the border.
/// The content is cached by default as it's assumed to be expensive to
/// repaint and repaints can occur frequently.
///
/// @sa #contentCacheEnabled()
virtual void paintContent(QPainter &painter) = 0;
/// @brief Paint hover and click effects.
///
/// This is provided for custom setups that override #paintEvent().
/// When possible, prefer #paintContent().
void fancyPaint(QPainter &painter);
/// @brief Indicate that the cache is invalid and needs to be repainted.
///
/// This calls update().
void invalidateContent();
/// @brief Returns true if the content drawn in #paintContent() should be
/// cached.
///
/// For complex static content, this makes sense, as the button is often
/// repainted when the user is hovering over it.
///
/// By default, the cache is disabled.
[[nodiscard]] bool contentCacheEnabled() const noexcept;
/// @brief Setter for #contentCacheEnabled()
///
/// When disabling the content-cache, the cached pixmap is destroyed.
void setContentCacheEnabled(bool enabled);
/// @brief Returns true if the content drawn in #paintContent() is fully
/// opaque.
///
/// If the content is fully opaque, the cached pixmap isn't filled with a
/// transparent color, but left in the default state.
///
/// @sa #setOpaqueContent()
[[nodiscard]] bool opaqueContent() const noexcept;
/// Setter for #opaqueContent()
void setOpaqueContent(bool opaqueContent);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent * /*event*/) override;
#else
void enterEvent(QEvent * /*event*/) override;
#endif
void leaveEvent(QEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
private:
void onMouseEffectTimeout();
void showMenu();
void paintButton(QPainter &painter);
QColor borderColor_;
QPoint mousePos_;
double hoverMultiplier_ = 0.0;
std::unique_ptr<QMenu> menu_;
QTimer effectTimer_;
std::vector<ClickEffect> clickEffects_;
std::optional<QColor> mouseEffectColor_;
bool enabled_ = true;
bool mouseOver_ = false;
bool mouseDown_ = false;
bool menuVisible_ = false;
QPixmap cachedPixmap_;
bool pixmapValid_ = false;
bool cachePixmap_ = false;
bool opaqueContent_ = false;
};
} // namespace chatterino
+36
View File
@@ -0,0 +1,36 @@
#include "widgets/buttons/DimButton.hpp"
namespace chatterino {
DimButton::DimButton(BaseWidget *parent)
: Button(parent)
{
}
DimButton::Dim DimButton::dim() const noexcept
{
return this->dim_;
}
void DimButton::setDim(Dim value)
{
this->dim_ = value;
this->invalidateContent();
}
qreal DimButton::currentContentOpacity() const noexcept
{
if (this->dim_ == Dim::None || this->mouseOver())
{
return 1;
}
if (this->dim_ == Dim::Some)
{
return 0.7;
}
return 0.15;
}
} // namespace chatterino
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "widgets/buttons/Button.hpp"
namespace chatterino {
/// @brief A button with a #dim() setting for controlling the opacity of its
/// content.
///
/// This button doesn't paint anything.
///
/// @sa #currentContentOpacity()
class DimButton : public Button
{
public:
enum class Dim : std::uint8_t {
/// Fully opaque (100% opcaity)
None,
/// Slightly transparent (70% opacity)
Some,
/// Almost transparent (15% opacity)
Lots,
};
DimButton(BaseWidget *parent = nullptr);
/// Returns the current dim level.
[[nodiscard]] Dim dim() const noexcept;
/// Setter for #dim()
void setDim(Dim value);
/// Returns the current opacity based on the current dim level.
[[nodiscard]] qreal currentContentOpacity() const noexcept;
private:
Dim dim_ = Dim::Some;
};
} // namespace chatterino
@@ -1,12 +1,12 @@
#include "util/InitUpdateButton.hpp" #include "widgets/buttons/InitUpdateButton.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "widgets/buttons/PixmapButton.hpp"
#include "widgets/dialogs/UpdateDialog.hpp" #include "widgets/dialogs/UpdateDialog.hpp"
#include "widgets/helper/Button.hpp"
namespace chatterino { namespace chatterino {
void initUpdateButton(Button &button, void initUpdateButton(PixmapButton &button,
pajlada::Signals::SignalHolder &signalHolder) pajlada::Signals::SignalHolder &signalHolder)
{ {
button.hide(); button.hide();
+15
View File
@@ -0,0 +1,15 @@
#pragma once
namespace pajlada::Signals {
class SignalHolder;
} // namespace pajlada::Signals
namespace chatterino {
class PixmapButton;
class UpdateDialog;
void initUpdateButton(PixmapButton &button,
pajlada::Signals::SignalHolder &signalHolder);
} // namespace chatterino
+56
View File
@@ -0,0 +1,56 @@
#include "widgets/buttons/LabelButton.hpp"
namespace chatterino {
LabelButton::LabelButton(const QString &text, BaseWidget *parent, QSize padding)
: Button(parent)
, layout_(this)
, label_(text)
, padding_(padding)
{
this->layout_.setContentsMargins(0, 0, 0, 0);
this->layout_.addWidget(&this->label_);
this->label_.setAttribute(Qt::WA_TransparentForMouseEvents);
this->label_.setAlignment(Qt::AlignCenter);
this->updatePadding();
}
void LabelButton::setText(const QString &text)
{
this->label_.setText(text);
}
QSize LabelButton::padding() const noexcept
{
return this->padding_;
}
void LabelButton::setPadding(QSize padding)
{
if (this->padding_ == padding)
{
return;
}
this->padding_ = padding;
this->updatePadding();
}
void LabelButton::enableRichText()
{
this->label_.setTextFormat(Qt::RichText);
}
void LabelButton::paintContent(QPainter &painter)
{
}
void LabelButton::updatePadding()
{
auto x = this->padding_.width();
auto y = this->padding_.height();
this->label_.setContentsMargins(x, y, x, y);
}
} // namespace chatterino
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "widgets/buttons/Button.hpp"
#include <QHBoxLayout>
#include <QLabel>
namespace chatterino {
/// @brief A button with a label on top.
///
/// The content can be set with #setText(), #setPixmap(), and #setMovie().
class LabelButton : public Button
{
public:
LabelButton(const QString &text = {}, BaseWidget *parent = nullptr,
QSize padding = {6, 0});
/// @brief Returns the current text
///
/// If no text has been set this will return an empty string.
[[nodiscard]] QString text() const;
/// @brief Sets the label's text to @a text.
///
/// Any previous content is cleared.
/// Supports Qt's HTML subset.
void setText(const QString &text);
/// @brief Returns the padding inside the button.
///
/// `width` is the padding applied horizontally (left and right).
/// `height` is the padding applied vertically (top and bottom).
///
/// By default, the button has no vertical padding and a horizontal padding of 6.
[[nodiscard]] QSize padding() const noexcept;
/// Setter for #padding()
void setPadding(QSize padding);
/// Sets the label to display rich text (Qt's HTML subset)
void enableRichText();
protected:
void paintContent(QPainter &painter) override;
private:
void updatePadding();
QHBoxLayout layout_;
QLabel label_;
QSize padding_;
};
} // namespace chatterino
@@ -1,9 +1,8 @@
#include "widgets/helper/NotebookButton.hpp" #include "widgets/buttons/NotebookButton.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "common/QLogging.hpp" #include "common/QLogging.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "widgets/helper/Button.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
#include "widgets/splits/DraggedSplit.hpp" #include "widgets/splits/DraggedSplit.hpp"
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
@@ -41,14 +40,12 @@ void NotebookButton::themeChangedEvent()
this->setMouseEffectColor(this->theme->tabs.regular.text); this->setMouseEffectColor(this->theme->tabs.regular.text);
} }
void NotebookButton::paintEvent(QPaintEvent *event) void NotebookButton::paintContent(QPainter &painter)
{ {
QPainter painter(this);
QColor background; QColor background;
QColor foreground; QColor foreground;
if (mouseDown_ || mouseOver_) if (this->mouseDown() || this->mouseOver())
{ {
background = this->theme->tabs.regular.backgrounds.hover; background = this->theme->tabs.regular.backgrounds.hover;
foreground = this->theme->tabs.regular.text; foreground = this->theme->tabs.regular.text;
@@ -72,7 +69,7 @@ void NotebookButton::paintEvent(QPaintEvent *event)
{ {
tmp = this->theme->tabs.selected.line.regular; tmp = this->theme->tabs.selected.line.regular;
} }
else if (!this->mouseOver_) else if (!this->mouseOver())
{ {
tmp.setAlpha(180); tmp.setAlpha(180);
} }
@@ -136,22 +133,6 @@ void NotebookButton::paintEvent(QPaintEvent *event)
default:; default:;
} }
this->paintButton(painter);
}
void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
mouseDown_ = false;
update();
leftClicked();
}
Button::mouseReleaseEvent(event);
} }
void NotebookButton::dragEnterEvent(QDragEnterEvent *event) void NotebookButton::dragEnterEvent(QDragEnterEvent *event)
@@ -173,7 +154,6 @@ void NotebookButton::dragEnterEvent(QDragEnterEvent *event)
void NotebookButton::dragLeaveEvent(QDragLeaveEvent *) void NotebookButton::dragLeaveEvent(QDragLeaveEvent *)
{ {
this->mouseDown_ = true;
this->update(); this->update();
auto *e = auto *e =
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "widgets/helper/Button.hpp" #include "widgets/buttons/Button.hpp"
#include <QWidget> #include <QWidget>
@@ -21,9 +21,9 @@ public:
Icon getIcon() const; Icon getIcon() const;
protected: protected:
void paintContent(QPainter &painter) override;
void themeChangedEvent() override; void themeChangedEvent() override;
void paintEvent(QPaintEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void dragEnterEvent(QDragEnterEvent *) override; void dragEnterEvent(QDragEnterEvent *) override;
void dragLeaveEvent(QDragLeaveEvent *) override; void dragLeaveEvent(QDragLeaveEvent *) override;
void dropEvent(QDropEvent *) override; void dropEvent(QDropEvent *) override;
@@ -31,9 +31,6 @@ protected:
void hideEvent(QHideEvent *) override; void hideEvent(QHideEvent *) override;
void showEvent(QShowEvent *) override; void showEvent(QShowEvent *) override;
Q_SIGNALS:
void leftClicked();
private: private:
Notebook *parent_ = nullptr; Notebook *parent_ = nullptr;
QPoint mousePos_; QPoint mousePos_;
+98
View File
@@ -0,0 +1,98 @@
#include "widgets/buttons/PixmapButton.hpp"
namespace {
/**
* Resizes a pixmap to a desired size.
* Does nothing if the target pixmap is already sized correctly.
*
* @param target The target pixmap.
* @param source The unscaled pixmap.
* @param size The desired device independent size.
* @param dpr The device pixel ratio of the target area. The size of the target in pixels will be `size * dpr`.
*/
void resizePixmap(QPixmap &target, const QPixmap &source, const QSize &size,
qreal dpr)
{
if (target.deviceIndependentSize() == size)
{
return;
}
QPixmap resized = source;
resized.setDevicePixelRatio(dpr);
target = resized.scaled(size * dpr, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
} // namespace
namespace chatterino {
PixmapButton::PixmapButton(BaseWidget *parent)
: DimButton(parent)
{
}
QPixmap PixmapButton::pixmap() const
{
return this->pixmap_;
}
void PixmapButton::setPixmap(const QPixmap &pixmap)
{
// Avoid updates if the pixmap didn't change
if (pixmap.cacheKey() == this->pixmap_.cacheKey())
{
return;
}
this->pixmap_ = pixmap;
this->resizedPixmap_ = {};
this->update();
}
bool PixmapButton::marginEnabled() const noexcept
{
return this->marginEnabled_;
}
void PixmapButton::setMarginEnabled(bool enableMargin)
{
this->marginEnabled_ = enableMargin;
this->update();
}
void PixmapButton::paintContent(QPainter &painter)
{
if (this->pixmap_.isNull())
{
return;
}
painter.setOpacity(this->currentContentOpacity());
QRect rect = this->rect();
int shift = 0;
if (this->marginEnabled_)
{
int margin =
this->height() < static_cast<int>(22 * this->scale()) ? 3 : 6;
shift = static_cast<int>(static_cast<float>(margin) * this->scale());
}
rect.moveLeft(shift);
rect.setRight(rect.right() - (2 * shift));
rect.moveTop(shift);
rect.setBottom(rect.bottom() - (2 * shift));
resizePixmap(this->resizedPixmap_, this->pixmap_, rect.size(),
this->devicePixelRatio());
painter.drawPixmap(rect, this->resizedPixmap_);
painter.setOpacity(1);
}
} // namespace chatterino
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include "widgets/buttons/DimButton.hpp"
namespace chatterino {
/// @brief A (dimmable) button displaying a pixmap.
///
/// The pixmap is scaled to fit the button, ignoring any aspect ratio.
///
/// By default, a margin is added to the button (@see #marginEnabled()).
///
/// When possible, prefer using a `SvgButton` as it can scale its contents
/// to any size without blur and supports scaling and themeing.
///
/// @sa #setPixmap()
class PixmapButton : public DimButton
{
public:
PixmapButton(BaseWidget *parent = nullptr);
/// Returns the current, non-scaled pixmap
[[nodiscard]] QPixmap pixmap() const;
/// Setter for #pixmap()
void setPixmap(const QPixmap &pixmap);
/// @brief Returns true if this button has margin.
///
/// The margin is dynamically calculated based on the UI-scale
/// and the size of the pixmap.
[[nodiscard]] bool marginEnabled() const noexcept;
/// Setter for #marginEnabled()
void setMarginEnabled(bool enableMargin);
protected:
void paintContent(QPainter &painter) override;
private:
QPixmap pixmap_;
QPixmap resizedPixmap_;
bool marginEnabled_ = true;
};
} // namespace chatterino
@@ -1,4 +1,4 @@
#include "widgets/helper/SignalLabel.hpp" #include "widgets/buttons/SignalLabel.hpp"
namespace chatterino { namespace chatterino {
+80
View File
@@ -0,0 +1,80 @@
#include "widgets/buttons/SvgButton.hpp"
#include "singletons/Theme.hpp"
#include <QSvgRenderer>
namespace chatterino {
SvgButton::SvgButton(Src source, BaseWidget *parent, QSize padding)
: Button(parent)
, source_(std::move(source))
, svg_(new QSvgRenderer(this->currentSvgPath(), this))
, padding_(padding)
{
this->svg_->setAspectRatioMode(Qt::KeepAspectRatio);
this->setContentCacheEnabled(true);
}
void SvgButton::setSource(Src source)
{
this->source_ = std::move(source);
this->svg_->load(this->currentSvgPath());
this->invalidateContent();
}
void SvgButton::setPadding(QSize padding)
{
if (this->padding_ == padding)
{
return;
}
this->padding_ = padding;
this->invalidateContent();
}
void SvgButton::themeChangedEvent()
{
Button::themeChangedEvent();
if (this->source_.dark == this->source_.light)
{
return;
}
this->svg_->load(this->currentSvgPath());
this->invalidateContent();
}
void SvgButton::scaleChangedEvent(float scale)
{
Button::scaleChangedEvent(scale);
this->invalidateContent();
}
void SvgButton::resizeEvent(QResizeEvent *e)
{
Button::resizeEvent(e);
this->invalidateContent();
}
void SvgButton::paintContent(QPainter &painter)
{
QSize actualPadding = this->scale() * this->padding_;
QPoint topLeft{actualPadding.width(), actualPadding.height()};
QSize contentSize = this->size() - 2 * actualPadding;
this->svg_->render(&painter, {topLeft, contentSize});
}
QString SvgButton::currentSvgPath() const
{
if (this->theme->isLightTheme())
{
return this->source_.light;
}
return this->source_.dark;
}
} // namespace chatterino
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include "widgets/buttons/Button.hpp"
#include <QString>
class QSvgRenderer;
namespace chatterino {
/// @brief A button displaying SVGs.
///
/// The source can be specified per theme (dark/light)
/// and will get automatically updated when the theme changes.
///
/// To set the size, use #setInnerHeight(). The width is adjusted according
/// to the aspect ratio of the SVG. If the button should scale with the UI,
/// turn on auto-scaling (@see #setAutoScale()), which will scale the button
/// and padding according to the current UI-scale (linearly).
///
/// The button has #padding() around the SVG.
class SvgButton : public Button
{
Q_OBJECT
public:
/// Source configuration for the button
struct Src {
/// Source to load in a dark theme
QString dark;
/// Source to load in a light theme
QString light;
};
[[nodiscard]] SvgButton(Src source, BaseWidget *parent = nullptr,
QSize padding = {6, 3});
/// Returns the current source configuration.
[[nodiscard]] Src source() const;
/// Setter for #source()
void setSource(Src source);
/// @brief Returns the padding inside the button.
///
/// `width` is the padding applied horizontally (left and right).
/// `height` is the padding applied vertically (top and bottom).
///
/// By default, the button has a vertical padding of 3 and a horizontal padding of 6.
[[nodiscard]] QSize padding() const;
/// Setter for #padding()
void setPadding(QSize padding);
protected:
void themeChangedEvent() override;
void scaleChangedEvent(float scale) override;
void resizeEvent(QResizeEvent *e) override;
void paintContent(QPainter &painter) override;
private:
[[nodiscard]] QString currentSvgPath() const;
Src source_;
QSvgRenderer *svg_;
QSize padding_;
};
} // namespace chatterino
@@ -1,4 +1,4 @@
#include "widgets/helper/TitlebarButton.hpp" #include "widgets/buttons/TitlebarButton.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
@@ -6,9 +6,11 @@
namespace chatterino { namespace chatterino {
TitleBarButton::TitleBarButton() TitleBarButton::TitleBarButton(TitleBarButtonStyle style)
: Button(nullptr) : DimButton(nullptr)
, style_(style)
{ {
this->setContentCacheEnabled(true);
} }
TitleBarButtonStyle TitleBarButton::getButtonStyle() const TitleBarButtonStyle TitleBarButton::getButtonStyle() const
@@ -16,17 +18,20 @@ TitleBarButtonStyle TitleBarButton::getButtonStyle() const
return this->style_; return this->style_;
} }
void TitleBarButton::setButtonStyle(TitleBarButtonStyle _style) void TitleBarButton::setButtonStyle(TitleBarButtonStyle style)
{ {
this->style_ = _style; this->style_ = style;
this->update(); this->invalidateContent();
} }
void TitleBarButton::paintEvent(QPaintEvent *event) void TitleBarButton::themeChangedEvent()
{ {
QPainter painter(this); this->invalidateContent();
}
painter.setOpacity(this->getCurrentDimAmount()); void TitleBarButton::paintContent(QPainter &painter)
{
painter.setOpacity(this->currentContentOpacity());
QColor color = this->theme->window.text; QColor color = this->theme->window.text;
QColor background = this->theme->window.background; QColor background = this->theme->window.background;
@@ -65,32 +70,8 @@ void TitleBarButton::paintEvent(QPaintEvent *event)
painter.drawLine(rect.topRight(), rect.bottomLeft()); painter.drawLine(rect.topRight(), rect.bottomLeft());
break; break;
} }
case TitleBarButtonStyle::User: {
color = "#999";
painter.setRenderHint(QPainter::Antialiasing);
auto a = xD / 3;
QPainterPath path;
painter.save();
painter.translate(3, 3);
path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0);
path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180);
painter.fillPath(path, color);
painter.setBrush(background);
painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a);
painter.setBrush(color);
painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a);
painter.restore();
break;
}
case TitleBarButtonStyle::Settings: { case TitleBarButtonStyle::Settings: {
painter.setPen(color);
color = "#999"; color = "#999";
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
@@ -121,7 +102,7 @@ void TitleBarButton::paintEvent(QPaintEvent *event)
default:; default:;
} }
this->paintButton(painter); painter.setOpacity(1.0);
} }
void TitleBarButton::ncEnter() void TitleBarButton::ncEnter()
@@ -1,27 +1,25 @@
#pragma once #pragma once
#include "widgets/helper/Button.hpp" #include "widgets/buttons/DimButton.hpp"
namespace chatterino { namespace chatterino {
enum class TitleBarButtonStyle { enum class TitleBarButtonStyle : std::uint8_t {
None = 0, None,
Minimize = 1, Minimize,
Maximize = 2, Maximize,
Unmaximize = 4, Unmaximize,
Close = 8, Close,
User = 16, Settings,
Settings = 32,
StreamerMode = 64,
}; };
class TitleBarButton : public Button class TitleBarButton : public DimButton
{ {
public: public:
TitleBarButton(); TitleBarButton(TitleBarButtonStyle style = {});
TitleBarButtonStyle getButtonStyle() const; TitleBarButtonStyle getButtonStyle() const;
void setButtonStyle(TitleBarButtonStyle style_); void setButtonStyle(TitleBarButtonStyle style);
/// Simulate a `mouseEnter` event. /// Simulate a `mouseEnter` event.
void ncEnter(); void ncEnter();
@@ -42,7 +40,9 @@ public:
void ncMouseRelease(QPoint at); void ncMouseRelease(QPoint at);
protected: protected:
void paintEvent(QPaintEvent *) override; void themeChangedEvent() override;
void paintContent(QPainter &painter) override;
private: private:
TitleBarButtonStyle style_{}; TitleBarButtonStyle style_{};
@@ -1,8 +1,8 @@
#include "widgets/helper/TitlebarButtons.hpp" #include "widgets/buttons/TitlebarButtons.hpp"
#ifdef USEWINSDK #ifdef USEWINSDK
# include "widgets/helper/TitlebarButton.hpp" # include "widgets/buttons/TitlebarButton.hpp"
# include <Windows.h> # include <Windows.h>
+1 -1
View File
@@ -11,7 +11,7 @@
#include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchChannel.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "widgets/helper/Button.hpp" #include "widgets/buttons/Button.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/InvisibleSizeGrip.hpp" #include "widgets/helper/InvisibleSizeGrip.hpp"
#include "widgets/Scrollbar.hpp" #include "widgets/Scrollbar.hpp"
-1
View File
@@ -7,7 +7,6 @@
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "widgets/BaseWindow.hpp" #include "widgets/BaseWindow.hpp"
#include "widgets/helper/Button.hpp"
#include "widgets/helper/SettingsDialogTab.hpp" #include "widgets/helper/SettingsDialogTab.hpp"
#include "widgets/settingspages/AboutPage.hpp" #include "widgets/settingspages/AboutPage.hpp"
#include "widgets/settingspages/AccountsPage.hpp" #include "widgets/settingspages/AccountsPage.hpp"
+21 -22
View File
@@ -26,9 +26,10 @@
#include "util/Helpers.hpp" #include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "util/PostToThread.hpp" #include "util/PostToThread.hpp"
#include "widgets/buttons/LabelButton.hpp"
#include "widgets/buttons/PixmapButton.hpp"
#include "widgets/dialogs/EditUserNotesDialog.hpp" #include "widgets/dialogs/EditUserNotesDialog.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/helper/InvisibleSizeGrip.hpp" #include "widgets/helper/InvisibleSizeGrip.hpp"
#include "widgets/helper/Line.hpp" #include "widgets/helper/Line.hpp"
#include "widgets/Label.hpp" #include "widgets/Label.hpp"
@@ -47,7 +48,6 @@
#include <QStringBuilder> #include <QStringBuilder>
namespace { namespace {
constexpr QStringView TEXT_FOLLOWERS = u"Followers: %1"; constexpr QStringView TEXT_FOLLOWERS = u"Followers: %1";
constexpr QStringView TEXT_CREATED = u"Created: %1"; constexpr QStringView TEXT_CREATED = u"Created: %1";
constexpr QStringView TEXT_TITLE = u"%1's Usercard - #%2"; constexpr QStringView TEXT_TITLE = u"%1's Usercard - #%2";
@@ -61,17 +61,17 @@ constexpr qsizetype NOTES_PREVIEW_LENGTH = 80;
using namespace chatterino; using namespace chatterino;
Label *addCopyableLabel(LayoutCreator<QHBoxLayout> box, const char *tooltip, Label *addCopyableLabel(LayoutCreator<QHBoxLayout> box, const char *tooltip,
Button **copyButton = nullptr) PixmapButton **copyButton = nullptr)
{ {
auto label = box.emplace<Label>(); auto label = box.emplace<Label>();
auto button = box.emplace<Button>(); auto button = box.emplace<PixmapButton>();
if (copyButton != nullptr) if (copyButton != nullptr)
{ {
button.assign(copyButton); button.assign(copyButton);
} }
button->setPixmap(getApp()->getThemes()->buttons.copy); button->setPixmap(getApp()->getThemes()->buttons.copy);
button->setScaleIndependentSize(18, 18); button->setScaleIndependentSize(18, 18);
button->setDim(Button::Dim::Lots); button->setDim(DimButton::Dim::Lots);
button->setToolTip(tooltip); button->setToolTip(tooltip);
QObject::connect( QObject::connect(
button.getElement(), &Button::leftClicked, button.getElement(), &Button::leftClicked,
@@ -272,9 +272,9 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, Split *split)
{ {
// avatar // avatar
auto avatar = auto avatar =
head.emplace<Button>(nullptr).assign(&this->ui_.avatarButton); head.emplace<PixmapButton>(nullptr).assign(&this->ui_.avatarButton);
avatar->setScaleIndependentSize(100, 100); avatar->setScaleIndependentSize(100, 100);
avatar->setDim(Button::Dim::None); avatar->setDim(DimButton::Dim::None);
QObject::connect( QObject::connect(
avatar.getElement(), &Button::clicked, avatar.getElement(), &Button::clicked,
[this](Qt::MouseButton button) { [this](Qt::MouseButton button) {
@@ -409,22 +409,20 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, Split *split)
.assign(&this->ui_.ignoreHighlights); .assign(&this->ui_.ignoreHighlights);
// visibility of this is updated in setData // visibility of this is updated in setData
auto notesAdd = user.emplace<LabelButton>("Add notes", this)
user.emplace<EffectLabel2>(this).assign(&this->ui_.notesAdd); .assign(&this->ui_.notesAdd);
notesAdd->getLabel().setText("Add notes"); auto usercard = user.emplace<LabelButton>("Usercard", this)
auto usercard = .assign(&this->ui_.usercardLabel);
user.emplace<EffectLabel2>(this).assign(&this->ui_.usercardLabel); auto mod = user.emplace<PixmapButton>(this);
usercard->getLabel().setText("Usercard");
auto mod = user.emplace<Button>(this);
mod->setPixmap(getResources().buttons.mod); mod->setPixmap(getResources().buttons.mod);
mod->setScaleIndependentSize(30, 30); mod->setScaleIndependentSize(30, 30);
auto unmod = user.emplace<Button>(this); auto unmod = user.emplace<PixmapButton>(this);
unmod->setPixmap(getResources().buttons.unmod); unmod->setPixmap(getResources().buttons.unmod);
unmod->setScaleIndependentSize(30, 30); unmod->setScaleIndependentSize(30, 30);
auto vip = user.emplace<Button>(this); auto vip = user.emplace<PixmapButton>(this);
vip->setPixmap(getResources().buttons.vip); vip->setPixmap(getResources().buttons.vip);
vip->setScaleIndependentSize(30, 30); vip->setScaleIndependentSize(30, 30);
auto unvip = user.emplace<Button>(this); auto unvip = user.emplace<PixmapButton>(this);
unvip->setPixmap(getResources().buttons.unvip); unvip->setPixmap(getResources().buttons.unvip);
unvip->setScaleIndependentSize(30, 30); unvip->setScaleIndependentSize(30, 30);
@@ -762,7 +760,7 @@ void UserInfoPopup::installEvents()
// user notes // user notes
QObject::connect( QObject::connect(
this->ui_.notesAdd, &EffectLabel2::clicked, [this]() mutable { this->ui_.notesAdd, &LabelButton::clicked, [this]() mutable {
if (this->editUserNotesDialog_.isNull()) if (this->editUserNotesDialog_.isNull())
{ {
this->editUserNotesDialog_ = new EditUserNotesDialog(this); this->editUserNotesDialog_ = new EditUserNotesDialog(this);
@@ -1185,7 +1183,7 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
const auto addButton = [&](Action action, const QString &title, const auto addButton = [&](Action action, const QString &title,
const QPixmap &pixmap) { const QPixmap &pixmap) {
auto button = addLayout(title).emplace<Button>(nullptr); auto button = addLayout(title).emplace<PixmapButton>(nullptr);
button->setPixmap(pixmap); button->setPixmap(pixmap);
button->setScaleIndependentSize(buttonHeight, buttonHeight); button->setScaleIndependentSize(buttonHeight, buttonHeight);
button->setBorderColor(QColor(255, 255, 255, 127)); button->setBorderColor(QColor(255, 255, 255, 127));
@@ -1201,8 +1199,9 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
for (const auto &item : getSettings()->timeoutButtons.getValue()) for (const auto &item : getSettings()->timeoutButtons.getValue())
{ {
auto a = hbox.emplace<EffectLabel2>(); auto a = hbox.emplace<LabelButton>();
a->getLabel().setText(QString::number(item.second) + item.first); a->setPadding({0, 0});
a->setText(QString::number(item.second) + item.first);
a->setScaleIndependentSize(buttonWidth, buttonHeight); a->setScaleIndependentSize(buttonWidth, buttonHeight);
a->setBorderColor(borderColor); a->setBorderColor(borderColor);
@@ -1210,7 +1209,7 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
const auto pair = const auto pair =
std::make_pair(Action::Timeout, calculateTimeoutDuration(item)); std::make_pair(Action::Timeout, calculateTimeoutDuration(item));
QObject::connect(a.getElement(), &EffectLabel2::leftClicked, QObject::connect(a.getElement(), &LabelButton::leftClicked,
[this, pair] { [this, pair] {
this->buttonClicked.invoke(pair); this->buttonClicked.invoke(pair);
}); });
+6 -5
View File
@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "widgets/DraggablePopup.hpp" #include "widgets/DraggablePopup.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include <pajlada/signals/scoped-connection.hpp> #include <pajlada/signals/scoped-connection.hpp>
#include <pajlada/signals/signal.hpp> #include <pajlada/signals/signal.hpp>
@@ -17,6 +16,8 @@ class Label;
class EditUserNotesDialog; class EditUserNotesDialog;
class ChannelView; class ChannelView;
class Split; class Split;
class LabelButton;
class PixmapButton;
class UserInfoPopup final : public DraggablePopup class UserInfoPopup final : public DraggablePopup
{ {
@@ -72,8 +73,8 @@ private:
const bool closeAutomatically_; const bool closeAutomatically_;
struct { struct {
Button *avatarButton = nullptr; PixmapButton *avatarButton = nullptr;
Button *localizedNameCopyButton = nullptr; PixmapButton *localizedNameCopyButton = nullptr;
Label *nameLabel = nullptr; Label *nameLabel = nullptr;
Label *localizedNameLabel = nullptr; Label *localizedNameLabel = nullptr;
@@ -87,12 +88,12 @@ private:
QCheckBox *block = nullptr; QCheckBox *block = nullptr;
QCheckBox *ignoreHighlights = nullptr; QCheckBox *ignoreHighlights = nullptr;
Label *notesPreview = nullptr; Label *notesPreview = nullptr;
EffectLabel2 *notesAdd = nullptr; LabelButton *notesAdd = nullptr;
Label *noMessagesLabel = nullptr; Label *noMessagesLabel = nullptr;
ChannelView *latestMessages = nullptr; ChannelView *latestMessages = nullptr;
EffectLabel2 *usercardLabel = nullptr; LabelButton *usercardLabel = nullptr;
} ui_; } ui_;
QPointer<EditUserNotesDialog> editUserNotesDialog_; QPointer<EditUserNotesDialog> editUserNotesDialog_;
-108
View File
@@ -1,108 +0,0 @@
#pragma once
#include "widgets/BaseWidget.hpp"
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPoint>
#include <QSvgRenderer>
#include <QTimer>
#include <QWidget>
#include <optional>
namespace chatterino {
class Button : public BaseWidget
{
Q_OBJECT
struct ClickEffect {
double progress = 0.0;
QPoint position;
ClickEffect(QPoint _position)
: position(_position)
{
}
};
public:
enum class Dim { None, Some, Lots };
Button(BaseWidget *parent = nullptr);
void setMouseEffectColor(std::optional<QColor> color);
void setPixmap(const QPixmap &pixmap_);
void setSvgResource(const QString &resourcePath);
const QPixmap &getPixmap() const;
void setDim(Dim value);
Dim getDim() const;
qreal getCurrentDimAmount() const;
void setEnable(bool value);
bool getEnable() const;
void setEnableMargin(bool value);
bool getEnableMargin() const;
void setBorderColor(const QColor &color);
const QColor &getBorderColor() const;
void setMenu(std::unique_ptr<QMenu> menu);
Q_SIGNALS:
void leftClicked();
void clicked(Qt::MouseButton button);
void leftMousePress();
protected:
void paintEvent(QPaintEvent * /*event*/) override;
/// Paint this button.
/// This is intended for child classes that may want to paint the overlay.
/// This function should be used after rendering the custom button,
/// because the painter's state will be modified by this function.
void paintButton(QPainter &painter);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent * /*event*/) override;
#else
void enterEvent(QEvent * /*event*/) override;
#endif
void leaveEvent(QEvent *) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void fancyPaint(QPainter &painter);
bool enabled_{true};
bool selected_{false};
bool mouseOver_{false};
bool mouseDown_{false};
bool menuVisible_{false};
private:
void onMouseEffectTimeout();
void showMenu();
int getMargin() const;
QColor borderColor_{};
QPixmap pixmap_{};
QSvgRenderer *svgRenderer{};
QString svgResourcePath;
QPixmap resizedPixmap_{};
Dim dimPixmap_{Dim::Some};
bool enableMargin_{true};
QPoint mousePos_{};
double hoverMultiplier_{0.0};
QTimer effectTimer_{};
std::vector<ClickEffect> clickEffects_{};
std::optional<QColor> mouseEffectColor_{};
std::unique_ptr<QMenu> menu_{};
};
} // namespace chatterino
+8 -10
View File
@@ -35,10 +35,10 @@
#include "util/IncognitoBrowser.hpp" #include "util/IncognitoBrowser.hpp"
#include "util/QMagicEnum.hpp" #include "util/QMagicEnum.hpp"
#include "util/Twitch.hpp" #include "util/Twitch.hpp"
#include "widgets/buttons/LabelButton.hpp"
#include "widgets/dialogs/ReplyThreadPopup.hpp" #include "widgets/dialogs/ReplyThreadPopup.hpp"
#include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/dialogs/UserInfoPopup.hpp" #include "widgets/dialogs/UserInfoPopup.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/helper/ScrollbarHighlight.hpp" #include "widgets/helper/ScrollbarHighlight.hpp"
#include "widgets/helper/SearchPopup.hpp" #include "widgets/helper/SearchPopup.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
@@ -372,19 +372,17 @@ ChannelView::ChannelView(InternalCtor /*tag*/, QWidget *parent, Split *split,
void ChannelView::initializeLayout() void ChannelView::initializeLayout()
{ {
this->goToBottom_ = new EffectLabel(this, 0); this->goToBottom_ = new LabelButton("More messages below", this);
this->goToBottom_->setStyleSheet( this->goToBottom_->setStyleSheet(
"background-color: rgba(0,0,0,0.66); color: #FFF;"); "background-color: rgba(0,0,0,0.66); color: #FFF;");
this->goToBottom_->getLabel().setText("More messages below");
this->goToBottom_->setVisible(false); this->goToBottom_->setVisible(false);
QObject::connect( QObject::connect(this->goToBottom_, &Button::leftClicked, this, [this] {
this->goToBottom_, &EffectLabel::leftClicked, this, [this] { QTimer::singleShot(180, this, [this] {
QTimer::singleShot(180, this, [this] { this->scrollBar_->scrollToBottom(
this->scrollBar_->scrollToBottom( getSettings()->enableSmoothScrollingNewMessages.getValue());
getSettings()->enableSmoothScrollingNewMessages.getValue());
});
}); });
});
} }
void ChannelView::initializeScrollbar() void ChannelView::initializeScrollbar()
@@ -624,7 +622,7 @@ void ChannelView::scaleChangedEvent(float scale)
std::max<float>( std::max<float>(
0.01, this->logicalDpiX() * this->devicePixelRatioF()); 0.01, this->logicalDpiX() * this->devicePixelRatioF());
#endif #endif
this->goToBottom_->getLabel().setFont( this->goToBottom_->setFont(
getApp()->getFonts()->getFont(FontStyle::UiMedium, factor)); getApp()->getFonts()->getFont(FontStyle::UiMedium, factor));
} }
} }
+2 -2
View File
@@ -40,7 +40,7 @@ enum class MessageElementFlag : int64_t;
using MessageElementFlags = FlagsEnum<MessageElementFlag>; using MessageElementFlags = FlagsEnum<MessageElementFlag>;
class Scrollbar; class Scrollbar;
class EffectLabel; class LabelButton;
struct Link; struct Link;
class MessageLayoutElement; class MessageLayoutElement;
class Split; class Split;
@@ -381,7 +381,7 @@ private:
Split *split_; Split *split_;
Scrollbar *scrollBar_; Scrollbar *scrollBar_;
EffectLabel *goToBottom_{}; LabelButton *goToBottom_{};
bool showScrollBar_ = false; bool showScrollBar_ = false;
FilterSetPtr channelFilters_; FilterSetPtr channelFilters_;
-43
View File
@@ -1,43 +0,0 @@
#include "widgets/helper/EffectLabel.hpp"
#include <QBrush>
#include <QPainter>
namespace chatterino {
EffectLabel::EffectLabel(BaseWidget *parent, int spacing)
: Button(parent)
, label_(this)
{
setLayout(&this->hbox_);
this->label_.setAlignment(Qt::AlignCenter);
this->hbox_.setContentsMargins(0, 0, 0, 0);
this->hbox_.addSpacing(spacing);
this->hbox_.addWidget(&this->label_);
this->hbox_.addSpacing(spacing);
}
EffectLabel2::EffectLabel2(BaseWidget *parent, int padding)
: Button(parent)
, label_(this)
{
auto *hbox = new QHBoxLayout(this);
this->setLayout(hbox);
// this->label_.setAlignment(Qt::AlignCenter);
this->label_.setCentered(true);
hbox->setContentsMargins(0, 0, 0, 0);
// hbox.addSpacing(spacing);
hbox->addWidget(&this->label_);
// hbox.addSpacing(spacing);
}
Label &EffectLabel2::getLabel()
{
return this->label_;
}
} // namespace chatterino
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include "widgets/helper/Button.hpp"
#include "widgets/helper/SignalLabel.hpp"
#include "widgets/Label.hpp"
#include <QHBoxLayout>
#include <QLabel>
#include <QPaintEvent>
#include <QWidget>
namespace chatterino {
class EffectLabel : public Button
{
public:
explicit EffectLabel(BaseWidget *parent = nullptr, int spacing = 6);
SignalLabel &getLabel()
{
return this->label_;
}
private:
QHBoxLayout hbox_;
SignalLabel label_;
};
class EffectLabel2 : public Button
{
public:
explicit EffectLabel2(BaseWidget *parent = nullptr, int padding = 6);
Label &getLabel();
private:
Label label_;
};
} // namespace chatterino
+1
View File
@@ -87,6 +87,7 @@ NotebookTab::NotebookTab(Notebook *notebook)
, notebook_(notebook) , notebook_(notebook)
, menu_(this) , menu_(this)
{ {
this->setContentCacheEnabled(false);
this->setAcceptDrops(true); this->setAcceptDrops(true);
this->positionChangedAnimation_.setEasingCurve( this->positionChangedAnimation_.setEasingCurve(
+4 -1
View File
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "common/Common.hpp" #include "common/Common.hpp"
#include "widgets/helper/Button.hpp" #include "widgets/buttons/Button.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
@@ -91,6 +91,9 @@ protected:
void themeChangedEvent() override; void themeChangedEvent() override;
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
void paintContent(QPainter &painter) override
{
}
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "widgets/helper/TitlebarButton.hpp" #include "widgets/buttons/TitlebarButton.hpp"
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QWidget> #include <QWidget>
+1
View File
@@ -6,6 +6,7 @@
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp" #include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/BasePopup.hpp" #include "widgets/BasePopup.hpp"
#include "widgets/buttons/SignalLabel.hpp"
#include "widgets/layout/FlowLayout.hpp" #include "widgets/layout/FlowLayout.hpp"
#include <QFile> #include <QFile>
@@ -3,7 +3,7 @@
#include "Application.hpp" #include "Application.hpp"
#include "common/ChatterinoSetting.hpp" #include "common/ChatterinoSetting.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "widgets/helper/SignalLabel.hpp" #include "widgets/buttons/SignalLabel.hpp"
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <pajlada/signals/signalholder.hpp> #include <pajlada/signals/signalholder.hpp>
+13 -10
View File
@@ -20,9 +20,10 @@
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/Helpers.hpp" #include "util/Helpers.hpp"
#include "util/LayoutHelper.hpp" #include "util/LayoutHelper.hpp"
#include "widgets/buttons/LabelButton.hpp"
#include "widgets/buttons/PixmapButton.hpp"
#include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/helper/CommonTexts.hpp" #include "widgets/helper/CommonTexts.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/Label.hpp" #include "widgets/Label.hpp"
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp" #include "widgets/splits/SplitContainer.hpp"
@@ -296,13 +297,13 @@ void SplitHeader::initializeLayout()
w->setScaleIndependentSize(8, 4); w->setScaleIndependentSize(8, 4);
}), }),
// mode // mode
this->modeButton_ = makeWidget<EffectLabel>([&](auto w) { this->modeButton_ = makeWidget<LabelButton>([&](auto w) {
w->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); w->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
w->hide(); w->hide();
w->setMenu(this->createChatModeMenu()); w->setMenu(this->createChatModeMenu());
}), }),
// moderator // moderator
this->moderationButton_ = makeWidget<Button>([&](auto w) { this->moderationButton_ = makeWidget<PixmapButton>([&](auto w) {
QObject::connect( QObject::connect(
w, &Button::clicked, this, w, &Button::clicked, this,
[this, w](Qt::MouseButton button) mutable { [this, w](Qt::MouseButton button) mutable {
@@ -323,7 +324,9 @@ void SplitHeader::initializeLayout()
this->split_->setModerationMode( this->split_->setModerationMode(
!moderationMode); !moderationMode);
w->setDim(Button::Dim(moderationMode)); w->setDim(moderationMode
? DimButton::Dim::Some
: DimButton::Dim::None);
} }
break; break;
@@ -337,22 +340,22 @@ void SplitHeader::initializeLayout()
}); });
}), }),
// chatter list // chatter list
this->chattersButton_ = makeWidget<Button>([&](auto w) { this->chattersButton_ = makeWidget<PixmapButton>([&](auto w) {
QObject::connect(w, &Button::leftClicked, this, [this]() { QObject::connect(w, &Button::leftClicked, this, [this]() {
this->split_->showChatterList(); this->split_->showChatterList();
}); });
}), }),
// dropdown // dropdown
this->dropdownButton_ = makeWidget<Button>([&](auto w) { this->dropdownButton_ = makeWidget<PixmapButton>([&](auto w) {
/// XXX: this never gets disconnected /// XXX: this never gets disconnected
QObject::connect(w, &Button::leftMousePress, this, [this] { QObject::connect(w, &Button::leftMousePress, this, [this] {
this->dropdownButton_->setMenu(this->createMainMenu()); this->dropdownButton_->setMenu(this->createMainMenu());
}); });
}), }),
// add split // add split
this->addButton_ = makeWidget<Button>([&](auto w) { this->addButton_ = makeWidget<PixmapButton>([&](auto w) {
w->setPixmap(getResources().buttons.addSplitDark); w->setPixmap(getResources().buttons.addSplitDark);
w->setEnableMargin(false); w->setMarginEnabled(false);
QObject::connect(w, &Button::leftClicked, this, [this]() { QObject::connect(w, &Button::leftClicked, this, [this]() {
this->split_->addSibling(); this->split_->addSibling();
@@ -725,7 +728,7 @@ void SplitHeader::updateRoomModes()
if (auto *twitchChannel = if (auto *twitchChannel =
dynamic_cast<TwitchChannel *>(this->split_->getChannel().get())) dynamic_cast<TwitchChannel *>(this->split_->getChannel().get()))
{ {
this->modeButton_->setEnable(twitchChannel->hasModRights()); this->modeButton_->setEnabled(twitchChannel->hasModRights());
QString text; QString text;
{ {
@@ -746,7 +749,7 @@ void SplitHeader::updateRoomModes()
if (!text.isEmpty()) if (!text.isEmpty())
{ {
this->modeButton_->getLabel().setText(text); this->modeButton_->setText(text);
this->modeButton_->show(); this->modeButton_->show();
} }
else else
+7 -8
View File
@@ -16,8 +16,8 @@
namespace chatterino { namespace chatterino {
class Button; class PixmapButton;
class EffectLabel; class LabelButton;
class Label; class Label;
class Split; class Split;
@@ -54,7 +54,6 @@ protected:
private: private:
void initializeLayout(); void initializeLayout();
void initializeModeSignals(EffectLabel &label);
std::unique_ptr<QMenu> createMainMenu(); std::unique_ptr<QMenu> createMainMenu();
std::unique_ptr<QMenu> createChatModeMenu(); std::unique_ptr<QMenu> createChatModeMenu();
@@ -76,19 +75,19 @@ private:
std::chrono::steady_clock::time_point lastReloadedSubEmotes_; std::chrono::steady_clock::time_point lastReloadedSubEmotes_;
// ui // ui
Button *dropdownButton_{}; PixmapButton *dropdownButton_{};
Label *titleLabel_{}; Label *titleLabel_{};
EffectLabel *modeButton_{}; LabelButton *modeButton_{};
QAction *modeActionSetEmote{}; QAction *modeActionSetEmote{};
QAction *modeActionSetSub{}; QAction *modeActionSetSub{};
QAction *modeActionSetSlow{}; QAction *modeActionSetSlow{};
QAction *modeActionSetR9k{}; QAction *modeActionSetR9k{};
QAction *modeActionSetFollowers{}; QAction *modeActionSetFollowers{};
Button *moderationButton_{}; PixmapButton *moderationButton_{};
Button *chattersButton_{}; PixmapButton *chattersButton_{};
Button *addButton_{}; PixmapButton *addButton_{};
// states // states
QPoint dragStart_{}; QPoint dragStart_{};
+25 -33
View File
@@ -10,13 +10,15 @@
#include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchIrcServer.hpp" #include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Fonts.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "singletons/Theme.hpp" #include "singletons/Theme.hpp"
#include "util/Helpers.hpp" #include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "widgets/buttons/LabelButton.hpp"
#include "widgets/buttons/SvgButton.hpp"
#include "widgets/dialogs/EmotePopup.hpp" #include "widgets/dialogs/EmotePopup.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/helper/MessageView.hpp" #include "widgets/helper/MessageView.hpp"
#include "widgets/helper/ResizingTextEdit.hpp" #include "widgets/helper/ResizingTextEdit.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
@@ -112,8 +114,14 @@ void SplitInput::initLayout()
replyHbox->addStretch(1); replyHbox->addStretch(1);
auto replyCancelButton = auto replyCancelButton = replyHbox
replyHbox.emplace<Button>().assign(&this->ui_.cancelReplyButton); .emplace<SvgButton>(
SvgButton::Src{
.dark = ":/buttons/cancel.svg",
.light = ":/buttons/cancelDark.svg",
},
nullptr, QSize{4, 0})
.assign(&this->ui_.cancelReplyButton);
replyCancelButton->hide(); replyCancelButton->hide();
replyLabel->hide(); replyLabel->hide();
@@ -133,11 +141,10 @@ void SplitInput::initLayout()
connect(textEdit.getElement(), &ResizingTextEdit::textChanged, this, connect(textEdit.getElement(), &ResizingTextEdit::textChanged, this,
&SplitInput::editTextChanged); &SplitInput::editTextChanged);
hboxLayout.emplace<EffectLabel>().assign(&this->ui_.sendButton); hboxLayout.emplace<LabelButton>("SEND").assign(&this->ui_.sendButton);
this->ui_.sendButton->getLabel().setText("SEND");
this->ui_.sendButton->hide(); this->ui_.sendButton->hide();
QObject::connect(this->ui_.sendButton, &EffectLabel::leftClicked, [this] { QObject::connect(this->ui_.sendButton, &Button::leftClicked, [this] {
std::vector<QString> arguments; std::vector<QString> arguments;
this->handleSendMessage(arguments); this->handleSendMessage(arguments);
}); });
@@ -164,7 +171,13 @@ void SplitInput::initLayout()
textEditLength->setAlignment(Qt::AlignRight); textEditLength->setAlignment(Qt::AlignRight);
box->addStretch(1); box->addStretch(1);
box.emplace<Button>().assign(&this->ui_.emoteButton); box.emplace<SvgButton>(
SvgButton::Src{
.dark = ":/buttons/emote.svg",
.light = ":/buttons/emoteDark.svg",
},
nullptr, QSize{6, 3})
.assign(&this->ui_.emoteButton);
} }
// ---- misc // ---- misc
@@ -186,15 +199,14 @@ void SplitInput::initLayout()
}); });
// open emote popup // open emote popup
QObject::connect(this->ui_.emoteButton, &EffectLabel::leftClicked, [this] { QObject::connect(this->ui_.emoteButton, &Button::leftClicked, [this] {
this->openEmotePopup(); this->openEmotePopup();
}); });
// remove reply target // clear input and remove reply thread
QObject::connect(this->ui_.cancelReplyButton, &EffectLabel::leftClicked, QObject::connect(this->ui_.cancelReplyButton, &Button::leftClicked, [this] {
[this] { this->setReply(nullptr);
this->setReply(nullptr); });
});
// Forward selection change signal // Forward selection change signal
QObject::connect(this->ui_.textEdit, &QTextEdit::copyAvailable, QObject::connect(this->ui_.textEdit, &QTextEdit::copyAvailable,
@@ -256,8 +268,6 @@ void SplitInput::themeChangedEvent()
QPalette::PlaceholderText, QPalette::PlaceholderText,
this->theme->messages.textColors.chatPlaceholder); this->theme->messages.textColors.chatPlaceholder);
this->updateEmoteButton();
this->updateCancelReplyButton();
this->ui_.textEditLength->setPalette(palette); this->ui_.textEditLength->setPalette(palette);
this->ui_.textEdit->setStyleSheet(this->theme->splits.input.styleSheet); this->ui_.textEdit->setStyleSheet(this->theme->splits.input.styleSheet);
@@ -284,15 +294,6 @@ void SplitInput::updateEmoteButton()
{ {
auto scale = this->scale(); auto scale = this->scale();
if (this->theme->isLightTheme())
{
this->ui_.emoteButton->setSvgResource(":/buttons/emoteDark.svg");
}
else
{
this->ui_.emoteButton->setSvgResource(":/buttons/emote.svg");
}
this->ui_.emoteButton->setFixedHeight(int(18 * scale)); this->ui_.emoteButton->setFixedHeight(int(18 * scale));
// Make button slightly wider so it's easier to click // Make button slightly wider so it's easier to click
this->ui_.emoteButton->setFixedWidth(int(24 * scale)); this->ui_.emoteButton->setFixedWidth(int(24 * scale));
@@ -302,15 +303,6 @@ void SplitInput::updateCancelReplyButton()
{ {
float scale = this->scale(); float scale = this->scale();
if (this->theme->isLightTheme())
{
this->ui_.cancelReplyButton->setSvgResource(":/buttons/cancelDark.svg");
}
else
{
this->ui_.cancelReplyButton->setSvgResource(":/buttons/cancel.svg");
}
this->ui_.cancelReplyButton->setEnableMargin(false);
this->ui_.cancelReplyButton->setFixedHeight(int(12 * scale)); this->ui_.cancelReplyButton->setFixedHeight(int(12 * scale));
this->ui_.cancelReplyButton->setFixedWidth(int(20 * scale)); this->ui_.cancelReplyButton->setFixedWidth(int(20 * scale));
} }
+5 -5
View File
@@ -2,7 +2,6 @@
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "widgets/BaseWidget.hpp" #include "widgets/BaseWidget.hpp"
#include "widgets/helper/Button.hpp"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@@ -20,10 +19,11 @@ namespace chatterino {
class Split; class Split;
class EmotePopup; class EmotePopup;
class InputCompletionPopup; class InputCompletionPopup;
class EffectLabel;
class MessageView; class MessageView;
class LabelButton;
class ResizingTextEdit; class ResizingTextEdit;
class ChannelView; class ChannelView;
class SvgButton;
enum class CompletionKind; enum class CompletionKind;
class SplitInput : public BaseWidget class SplitInput : public BaseWidget
@@ -144,15 +144,15 @@ protected:
QHBoxLayout *replyHbox; QHBoxLayout *replyHbox;
MessageView *replyMessage; MessageView *replyMessage;
QLabel *replyLabel; QLabel *replyLabel;
Button *cancelReplyButton; SvgButton *cancelReplyButton;
// input widgets // input widgets
QWidget *inputWrapper; QWidget *inputWrapper;
QHBoxLayout *inputHbox; QHBoxLayout *inputHbox;
ResizingTextEdit *textEdit; ResizingTextEdit *textEdit;
QLabel *textEditLength; QLabel *textEditLength;
EffectLabel *sendButton; LabelButton *sendButton;
Button *emoteButton; SvgButton *emoteButton;
} ui_; } ui_;
MessagePtr replyTarget_ = nullptr; MessagePtr replyTarget_ = nullptr;