Merge branch 'master' into git_is_pepega

This commit is contained in:
Mm2PL
2020-01-01 21:06:29 +01:00
committed by GitHub
174 changed files with 2445 additions and 1112 deletions
+13 -6
View File
@@ -1,6 +1,7 @@
#include "AttachedWindow.hpp"
#include "Application.hpp"
#include "singletons/Settings.hpp"
#include "util/DebugCount.hpp"
#include "widgets/splits/Split.hpp"
@@ -180,13 +181,19 @@ void AttachedWindow::attachToHwnd(void *_attachedPtr)
QString qfilename =
QString::fromWCharArray(filename.get(), int(filenameLength));
if (!qfilename.endsWith("chrome.exe") &&
!qfilename.endsWith("firefox.exe"))
if (!getSettings()->attachExtensionToAnyProcess)
{
qDebug() << "NM Illegal caller" << qfilename;
this->timer_.stop();
this->deleteLater();
return;
// We don't attach to non-browser processes by default.
if (!qfilename.endsWith("chrome.exe") &&
!qfilename.endsWith("firefox.exe") &&
!qfilename.endsWith("vivaldi.exe") &&
!qfilename.endsWith("opera.exe"))
{
qDebug() << "NM Illegal caller" << qfilename;
this->timer_.stop();
this->deleteLater();
return;
}
}
this->validProcessName_ = true;
}
+21
View File
@@ -0,0 +1,21 @@
#include "widgets/BasePopup.hpp"
namespace chatterino {
BasePopup::BasePopup(FlagsEnum<Flags> _flags, QWidget *parent)
: BaseWindow(std::move(_flags), parent)
{
}
void BasePopup::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
{
this->close();
return;
}
BaseWindow::keyPressEvent(e);
}
} // namespace chatterino
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "common/FlagsEnum.hpp"
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class BasePopup : public BaseWindow
{
public:
explicit BasePopup(FlagsEnum<BaseWindow::Flags> flags_ = None,
QWidget *parent = nullptr);
virtual ~BasePopup() = default;
protected:
void keyPressEvent(QKeyEvent *e) override;
};
} // namespace chatterino
+164
View File
@@ -0,0 +1,164 @@
#include "widgets/BaseWidget.hpp"
#include "BaseSettings.hpp"
#include "BaseTheme.hpp"
#include "debug/Log.hpp"
#include "widgets/BaseWindow.hpp"
#include <QChildEvent>
#include <QDebug>
#include <QIcon>
#include <QLayout>
#include <QtGlobal>
namespace chatterino {
BaseWidget::BaseWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
// REMOVED
this->theme = getTheme();
this->signalHolder_.managedConnect(this->theme->updated, [this]() {
this->themeChangedEvent();
this->update();
});
}
float BaseWidget::scale() const
{
if (this->overrideScale_)
{
return this->overrideScale_.get();
}
else if (auto baseWidget = dynamic_cast<BaseWidget *>(this->window()))
{
return baseWidget->scale_;
}
else
{
return 1.f;
}
}
void BaseWidget::setScale(float value)
{
// update scale value
this->scale_ = value;
this->scaleChangedEvent(this->scale());
this->scaleChanged.invoke(this->scale());
this->setScaleIndependantSize(this->scaleIndependantSize());
}
void BaseWidget::setOverrideScale(boost::optional<float> value)
{
this->overrideScale_ = value;
this->setScale(this->scale());
}
boost::optional<float> BaseWidget::overrideScale() const
{
return this->overrideScale_;
}
QSize BaseWidget::scaleIndependantSize() const
{
return this->scaleIndependantSize_;
}
int BaseWidget::scaleIndependantWidth() const
{
return this->scaleIndependantSize_.width();
}
int BaseWidget::scaleIndependantHeight() const
{
return this->scaleIndependantSize_.height();
}
void BaseWidget::setScaleIndependantSize(int width, int height)
{
this->setScaleIndependantSize(QSize(width, height));
}
void BaseWidget::setScaleIndependantSize(QSize size)
{
this->scaleIndependantSize_ = size;
if (size.width() > 0)
{
this->setFixedWidth(int(size.width() * this->scale()));
}
if (size.height() > 0)
{
this->setFixedHeight(int(size.height() * this->scale()));
}
}
void BaseWidget::setScaleIndependantWidth(int value)
{
this->setScaleIndependantSize(
QSize(value, this->scaleIndependantSize_.height()));
}
void BaseWidget::setScaleIndependantHeight(int value)
{
this->setScaleIndependantSize(
QSize(this->scaleIndependantSize_.width(), value));
}
float BaseWidget::qtFontScale() const
{
if (auto window = dynamic_cast<BaseWindow *>(this->window()))
{
return this->scale() / window->nativeScale_;
}
else
{
return this->scale();
}
}
void BaseWidget::childEvent(QChildEvent *event)
{
if (event->added())
{
// add element if it's a basewidget
if (auto widget = dynamic_cast<BaseWidget *>(event->child()))
{
this->widgets_.push_back(widget);
}
}
else if (event->removed())
{
// find element to be removed
auto it = std::find_if(this->widgets_.begin(), this->widgets_.end(),
[&](auto &&x) { return x == event->child(); });
// remove if found
if (it != this->widgets_.end())
{
this->widgets_.erase(it);
}
}
}
void BaseWidget::showEvent(QShowEvent *)
{
this->setScale(this->scale());
this->themeChangedEvent();
}
void BaseWidget::scaleChangedEvent(float newDpi)
{
}
void BaseWidget::themeChangedEvent()
{
// Do any color scheme updates here
}
} // namespace chatterino
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <QWidget>
#include <boost/optional.hpp>
#include <pajlada/signals/signal.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
class Theme;
class BaseWindow;
class BaseWidget : public QWidget
{
Q_OBJECT
public:
explicit BaseWidget(QWidget *parent, Qt::WindowFlags f = Qt::WindowFlags());
virtual float scale() const;
pajlada::Signals::Signal<float> scaleChanged;
boost::optional<float> overrideScale() const;
void setOverrideScale(boost::optional<float>);
QSize scaleIndependantSize() const;
int scaleIndependantWidth() const;
int scaleIndependantHeight() const;
void setScaleIndependantSize(int width, int height);
void setScaleIndependantSize(QSize);
void setScaleIndependantWidth(int value);
void setScaleIndependantHeight(int value);
float qtFontScale() const;
protected:
virtual void childEvent(QChildEvent *) override;
virtual void showEvent(QShowEvent *) override;
virtual void scaleChangedEvent(float newScale);
virtual void themeChangedEvent();
void setScale(float value);
Theme *theme;
private:
float scale_{1.f};
boost::optional<float> overrideScale_;
QSize scaleIndependantSize_;
std::vector<BaseWidget *> widgets_;
pajlada::Signals::SignalHolder signalHolder_;
friend class BaseWindow;
};
} // namespace chatterino
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
#pragma once
#include "widgets/BaseWidget.hpp"
#include <functional>
#include <pajlada/signals/signalholder.hpp>
#include "common/FlagsEnum.hpp"
class QHBoxLayout;
struct tagMSG;
typedef struct tagMSG MSG;
namespace chatterino {
class Button;
class EffectLabel;
class TitleBarButton;
enum class TitleBarButtonStyle;
class BaseWindow : public BaseWidget
{
Q_OBJECT
public:
enum Flags {
None = 0,
EnableCustomFrame = 1,
Frameless = 2,
TopMost = 4,
DisableCustomScaling = 8,
FramelessDraggable = 16,
};
enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
explicit BaseWindow(FlagsEnum<Flags> flags_ = None,
QWidget *parent = nullptr);
void setInitialBounds(const QRect &bounds);
QRect getBounds();
QWidget *getLayoutContainer();
bool hasCustomWindowFrame();
TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style,
std::function<void()> onClicked);
EffectLabel *addTitleBarLabel(std::function<void()> onClicked);
void setStayInScreenRect(bool value);
bool getStayInScreenRect() const;
void setActionOnFocusLoss(ActionOnFocusLoss value);
ActionOnFocusLoss getActionOnFocusLoss() const;
void moveTo(QWidget *widget, QPoint point, bool offset = true);
virtual float scale() const override;
float qtFontScale() const;
pajlada::Signals::NoArgSignal closing;
static bool supportsCustomWindowFrame();
protected:
virtual bool nativeEvent(const QByteArray &eventType, void *message,
long *result) override;
virtual void scaleChangedEvent(float) override;
virtual void paintEvent(QPaintEvent *) override;
virtual void changeEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override;
virtual void resizeEvent(QResizeEvent *) override;
virtual void moveEvent(QMoveEvent *) override;
virtual void closeEvent(QCloseEvent *) override;
virtual void showEvent(QShowEvent *) override;
virtual void themeChangedEvent() override;
virtual bool event(QEvent *event) override;
virtual void wheelEvent(QWheelEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
QPointF movingRelativePos;
bool moving{};
void updateScale();
boost::optional<QColor> overrideBackgroundColor_;
private:
void init();
void moveIntoDesktopRect(QWidget *parent);
void calcButtonsSizes();
void drawCustomWindowFrame(QPainter &painter);
void onFocusLost();
bool handleDPICHANGED(MSG *msg);
bool handleSHOWWINDOW(MSG *msg);
bool handleNCCALCSIZE(MSG *msg, long *result);
bool handleSIZE(MSG *msg);
bool handleMOVE(MSG *msg);
bool handleNCHITTEST(MSG *msg, long *result);
bool enableCustomFrame_;
ActionOnFocusLoss actionOnFocusLoss_ = Nothing;
bool frameless_;
bool stayInScreenRect_ = false;
bool shown_ = false;
FlagsEnum<Flags> flags_;
float nativeScale_ = 1;
bool isResizeFixing_ = false;
struct {
QLayout *windowLayout = nullptr;
QHBoxLayout *titlebarBox = nullptr;
QWidget *titleLabel = nullptr;
TitleBarButton *minButton = nullptr;
TitleBarButton *maxButton = nullptr;
TitleBarButton *exitButton = nullptr;
QWidget *layoutBase = nullptr;
std::vector<Button *> buttons;
} ui_;
#ifdef USEWINSDK
QRect initalBounds_;
QRect currentBounds_;
QRect nextBounds_;
QTimer useNextBounds_;
bool isNotMinimizedOrMaximized_{};
#endif
pajlada::Signals::SignalHolder connections_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
friend class BaseWidget;
};
} // namespace chatterino
+140
View File
@@ -0,0 +1,140 @@
#include "Label.hpp"
#include <QPainter>
namespace chatterino {
Label::Label(QString text, FontStyle style)
: Label(nullptr, text, style)
{
}
Label::Label(BaseWidget *parent, QString text, FontStyle style)
: BaseWidget(parent)
, text_(text)
, fontStyle_(style)
{
this->connections_.managedConnect(getFonts()->fontChanged,
[this] { this->updateSize(); });
}
const QString &Label::getText() const
{
return this->text_;
}
void Label::setText(const QString &text)
{
if (this->text_ != text)
{
this->text_ = text;
this->updateSize();
this->update();
}
}
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 scale)
{
this->updateSize();
}
QSize Label::sizeHint() const
{
return this->preferedSize_;
}
QSize Label::minimumSizeHint() const
{
return this->preferedSize_;
}
void Label::paintEvent(QPaintEvent *)
{
QPainter painter(this);
qreal deviceDpi =
#ifdef Q_OS_WIN
this->devicePixelRatioF();
#else
1.0;
#endif
QFontMetrics metrics = getFonts()->getFontMetrics(
this->getFontStyle(),
this->scale() * 96.f / this->logicalDpiX() * deviceDpi);
painter.setFont(getFonts()->getFont(
this->getFontStyle(),
this->scale() * 96.f / this->logicalDpiX() * deviceDpi));
int offset = this->getOffset();
// 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;
painter.setBrush(this->palette().windowText());
QTextOption option(alignment);
option.setWrapMode(QTextOption::NoWrap);
painter.drawText(textRect, this->text_, option);
#if 0
painter.setPen(QColor(255, 0, 0));
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
#endif
}
void Label::updateSize()
{
QFontMetrics metrics =
getFonts()->getFontMetrics(this->fontStyle_, this->scale());
int width = metrics.width(this->text_) + (2 * this->getOffset());
int height = metrics.height();
this->preferedSize_ = QSize(width, height);
this->updateGeometry();
}
int Label::getOffset()
{
return this->hasOffset_ ? int(8 * this->scale()) : 0;
}
} // namespace chatterino
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "singletons/Fonts.hpp"
#include "widgets/BaseWidget.hpp"
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
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 hasOffset);
protected:
virtual void scaleChangedEvent(float scale_) override;
virtual void paintEvent(QPaintEvent *) override;
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
private:
void updateSize();
int getOffset();
QString text_;
FontStyle fontStyle_;
QSize preferedSize_;
bool centered_ = false;
bool hasOffset_ = true;
pajlada::Signals::SignalHolder connections_;
};
} // namespace chatterino
+2 -4
View File
@@ -297,15 +297,13 @@ void Scrollbar::paintEvent(QPaintEvent *)
switch (highlight.getStyle())
{
case ScrollbarHighlight::Default:
{
case ScrollbarHighlight::Default: {
painter.fillRect(w / 8 * 3, int(y), w / 4, highlightHeight,
color);
}
break;
case ScrollbarHighlight::Line:
{
case ScrollbarHighlight::Line: {
painter.fillRect(0, int(y), w, 1, color);
}
break;
+122
View File
@@ -0,0 +1,122 @@
#include "TooltipWidget.hpp"
#include "BaseTheme.hpp"
#include "singletons/Fonts.hpp"
#include <QDebug>
#include <QDesktopWidget>
#include <QStyle>
#include <QVBoxLayout>
#ifdef USEWINSDK
# include <Windows.h>
#endif
namespace chatterino {
TooltipWidget *TooltipWidget::instance()
{
static TooltipWidget *tooltipWidget = new TooltipWidget();
return tooltipWidget;
}
TooltipWidget::TooltipWidget(BaseWidget *parent)
: BaseWindow(BaseWindow::TopMost, parent)
, displayImage_(new QLabel())
, displayText_(new QLabel())
{
this->setStyleSheet("color: #fff; background: rgba(11, 11, 11, 0.8)");
this->setAttribute(Qt::WA_TranslucentBackground);
//this->setWindowOpacity(0.8);
this->updateFont();
this->setStayInScreenRect(true);
this->setAttribute(Qt::WA_ShowWithoutActivating);
this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
Qt::X11BypassWindowManagerHint |
Qt::BypassWindowManagerHint);
displayImage_->hide();
displayImage_->setAlignment(Qt::AlignHCenter);
displayImage_->setStyleSheet("background: transparent");
displayText_->setAlignment(Qt::AlignHCenter);
displayText_->setText("tooltip text");
displayText_->setStyleSheet("background: transparent");
auto layout = new QVBoxLayout();
layout->setContentsMargins(10, 5, 10, 5);
layout->addWidget(displayImage_);
layout->addWidget(displayText_);
this->setLayout(layout);
this->fontChangedConnection_ =
getFonts()->fontChanged.connect([this] { this->updateFont(); });
}
TooltipWidget::~TooltipWidget()
{
this->fontChangedConnection_.disconnect();
}
#ifdef USEWINSDK
void TooltipWidget::raise()
{
::SetWindowPos(HWND(this->winId()), HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
#endif
void TooltipWidget::themeChangedEvent()
{
// this->setStyleSheet("color: #fff; background: #000");
}
void TooltipWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(this->rect(), QColor(0, 0, 0, int(0.8 * 255)));
}
void TooltipWidget::scaleChangedEvent(float)
{
this->updateFont();
}
void TooltipWidget::updateFont()
{
this->setFont(
getFonts()->getFont(FontStyle::ChatMediumSmall, this->scale()));
}
void TooltipWidget::setText(QString text)
{
this->displayText_->setText(text);
}
void TooltipWidget::setWordWrap(bool wrap)
{
this->displayText_->setWordWrap(wrap);
}
void TooltipWidget::clearImage()
{
this->displayImage_->hide();
}
void TooltipWidget::setImage(QPixmap image)
{
this->displayImage_->show();
this->displayImage_->setPixmap(image);
}
void TooltipWidget::changeEvent(QEvent *)
{
// clear parents event
}
void TooltipWidget::leaveEvent(QEvent *)
{
// clear parents event
}
} // namespace chatterino
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "widgets/BaseWindow.hpp"
#include <QLabel>
#include <QWidget>
#include <pajlada/signals/signal.hpp>
namespace chatterino {
class TooltipWidget : public BaseWindow
{
Q_OBJECT
public:
static TooltipWidget *instance();
TooltipWidget(BaseWidget *parent = nullptr);
~TooltipWidget() override;
void setText(QString text);
void setWordWrap(bool wrap);
void clearImage();
void setImage(QPixmap image);
#ifdef USEWINSDK
void raise();
#endif
protected:
void changeEvent(QEvent *) override;
void leaveEvent(QEvent *) override;
void themeChangedEvent() override;
void scaleChangedEvent(float) override;
void paintEvent(QPaintEvent *) override;
private:
void updateFont();
QLabel *displayImage_;
QLabel *displayText_;
pajlada::Signals::Connection fontChangedConnection_;
};
} // namespace chatterino
+24 -9
View File
@@ -24,19 +24,19 @@
#include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp"
#ifdef QT_DEBUG
#ifdef C_DEBUG
# include "util/SampleCheerMessages.hpp"
# include "util/SampleLinks.hpp"
#endif
#include <QApplication>
#include <QDesktopServices>
#include <QHeaderView>
#include <QMenuBar>
#include <QPalette>
#include <QShortcut>
#include <QVBoxLayout>
#include <QMenuBar>
#include <QStandardItemModel>
#include <QVBoxLayout>
namespace chatterino {
@@ -86,8 +86,7 @@ bool Window::event(QEvent *event)
case QEvent::WindowActivate:
break;
case QEvent::WindowDeactivate:
{
case QEvent::WindowDeactivate: {
auto page = this->notebook_->getOrAddSelectedPage();
if (page != nullptr)
@@ -200,10 +199,12 @@ void Window::addCustomTitlebarButtons()
void Window::addDebugStuff()
{
#ifdef QT_DEBUG
std::vector<QString> cheerMessages, subMessages, miscMessages;
#ifdef C_DEBUG
std::vector<QString> cheerMessages, subMessages, miscMessages, linkMessages;
cheerMessages = getSampleCheerMessage();
auto validLinks = getValidLinks();
auto invalidLinks = getInvalidLinks();
// clang-format off
subMessages.emplace_back(R"(@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=ronni;emotes=;id=db25007f-7a18-43eb-9379-80131e44d633;login=ronni;mod=0;msg-id=resub;msg-param-months=6;msg-param-sub-plan=Prime;msg-param-sub-plan-name=Prime;room-id=1337;subscriber=1;system-msg=ronni\shas\ssubscribed\sfor\s6\smonths!;tmi-sent-ts=1507246572675;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada :Great stream -- keep it up!)");
@@ -232,6 +233,12 @@ void Window::addDebugStuff()
// display name renders strangely
miscMessages.emplace_back(R"(@badges=;color=#00AD2B;display-name=Iamme420\s;emotes=;id=d47a1e4b-a3c6-4b9e-9bf1-51b8f3dbc76e;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1529670347537;turbo=0;user-id=56422869;user-type= :iamme420!iamme420@iamme420.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)");
miscMessages.emplace_back(R"(@badge-info=founder/47;badges=moderator/1,founder/0,premium/1;color=#00FF80;display-name=gempir;emotes=;flags=;id=d4514490-202e-43cb-b429-ef01a9d9c2fe;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1575198233854;turbo=0;user-id=77829817;user-type=mod :gempir!gempir@gempir.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)");
// various link tests
linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should pass: )" + getValidLinks().join(' '));
linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should NOT pass: )" + getInvalidLinks().join(' '));
linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should technically pass but we choose not to parse them: )" + getValidButIgnoredLinks().join(' '));
// clang-format on
createWindowShortcut(this, "F6", [=] {
@@ -249,6 +256,14 @@ void Window::addDebugStuff()
getApp()->twitch.server->addFakeMessage(msg);
});
createWindowShortcut(this, "F8", [=] {
const auto &messages = linkMessages;
static int index = 0;
auto app = getApp();
const auto &msg = messages[index++ % messages.size()];
app->twitch.server->addFakeMessage(msg);
});
createWindowShortcut(this, "F9", [=] {
auto *dialog = new WelcomeDialog();
dialog->setAttribute(Qt::WA_DeleteOnClose);
@@ -369,7 +384,7 @@ void Window::onAccountSelected()
auto user = getApp()->accounts->twitch.getCurrent();
// update title
this->setWindowTitle(Version::getInstance().getFullVersion());
this->setWindowTitle(Version::instance().fullVersion());
// update user
if (user->isAnon())
+1 -1
View File
@@ -101,7 +101,7 @@ namespace {
} // namespace
EmotePopup::EmotePopup(QWidget *parent)
: BaseWindow(BaseWindow::EnableCustomFrame, parent)
: BasePopup(BaseWindow::EnableCustomFrame, parent)
{
auto layout = new QVBoxLayout(this);
this->getLayoutContainer()->setLayout(layout);
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once
#include "widgets/BaseWindow.hpp"
#include "widgets/BasePopup.hpp"
#include <pajlada/signals/signal.hpp>
@@ -11,7 +11,7 @@ class ChannelView;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
class EmotePopup : public BaseWindow
class EmotePopup : public BasePopup
{
public:
EmotePopup(QWidget *parent = nullptr);
+2 -2
View File
@@ -31,7 +31,7 @@ LastRunCrashDialog::LastRunCrashDialog()
// QDialogButtonBox::NoRole); installUpdateButton->setEnabled(false);
// QObject::connect(installUpdateButton, &QPushButton::clicked, [this,
// update]() mutable {
// auto &updateManager = UpdateManager::getInstance();
// auto &updateManager = UpdateManager::instance();
// updateManager.installUpdates();
// this->setEnabled(false);
@@ -45,7 +45,7 @@ LastRunCrashDialog::LastRunCrashDialog()
// Updates
// auto updateUpdateLabel = [update]() mutable {
// auto &updateManager = UpdateManager::getInstance();
// auto &updateManager = UpdateManager::instance();
// switch (updateManager.getStatus()) {
// case UpdateManager::None: {
+1 -2
View File
@@ -35,8 +35,7 @@ void NotificationPopup::updatePosition()
switch (location)
{
case BottomRight:
{
case BottomRight: {
this->move(rect.right() - this->width(),
rect.bottom() - this->height());
}
+31 -30
View File
@@ -133,8 +133,8 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
auto outerBox = obj.setLayoutType<QFormLayout>();
{
auto view = this->ui_.irc.servers = new EditableModelView(
Irc::getInstance().newConnectionModel(this));
auto view = this->ui_.irc.servers =
new EditableModelView(Irc::instance().newConnectionModel(this));
view->setTitles({"host", "port", "ssl", "user", "nick", "real",
"password", "login command"});
@@ -147,12 +147,12 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
view->addButtonPressed.connect([] {
auto unique = IrcServerData{};
unique.id = Irc::getInstance().uniqueId();
unique.id = Irc::instance().uniqueId();
auto editor = new IrcConnectionEditor(unique);
if (editor->exec() == QDialog::Accepted)
{
Irc::getInstance().connections.appendItem(editor->data());
Irc::instance().connections.appendItem(editor->data());
}
});
@@ -160,23 +160,21 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
view->getTableView(), &QTableView::doubleClicked,
[](const QModelIndex &index) {
auto editor = new IrcConnectionEditor(
Irc::getInstance()
Irc::instance()
.connections.getVector()[size_t(index.row())]);
if (editor->exec() == QDialog::Accepted)
{
auto data = editor->data();
auto &&conns =
Irc::getInstance().connections.getVector();
auto &&conns = Irc::instance().connections.getVector();
int i = 0;
for (auto &&conn : conns)
{
if (conn.id == data.id)
{
Irc::getInstance().connections.removeItem(
Irc::instance().connections.removeItem(
i, Irc::noEraseCredentialCaller);
Irc::getInstance().connections.insertItem(data,
i);
Irc::instance().connections.insertItem(data, i);
}
i++;
}
@@ -190,6 +188,12 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
auto tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Irc (Beta)");
if (!getSettings()->enableExperimentalIrc)
{
tab->setEnable(false);
tab->setVisible(false);
}
}
layout->setStretchFactor(notebook.getElement(), 1);
@@ -217,7 +221,12 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
[=] { this->close(); });
// restore ui state
this->ui_.notebook->selectIndex(getSettings()->lastSelectChannelTab);
// fourtf: enable when releasing irc
if (getSettings()->enableExperimentalIrc)
{
this->ui_.notebook->selectIndex(getSettings()->lastSelectChannelTab);
}
this->ui_.irc.servers->getTableView()->selectRow(
getSettings()->lastSelectIrcConn);
}
@@ -247,33 +256,28 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
switch (_channel.getType())
{
case Channel::Type::Twitch:
{
case Channel::Type::Twitch: {
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
this->ui_.twitch.channelName->setText(channel->getName());
}
break;
case Channel::Type::TwitchWatching:
{
case Channel::Type::TwitchWatching: {
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.watching->setFocus();
}
break;
case Channel::Type::TwitchMentions:
{
case Channel::Type::TwitchMentions: {
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.mentions->setFocus();
}
break;
case Channel::Type::TwitchWhispers:
{
case Channel::Type::TwitchWhispers: {
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.whispers->setFocus();
}
break;
case Channel::Type::Irc:
{
case Channel::Type::Irc: {
this->ui_.notebook->selectIndex(TAB_IRC);
this->ui_.irc.channel->setText(_channel.get()->getName());
@@ -283,7 +287,7 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
if (auto server = ircChannel->server())
{
int i = 0;
for (auto &&conn : Irc::getInstance().connections)
for (auto &&conn : Irc::instance().connections)
{
if (conn.id == server->id())
{
@@ -298,8 +302,7 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
this->ui_.irc.channel->setFocus();
}
break;
default:
{
default: {
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
}
@@ -319,8 +322,7 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
switch (this->ui_.notebook->getSelectedIndex())
{
case TAB_TWITCH:
{
case TAB_TWITCH: {
if (this->ui_.twitch.channel->isChecked())
{
return app->twitch.server->getOrAddChannel(
@@ -340,18 +342,17 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
}
}
break;
case TAB_IRC:
{
case TAB_IRC: {
int row = this->ui_.irc.servers->getTableView()
->selectionModel()
->currentIndex()
.row();
auto &&vector = Irc::getInstance().connections.getVector();
auto &&vector = Irc::instance().connections.getVector();
if (row >= 0 && row < int(vector.size()))
{
return Irc::getInstance().getOrAddChannel(
return Irc::instance().getOrAddChannel(
vector[size_t(row)].id, this->ui_.irc.channel->text());
}
else
+5
View File
@@ -285,6 +285,11 @@ void SettingsDialog::themeChangedEvent()
this->setPalette(palette);
}
void SettingsDialog::showEvent(QShowEvent *)
{
this->ui_.search->setText("");
}
///// Widget creation helpers
void SettingsDialog::onOkClicked()
{
+1
View File
@@ -39,6 +39,7 @@ public:
protected:
virtual void scaleChangedEvent(float newDpi) override;
virtual void themeChangedEvent() override;
virtual void showEvent(QShowEvent *) override;
private:
static SettingsDialog *handle;
+19 -16
View File
@@ -26,7 +26,7 @@ UpdateDialog::UpdateDialog()
auto dismiss = buttons->addButton("Dismiss", QDialogButtonBox::RejectRole);
QObject::connect(install, &QPushButton::clicked, this, [this] {
Updates::getInstance().installUpdates();
Updates::instance().installUpdates();
this->close();
});
QObject::connect(dismiss, &QPushButton::clicked, this, [this] {
@@ -34,9 +34,9 @@ UpdateDialog::UpdateDialog()
this->close();
});
this->updateStatusChanged(Updates::getInstance().getStatus());
this->updateStatusChanged(Updates::instance().getStatus());
this->connections_.managedConnect(
Updates::getInstance().statusUpdated,
Updates::instance().statusUpdated,
[this](auto status) { this->updateStatusChanged(status); });
this->setScaleIndependantHeight(150);
@@ -48,38 +48,41 @@ void UpdateDialog::updateStatusChanged(Updates::Status status)
switch (status)
{
case Updates::UpdateAvailable:
{
case Updates::UpdateAvailable: {
this->ui_.label->setText(
QString("An update (%1) is available.\n\nDo you want to "
"download and install it?")
.arg(Updates::getInstance().getOnlineVersion()));
(Updates::instance().isDowngrade()
? QString(
"The version online (%1) seems to be lower than the "
"current (%2).\nEither a version was reverted or "
"you are running a newer build.\n\nDo you want to "
"download and install it?")
.arg(Updates::instance().getOnlineVersion(),
Updates::instance().getCurrentVersion())
: QString("An update (%1) is available.\n\nDo you want to "
"download and install it?")
.arg(Updates::instance().getOnlineVersion())));
this->updateGeometry();
}
break;
case Updates::SearchFailed:
{
case Updates::SearchFailed: {
this->ui_.label->setText("Failed to load version information.");
}
break;
case Updates::Downloading:
{
case Updates::Downloading: {
this->ui_.label->setText(
"Downloading updates.\n\nChatterino will restart "
"automatically when the download is done.");
}
break;
case Updates::DownloadFailed:
{
case Updates::DownloadFailed: {
this->ui_.label->setText("Failed to download the update.");
}
break;
case Updates::WriteFileFailed:
{
case Updates::WriteFileFailed: {
this->ui_.label->setText("Failed to save the update to disk.");
}
break;
+30 -29
View File
@@ -28,17 +28,23 @@
namespace chatterino {
namespace {
void addCopyableLabel(LayoutCreator<QHBoxLayout> box, Label **assign)
Label *addCopyableLabel(LayoutCreator<QHBoxLayout> box)
{
auto label = box.emplace<Label>().assign(assign);
auto label = box.emplace<Label>();
auto button = box.emplace<Button>();
button->setPixmap(getApp()->resources->buttons.copyDark);
button->setPixmap(getResources().buttons.copyDark);
button->setScaleIndependantSize(18, 18);
button->setDim(Button::Dim::Lots);
QObject::connect(button.getElement(), &Button::leftClicked,
[label = label.getElement()] {
qApp->clipboard()->setText(label->getText());
});
QObject::connect(
button.getElement(), &Button::leftClicked,
[label = label.getElement()] {
auto copyText = label->property("copy-text").toString();
qApp->clipboard()->setText(copyText.isEmpty() ? label->getText()
: copyText);
});
return label.getElement();
};
} // namespace
@@ -52,8 +58,6 @@ UserInfoPopup::UserInfoPopup()
this->setWindowFlag(Qt::Popup);
#endif
auto app = getApp();
auto layout = LayoutCreator<QWidget>(this->getLayoutContainer())
.setLayoutType<QVBoxLayout>();
@@ -77,10 +81,10 @@ UserInfoPopup::UserInfoPopup()
auto box = vbox.emplace<QHBoxLayout>()
.withoutMargin()
.withoutSpacing();
addCopyableLabel(box, &this->ui_.nameLabel);
this->ui_.nameLabel = addCopyableLabel(box);
this->ui_.nameLabel->setFontStyle(FontStyle::UiMediumBold);
box->addStretch(1);
addCopyableLabel(box, &this->ui_.userIDLabel);
this->ui_.userIDLabel = addCopyableLabel(box);
auto palette = QPalette();
palette.setColor(QPalette::WindowText, QColor("#aaa"));
this->ui_.userIDLabel->setPalette(palette);
@@ -111,10 +115,10 @@ UserInfoPopup::UserInfoPopup()
usercard->getLabel().setText("Usercard");
auto mod = user.emplace<Button>(this);
mod->setPixmap(app->resources->buttons.mod);
mod->setPixmap(getResources().buttons.mod);
mod->setScaleIndependantSize(30, 30);
auto unmod = user.emplace<Button>(this);
unmod->setPixmap(app->resources->buttons.unmod);
unmod->setPixmap(getResources().buttons.unmod);
unmod->setScaleIndependantSize(30, 30);
user->addStretch(1);
@@ -146,8 +150,7 @@ UserInfoPopup::UserInfoPopup()
TwitchChannel *twitchChannel =
dynamic_cast<TwitchChannel *>(this->channel_.get());
bool visibilityMod = false;
bool visibilityUnmod = false;
bool visibilityModButtons = false;
if (twitchChannel)
{
@@ -158,12 +161,11 @@ UserInfoPopup::UserInfoPopup()
getApp()->accounts->twitch.getCurrent()->getUserName(),
this->userName_, Qt::CaseInsensitive) == 0;
visibilityMod = twitchChannel->isBroadcaster() && !isMyself;
visibilityUnmod =
visibilityMod || (twitchChannel->isMod() && isMyself);
visibilityModButtons =
twitchChannel->isBroadcaster() && !isMyself;
}
mod->setVisible(visibilityMod);
unmod->setVisible(visibilityUnmod);
mod->setVisible(visibilityModButtons);
unmod->setVisible(visibilityModButtons);
});
}
@@ -191,16 +193,14 @@ UserInfoPopup::UserInfoPopup()
switch (action)
{
case TimeoutWidget::Ban:
{
case TimeoutWidget::Ban: {
if (this->channel_)
{
this->channel_->sendMessage("/ban " + this->userName_);
}
}
break;
case TimeoutWidget::Unban:
{
case TimeoutWidget::Unban: {
if (this->channel_)
{
this->channel_->sendMessage("/unban " +
@@ -208,8 +208,7 @@ UserInfoPopup::UserInfoPopup()
}
}
break;
case TimeoutWidget::Timeout:
{
case TimeoutWidget::Timeout: {
if (this->channel_)
{
this->channel_->sendMessage("/timeout " +
@@ -372,6 +371,7 @@ void UserInfoPopup::setData(const QString &name, const ChannelPtr &channel)
this->channel_ = channel;
this->ui_.nameLabel->setText(name);
this->ui_.nameLabel->setProperty("copy-text", name);
this->updateUserData();
@@ -387,7 +387,8 @@ void UserInfoPopup::updateUserData()
this->userId_ = id;
this->ui_.userIDLabel->setText(TEXT_USER_ID + this->userId_);
this->ui_.userIDLabel->setText(TEXT_USER_ID + id);
this->ui_.userIDLabel->setProperty("copy-text", id);
// don't wait for the request to complete, just put the user id in the card
// right away
@@ -575,7 +576,7 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
}
};
addButton(Unban, "unban", getApp()->resources->buttons.unban);
addButton(Unban, "unban", getResources().buttons.unban);
addTimeouts("sec", {{"1", 1}});
addTimeouts("min", {
@@ -596,7 +597,7 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
{"2", 2 * 60 * 60 * 24 * 7},
});
addButton(Ban, "ban", getApp()->resources->buttons.ban);
addButton(Ban, "ban", getResources().buttons.ban);
}
void UserInfoPopup::TimeoutWidget::paintEvent(QPaintEvent *)
+343
View File
@@ -0,0 +1,343 @@
#include "Button.hpp"
#include <QApplication>
#include <QDebug>
#include <QDesktopWidget>
#include <QPainter>
#include "BaseTheme.hpp"
#include "util/FunctionEventFilter.hpp"
namespace chatterino {
Button::Button(BaseWidget *parent)
: BaseWidget(parent)
{
connect(&effectTimer_, &QTimer::timeout, this,
&Button::onMouseEffectTimeout);
this->effectTimer_.setInterval(20);
this->effectTimer_.start();
this->setMouseTracking(true);
}
void Button::setMouseEffectColor(boost::optional<QColor> color)
{
this->mouseEffectColor_ = color;
}
void Button::setPixmap(const QPixmap &_pixmap)
{
this->pixmap_ = _pixmap;
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_;
}
void Button::setEnableMargin(bool value)
{
this->enableMargin_ = value;
this->update();
}
bool Button::getEnableMargin() const
{
return this->enableMargin_;
}
qreal Button::getCurrentDimAmount() const
{
if (this->dimPixmap_ == Dim::None || this->mouseOver_)
return 1;
else if (this->dimPixmap_ == Dim::Some)
return 0.7;
else
return 0.15;
}
void Button::setBorderColor(const QColor &color)
{
this->borderColor_ = color;
this->update();
}
const QColor &Button::getBorderColor() const
{
return this->borderColor_;
}
void Button::setMenu(std::unique_ptr<QMenu> menu)
{
this->menu_ = std::move(menu);
this->menu_->installEventFilter(
new FunctionEventFilter(this, [this](QObject *, QEvent *event) {
if (event->type() == QEvent::Hide)
{
QTimer::singleShot(20, this,
[this] { this->menuVisible_ = false; });
}
return false;
}));
}
void Button::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
if (!this->pixmap_.isNull())
{
painter.setOpacity(this->getCurrentDimAmount());
QRect rect = this->rect();
int margin = this->height() < 22 * this->scale() ? 3 : 6;
int s = this->enableMargin_ ? int(margin * this->scale()) : 0;
rect.moveLeft(s);
rect.setRight(rect.right() - s - s);
rect.moveTop(s);
rect.setBottom(rect.bottom() - s - s);
painter.drawPixmap(rect, this->pixmap_);
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)
{
if (!this->enabled_)
{
return;
}
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setRenderHint(QPainter::Antialiasing);
QColor c;
if (this->mouseEffectColor_)
{
c = this->mouseEffectColor_.get();
}
else
{
c = this->theme->isLightTheme() ? QColor(0, 0, 0)
: QColor(255, 255, 255);
}
if (this->hoverMultiplier_ > 0)
{
QRadialGradient gradient(QPointF(mousePos_), this->width() / 2);
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_)
{
QRadialGradient gradient(effect.position.x(), effect.position.y(),
effect.progress * qreal(width()) * 2,
effect.position.x(), effect.position.y());
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(),
int((1 - effect.progress) * 95)));
gradient.setColorAt(0.9999, QColor(c.red(), c.green(), c.blue(),
int((1 - effect.progress) * 95)));
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), int(0)));
painter.fillRect(this->rect(), gradient);
}
}
void Button::enterEvent(QEvent *)
{
this->mouseOver_ = true;
}
void Button::leaveEvent(QEvent *)
{
this->mouseOver_ = false;
}
void Button::mousePressEvent(QMouseEvent *event)
{
if (!this->enabled_)
{
return;
}
if (event->button() != Qt::LeftButton)
{
return;
}
this->clickEffects_.push_back(ClickEffect(event->pos()));
this->mouseDown_ = true;
emit this->leftMousePress();
if (this->menu_ && !this->menuVisible_)
{
QTimer::singleShot(80, this, [this] { this->showMenu(); });
this->mouseDown_ = false;
this->mouseOver_ = false;
}
}
void Button::mouseReleaseEvent(QMouseEvent *event)
{
if (!this->enabled_)
return;
if (event->button() == Qt::LeftButton)
{
this->mouseDown_ = false;
if (this->rect().contains(event->pos()))
emit leftClicked();
}
emit clicked(event->button());
}
void Button::mouseMoveEvent(QMouseEvent *event)
{
if (this->enabled_)
{
this->mousePos_ = event->pos();
this->update();
}
}
void Button::onMouseEffectTimeout()
{
bool performUpdate = false;
if (selected_)
{
if (this->hoverMultiplier_ != 0)
{
this->hoverMultiplier_ =
std::max(0.0, this->hoverMultiplier_ - 0.1);
performUpdate = true;
}
}
else if (mouseOver_)
{
if (this->hoverMultiplier_ != 1)
{
this->hoverMultiplier_ =
std::min(1.0, this->hoverMultiplier_ + 0.5);
performUpdate = true;
}
}
else
{
if (this->hoverMultiplier_ != 0)
{
this->hoverMultiplier_ =
std::max(0.0, this->hoverMultiplier_ - 0.3);
performUpdate = true;
}
}
if (this->clickEffects_.size() != 0)
{
performUpdate = true;
for (auto it = this->clickEffects_.begin();
it != this->clickEffects_.end();)
{
it->progress += mouseDown_ ? 0.02 : 0.07;
if (it->progress >= 1.0)
{
it = this->clickEffects_.erase(it);
}
else
{
it++;
}
}
}
if (performUpdate)
{
update();
}
}
void Button::showMenu()
{
if (!this->menu_)
return;
auto point = [this] {
auto bounds = QApplication::desktop()->availableGeometry(this);
auto point = this->mapToGlobal(
QPoint(this->width() - this->menu_->width(), this->height()));
if (point.y() + this->menu_->height() > bounds.bottom())
{
point.setY(point.y() - this->menu_->height() - this->height());
}
return point;
};
this->menu_->popup(point());
this->menu_->move(point());
this->menuVisible_ = true;
}
} // namespace chatterino
+91
View File
@@ -0,0 +1,91 @@
#pragma once
#include <boost/optional.hpp>
#include "widgets/BaseWidget.hpp"
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPoint>
#include <QTimer>
#include <QWidget>
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(boost::optional<QColor> color);
void setPixmap(const QPixmap &pixmap_);
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);
signals:
void leftClicked();
void clicked(Qt::MouseButton button);
void leftMousePress();
protected:
virtual void paintEvent(QPaintEvent *) override;
virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual 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();
QColor borderColor_{};
QPixmap pixmap_{};
Dim dimPixmap_{Dim::Some};
bool enableMargin_{true};
QPoint mousePos_{};
double hoverMultiplier_{0.0};
QTimer effectTimer_{};
std::vector<ClickEffect> clickEffects_{};
boost::optional<QColor> mouseEffectColor_{};
std::unique_ptr<QMenu> menu_{};
};
} // namespace chatterino
+47 -54
View File
@@ -1,5 +1,17 @@
#include "ChannelView.hpp"
#include <QClipboard>
#include <QDebug>
#include <QDesktopServices>
#include <QGraphicsBlurEffect>
#include <QMessageBox>
#include <QPainter>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <functional>
#include <memory>
#include "Application.hpp"
#include "common/Common.hpp"
#include "controllers/accounts/AccountController.hpp"
@@ -25,19 +37,6 @@
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/splits/Split.hpp"
#include <QClipboard>
#include <QDebug>
#include <QDesktopServices>
#include <QGraphicsBlurEffect>
#include <QMessageBox>
#include <QPainter>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <functional>
#include <memory>
#define DRAW_WIDTH (this->width())
#define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3
#define CHAT_HOVER_PAUSE_DURATION 1000
@@ -122,7 +121,7 @@ ChannelView::ChannelView(BaseWidget *parent)
for (auto it = this->pauses_.begin(); it != this->pauses_.end();)
it = it->second ? this->pauses_.erase(it) : ++it;
this->updatePauseTimer();
this->updatePauses();
});
auto shortcut = new QShortcut(QKeySequence("Ctrl+C"), this);
@@ -232,7 +231,7 @@ void ChannelView::pause(PauseReason reason, boost::optional<uint> msecs)
this->pauses_[reason] = boost::none;
}
this->updatePauseTimer();
this->updatePauses();
}
void ChannelView::unpause(PauseReason reason)
@@ -240,23 +239,17 @@ void ChannelView::unpause(PauseReason reason)
/// Remove the value from the map
this->pauses_.erase(reason);
this->updatePauseTimer();
/// Move selection
this->selection_.selectionMin.messageIndex -= this->pauseSelectionOffset_;
this->selection_.selectionMax.messageIndex -= this->pauseSelectionOffset_;
this->selection_.start.messageIndex -= this->pauseSelectionOffset_;
this->selection_.end.messageIndex -= this->pauseSelectionOffset_;
this->pauseSelectionOffset_ = 0;
this->updatePauses();
}
void ChannelView::updatePauseTimer()
void ChannelView::updatePauses()
{
using namespace std::chrono;
if (this->pauses_.empty())
{
this->unpaused();
/// No pauses so we can stop the timer
this->pauseEnd_ = boost::none;
this->pauseTimer_.stop();
@@ -292,6 +285,17 @@ void ChannelView::updatePauseTimer()
}
}
void ChannelView::unpaused()
{
/// Move selection
this->selection_.selectionMin.messageIndex -= this->pauseSelectionOffset_;
this->selection_.selectionMax.messageIndex -= this->pauseSelectionOffset_;
this->selection_.start.messageIndex -= this->pauseSelectionOffset_;
this->selection_.end.messageIndex -= this->pauseSelectionOffset_;
this->pauseSelectionOffset_ = 0;
}
void ChannelView::themeChangedEvent()
{
BaseWidget::themeChangedEvent();
@@ -582,6 +586,11 @@ void ChannelView::setChannel(ChannelPtr channel)
this->lastMessageHasAlternateBackground_ =
!this->lastMessageHasAlternateBackground_;
if (channel->shouldIgnoreHighlights())
{
messageLayout->flags.set(MessageLayoutFlag::IgnoreHighlights);
}
this->messages_.pushBack(MessageLayoutPtr(messageLayout), deleted);
this->scrollBar_->addHighlight(snapshot[i]->getScrollBarHighlight());
}
@@ -1029,14 +1038,6 @@ void ChannelView::leaveEvent(QEvent *)
void ChannelView::mouseMoveEvent(QMouseEvent *event)
{
if (event->modifiers() & (Qt::AltModifier | Qt::ControlModifier))
{
this->unsetCursor();
event->ignore();
return;
}
/// Pause on hover
if (float pauseTime = getSettings()->pauseOnHoverDuration;
pauseTime > 0.001f)
@@ -1048,7 +1049,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
this->pause(PauseReason::Mouse);
}
auto tooltipWidget = TooltipWidget::getInstance();
auto tooltipWidget = TooltipWidget::instance();
std::shared_ptr<MessageLayout> layout;
QPoint relativePos;
int messageIndex;
@@ -1241,7 +1242,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
}
else
{
auto &tooltipPreviewImage = TooltipPreviewImage::getInstance();
auto &tooltipPreviewImage = TooltipPreviewImage::instance();
auto emoteElement = dynamic_cast<const EmoteElement *>(
&hoverLayoutElement->getCreator());
auto badgeElement = dynamic_cast<const BadgeElement *>(
@@ -1326,8 +1327,7 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
// check if message is collapsed
switch (event->button())
{
case Qt::LeftButton:
{
case Qt::LeftButton: {
this->lastPressPosition_ = event->screenPos();
this->isMouseDown_ = true;
@@ -1345,8 +1345,7 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
}
break;
case Qt::RightButton:
{
case Qt::RightButton: {
this->lastRightPressPosition_ = event->screenPos();
this->isRightMouseDown_ = true;
}
@@ -1464,8 +1463,7 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
{
switch (event->button())
{
case Qt::LeftButton:
{
case Qt::LeftButton: {
if (this->selecting_)
{
// this->pausedBySelection = false;
@@ -1489,8 +1487,7 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
}
}
break;
case Qt::RightButton:
{
case Qt::RightButton: {
auto insertText = [=](QString text) {
if (auto split = dynamic_cast<Split *>(this->parentWidget()))
{
@@ -1501,7 +1498,8 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
auto &link = hoveredElement->getLink();
if (link.type == Link::UserInfo)
{
insertText("@" + link.value + ", ");
const bool commaMention = getSettings()->mentionUsersWithComma;
insertText("@" + link.value + (commaMention ? ", " : " "));
}
else if (link.type == Link::UserWhisper)
{
@@ -1705,16 +1703,14 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
switch (link.type)
{
case Link::UserWhisper:
case Link::UserInfo:
{
case Link::UserInfo: {
auto user = link.value;
this->showUserInfoPopup(user);
qDebug() << "Clicked " << user << "s message";
}
break;
case Link::Url:
{
case Link::Url: {
if (getSettings()->openLinksIncognito && supportsIncognitoLinks())
openLinkIncognito(link.value);
else
@@ -1722,8 +1718,7 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
}
break;
case Link::UserAction:
{
case Link::UserAction: {
QString value = link.value;
value.replace("{user}", layout->getMessage()->loginName)
@@ -1735,14 +1730,12 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
}
break;
case Link::AutoModAllow:
{
case Link::AutoModAllow: {
getApp()->accounts->twitch.getCurrent()->autoModAllow(link.value);
}
break;
case Link::AutoModDeny:
{
case Link::AutoModDeny: {
getApp()->accounts->twitch.getCurrent()->autoModDeny(link.value);
}
+9 -9
View File
@@ -1,22 +1,21 @@
#pragma once
#include "common/FlagsEnum.hpp"
#include "messages/Image.hpp"
#include "messages/LimitedQueue.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "messages/Selection.hpp"
#include "widgets/BaseWidget.hpp"
#include <QPaintEvent>
#include <QScroller>
#include <QTimer>
#include <QWheelEvent>
#include <QWidget>
#include <pajlada/signals/signal.hpp>
#include <unordered_map>
#include <unordered_set>
#include "common/FlagsEnum.hpp"
#include "messages/Image.hpp"
#include "messages/LimitedQueue.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "messages/Selection.hpp"
#include "widgets/BaseWidget.hpp"
namespace chatterino {
enum class HighlightState;
@@ -146,7 +145,8 @@ private:
void addContextMenuItems(const MessageLayoutElement *hoveredElement,
MessageLayout *layout);
int getLayoutWidth() const;
void updatePauseTimer();
void updatePauses();
void unpaused();
QTimer *layoutCooldown_;
bool layoutQueued_;
+43
View File
@@ -0,0 +1,43 @@
#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_.setMargin(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->setMargin(0);
// hbox.addSpacing(spacing);
hbox->addWidget(&this->label_);
// hbox.addSpacing(spacing);
}
Label &EffectLabel2::getLabel()
{
return this->label_;
}
} // namespace chatterino
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "widgets/BaseWidget.hpp"
#include "widgets/Label.hpp"
#include "widgets/helper/Button.hpp"
#include "widgets/helper/SignalLabel.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
+3 -6
View File
@@ -62,8 +62,7 @@ void NotebookButton::paintEvent(QPaintEvent *event)
switch (icon_)
{
case Plus:
{
case Plus: {
painter.setPen([&] {
QColor tmp = foreground;
if (SplitContainer::isDraggingSplit)
@@ -90,8 +89,7 @@ void NotebookButton::paintEvent(QPaintEvent *event)
}
break;
case User:
{
case User: {
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
@@ -111,8 +109,7 @@ void NotebookButton::paintEvent(QPaintEvent *event)
}
break;
case Settings:
{
case Settings: {
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
+19 -10
View File
@@ -22,6 +22,16 @@
#include <boost/bind.hpp>
namespace chatterino {
namespace {
qreal deviceDpi(QWidget *widget)
{
#ifdef Q_OS_WIN
return widget->devicePixelRatioF();
#else
return 1.0;
#endif
}
} // namespace
NotebookTab::NotebookTab(Notebook *notebook)
: Button(notebook)
@@ -83,11 +93,10 @@ void NotebookTab::themeChangedEvent()
void NotebookTab::updateSize()
{
float scale = this->scale();
int width;
QFontMetrics metrics = getApp()->fonts->getFontMetrics(
FontStyle::UiTabs,
float(qreal(this->scale()) * this->devicePixelRatioF()));
FontStyle::UiTabs, float(qreal(this->scale()) * deviceDpi(this)));
if (this->hasXButton())
{
@@ -276,10 +285,10 @@ void NotebookTab::paintEvent(QPaintEvent *)
painter.setFont(getApp()->fonts->getFont(
FontStyle::UiTabs,
scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF()));
scale * 96.f / this->logicalDpiX() * deviceDpi(this)));
QFontMetrics metrics = app->fonts->getFontMetrics(
FontStyle::UiTabs,
scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF());
scale * 96.f / this->logicalDpiX() * deviceDpi(this));
int height = int(scale * NOTEBOOK_TAB_HEIGHT);
@@ -299,12 +308,12 @@ void NotebookTab::paintEvent(QPaintEvent *)
bool windowFocused = this->window() == QApplication::activeWindow();
QBrush tabBackground = /*this->mouseOver_ ? colors.backgrounds.hover
:*/
:*/
(windowFocused ? colors.backgrounds.regular
: colors.backgrounds.unfocused);
// fill the tab background
auto bgRect = rect();
auto bgRect = this->rect();
bgRect.setTop(ceil((this->selected_ ? 0.f : 1.f) * scale));
painter.fillRect(bgRect, tabBackground);
@@ -432,8 +441,7 @@ void NotebookTab::mousePressEvent(QMouseEvent *event)
{
switch (event->button())
{
case Qt::RightButton:
{
case Qt::RightButton: {
this->menu_.popup(event->globalPos());
}
break;
@@ -458,7 +466,8 @@ void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
}
};
if (event->button() == Qt::MiddleButton)
if (event->button() == Qt::MiddleButton &&
this->notebook_->getAllowUserTabManagement())
{
if (this->rect().contains(event->pos()))
{
-11
View File
@@ -73,17 +73,6 @@ void SearchPopup::search()
this->channelName_, this->snapshot_));
}
void SearchPopup::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
{
this->close();
return;
}
BaseWidget::keyPressEvent(e);
}
void SearchPopup::initLayout()
{
// VBOX
+2 -4
View File
@@ -3,7 +3,7 @@
#include "ForwardDecl.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "messages/search/MessagePredicate.hpp"
#include "widgets/BaseWindow.hpp"
#include "widgets/BasePopup.hpp"
#include <memory>
@@ -11,7 +11,7 @@ class QLineEdit;
namespace chatterino {
class SearchPopup : public BaseWindow
class SearchPopup : public BasePopup
{
public:
SearchPopup();
@@ -19,8 +19,6 @@ public:
virtual void setChannel(const ChannelPtr &channel);
protected:
void keyPressEvent(QKeyEvent *e) override;
virtual void updateWindowTitle();
private:
+41
View File
@@ -0,0 +1,41 @@
#include "widgets/helper/SignalLabel.hpp"
namespace chatterino {
SignalLabel::SignalLabel(QWidget *parent, Qt::WindowFlags f)
: QLabel(parent, f)
{
}
void SignalLabel::mouseDoubleClickEvent(QMouseEvent *ev)
{
emit this->mouseDoubleClick(ev);
}
void SignalLabel::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
emit mouseDown();
}
event->ignore();
}
void SignalLabel::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
emit mouseUp();
}
event->ignore();
}
void SignalLabel::mouseMoveEvent(QMouseEvent *event)
{
emit this->mouseMove(event);
event->ignore();
}
} // namespace chatterino
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <QFlags>
#include <QLabel>
#include <QMouseEvent>
#include <QWidget>
namespace chatterino {
class SignalLabel : public QLabel
{
Q_OBJECT
public:
explicit SignalLabel(QWidget *parent = nullptr, Qt::WindowFlags f = 0);
virtual ~SignalLabel() override = default;
signals:
void mouseDoubleClick(QMouseEvent *ev);
void mouseDown();
void mouseUp();
void mouseMove(QMouseEvent *event);
protected:
void mouseDoubleClickEvent(QMouseEvent *ev) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
};
} // namespace chatterino
+128
View File
@@ -0,0 +1,128 @@
#include "TitlebarButton.hpp"
#include "BaseTheme.hpp"
namespace chatterino {
TitleBarButton::TitleBarButton()
: Button(nullptr)
{
}
TitleBarButtonStyle TitleBarButton::getButtonStyle() const
{
return this->style_;
}
void TitleBarButton::setButtonStyle(TitleBarButtonStyle _style)
{
this->style_ = _style;
this->update();
}
void TitleBarButton::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setOpacity(this->getCurrentDimAmount());
QColor color = this->theme->window.text;
QColor background = this->theme->window.background;
int xD = this->height() / 3;
int centerX = this->width() / 2;
painter.setRenderHint(QPainter::Antialiasing, false);
switch (this->style_)
{
case TitleBarButtonStyle::Minimize: {
painter.fillRect(centerX - xD / 2, xD * 3 / 2, xD, 1, color);
break;
}
case TitleBarButtonStyle::Maximize: {
painter.setPen(color);
painter.drawRect(centerX - xD / 2, xD, xD - 1, xD - 1);
break;
}
case TitleBarButtonStyle::Unmaximize: {
int xD2 = xD * 1 / 5;
int xD3 = xD * 4 / 5;
painter.drawRect(centerX - xD / 2 + xD2, xD, xD3, xD3);
painter.fillRect(centerX - xD / 2, xD + xD2, xD3, xD3,
this->theme->window.background);
painter.drawRect(centerX - xD / 2, xD + xD2, xD3, xD3);
break;
}
case TitleBarButtonStyle::Close: {
QRect rect(centerX - xD / 2, xD, xD - 1, xD - 1);
painter.setPen(QPen(color, 1));
painter.drawLine(rect.topLeft(), rect.bottomRight());
painter.drawLine(rect.topRight(), rect.bottomLeft());
break;
}
case TitleBarButtonStyle::User: {
color = "#999";
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
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: {
color = "#999";
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.save();
painter.translate(3, 3);
auto a = xD / 3;
QPainterPath path;
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
for (int i = 0; i < 8; i++)
{
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0),
(360 / 32.0));
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a,
i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
}
painter.strokePath(path, color);
painter.fillPath(path, color);
painter.setBrush(background);
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
painter.restore();
break;
}
default:;
}
Button::paintEvent(event);
// this->fancyPaint(painter);
}
} // namespace chatterino
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include "widgets/helper/Button.hpp"
namespace chatterino {
enum class TitleBarButtonStyle {
None = 0,
Minimize = 1,
Maximize = 2,
Unmaximize = 4,
Close = 8,
User = 16,
Settings = 32
};
class TitleBarButton : public Button
{
public:
TitleBarButton();
TitleBarButtonStyle getButtonStyle() const;
void setButtonStyle(TitleBarButtonStyle style_);
protected:
void paintEvent(QPaintEvent *) override;
private:
TitleBarButtonStyle style_;
};
} // namespace chatterino
+7 -5
View File
@@ -75,15 +75,17 @@ AboutPage::AboutPage()
auto versionInfo = layout.emplace<QGroupBox>("Version");
{
auto version = Version::getInstance();
auto version = Version::instance();
QString text = QString("%1 (commit %2%3)")
.arg(version.getFullVersion())
.arg(version.fullVersion())
.arg("<a "
"href=\"https://github.com/Chatterino/"
"chatterino2/commit/" +
version.getCommitHash() + "\">" +
version.getCommitHash() + "</a>")
.arg(Modes::getInstance().isNightly ? ", " + version.getDateOfBuild() : "");
version.commitHash() + "\">" +
version.commitHash() + "</a>")
.arg(Modes::instance().isNightly
? ", " + version.dateOfBuild()
: "");
auto versionLabel = versionInfo.emplace<QLabel>(text);
versionLabel->setOpenExternalLinks(true);
+14 -4
View File
@@ -303,6 +303,7 @@ void GeneralPage::initLayout(SettingsLayout &layout)
#ifdef USEWINSDK
layout.addCheckbox("Start with Windows", s.autorun);
#endif
layout.addCheckbox("Restart on crash", s.restartOnCrash);
layout.addTitle("Chat");
@@ -390,6 +391,7 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addCheckbox("Animate", s.animateEmotes);
layout.addCheckbox("Animate only when Chatterino is focused",
s.animationsWhenFocused);
layout.addCheckbox("Stack bits", s.stackBits);
layout.addDropdown<float>(
"Size", {"0.5x", "0.75x", "Default", "1.25x", "1.5x", "2x"},
s.emoteScale,
@@ -438,10 +440,16 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addDescription("The browser extension replaces the default "
"Twitch.tv chat with chatterino.");
layout.addDescription(formatRichNamedLink(CHROME_EXTENSION_LINK,
"Download for Google Chrome"));
layout.addDescription(formatRichNamedLink(
CHROME_EXTENSION_LINK,
"Download for Google Chrome and similar browsers."));
layout.addDescription(
formatRichNamedLink(FIREFOX_EXTENSION_LINK, "Download for Firefox"));
layout.addDescription("Chatterino only attaches to known browsers to avoid "
"attaching to other windows by accident.");
layout.addCheckbox("Attach to any browser (may cause issues).",
s.attachExtensionToAnyProcess);
#endif
layout.addTitle("Miscellaneous");
@@ -496,13 +504,15 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addCheckbox("Load message history on connect",
s.loadTwitchMessageHistoryOnConnect);
layout.addCheckbox("Show unhandled irc messages",
layout.addCheckbox("Enable experimental IRC support (requires restart)",
s.enableExperimentalIrc);
layout.addCheckbox("Show unhandled IRC messages",
s.showUnhandledIrcMessages);
layout.addTitle("Cache");
layout.addDescription(
"Files that are used often (such as emotes) are saved to disk to "
"reduce bandwidth usage and tho speed up loading.");
"reduce bandwidth usage and to speed up loading.");
auto cachePathLabel = layout.addDescription("placeholder :D");
getSettings()->cachePath.connect([cachePathLabel](const auto &,
+1 -1
View File
@@ -418,7 +418,7 @@ void Split::leaveEvent(QEvent *event)
this->overlay_->hide();
TooltipWidget::getInstance()->hide();
TooltipWidget::instance()->hide();
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
}
+8 -16
View File
@@ -339,15 +339,13 @@ void SplitContainer::focusSplitRecursive(Node *node, Direction direction)
{
switch (node->type_)
{
case Node::_Split:
{
case Node::_Split: {
node->split_->giveFocus(Qt::OtherFocusReason);
}
break;
case Node::HorizontalContainer:
case Node::VerticalContainer:
{
case Node::VerticalContainer: {
auto &children = node->children_;
auto it = std::find_if(
@@ -898,23 +896,19 @@ void SplitContainer::Node::insertSplitRelative(Split *_split,
{
switch (this->type_)
{
case Node::EmptyRoot:
{
case Node::EmptyRoot: {
this->setSplit(_split);
}
break;
case Node::_Split:
{
case Node::_Split: {
this->nestSplitIntoCollection(_split, _direction);
}
break;
case Node::HorizontalContainer:
{
case Node::HorizontalContainer: {
this->nestSplitIntoCollection(_split, _direction);
}
break;
case Node::VerticalContainer:
{
case Node::VerticalContainer: {
this->nestSplitIntoCollection(_split, _direction);
}
break;
@@ -1110,16 +1104,14 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale,
switch (this->type_)
{
case Node::_Split:
{
case Node::_Split: {
QRect rect = this->geometry_.toRect();
this->split_->setGeometry(
rect.marginsRemoved(QMargins(1, 1, 0, 0)));
}
break;
case Node::VerticalContainer:
case Node::HorizontalContainer:
{
case Node::HorizontalContainer: {
bool isVertical = this->type_ == Node::VerticalContainer;
// vars
+32 -20
View File
@@ -99,6 +99,12 @@ namespace {
.arg(s.uptime)
.arg(QString::number(s.viewerCount));
}
auto formatOfflineTooltip(const TwitchChannel::StreamStatus &s)
{
return QString("<style>.center { text-align: center; }</style> \
<p class=\"center\">Offline<br>%1</p>")
.arg(s.title.toHtmlEscaped());
}
auto formatTitle(const TwitchChannel::StreamStatus &s, Settings &settings)
{
auto title = QString();
@@ -213,14 +219,12 @@ void SplitHeader::initializeLayout()
this->dropdownButton_ = makeWidget<Button>([&](auto w) {
/// XXX: this never gets disconnected
this->split_->channelChanged.connect([this] {
auto menu = this->createMainMenu();
this->mainMenu_ = menu.get();
this->dropdownButton_->setMenu(std::move(menu));
this->dropdownButton_->setMenu(this->createMainMenu());
});
}),
// add split
this->addButton_ = makeWidget<Button>([&](auto w) {
w->setPixmap(getApp()->resources->buttons.addSplitDark);
w->setPixmap(getResources().buttons.addSplitDark);
w->setEnableMargin(false);
QObject::connect(w, &Button::leftClicked, this,
@@ -301,7 +305,6 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
// sub menu
auto moreMenu = new QMenu("More", this);
moreMenu->addAction("Toggle moderation mode", this->split_, [this]() {
this->split_->setModerationMode(!this->split_->getModerationMode());
});
@@ -553,6 +556,10 @@ void SplitHeader::updateChannelText()
this->tooltipText_ = formatTooltip(*streamStatus);
title += formatTitle(*streamStatus, *getSettings());
}
else
{
this->tooltipText_ = formatOfflineTooltip(*streamStatus);
}
}
this->titleLabel_->setText(title.isEmpty() ? "<empty>" : title);
@@ -564,8 +571,8 @@ void SplitHeader::updateModerationModeIcon()
!getApp()->moderationActions->items.empty();
this->moderationButton_->setPixmap(
moderationMode ? getApp()->resources->buttons.modModeEnabled
: getApp()->resources->buttons.modModeDisabled);
moderationMode ? getResources().buttons.modModeEnabled
: getResources().buttons.modModeDisabled);
auto channel = this->split_->getChannel();
auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
@@ -591,17 +598,17 @@ void SplitHeader::mousePressEvent(QMouseEvent *event)
{
switch (event->button())
{
case Qt::LeftButton:
{
case Qt::LeftButton: {
this->dragging_ = true;
this->dragStart_ = event->pos();
}
break;
case Qt::RightButton:
{
this->mainMenu_->popup(this->mapToGlobal(event->pos()));
case Qt::RightButton: {
auto menu = this->createMainMenu().release();
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->popup(this->mapToGlobal(event->pos() + QPoint(0, 4)));
}
break;
}
@@ -673,9 +680,15 @@ void SplitHeader::enterEvent(QEvent *event)
{
if (!this->tooltipText_.isEmpty())
{
TooltipPreviewImage::getInstance().setImage(nullptr);
auto channel = this->split_->getChannel().get();
if (channel->getType() == Channel::Type::Twitch)
{
dynamic_cast<TwitchChannel *>(channel)->refreshTitle();
}
auto tooltip = TooltipWidget::getInstance();
TooltipPreviewImage::instance().setImage(nullptr);
auto tooltip = TooltipWidget::instance();
tooltip->moveTo(this, this->mapToGlobal(this->rect().bottomLeft()),
false);
tooltip->setText(this->tooltipText_);
@@ -690,7 +703,7 @@ void SplitHeader::enterEvent(QEvent *event)
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
TooltipWidget::instance()->hide();
BaseWidget::leaveEvent(event);
}
@@ -713,14 +726,13 @@ void SplitHeader::themeChangedEvent()
// --
if (this->theme->isLightTheme())
{
this->dropdownButton_->setPixmap(getApp()->resources->buttons.menuDark);
this->addButton_->setPixmap(getApp()->resources->buttons.addSplit);
this->dropdownButton_->setPixmap(getResources().buttons.menuDark);
this->addButton_->setPixmap(getResources().buttons.addSplit);
}
else
{
this->dropdownButton_->setPixmap(
getApp()->resources->buttons.menuLight);
this->addButton_->setPixmap(getApp()->resources->buttons.addSplitDark);
this->dropdownButton_->setPixmap(getResources().buttons.menuLight);
this->addButton_->setPixmap(getResources().buttons.addSplitDark);
}
}
-1
View File
@@ -55,7 +55,6 @@ private:
// ui
Button *dropdownButton_{};
QMenu *mainMenu_{};
Label *titleLabel_{};
EffectLabel *modeButton_{};
Button *moderationButton_{};
+1
View File
@@ -154,6 +154,7 @@ void SplitInput::openEmotePopup()
if (!this->emotePopup_)
{
this->emotePopup_ = new EmotePopup(this);
this->emotePopup_->setStayInScreenRect(true);
this->emotePopup_->setAttribute(Qt::WA_DeleteOnClose);
this->emotePopup_->linkClicked.connect([this](const Link &link) {
+13 -22
View File
@@ -30,15 +30,14 @@ SplitOverlay::SplitOverlay(Split *parent)
layout->setColumnStretch(1, 1);
layout->setColumnStretch(3, 1);
auto *move = new QPushButton(getApp()->resources->split.move, QString());
auto *move = new QPushButton(getResources().split.move, QString());
auto *left = this->left_ =
new QPushButton(getApp()->resources->split.left, QString());
new QPushButton(getResources().split.left, QString());
auto *right = this->right_ =
new QPushButton(getApp()->resources->split.right, QString());
auto *up = this->up_ =
new QPushButton(getApp()->resources->split.up, QString());
new QPushButton(getResources().split.right, QString());
auto *up = this->up_ = new QPushButton(getResources().split.up, QString());
auto *down = this->down_ =
new QPushButton(getApp()->resources->split.down, QString());
new QPushButton(getResources().split.down, QString());
move->setGraphicsEffect(new QGraphicsOpacityEffect(this));
left->setGraphicsEffect(new QGraphicsOpacityEffect(this));
@@ -107,27 +106,23 @@ void SplitOverlay::paintEvent(QPaintEvent *)
QRect rect;
switch (this->hoveredElement_)
{
case SplitLeft:
{
case SplitLeft: {
rect = QRect(0, 0, this->width() / 2, this->height());
}
break;
case SplitRight:
{
case SplitRight: {
rect =
QRect(this->width() / 2, 0, this->width() / 2, this->height());
}
break;
case SplitUp:
{
case SplitUp: {
rect = QRect(0, 0, this->width(), this->height() / 2);
}
break;
case SplitDown:
{
case SplitDown: {
rect =
QRect(0, this->height() / 2, this->width(), this->height() / 2);
}
@@ -184,8 +179,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
{
switch (event->type())
{
case QEvent::Enter:
{
case QEvent::Enter: {
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
@@ -199,8 +193,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
this->parent->update();
}
break;
case QEvent::Leave:
{
case QEvent::Leave: {
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
@@ -214,8 +207,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
this->parent->update();
}
break;
case QEvent::MouseButtonPress:
{
case QEvent::MouseButtonPress: {
if (this->hoveredElement == HoveredElement::SplitMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
@@ -227,8 +219,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
}
}
break;
case QEvent::MouseButtonRelease:
{
case QEvent::MouseButtonRelease: {
if (this->hoveredElement != HoveredElement::SplitMove)
{
SplitContainer *container =