added ui scaling

This commit is contained in:
fourtf 2018-06-11 15:04:54 +02:00
parent 9b26fce781
commit ea9f9e7f18
27 changed files with 488 additions and 271 deletions

View file

@ -140,7 +140,6 @@ SOURCES += \
src/widgets/emotepopup.cpp \
src/widgets/helper/channelview.cpp \
src/widgets/helper/droppreview.cpp \
src/widgets/helper/label.cpp \
src/widgets/helper/notebookbutton.cpp \
src/widgets/helper/notebooktab.cpp \
src/widgets/helper/resizingtextedit.cpp \
@ -217,7 +216,8 @@ SOURCES += \
src/util/emotemap.cpp \
src/providers/irc/ircconnection2.cpp \
src/widgets/userinfopopup.cpp \
src/widgets/welcomedialog.cpp
src/widgets/welcomedialog.cpp \
src/widgets/label.cpp
HEADERS += \
src/precompiled_header.hpp \
@ -285,7 +285,6 @@ HEADERS += \
src/widgets/emotepopup.hpp \
src/widgets/helper/channelview.hpp \
src/widgets/helper/droppreview.hpp \
src/widgets/helper/label.hpp \
src/widgets/helper/notebookbutton.hpp \
src/widgets/helper/notebooktab.hpp \
src/widgets/helper/resizingtextedit.hpp \
@ -377,7 +376,9 @@ HEADERS += \
src/providers/irc/ircconnection2.hpp \
src/widgets/helper/line.hpp \
src/widgets/userinfopopup.hpp \
src/widgets/welcomedialog.hpp
src/widgets/welcomedialog.hpp \
src/util/clamp.hpp \
src/widgets/label.hpp
RESOURCES += \
resources/resources.qrc

View file

@ -121,7 +121,7 @@ FontManager::FontData FontManager::createFontData(Type type, float scale)
static std::unordered_map<Type, UiFontData> defaultSize{
{Tiny, {8, "Monospace", false, QFont::Normal}},
{UiMedium, {int(12 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
{UiMedium, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
{UiTabs, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
};

View file

@ -44,6 +44,7 @@ public:
IntSetting collpseMessagesMinLines = {"/appearance/messages/collapseMessagesMinLines", 0};
BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground",
false};
IntSetting uiScale = {"/appearance/uiScale", 0};
BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false};
BoolSetting showTabCloseButton = {"/appearance/showTabCloseButton", true};
BoolSetting hidePreferencesButton = {"/appearance/hidePreferencesButton", false};

View file

@ -7,6 +7,7 @@
#include "singletons/pathmanager.hpp"
#include "singletons/thememanager.hpp"
#include "util/assertinguithread.hpp"
#include "util/clamp.hpp"
#include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/settingsdialog.hpp"
@ -410,5 +411,53 @@ void WindowManager::incGeneration()
this->generation++;
}
int WindowManager::clampUiScale(int scale)
{
return util::clamp(scale, uiScaleMin, uiScaleMax);
}
float WindowManager::getUiScaleValue()
{
return getUiScaleValue(getApp()->settings->uiScale.getValue());
}
float WindowManager::getUiScaleValue(int scale)
{
switch (clampUiScale(scale)) {
case -5:
return 0.5f;
case -4:
return 0.6f;
case -3:
return 0.7f;
case -2:
return 0.8f;
case -1:
return 0.9f;
case 0:
return 1;
case 1:
return 1.2f;
case 2:
return 1.4f;
case 3:
return 1.6f;
case 4:
return 1.6f;
case 5:
return 2;
case 6:
return 2.33f;
case 7:
return 2.66f;
case 8:
return 3;
case 9:
return 3.5f;
case 10:
return 4;
}
}
} // namespace singletons
} // namespace chatterino

View file

@ -42,6 +42,12 @@ public:
pajlada::Signals::NoArgSignal repaintGifs;
pajlada::Signals::Signal<Channel *> layout;
static const int uiScaleMin = -5;
static const int uiScaleMax = 10;
static int clampUiScale(int scale);
static float getUiScaleValue();
static float getUiScaleValue(int scale);
private:
bool initialized = false;

23
src/util/clamp.hpp Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <algorithm>
namespace chatterino {
namespace util {
// http://en.cppreference.com/w/cpp/algorithm/clamp
template <class T>
constexpr const T &clamp(const T &v, const T &lo, const T &hi)
{
return clamp(v, lo, hi, std::less<>());
}
template <class T, class Compare>
constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp)
{
return assert(!comp(hi, lo)), comp(v, lo) ? lo : comp(hi, v) ? hi : v;
}
} // namespace util
} // namespace chatterino

View file

@ -1,10 +1,15 @@
#include "basewindow.hpp"
#include "application.hpp"
#include "boost/algorithm/algorithm.hpp"
#include "debug/log.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/windowmanager.hpp"
#include "util/nativeeventhelper.hpp"
#include "util/posttothread.hpp"
#include "widgets/helper/rippleeffectlabel.hpp"
#include "widgets/helper/shortcut.hpp"
#include "widgets/label.hpp"
#include "widgets/tooltipwidget.hpp"
#include <QApplication>
@ -35,25 +40,33 @@ namespace widgets {
BaseWindow::BaseWindow(QWidget *parent, Flags _flags)
: BaseWidget(parent,
Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : Qt::WindowFlags()))
, enableCustomFrame(_flags & EnableCustomFrame)
, frameless(_flags & Frameless)
, flags(_flags)
, enableCustomFrame_(_flags & EnableCustomFrame)
, frameless_(_flags & Frameless)
, flags_(_flags)
{
if (this->frameless) {
this->enableCustomFrame = false;
if (this->frameless_) {
this->enableCustomFrame_ = false;
this->setWindowFlag(Qt::FramelessWindowHint);
}
if (this->flags & DeleteOnFocusOut) {
if (this->flags_ & DeleteOnFocusOut) {
this->setAttribute(Qt::WA_DeleteOnClose);
}
this->init();
this->connections_.managedConnect(
getApp()->settings->uiScale.getValueChangedSignal(),
[this](auto, auto) { util::postToThread([this] { this->updateScale(); }); });
this->updateScale();
CreateWindowShortcut(this, "CTRL+0", [] { getApp()->settings->uiScale.setValue(1); });
}
BaseWindow::Flags BaseWindow::getFlags()
{
return this->flags;
return this->flags_;
}
void BaseWindow::init()
@ -68,23 +81,23 @@ void BaseWindow::init()
layout->setSpacing(0);
this->setLayout(layout);
{
if (!this->frameless) {
QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout();
if (!this->frameless_) {
QHBoxLayout *buttonLayout = this->ui_.titlebarBox = new QHBoxLayout();
buttonLayout->setMargin(0);
layout->addLayout(buttonLayout);
// title
QLabel *title = new QLabel(" Chatterino");
Label *title = new Label("Chatterino");
QObject::connect(this, &QWidget::windowTitleChanged,
[title](const QString &text) { title->setText(" " + text); });
[title](const QString &text) { title->setText(text); });
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
policy.setHorizontalStretch(1);
// title->setBaseSize(0, 0);
title->setScaledContents(true);
// title->setScaledContents(true);
title->setSizePolicy(policy);
buttonLayout->addWidget(title);
this->ui.titleLabel = title;
this->ui_.titleLabel = title;
// buttons
TitleBarButton *_minButton = new TitleBarButton;
@ -105,13 +118,13 @@ void BaseWindow::init()
QObject::connect(_exitButton, &TitleBarButton::clicked, this,
[this] { this->close(); });
this->ui.minButton = _minButton;
this->ui.maxButton = _maxButton;
this->ui.exitButton = _exitButton;
this->ui_.minButton = _minButton;
this->ui_.maxButton = _maxButton;
this->ui_.exitButton = _exitButton;
this->ui.buttons.push_back(_minButton);
this->ui.buttons.push_back(_maxButton);
this->ui.buttons.push_back(_exitButton);
this->ui_.buttons.push_back(_minButton);
this->ui_.buttons.push_back(_maxButton);
this->ui_.buttons.push_back(_exitButton);
// buttonLayout->addStretch(1);
buttonLayout->addWidget(_minButton);
@ -120,8 +133,8 @@ void BaseWindow::init()
buttonLayout->setSpacing(0);
}
}
this->ui.layoutBase = new BaseWidget(this);
layout->addWidget(this->ui.layoutBase);
this->ui_.layoutBase = new BaseWidget(this);
layout->addWidget(this->ui_.layoutBase);
}
// DPI
@ -134,7 +147,7 @@ void BaseWindow::init()
#ifdef USEWINSDK
// fourtf: don't ask me why we need to delay this
if (!(this->flags & Flags::TopMost)) {
if (!(this->flags_ & Flags::TopMost)) {
QTimer::singleShot(1, this, [this] {
getApp()->settings->windowTopMost.connect([this](bool topMost, auto) {
::SetWindowPos(HWND(this->winId()), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0,
@ -151,18 +164,18 @@ void BaseWindow::init()
void BaseWindow::setStayInScreenRect(bool value)
{
this->stayInScreenRect = value;
this->stayInScreenRect_ = value;
}
bool BaseWindow::getStayInScreenRect() const
{
return this->stayInScreenRect;
return this->stayInScreenRect_;
}
QWidget *BaseWindow::getLayoutContainer()
{
if (this->hasCustomWindowFrame()) {
return this->ui.layoutBase;
return this->ui_.layoutBase;
} else {
return this;
}
@ -173,7 +186,7 @@ bool BaseWindow::hasCustomWindowFrame()
#ifdef USEWINSDK
static bool isWin8 = IsWindows8OrGreater();
return isWin8 && this->enableCustomFrame;
return isWin8 && this->enableCustomFrame_;
#else
return false;
#endif
@ -187,14 +200,14 @@ void BaseWindow::themeRefreshEvent()
palette.setColor(QPalette::Foreground, this->themeManager->window.text);
this->setPalette(palette);
if (this->ui.titleLabel) {
if (this->ui_.titleLabel) {
QPalette palette_title;
palette_title.setColor(QPalette::Foreground,
this->themeManager->isLightTheme() ? "#333" : "#ccc");
this->ui.titleLabel->setPalette(palette_title);
this->ui_.titleLabel->setPalette(palette_title);
}
for (RippleEffectButton *button : this->ui.buttons) {
for (RippleEffectButton *button : this->ui_.buttons) {
button->setMouseEffectColor(this->themeManager->window.text);
}
} else {
@ -208,7 +221,7 @@ void BaseWindow::themeRefreshEvent()
bool BaseWindow::event(QEvent *event)
{
if (event->type() == QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) {
if (this->flags & DeleteOnFocusOut) {
if (this->flags_ & DeleteOnFocusOut) {
this->close();
}
}
@ -216,14 +229,27 @@ bool BaseWindow::event(QEvent *event)
return QWidget::event(event);
}
void BaseWindow::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::ControlModifier) {
if (event->delta() > 0) {
getApp()->settings->uiScale.setValue(singletons::WindowManager::clampUiScale(
getApp()->settings->uiScale.getValue() + 1));
} else {
getApp()->settings->uiScale.setValue(singletons::WindowManager::clampUiScale(
getApp()->settings->uiScale.getValue() - 1));
}
}
}
void BaseWindow::addTitleBarButton(const TitleBarButton::Style &style,
std::function<void()> onClicked)
{
TitleBarButton *button = new TitleBarButton;
button->setScaleIndependantSize(30, 30);
this->ui.buttons.push_back(button);
this->ui.titlebarBox->insertWidget(1, button);
this->ui_.buttons.push_back(button);
this->ui_.titlebarBox->insertWidget(1, button);
button->setButtonStyle(style);
QObject::connect(button, &TitleBarButton::clicked, this, [onClicked] { onClicked(); });
@ -234,8 +260,8 @@ RippleEffectLabel *BaseWindow::addTitleBarLabel(std::function<void()> onClicked)
RippleEffectLabel *button = new RippleEffectLabel;
button->setScaleIndependantHeight(30);
this->ui.buttons.push_back(button);
this->ui.titlebarBox->insertWidget(1, button);
this->ui_.buttons.push_back(button);
this->ui_.titlebarBox->insertWidget(1, button);
QObject::connect(button, &RippleEffectLabel::clicked, this, [onClicked] { onClicked(); });
@ -247,8 +273,8 @@ void BaseWindow::changeEvent(QEvent *)
TooltipWidget::getInstance()->hide();
#ifdef USEWINSDK
if (this->ui.maxButton) {
this->ui.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized
if (this->ui_.maxButton) {
this->ui_.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized
? TitleBarButton::Unmaximize
: TitleBarButton::Maximize);
}
@ -284,7 +310,7 @@ void BaseWindow::resizeEvent(QResizeEvent *)
void BaseWindow::moveIntoDesktopRect(QWidget *parent)
{
if (!this->stayInScreenRect)
if (!this->stayInScreenRect_)
return;
// move the widget into the screen geometry if it's not already in there
@ -326,7 +352,8 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
this->resize(static_cast<int>(this->width() * resizeScale),
static_cast<int>(this->height() * resizeScale));
this->setScale(_scale);
this->nativeScale_ = _scale;
this->updateScale();
return true;
}
@ -419,13 +446,13 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
bool client = false;
QPoint point(x - winrect.left, y - winrect.top);
for (QWidget *widget : this->ui.buttons) {
for (QWidget *widget : this->ui_.buttons) {
if (widget->geometry().contains(point)) {
client = true;
}
}
if (this->ui.layoutBase->geometry().contains(point)) {
if (this->ui_.layoutBase->geometry().contains(point)) {
client = true;
}
@ -449,8 +476,8 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
void BaseWindow::showEvent(QShowEvent *event)
{
if (!this->shown && this->isVisible() && this->hasCustomWindowFrame()) {
this->shown = true;
if (!this->shown_ && this->isVisible() && this->hasCustomWindowFrame()) {
this->shown_ = true;
// SetWindowLongPtr((HWND)this->winId(), GWL_STYLE,
// WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX |
// WS_MINIMIZEBOX);
@ -470,7 +497,7 @@ void BaseWindow::scaleChangedEvent(float)
void BaseWindow::paintEvent(QPaintEvent *)
{
if (this->frameless) {
if (this->frameless_) {
QPainter painter(this);
painter.setPen(QColor("#999"));
@ -489,25 +516,32 @@ void BaseWindow::paintEvent(QPaintEvent *)
#endif
}
void BaseWindow::updateScale()
{
this->setScale(this->nativeScale_ * (this->flags_ & DisableCustomScaling
? 1
: getApp()->windows->getUiScaleValue()));
}
void BaseWindow::calcButtonsSizes()
{
if (!this->shown) {
if (!this->shown_) {
return;
}
if ((this->width() / this->getScale()) < 300) {
if (this->ui.minButton)
this->ui.minButton->setScaleIndependantSize(30, 30);
if (this->ui.maxButton)
this->ui.maxButton->setScaleIndependantSize(30, 30);
if (this->ui.exitButton)
this->ui.exitButton->setScaleIndependantSize(30, 30);
if (this->ui_.minButton)
this->ui_.minButton->setScaleIndependantSize(30, 30);
if (this->ui_.maxButton)
this->ui_.maxButton->setScaleIndependantSize(30, 30);
if (this->ui_.exitButton)
this->ui_.exitButton->setScaleIndependantSize(30, 30);
} else {
if (this->ui.minButton)
this->ui.minButton->setScaleIndependantSize(46, 30);
if (this->ui.maxButton)
this->ui.maxButton->setScaleIndependantSize(46, 30);
if (this->ui.exitButton)
this->ui.exitButton->setScaleIndependantSize(46, 30);
if (this->ui_.minButton)
this->ui_.minButton->setScaleIndependantSize(46, 30);
if (this->ui_.maxButton)
this->ui_.maxButton->setScaleIndependantSize(46, 30);
if (this->ui_.exitButton)
this->ui_.exitButton->setScaleIndependantSize(46, 30);
}
}
} // namespace widgets

View file

@ -4,6 +4,7 @@
#include "widgets/helper/titlebarbutton.hpp"
#include <functional>
#include <pajlada/signals/signalholder.hpp>
class QHBoxLayout;
@ -24,10 +25,11 @@ public:
EnableCustomFrame = 1,
Frameless = 2,
TopMost = 4,
DeleteOnFocusOut = 8
DeleteOnFocusOut = 8,
DisableCustomScaling = 16,
};
explicit BaseWindow(QWidget *parent = nullptr, Flags flags = None);
explicit BaseWindow(QWidget *parent = nullptr, Flags flags_ = None);
QWidget *getLayoutContainer();
bool hasCustomWindowFrame();
@ -56,17 +58,21 @@ protected:
virtual void themeRefreshEvent() override;
virtual bool event(QEvent *event) override;
virtual void wheelEvent(QWheelEvent *event) override;
void updateScale();
private:
void init();
void moveIntoDesktopRect(QWidget *parent);
void calcButtonsSizes();
bool enableCustomFrame;
bool frameless;
bool stayInScreenRect = false;
bool shown = false;
Flags flags;
bool enableCustomFrame_;
bool frameless_;
bool stayInScreenRect_ = false;
bool shown_ = false;
Flags flags_;
float nativeScale_ = 1;
struct {
QHBoxLayout *titlebarBox = nullptr;
@ -76,7 +82,9 @@ private:
TitleBarButton *exitButton = nullptr;
QWidget *layoutBase = nullptr;
std::vector<RippleEffectButton *> buttons;
} ui;
} ui_;
pajlada::Signals::SignalHolder connections_;
};
} // namespace widgets

View file

@ -660,6 +660,11 @@ void ChannelView::drawMessages(QPainter &painter)
void ChannelView::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::ControlModifier) {
event->ignore();
return;
}
this->pausedBySelection = false;
this->pausedTemporarily = false;

View file

@ -1,86 +0,0 @@
#include "label.hpp"
#include "application.hpp"
#include "singletons/fontmanager.hpp"
#include <QPainter>
namespace chatterino {
namespace widgets {
Label::Label(BaseWidget *parent)
: BaseWidget(parent)
{
auto app = getApp();
app->fonts->fontChanged.connect([=]() {
this->scaleChangedEvent(this->getScale()); //
});
}
const QString &Label::getText() const
{
return this->text;
}
void Label::setText(const QString &value)
{
this->text = value;
this->scaleChangedEvent(this->getScale());
}
FontStyle Label::getFontStyle() const
{
return this->fontStyle;
}
void Label::setFontStyle(FontStyle style)
{
this->fontStyle = style;
this->scaleChangedEvent(this->getScale());
}
void Label::scaleChangedEvent(float scale)
{
auto app = getApp();
QFontMetrics metrics = app->fonts->getFontMetrics(this->fontStyle, scale);
this->preferedSize = QSize(metrics.width(this->text), metrics.height());
this->updateGeometry();
}
QSize Label::sizeHint() const
{
return this->preferedSize;
}
QSize Label::minimumSizeHint() const
{
return this->preferedSize;
}
void Label::paintEvent(QPaintEvent *)
{
auto app = getApp();
QPainter painter(this);
painter.setFont(app->fonts->getFont(this->fontStyle,
this->getScale() / painter.device()->devicePixelRatioF()));
int width = app->fonts->getFontMetrics(this->fontStyle, this->getScale()).width(this->text);
int flags = Qt::TextSingleLine;
if (this->width() < width) {
flags |= Qt::AlignLeft | Qt::AlignVCenter;
} else {
flags |= Qt::AlignCenter;
}
painter.drawText(this->rect(), flags, this->text);
}
} // namespace widgets
} // namespace chatterino

View file

@ -1,34 +0,0 @@
#pragma once
#include "singletons/fontmanager.hpp"
#include "widgets/basewidget.hpp"
namespace chatterino {
namespace widgets {
class Label : public BaseWidget
{
public:
Label(BaseWidget *parent);
const QString &getText() const;
void setText(const QString &text);
FontStyle getFontStyle() const;
void setFontStyle(FontStyle style);
protected:
virtual void scaleChangedEvent(float scale) override;
virtual void paintEvent(QPaintEvent *event) override;
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
private:
QSize preferedSize;
QString text;
FontStyle fontStyle = FontStyle::ChatMedium;
};
} // namespace widgets
} // namespace chatterino

View file

@ -5,6 +5,7 @@
#include "debug/log.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "util/clamp.hpp"
#include "util/helpers.hpp"
#include "widgets/notebook.hpp"
#include "widgets/settingsdialog.hpp"
@ -78,12 +79,12 @@ void NotebookTab::updateSize()
FontStyle::UiTabs, float(qreal(this->getScale()) * this->devicePixelRatioF()));
if (this->hasXButton()) {
width = int((metrics.width(this->getTitle()) + 32) * scale);
width = (metrics.width(this->getTitle()) + int(32 * scale));
} else {
width = int((metrics.width(this->getTitle()) + 16) * scale);
width = (metrics.width(this->getTitle()) + int(16 * scale));
}
width = std::max<int>(this->height(), std::min(int(150 * scale), width));
width = util::clamp(width, this->height(), int(150 * scale));
if (this->width() != width) {
this->resize(width, int(NOTEBOOK_TAB_HEIGHT * scale));

View file

@ -7,7 +7,7 @@
#include "singletons/thememanager.hpp"
#include "util/layoutcreator.hpp"
#include "util/urlfetch.hpp"
#include "widgets/helper/label.hpp"
#include "widgets/label.hpp"
#include "widgets/split.hpp"
#include "widgets/splitcontainer.hpp"
#include "widgets/tooltipwidget.hpp"
@ -48,19 +48,11 @@ SplitHeader::SplitHeader(Split *_split)
});
});
layout->addStretch(1);
// channel name label
// auto title = layout.emplace<Label>(this).assign(&this->titleLabel);
auto title = layout.emplace<QLabel>().assign(&this->titleLabel);
auto title = layout.emplace<Label>().assign(&this->titleLabel);
title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
// title->setMouseTracking(true);
// QObject::connect(this->titleLabel, &SignalLabel::mouseDoubleClick, this,
// &SplitHeader::mouseDoubleClickEvent);
// QObject::connect(this->titleLabel, &SignalLabel::mouseMove, this,
// &SplitHeader::mouseMoveEvent);
layout->addStretch(1);
title->setCentered(true);
title->setHasOffset(false);
// mode button
auto mode = layout.emplace<RippleEffectLabel>(this).assign(&this->modeButton);

View file

@ -1,7 +1,6 @@
#pragma once
#include "widgets/basewidget.hpp"
#include "widgets/helper/label.hpp"
#include "widgets/helper/rippleeffectlabel.hpp"
#include "widgets/helper/signallabel.hpp"
@ -23,6 +22,7 @@ namespace chatterino {
namespace widgets {
class Split;
class Label;
class SplitHeader : public BaseWidget, pajlada::Signals::SignalHolder
{
@ -61,7 +61,7 @@ private:
RippleEffectButton *dropdownButton;
// Label *titleLabel;
QLabel *titleLabel;
Label *titleLabel;
RippleEffectLabel *modeButton;
RippleEffectButton *moderationButton;

View file

@ -112,6 +112,7 @@ void SplitInput::scaleChangedEvent(float scale)
// set maximum height
this->setMaximumHeight(int(150 * this->getScale()));
this->ui_.textEdit->setFont(getApp()->fonts->getFont(FontStyle::ChatMedium, this->getScale()));
}
void SplitInput::themeRefreshEvent()

121
src/widgets/label.cpp Normal file
View file

@ -0,0 +1,121 @@
#include "label.hpp"
#include "application.hpp"
#include <QPainter>
namespace chatterino {
namespace widgets {
Label::Label(QString text, FontStyle style)
: Label(nullptr, text, style)
{
}
Label::Label(BaseWidget *parent, QString text, FontStyle style)
: BaseWidget(parent)
, text_(text)
, fontStyle_(style)
{
auto app = getApp();
app->fonts->fontChanged.connect([=] { this->updateSize(); });
}
const QString &Label::getText() const
{
return this->text_;
}
void Label::setText(const QString &text)
{
this->text_ = text;
this->updateSize();
}
FontStyle Label::getFontStyle() const
{
return this->fontStyle_;
}
bool Label::getCentered() const
{
return this->centered_;
}
void Label::setCentered(bool centered)
{
this->centered_ = centered;
this->updateSize();
}
bool Label::getHasOffset() const
{
return this->hasOffset_;
}
void Label::setHasOffset(bool hasOffset)
{
this->hasOffset_ = hasOffset;
this->updateSize();
}
void Label::setFontStyle(FontStyle style)
{
this->fontStyle_ = style;
this->updateSize();
}
void Label::scaleChangedEvent(float)
{
this->updateSize();
}
QSize Label::sizeHint() const
{
return this->preferedSize_;
}
QSize Label::minimumSizeHint() const
{
return this->preferedSize_;
}
void Label::paintEvent(QPaintEvent *)
{
auto app = getApp();
QPainter painter(this);
QFontMetrics metrics = app->fonts->getFontMetrics(this->getFontStyle(),
this->getScale() * this->devicePixelRatioF());
painter.setFont(
app->fonts->getFont(this->getFontStyle(), this->getScale() * this->devicePixelRatioF()));
int offset = this->hasOffset_ ? int(8 * this->getScale()) : 0;
// draw text
QRect textRect(offset, 0, this->width() - offset - offset, this->height());
int width = metrics.width(this->text_);
Qt::Alignment alignment = !this->centered_ || width > textRect.width()
? Qt::AlignLeft | Qt::AlignVCenter
: Qt::AlignCenter;
QTextOption option(alignment);
option.setWrapMode(QTextOption::NoWrap);
painter.drawText(textRect, this->text_, option);
}
void Label::updateSize()
{
auto app = getApp();
QFontMetrics metrics = app->fonts->getFontMetrics(this->fontStyle_, this->getScale());
this->preferedSize_ = QSize(metrics.width(this->text_), metrics.height());
this->updateGeometry();
}
} // namespace widgets
} // namespace chatterino

46
src/widgets/label.hpp Normal file
View file

@ -0,0 +1,46 @@
#pragma once
#include "singletons/fontmanager.hpp"
#include "widgets/basewidget.hpp"
namespace chatterino {
namespace widgets {
class Label : public BaseWidget
{
public:
explicit Label(QString text = QString(), FontStyle style = FontStyle::UiMedium);
explicit Label(BaseWidget *parent, QString text = QString(),
FontStyle style = FontStyle::UiMedium);
const QString &getText() const;
void setText(const QString &text);
FontStyle getFontStyle() const;
void setFontStyle(FontStyle style);
bool getCentered() const;
void setCentered(bool centered);
bool getHasOffset() const;
void setHasOffset(bool centered);
protected:
virtual void scaleChangedEvent(float scale) override;
virtual void paintEvent(QPaintEvent *) override;
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
private:
QString text_;
FontStyle fontStyle_;
QSize preferedSize_;
bool centered_ = false;
bool hasOffset_ = true;
void updateSize();
};
} // namespace widgets
} // namespace chatterino

View file

@ -340,7 +340,7 @@ void Notebook::performLayout(bool animated)
x += i->tab->width();
}
x += 1;
x += int(scale * 2);
first = false;
}

View file

@ -8,23 +8,23 @@ namespace widgets {
QualityPopup::QualityPopup(const QString &_channelName, QStringList options)
: channelName(_channelName)
{
this->ui.okButton.setText("OK");
this->ui.cancelButton.setText("Cancel");
this->ui_.okButton.setText("OK");
this->ui_.cancelButton.setText("Cancel");
QObject::connect(&this->ui.okButton, &QPushButton::clicked, this,
QObject::connect(&this->ui_.okButton, &QPushButton::clicked, this,
&QualityPopup::okButtonClicked);
QObject::connect(&this->ui.cancelButton, &QPushButton::clicked, this,
QObject::connect(&this->ui_.cancelButton, &QPushButton::clicked, this,
&QualityPopup::cancelButtonClicked);
this->ui.buttonBox.addButton(&this->ui.okButton, QDialogButtonBox::ButtonRole::AcceptRole);
this->ui.buttonBox.addButton(&this->ui.cancelButton, QDialogButtonBox::ButtonRole::RejectRole);
this->ui_.buttonBox.addButton(&this->ui_.okButton, QDialogButtonBox::ButtonRole::AcceptRole);
this->ui_.buttonBox.addButton(&this->ui_.cancelButton, QDialogButtonBox::ButtonRole::RejectRole);
this->ui.selector.addItems(options);
this->ui_.selector.addItems(options);
this->ui.vbox.addWidget(&this->ui.selector);
this->ui.vbox.addWidget(&this->ui.buttonBox);
this->ui_.vbox.addWidget(&this->ui_.selector);
this->ui_.vbox.addWidget(&this->ui_.buttonBox);
this->setLayout(&this->ui.vbox);
this->setLayout(&this->ui_.vbox);
}
void QualityPopup::showDialog(const QString &channelName, QStringList options)
@ -44,7 +44,7 @@ void QualityPopup::okButtonClicked()
QString channelURL = "twitch.tv/" + this->channelName;
try {
streamlink::OpenStreamlink(channelURL, this->ui.selector.currentText());
streamlink::OpenStreamlink(channelURL, this->ui_.selector.currentText());
} catch (const streamlink::Exception &ex) {
debug::Log("Exception caught trying to open streamlink: {}", ex.what());
}

View file

@ -23,7 +23,7 @@ private:
QDialogButtonBox buttonBox;
QPushButton okButton;
QPushButton cancelButton;
} ui;
} ui_;
QString channelName;

View file

@ -26,7 +26,7 @@ SelectChannelDialog::SelectChannelDialog()
util::LayoutCreator<QWidget> layoutWidget(this->getLayoutContainer());
auto layout = layoutWidget.setLayoutType<QVBoxLayout>().withoutMargin();
auto notebook = layout.emplace<Notebook>(this).assign(&this->ui.notebook);
auto notebook = layout.emplace<Notebook>(this).assign(&this->ui_.notebook);
// twitch
{
@ -34,10 +34,10 @@ SelectChannelDialog::SelectChannelDialog()
auto vbox = obj.setLayoutType<QVBoxLayout>();
// channel_btn
auto channel_btn = vbox.emplace<QRadioButton>("Channel").assign(&this->ui.twitch.channel);
auto channel_btn = vbox.emplace<QRadioButton>("Channel").assign(&this->ui_.twitch.channel);
auto channel_lbl = vbox.emplace<QLabel>("Join a twitch channel by its name.").hidden();
channel_lbl->setWordWrap(true);
auto channel_edit = vbox.emplace<QLineEdit>().hidden().assign(&this->ui.twitch.channelName);
auto channel_edit = vbox.emplace<QLineEdit>().hidden().assign(&this->ui_.twitch.channelName);
QObject::connect(*channel_btn, &QRadioButton::toggled, [=](bool enabled) mutable {
if (enabled) {
@ -54,7 +54,7 @@ SelectChannelDialog::SelectChannelDialog()
// whispers_btn
auto whispers_btn =
vbox.emplace<QRadioButton>("Whispers").assign(&this->ui.twitch.whispers);
vbox.emplace<QRadioButton>("Whispers").assign(&this->ui_.twitch.whispers);
auto whispers_lbl =
vbox.emplace<QLabel>("Shows the whispers that you receive while chatterino is running.")
.hidden();
@ -67,7 +67,7 @@ SelectChannelDialog::SelectChannelDialog()
// mentions_btn
auto mentions_btn =
vbox.emplace<QRadioButton>("Mentions").assign(&this->ui.twitch.mentions);
vbox.emplace<QRadioButton>("Mentions").assign(&this->ui_.twitch.mentions);
auto mentions_lbl =
vbox.emplace<QLabel>("Shows all the messages that highlight you from any channel.")
.hidden();
@ -80,7 +80,7 @@ SelectChannelDialog::SelectChannelDialog()
// watching_btn
auto watching_btn =
vbox.emplace<QRadioButton>("Watching").assign(&this->ui.twitch.watching);
vbox.emplace<QRadioButton>("Watching").assign(&this->ui_.twitch.watching);
auto watching_lbl =
vbox.emplace<QLabel>("Requires the chatterino browser extension.").hidden();
@ -151,25 +151,25 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
switch (_channel.getType()) {
case Channel::Twitch: {
this->ui.notebook->selectIndex(TAB_TWITCH);
this->ui.twitch.channel->setFocus();
this->ui.twitch.channelName->setText(channel->name);
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
this->ui_.twitch.channelName->setText(channel->name);
} break;
case Channel::TwitchWatching: {
this->ui.notebook->selectIndex(TAB_TWITCH);
this->ui.twitch.watching->setFocus();
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.watching->setFocus();
} break;
case Channel::TwitchMentions: {
this->ui.notebook->selectIndex(TAB_TWITCH);
this->ui.twitch.mentions->setFocus();
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.mentions->setFocus();
} break;
case Channel::TwitchWhispers: {
this->ui.notebook->selectIndex(TAB_TWITCH);
this->ui.twitch.whispers->setFocus();
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.whispers->setFocus();
} break;
default: {
this->ui.notebook->selectIndex(TAB_TWITCH);
this->ui.twitch.channel->setFocus();
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
}
}
@ -184,15 +184,15 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
auto app = getApp();
switch (this->ui.notebook->getSelectedIndex()) {
switch (this->ui_.notebook->getSelectedIndex()) {
case TAB_TWITCH: {
if (this->ui.twitch.channel->isChecked()) {
return app->twitch.server->getOrAddChannel(this->ui.twitch.channelName->text());
} else if (this->ui.twitch.watching->isChecked()) {
if (this->ui_.twitch.channel->isChecked()) {
return app->twitch.server->getOrAddChannel(this->ui_.twitch.channelName->text());
} else if (this->ui_.twitch.watching->isChecked()) {
return app->twitch.server->watchingChannel;
} else if (this->ui.twitch.mentions->isChecked()) {
} else if (this->ui_.twitch.mentions->isChecked()) {
return app->twitch.server->mentionsChannel;
} else if (this->ui.twitch.whispers->isChecked()) {
} else if (this->ui_.twitch.whispers->isChecked()) {
return app->twitch.server->whispersChannel;
}
}
@ -226,8 +226,8 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *eve
QKeyEvent *event_key = static_cast<QKeyEvent *>(event);
if ((event_key->key() == Qt::Key_Tab || event_key->key() == Qt::Key_Down) &&
event_key->modifiers() == Qt::NoModifier) {
if (widget == this->dialog->ui.twitch.channelName) {
this->dialog->ui.twitch.whispers->setFocus();
if (widget == this->dialog->ui_.twitch.channelName) {
this->dialog->ui_.twitch.whispers->setFocus();
return true;
} else {
widget->nextInFocusChain()->setFocus();
@ -236,11 +236,11 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *eve
} else if (((event_key->key() == Qt::Key_Tab || event_key->key() == Qt::Key_Backtab) &&
event_key->modifiers() == Qt::ShiftModifier) ||
((event_key->key() == Qt::Key_Up) && event_key->modifiers() == Qt::NoModifier)) {
if (widget == this->dialog->ui.twitch.channelName) {
this->dialog->ui.twitch.watching->setFocus();
if (widget == this->dialog->ui_.twitch.channelName) {
this->dialog->ui_.twitch.watching->setFocus();
return true;
} else if (widget == this->dialog->ui.twitch.whispers) {
this->dialog->ui.twitch.channel->setFocus();
} else if (widget == this->dialog->ui_.twitch.whispers) {
this->dialog->ui_.twitch.channel->setFocus();
return true;
}

View file

@ -46,7 +46,7 @@ private:
QRadioButton *mentions;
QRadioButton *watching;
} twitch;
} ui;
} ui_;
EventFilter tabFilter;

View file

@ -25,7 +25,7 @@ namespace widgets {
SettingsDialog *SettingsDialog::handle = nullptr;
SettingsDialog::SettingsDialog()
: BaseWindow()
: BaseWindow(nullptr, BaseWindow::DisableCustomScaling)
{
this->initUi();
@ -40,30 +40,30 @@ void SettingsDialog::initUi()
// tab pages
layoutCreator.emplace<QWidget>()
.assign(&this->ui.tabContainerContainer)
.assign(&this->ui_.tabContainerContainer)
.emplace<QVBoxLayout>()
.withoutMargin()
.assign(&this->ui.tabContainer);
.assign(&this->ui_.tabContainer);
// right side layout
auto right = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
right.emplace<QStackedLayout>().assign(&this->ui.pageStack).withoutMargin();
right.emplace<QStackedLayout>().assign(&this->ui_.pageStack).withoutMargin();
auto buttons = right.emplace<QDialogButtonBox>(Qt::Horizontal);
{
this->ui.okButton = buttons->addButton("Ok", QDialogButtonBox::YesRole);
this->ui.cancelButton = buttons->addButton("Cancel", QDialogButtonBox::NoRole);
this->ui_.okButton = buttons->addButton("Ok", QDialogButtonBox::YesRole);
this->ui_.cancelButton = buttons->addButton("Cancel", QDialogButtonBox::NoRole);
}
}
// ---- misc
this->ui.tabContainerContainer->setObjectName("tabWidget");
this->ui.pageStack->setObjectName("pages");
this->ui_.tabContainerContainer->setObjectName("tabWidget");
this->ui_.pageStack->setObjectName("pages");
QObject::connect(this->ui.okButton, &QPushButton::clicked, this,
QObject::connect(this->ui_.okButton, &QPushButton::clicked, this,
&SettingsDialog::okButtonClicked);
QObject::connect(this->ui.cancelButton, &QPushButton::clicked, this,
QObject::connect(this->ui_.cancelButton, &QPushButton::clicked, this,
&SettingsDialog::cancelButtonClicked);
}
@ -74,23 +74,23 @@ SettingsDialog *SettingsDialog::getHandle()
void SettingsDialog::addTabs()
{
this->ui.tabContainer->setSpacing(0);
this->ui_.tabContainer->setSpacing(0);
this->addTab(new settingspages::AccountsPage);
this->ui.tabContainer->addSpacing(16);
this->ui_.tabContainer->addSpacing(16);
this->addTab(new settingspages::AppearancePage);
this->addTab(new settingspages::BehaviourPage);
this->ui.tabContainer->addSpacing(16);
this->ui_.tabContainer->addSpacing(16);
this->addTab(new settingspages::CommandPage);
// this->addTab(new settingspages::EmotesPage);
this->addTab(new settingspages::HighlightingPage);
this->addTab(new settingspages::IgnoreUsersPage);
this->ui.tabContainer->addSpacing(16);
this->ui_.tabContainer->addSpacing(16);
this->addTab(new settingspages::KeyboardSettingsPage);
// this->addTab(new settingspages::LogsPage);
@ -98,7 +98,7 @@ void SettingsDialog::addTabs()
// this->addTab(new settingspages::SpecialChannelsPage);
this->addTab(new settingspages::ExternalToolsPage);
this->ui.tabContainer->addStretch(1);
this->ui_.tabContainer->addStretch(1);
this->addTab(new settingspages::AboutPage, Qt::AlignBottom);
}
@ -106,8 +106,8 @@ void SettingsDialog::addTab(settingspages::SettingsPage *page, Qt::Alignment ali
{
auto tab = new SettingsDialogTab(this, page, page->getIconResource());
this->ui.pageStack->addWidget(page);
this->ui.tabContainer->addWidget(tab, 0, alignment);
this->ui_.pageStack->addWidget(page);
this->ui_.tabContainer->addWidget(tab, 0, alignment);
this->tabs.push_back(tab);
if (this->tabs.size() == 1) {
@ -117,7 +117,7 @@ void SettingsDialog::addTab(settingspages::SettingsPage *page, Qt::Alignment ali
void SettingsDialog::select(SettingsDialogTab *tab)
{
this->ui.pageStack->setCurrentWidget(tab->getSettingsPage());
this->ui_.pageStack->setCurrentWidget(tab->getSettingsPage());
if (this->selectedTab != nullptr) {
this->selectedTab->setSelected(false);
@ -171,7 +171,7 @@ void SettingsDialog::scaleChangedEvent(float newDpi)
this->setStyleSheet(styleSheet);
this->ui.tabContainerContainer->setFixedWidth((int)(200 * newDpi));
this->ui_.tabContainerContainer->setFixedWidth((int)(200 * newDpi));
}
void SettingsDialog::themeRefreshEvent()

View file

@ -45,7 +45,7 @@ private:
QStackedLayout *pageStack;
QPushButton *okButton;
QPushButton *cancelButton;
} ui;
} ui_;
std::vector<SettingsDialogTab *> tabs;

View file

@ -56,6 +56,7 @@ AppearancePage::AppearancePage()
form->addRow("Theme:", theme);
// form->addRow("Theme color:", this->createThemeColorChanger());
form->addRow("UI Scaling:", this->createUiScaleSlider());
form->addRow("Font:", this->createFontChanger());
form->addRow("Tabs:", this->createCheckBox(TAB_X, app->settings->showTabCloseButton));
@ -240,6 +241,33 @@ QLayout *AppearancePage::createFontChanger()
return layout;
}
QLayout *AppearancePage::createUiScaleSlider()
{
auto layout = new QHBoxLayout();
auto slider = new QSlider(Qt::Horizontal);
auto label = new QLabel();
layout->addWidget(slider);
layout->addWidget(label);
slider->setMinimum(singletons::WindowManager::uiScaleMin);
slider->setMaximum(singletons::WindowManager::uiScaleMax);
slider->setValue(
singletons::WindowManager::clampUiScale(getApp()->settings->uiScale.getValue()));
label->setMinimumWidth(100);
QObject::connect(slider, &QSlider::valueChanged,
[](auto value) { getApp()->settings->uiScale.setValue(value); });
getApp()->settings->uiScale.connect(
[label](auto, auto) {
label->setText(QString::number(singletons::WindowManager::getUiScaleValue()));
},
this->connections_);
return layout;
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -3,6 +3,7 @@
#include "widgets/settingspages/settingspage.hpp"
#include <QScrollArea>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
namespace widgets {
@ -15,6 +16,9 @@ public:
QLayout *createThemeColorChanger();
QLayout *createFontChanger();
QLayout *createUiScaleSlider();
std::vector<pajlada::Signals::ScopedConnection> connections_;
};
} // namespace settingspages

View file

@ -96,6 +96,23 @@ Window::Window(WindowType _type)
CreateWindowShortcut(this, "CTRL+8", [this] { this->notebook.selectIndex(7); });
CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); });
{
auto s = new QShortcut(QKeySequence::ZoomIn, this);
s->setContext(Qt::WindowShortcut);
QObject::connect(s, &QShortcut::activated, this, [] {
getApp()->settings->uiScale.setValue(singletons::WindowManager::clampUiScale(
getApp()->settings->uiScale.getValue() + 1));
});
}
{
auto s = new QShortcut(QKeySequence::ZoomOut, this);
s->setContext(Qt::WindowShortcut);
QObject::connect(s, &QShortcut::activated, this, [] {
getApp()->settings->uiScale.setValue(singletons::WindowManager::clampUiScale(
getApp()->settings->uiScale.getValue() - 1));
});
}
// CTRL+SHIFT+T: New tab
CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addPage(true); });