Merge branch 'master' into logging

This commit is contained in:
pajlada
2018-06-05 13:16:20 +02:00
committed by GitHub
172 changed files with 5997 additions and 3348 deletions
+11 -12
View File
@@ -3,7 +3,6 @@
#include "application.hpp"
#include "channel.hpp"
#include "credentials.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "ui_accountpopupform.h"
#include "util/urlfetch.hpp"
@@ -40,8 +39,8 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
connect(this, &AccountPopupWidget::refreshButtons, this,
&AccountPopupWidget::actuallyRefreshButtons, Qt::QueuedConnection);
app->accounts->Twitch.currentUserChanged.connect([=] {
auto currentTwitchUser = app->accounts->Twitch.getCurrent();
app->accounts->twitch.currentUserChanged.connect([this] {
auto currentTwitchUser = getApp()->accounts->twitch.getCurrent();
if (!currentTwitchUser) {
// No twitch user set (should never happen)
return;
@@ -113,7 +112,7 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
});
QObject::connect(this->ui->ignore, &QPushButton::clicked, this, [=]() {
auto currentUser = getApp()->accounts->Twitch.getCurrent();
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (!this->relationship.isIgnoring()) {
currentUser->ignoreByID(this->popupWidgetUser.userID, this->popupWidgetUser.username,
@@ -121,11 +120,11 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
switch (result) {
case IgnoreResult_Success: {
this->relationship.setIgnoring(true);
emit refreshButtons();
emit this->refreshButtons();
} break;
case IgnoreResult_AlreadyIgnored: {
this->relationship.setIgnoring(true);
emit refreshButtons();
emit this->refreshButtons();
} break;
case IgnoreResult_Failed: {
} break;
@@ -137,7 +136,7 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
switch (result) {
case UnignoreResult_Success: {
this->relationship.setIgnoring(false);
emit refreshButtons();
emit this->refreshButtons();
} break;
case UnignoreResult_Failed: {
} break;
@@ -225,12 +224,12 @@ void AccountPopupWidget::getUserData()
});
auto app = getApp();
auto currentUser = app->accounts->Twitch.getCurrent();
auto currentUser = app->accounts->twitch.getCurrent();
currentUser->checkFollow(this->popupWidgetUser.userID, [=](auto result) {
this->relationship.setFollowing(result == FollowResult_Following);
emit refreshButtons();
emit this->refreshButtons();
});
bool isIgnoring = false;
@@ -273,9 +272,9 @@ void AccountPopupWidget::loadAvatar(const QUrl &avatarUrl)
void AccountPopupWidget::scaleChangedEvent(float newDpi)
{
this->setStyleSheet(QString("* { font-size: <font-size>px; }")
.replace("<font-size>", QString::number((int)(12 * newDpi))));
.replace("<font-size>", QString::number(int(12 * newDpi))));
this->ui->lblAvatar->setFixedSize((int)(100 * newDpi), (int)(100 * newDpi));
this->ui->lblAvatar->setFixedSize(int(100 * newDpi), int(100 * newDpi));
}
void AccountPopupWidget::updateButtons(QWidget *layout, bool state)
@@ -302,7 +301,7 @@ void AccountPopupWidget::sendCommand(QPushButton *button, QString command)
void AccountPopupWidget::refreshLayouts()
{
auto currentTwitchUser = getApp()->accounts->Twitch.getCurrent();
auto currentTwitchUser = getApp()->accounts->twitch.getCurrent();
if (!currentTwitchUser) {
// No twitch user set (should never happen)
return;
+2 -1
View File
@@ -19,9 +19,10 @@ class Channel;
namespace widgets {
class AccountPopupWidget : public BaseWindow
class AccountPopupWidget final : public BaseWindow
{
Q_OBJECT
public:
AccountPopupWidget(ChannelPtr _channel);
+5 -2
View File
@@ -36,6 +36,8 @@ AccountSwitchPopupWidget::AccountSwitchPopupWidget(QWidget *parent)
});
this->setLayout(vbox);
// this->setStyleSheet("background: #333");
}
void AccountSwitchPopupWidget::refresh()
@@ -48,11 +50,12 @@ void AccountSwitchPopupWidget::focusOutEvent(QFocusEvent *)
this->hide();
}
void AccountSwitchPopupWidget::paintEvent(QPaintEvent *event)
void AccountSwitchPopupWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(this->rect(), QColor(255, 255, 255));
painter.setPen(QColor("#999"));
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
} // namespace widgets
+7 -7
View File
@@ -2,7 +2,7 @@
#include "application.hpp"
#include "const.hpp"
#include "singletons/accountmanager.hpp"
#include "controllers/accounts/accountcontroller.hpp"
namespace chatterino {
namespace widgets {
@@ -14,18 +14,18 @@ AccountSwitchWidget::AccountSwitchWidget(QWidget *parent)
this->addItem(ANONYMOUS_USERNAME_LABEL);
for (const auto &userName : app->accounts->Twitch.getUsernames()) {
for (const auto &userName : app->accounts->twitch.getUsernames()) {
this->addItem(userName);
}
app->accounts->Twitch.userListUpdated.connect([=]() {
app->accounts->twitch.userListUpdated.connect([=]() {
this->blockSignals(true);
this->clear();
this->addItem(ANONYMOUS_USERNAME_LABEL);
for (const auto &userName : app->accounts->Twitch.getUsernames()) {
for (const auto &userName : app->accounts->twitch.getUsernames()) {
this->addItem(userName);
}
@@ -40,9 +40,9 @@ AccountSwitchWidget::AccountSwitchWidget(QWidget *parent)
if (!this->selectedItems().isEmpty()) {
QString newUsername = this->currentItem()->text();
if (newUsername.compare(ANONYMOUS_USERNAME_LABEL, Qt::CaseInsensitive) == 0) {
app->accounts->Twitch.currentUsername = "";
app->accounts->twitch.currentUsername = "";
} else {
app->accounts->Twitch.currentUsername = newUsername.toStdString();
app->accounts->twitch.currentUsername = newUsername.toStdString();
}
}
});
@@ -61,7 +61,7 @@ void AccountSwitchWidget::refreshSelection()
if (this->count() > 0) {
auto app = getApp();
auto currentUser = app->accounts->Twitch.getCurrent();
auto currentUser = app->accounts->twitch.getCurrent();
if (currentUser->isAnon()) {
this->setCurrentRow(0);
+121 -35
View File
@@ -1,14 +1,16 @@
#include "attachedwindow.hpp"
#include "application.hpp"
#include "util/debugcount.hpp"
#include "widgets/split.hpp"
#include <QTimer>
#include <QVBoxLayout>
#include "widgets/split.hpp"
#ifdef USEWINSDK
#include "Windows.h"
// don't even think about reordering these
#include "Psapi.h"
#pragma comment(lib, "Dwmapi.lib")
#endif
@@ -17,17 +19,19 @@ namespace widgets {
AttachedWindow::AttachedWindow(void *_target, int _yOffset)
: QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window)
, target(_target)
, yOffset(_yOffset)
, target_(_target)
, yOffset_(_yOffset)
{
QLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
this->setLayout(layout);
auto *split = new Split(this);
this->ui.split = split;
this->ui_.split = split;
split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
layout->addWidget(split);
util::DebugCount::increase("attached window");
}
AttachedWindow::~AttachedWindow()
@@ -38,18 +42,51 @@ AttachedWindow::~AttachedWindow()
break;
}
}
util::DebugCount::decrease("attached window");
}
AttachedWindow *AttachedWindow::get(void *target, const QString &winId, int yOffset)
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
{
for (Item &item : items) {
if (item.hwnd == target) {
return item.window;
AttachedWindow *window = [&]() {
for (Item &item : items) {
if (item.hwnd == target) {
return item.window;
}
}
auto *window = new AttachedWindow(target, args.yOffset);
items.push_back(Item{target, window, args.winId});
return window;
}();
bool show = true;
QSize size = window->size();
if (args.height != -1) {
if (args.height == 0) {
window->hide();
show = false;
} else {
window->height_ = args.height;
size.setHeight(args.height);
}
}
if (args.width != -1) {
if (args.width == 0) {
window->hide();
show = false;
} else {
window->width_ = args.width;
size.setWidth(args.width);
}
}
auto *window = new AttachedWindow(target, yOffset);
items.push_back(Item{target, window, winId});
if (show) {
window->updateWindowRect_(window->target_);
window->show();
}
return window;
}
@@ -64,43 +101,92 @@ void AttachedWindow::detach(const QString &winId)
void AttachedWindow::setChannel(ChannelPtr channel)
{
this->ui.split->setChannel(channel);
this->ui_.split->setChannel(channel);
}
void AttachedWindow::showEvent(QShowEvent *)
{
attachToHwnd(this->target);
attachToHwnd_(this->target_);
}
void AttachedWindow::attachToHwnd(void *_hwnd)
void AttachedWindow::attachToHwnd_(void *_attachedPtr)
{
#ifdef USEWINSDK
QTimer *timer = new QTimer(this);
timer->setInterval(1);
if (this->attached_) {
return;
}
HWND hwnd = (HWND)this->winId();
HWND attached = (HWND)_hwnd;
QObject::connect(timer, &QTimer::timeout, [this, hwnd, attached, timer] {
::SetLastError(0);
RECT xD;
::GetWindowRect(attached, &xD);
this->attached_ = true;
this->timer_.setInterval(1);
if (::GetLastError() != 0) {
timer->stop();
timer->deleteLater();
this->deleteLater();
auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
QObject::connect(&this->timer_, &QTimer::timeout, [this, hwnd, attached] {
// check process id
if (!this->validProcessName_) {
DWORD processId;
::GetWindowThreadProcessId(attached, &processId);
HANDLE process =
::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
std::unique_ptr<TCHAR[]> filename(new TCHAR[512]);
DWORD filenameLength = ::GetModuleFileNameEx(process, nullptr, filename.get(), 512);
QString qfilename = QString::fromWCharArray(filename.get(), filenameLength);
if (!qfilename.endsWith("chrome.exe")) {
qDebug() << "NM Illegal caller" << qfilename;
this->timer_.stop();
this->deleteLater();
return;
}
this->validProcessName_ = true;
}
HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
::MoveWindow(hwnd, xD.right - 360, xD.top + this->yOffset - 8, 360 - 8,
xD.bottom - xD.top - this->yOffset, false);
// ::MoveWindow(hwnd, xD.right - 360, xD.top + 82, 360 - 8, xD.bottom - xD.top - 82 -
// 8,
// false);
this->updateWindowRect_(attached);
});
timer->start();
this->timer_.start();
#endif
}
void AttachedWindow::updateWindowRect_(void *_attachedPtr)
{
#ifdef USEWINSDK
auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
// We get the window rect first so we can close this window when it returns an error.
// If we query the process first and check the filename then it will return and empty string
// that doens't match.
::SetLastError(0);
RECT rect;
::GetWindowRect(attached, &rect);
if (::GetLastError() != 0) {
qDebug() << "NM GetLastError()" << ::GetLastError();
this->timer_.stop();
this->deleteLater();
return;
}
// set the correct z-order
HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
if (this->height_ == -1) {
// ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.top + this->yOffset_ - 8,
// this->width_, rect.bottom - rect.top - this->yOffset_, false);
} else {
::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.bottom - this->height_ - 8,
this->width_, this->height_, false);
}
// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom -
// rect.top - 82 - 8, false);
#endif
}
+21 -7
View File
@@ -10,12 +10,19 @@ namespace widgets {
class AttachedWindow : public QWidget
{
AttachedWindow(void *target, int asdf);
AttachedWindow(void *target_, int asdf);
public:
~AttachedWindow();
struct GetArgs {
QString winId;
int yOffset = -1;
int width = -1;
int height = -1;
};
static AttachedWindow *get(void *target, const QString &winId, int yOffset);
virtual ~AttachedWindow() override;
static AttachedWindow *get(void *target_, const GetArgs &args);
static void detach(const QString &winId);
void setChannel(ChannelPtr channel);
@@ -26,14 +33,21 @@ protected:
// override;
private:
void *target;
int yOffset;
void *target_;
int yOffset_;
int currentYOffset_;
int width_ = 360;
int height_ = -1;
bool validProcessName_ = false;
bool attached_ = false;
QTimer timer_;
struct {
Split *split;
} ui;
} ui_;
void attachToHwnd(void *hwnd);
void attachToHwnd_(void *attached);
void updateWindowRect_(void *attached);
struct Item {
void *hwnd;
-1
View File
@@ -27,7 +27,6 @@ BaseWidget::~BaseWidget()
float BaseWidget::getScale() const
{
// return 1.f;
BaseWidget *baseWidget = dynamic_cast<BaseWidget *>(this->window());
if (baseWidget == nullptr) {
+2 -2
View File
@@ -18,7 +18,7 @@ class BaseWidget : public QWidget
public:
explicit BaseWidget(QWidget *parent, Qt::WindowFlags f = Qt::WindowFlags());
virtual ~BaseWidget();
virtual ~BaseWidget() override;
float getScale() const;
pajlada::Signals::Signal<float> scaleChanged;
@@ -26,7 +26,7 @@ public:
QSize getScaleIndependantSize() const;
int getScaleIndependantWidth() const;
int getScaleIndependantHeight() const;
void setScaleIndependantSize(int width, int yOffset);
void setScaleIndependantSize(int width, int height);
void setScaleIndependantSize(QSize);
void setScaleIndependantWidth(int value);
void setScaleIndependantHeight(int value);
+117 -74
View File
@@ -32,17 +32,28 @@
namespace chatterino {
namespace widgets {
BaseWindow::BaseWindow(QWidget *parent, bool _enableCustomFrame)
: BaseWidget(parent, Qt::Window)
, enableCustomFrame(_enableCustomFrame)
BaseWindow::BaseWindow(QWidget *parent, Flags _flags)
: BaseWidget(parent,
Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : (Qt::WindowFlags)0))
, enableCustomFrame(_flags & EnableCustomFrame)
, frameless(_flags & FrameLess)
, flags(_flags)
{
if (this->frameless) {
this->enableCustomFrame = false;
this->setWindowFlag(Qt::FramelessWindowHint);
}
this->init();
}
BaseWindow::Flags BaseWindow::getFlags()
{
return this->flags;
}
void BaseWindow::init()
{
auto app = getApp();
this->setWindowIcon(QIcon(":/images/icon.png"));
#ifdef USEWINSDK
@@ -53,55 +64,57 @@ void BaseWindow::init()
layout->setSpacing(0);
this->setLayout(layout);
{
QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout();
buttonLayout->setMargin(0);
layout->addLayout(buttonLayout);
if (!this->frameless) {
QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout();
buttonLayout->setMargin(0);
layout->addLayout(buttonLayout);
// title
QLabel *title = new QLabel(" Chatterino");
QObject::connect(this, &QWidget::windowTitleChanged,
[title](const QString &text) { title->setText(" " + text); });
// title
QLabel *title = new QLabel(" Chatterino");
QObject::connect(this, &QWidget::windowTitleChanged,
[title](const QString &text) { title->setText(" " + text); });
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
policy.setHorizontalStretch(1);
// title->setBaseSize(0, 0);
title->setScaledContents(true);
title->setSizePolicy(policy);
buttonLayout->addWidget(title);
this->ui.titleLabel = title;
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
policy.setHorizontalStretch(1);
// title->setBaseSize(0, 0);
title->setScaledContents(true);
title->setSizePolicy(policy);
buttonLayout->addWidget(title);
this->ui.titleLabel = title;
// buttons
TitleBarButton *_minButton = new TitleBarButton;
_minButton->setButtonStyle(TitleBarButton::Minimize);
TitleBarButton *_maxButton = new TitleBarButton;
_maxButton->setButtonStyle(TitleBarButton::Maximize);
TitleBarButton *_exitButton = new TitleBarButton;
_exitButton->setButtonStyle(TitleBarButton::Close);
// buttons
TitleBarButton *_minButton = new TitleBarButton;
_minButton->setButtonStyle(TitleBarButton::Minimize);
TitleBarButton *_maxButton = new TitleBarButton;
_maxButton->setButtonStyle(TitleBarButton::Maximize);
TitleBarButton *_exitButton = new TitleBarButton;
_exitButton->setButtonStyle(TitleBarButton::Close);
QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(Qt::WindowMinimized | this->windowState());
});
QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(this->windowState() == Qt::WindowMaximized
? Qt::WindowActive
: Qt::WindowMaximized);
});
QObject::connect(_exitButton, &TitleBarButton::clicked, this,
[this] { this->close(); });
QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(Qt::WindowMinimized | this->windowState());
});
QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(this->windowState() == Qt::WindowMaximized
? Qt::WindowActive
: Qt::WindowMaximized);
});
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);
buttonLayout->addWidget(_maxButton);
buttonLayout->addWidget(_exitButton);
buttonLayout->setSpacing(0);
// buttonLayout->addStretch(1);
buttonLayout->addWidget(_minButton);
buttonLayout->addWidget(_maxButton);
buttonLayout->addWidget(_exitButton);
buttonLayout->setSpacing(0);
}
}
this->ui.layoutBase = new BaseWidget(this);
layout->addWidget(this->ui.layoutBase);
@@ -115,9 +128,21 @@ void BaseWindow::init()
}
#endif
if (app->settings->windowTopMost.getValue()) {
this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint);
#ifdef USEWINSDK
// fourtf: don't ask me why we need to delay this
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,
0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
});
});
}
#else
// if (getApp()->settings->windowTopMost.getValue()) {
// this->setWindowFlag(Qt::WindowStaysOnTopHint);
// }
#endif
}
void BaseWindow::setStayInScreenRect(bool value)
@@ -158,10 +183,12 @@ void BaseWindow::themeRefreshEvent()
palette.setColor(QPalette::Foreground, this->themeManager->window.text);
this->setPalette(palette);
QPalette palette_title;
palette_title.setColor(QPalette::Foreground,
this->themeManager->isLightTheme() ? "#333" : "#ccc");
this->ui.titleLabel->setPalette(palette_title);
if (this->ui.titleLabel) {
QPalette palette_title;
palette_title.setColor(QPalette::Foreground,
this->themeManager->isLightTheme() ? "#333" : "#ccc");
this->ui.titleLabel->setPalette(palette_title);
}
for (RippleEffectButton *button : this->ui.buttons) {
button->setMouseEffectColor(this->themeManager->window.text);
@@ -205,7 +232,7 @@ void BaseWindow::changeEvent(QEvent *)
TooltipWidget::getInstance()->hide();
#ifdef USEWINSDK
if (this->hasCustomWindowFrame()) {
if (this->ui.maxButton) {
this->ui.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized
? TitleBarButton::Unmaximize
: TitleBarButton::Maximize);
@@ -222,10 +249,12 @@ void BaseWindow::leaveEvent(QEvent *)
TooltipWidget::getInstance()->hide();
}
void BaseWindow::moveTo(QWidget *parent, QPoint point)
void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset)
{
point.rx() += 16;
point.ry() += 16;
if (offset) {
point.rx() += 16;
point.ry() += 16;
}
this->move(point);
this->moveIntoDesktopRect(parent);
@@ -295,10 +324,18 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
NCCALCSIZE_PARAMS *ncp = (reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam));
ncp->lppos->flags |= SWP_NOREDRAW;
RECT *clientRect = &ncp->rgrc[0];
clientRect->left += cx;
clientRect->top += 0;
clientRect->right -= cx;
clientRect->bottom -= cy;
if (IsWindows10OrGreater()) {
clientRect->left += cx;
clientRect->top += 0;
clientRect->right -= cx;
clientRect->bottom -= cy;
} else {
clientRect->left += 1;
clientRect->top += 0;
clientRect->right -= 1;
clientRect->bottom -= 1;
}
}
*result = 0;
@@ -306,14 +343,13 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
} else {
return QWidget::nativeEvent(eventType, message, result);
}
break;
}
} break;
case WM_NCHITTEST: {
if (this->hasCustomWindowFrame()) {
*result = 0;
const LONG border_width = 8; // in pixels
RECT winrect;
GetWindowRect((HWND)winId(), &winrect);
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
@@ -400,17 +436,18 @@ void BaseWindow::showEvent(QShowEvent *event)
{
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);
// SetWindowLongPtr((HWND)this->winId(), GWL_STYLE,
// WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX |
// WS_MINIMIZEBOX);
const MARGINS shadow = {8, 8, 8, 8};
DwmExtendFrameIntoClientArea((HWND)this->winId(), &shadow);
DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow);
}
BaseWidget::showEvent(event);
}
void BaseWindow::paintEvent(QPaintEvent *event)
void BaseWindow::paintEvent(QPaintEvent *)
{
if (this->hasCustomWindowFrame()) {
QPainter painter(this);
@@ -434,13 +471,19 @@ void BaseWindow::calcButtonsSizes()
return;
}
if ((this->width() / this->getScale()) < 300) {
this->ui.minButton->setScaleIndependantSize(30, 30);
this->ui.maxButton->setScaleIndependantSize(30, 30);
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 {
this->ui.minButton->setScaleIndependantSize(46, 30);
this->ui.maxButton->setScaleIndependantSize(46, 30);
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
+11 -5
View File
@@ -19,7 +19,9 @@ class BaseWindow : public BaseWidget
Q_OBJECT
public:
explicit BaseWindow(QWidget *parent = nullptr, bool enableCustomFrame = false);
enum Flags { None = 0, EnableCustomFrame = 1, FrameLess = 2, TopMost = 4 };
explicit BaseWindow(QWidget *parent = nullptr, Flags flags = None);
QWidget *getLayoutContainer();
bool hasCustomWindowFrame();
@@ -29,7 +31,9 @@ public:
void setStayInScreenRect(bool value);
bool getStayInScreenRect() const;
void moveTo(QWidget *widget, QPoint point);
void moveTo(QWidget *widget, QPoint point, bool offset = true);
Flags getFlags();
protected:
#ifdef USEWINSDK
@@ -51,16 +55,18 @@ private:
void calcButtonsSizes();
bool enableCustomFrame;
bool frameless;
bool stayInScreenRect = false;
bool shown = false;
Flags flags;
struct {
QHBoxLayout *titlebarBox;
QWidget *titleLabel;
QHBoxLayout *titlebarBox = nullptr;
QWidget *titleLabel = nullptr;
TitleBarButton *minButton = nullptr;
TitleBarButton *maxButton = nullptr;
TitleBarButton *exitButton = nullptr;
QWidget *layoutBase;
QWidget *layoutBase = nullptr;
std::vector<RippleEffectButton *> buttons;
} ui;
};
+35 -8
View File
@@ -1,12 +1,13 @@
#include "emotepopup.hpp"
#include "application.hpp"
#include "controllers/accounts/accountcontroller.hpp"
#include "messages/messagebuilder.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "singletons/accountmanager.hpp"
#include "widgets/notebook.hpp"
#include <QHBoxLayout>
#include <QShortcut>
#include <QTabWidget>
using namespace chatterino::providers::twitch;
@@ -16,14 +17,14 @@ namespace chatterino {
namespace widgets {
EmotePopup::EmotePopup()
: BaseWindow(nullptr, true)
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
{
this->viewEmotes = new ChannelView();
this->viewEmojis = new ChannelView();
this->viewEmotes->setOverrideFlags((MessageElement::Flags)(
this->viewEmotes->setOverrideFlags(MessageElement::Flags(
MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages));
this->viewEmojis->setOverrideFlags((MessageElement::Flags)(
this->viewEmojis->setOverrideFlags(MessageElement::Flags(
MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages));
this->viewEmotes->setEnableScrollingToBottom(false);
@@ -32,7 +33,7 @@ EmotePopup::EmotePopup()
auto *layout = new QVBoxLayout(this);
this->getLayoutContainer()->setLayout(layout);
Notebook2 *notebook = new Notebook2(this);
Notebook *notebook = new Notebook(this);
layout->addWidget(notebook);
layout->setMargin(0);
@@ -83,10 +84,36 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
auto app = getApp();
QString userID = app->accounts->Twitch.getCurrent()->getUserId();
QString userID = app->accounts->twitch.getCurrent()->getUserId();
// fourtf: the entire emote manager needs to be refactored so there's no point in trying to
// fix this pile of garbage
for (const auto &set : app->emotes->twitchAccountEmotes[userID.toStdString()].emoteSets) {
// TITLE
messages::MessageBuilder builder1;
builder1.append(new TextElement("Twitch Account Emotes", MessageElement::Text));
builder1.getMessage()->flags |= Message::Centered;
emoteChannel->addMessage(builder1.getMessage());
// EMOTES
messages::MessageBuilder builder2;
builder2.getMessage()->flags |= Message::Centered;
builder2.getMessage()->flags |= Message::DisableCompactEmotes;
for (const auto &emote : set.second) {
[&](const QString &key, const util::EmoteData &value) {
builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::InsertText, key)));
}(QString::fromStdString(emote.code),
app->emotes->getTwitchEmoteById(QString::fromStdString(emote.id).toLong(),
QString::fromStdString(emote.code)));
}
emoteChannel->addMessage(builder2.getMessage());
}
addEmotes(app->emotes->twitchAccountEmotes[userID.toStdString()].emotes,
"Twitch Account Emotes", "Twitch Account Emote");
addEmotes(app->emotes->bttvGlobalEmotes, "BetterTTV Global Emotes", "BetterTTV Global Emote");
addEmotes(*channel->bttvChannelEmotes.get(), "BetterTTV Channel Emotes",
"BetterTTV Channel Emote");
+275 -123
View File
@@ -27,7 +27,10 @@
#include <functional>
#include <memory>
#define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale())
#define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 2) * this->getScale())
#define DRAW_WIDTH (this->width())
#define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3
#define CHAT_HOVER_PAUSE_DURATION 400
using namespace chatterino::messages;
using namespace chatterino::providers::twitch;
@@ -42,9 +45,6 @@ ChannelView::ChannelView(BaseWidget *parent)
{
auto app = getApp();
#ifndef Q_OS_MAC
// this->setAttribute(Qt::WA_OpaquePaintEvent);
#endif
this->setMouseTracking(true);
this->managedConnections.emplace_back(app->settings->wordFlagsChanged.connect([=] {
@@ -55,8 +55,12 @@ ChannelView::ChannelView(BaseWidget *parent)
this->scrollBar.getCurrentValueChanged().connect([this] {
// Whenever the scrollbar value has been changed, re-render the ChatWidgetView
this->actuallyLayoutMessages(true);
this->goToBottom->setVisible(this->enableScrollingToBottom && this->scrollBar.isVisible() &&
!this->scrollBar.isAtBottom());
if (!this->isPaused()) {
this->goToBottom->setVisible(this->enableScrollingToBottom &&
this->scrollBar.isVisible() &&
!this->scrollBar.isAtBottom());
}
this->queueUpdate();
});
@@ -97,12 +101,10 @@ ChannelView::ChannelView(BaseWidget *parent)
// });
this->pauseTimeout.setSingleShot(true);
// auto e = new QResizeEvent(this->size(), this->size());
// this->resizeEvent(e);
// delete e;
this->scrollBar.resize(this->scrollBar.width(), this->height() + 1);
QObject::connect(&this->pauseTimeout, &QTimer::timeout, [this] {
this->pausedTemporarily = false;
this->layoutMessages();
});
app->settings->showLastMessageIndicator.connect(
[this](auto, auto) {
@@ -120,6 +122,15 @@ ChannelView::ChannelView(BaseWidget *parent)
this->layoutQueued = false;
}
});
QTimer::singleShot(1000, this, [this] {
this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0,
this->scrollBar.width(), this->height());
});
QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+C"), this);
QObject::connect(shortcut, &QShortcut::activated,
[this] { QGuiApplication::clipboard()->setText(this->getSelectedText()); });
}
ChannelView::~ChannelView()
@@ -165,9 +176,10 @@ void ChannelView::layoutMessages()
void ChannelView::actuallyLayoutMessages(bool causedByScrollbar)
{
// BenchmarkGuard benchmark("layout messages");
auto app = getApp();
// BENCH(timer)
auto messagesSnapshot = this->getMessagesSnapshot();
if (messagesSnapshot.getLength() == 0) {
@@ -246,13 +258,14 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar)
// Perhaps also if the user scrolled with the scrollwheel in this ChatWidget in the last 0.2
// seconds or something
if (this->enableScrollingToBottom && this->showingLatestMessages && showScrollbar) {
this->scrollBar.scrollToBottom(this->messageWasAdded &&
app->settings->enableSmoothScrollingNewMessages.getValue());
if (!this->isPaused()) {
this->scrollBar.scrollToBottom(
// this->messageWasAdded &&
app->settings->enableSmoothScrollingNewMessages.getValue());
}
this->messageWasAdded = false;
}
// MARK(timer);
if (redrawRequired) {
this->queueUpdate();
}
@@ -336,10 +349,11 @@ const boost::optional<messages::MessageElement::Flags> &ChannelView::getOverride
messages::LimitedQueueSnapshot<MessageLayoutPtr> ChannelView::getMessagesSnapshot()
{
if (!this->paused) {
this->snapshot = this->messages.getSnapshot();
}
// if (!this->isPaused()) {
this->snapshot = this->messages.getSnapshot();
// }
// return this->snapshot;
return this->snapshot;
}
@@ -363,14 +377,18 @@ void ChannelView::setChannel(ChannelPtr newChannel)
}
this->lastMessageHasAlternateBackground = !this->lastMessageHasAlternateBackground;
if (this->isPaused()) {
this->messagesAddedSinceSelectionPause++;
}
if (this->messages.pushBack(MessageLayoutPtr(messageRef), deleted)) {
if (!this->paused) {
if (this->scrollBar.isAtBottom()) {
this->scrollBar.scrollToBottom();
} else {
this->scrollBar.offset(-1);
}
// if (!this->isPaused()) {
if (this->scrollBar.isAtBottom()) {
this->scrollBar.scrollToBottom();
} else {
this->scrollBar.offset(-1);
}
// }
}
if (!(message->flags & Message::DoNotTriggerNotification)) {
@@ -395,7 +413,7 @@ void ChannelView::setChannel(ChannelPtr newChannel)
messageRefs.at(i) = MessageLayoutPtr(new MessageLayout(messages.at(i)));
}
if (!this->paused) {
if (!this->isPaused()) {
if (this->messages.pushFront(messageRefs).size() > 0) {
if (this->scrollBar.isAtBottom()) {
this->scrollBar.scrollToBottom();
@@ -474,7 +492,7 @@ void ChannelView::detachChannel()
void ChannelView::pause(int msecTimeout)
{
this->paused = true;
this->pausedTemporarily = true;
this->pauseTimeout.start(msecTimeout);
}
@@ -492,8 +510,8 @@ void ChannelView::updateLastReadMessage()
void ChannelView::resizeEvent(QResizeEvent *)
{
this->scrollBar.resize(this->scrollBar.width(), this->height());
this->scrollBar.move(this->width() - this->scrollBar.width(), 0);
this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0, this->scrollBar.width(),
this->height());
this->goToBottom->setGeometry(0, this->height() - 32, this->width(), 32);
@@ -507,6 +525,13 @@ void ChannelView::resizeEvent(QResizeEvent *)
void ChannelView::setSelection(const SelectionItem &start, const SelectionItem &end)
{
// selections
if (!this->selecting && start != end) {
this->messagesAddedSinceSelectionPause = 0;
this->selecting = true;
this->pausedBySelection = true;
}
this->selection = Selection(start, end);
this->selectionChanged.invoke();
@@ -536,9 +561,26 @@ messages::MessageElement::Flags ChannelView::getFlags() const
return flags;
}
bool ChannelView::isPaused()
{
return this->pausedTemporarily || this->pausedBySelection;
}
// void ChannelView::beginPause()
//{
// if (this->scrollBar.isAtBottom()) {
// this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.001);
// this->layoutMessages();
// }
//}
// void ChannelView::endPause()
//{
//}
void ChannelView::paintEvent(QPaintEvent * /*event*/)
{
// BENCH(timer);
// BenchmarkGuard benchmark("paint event");
QPainter painter(this);
@@ -546,8 +588,6 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/)
// draw messages
this->drawMessages(painter);
// MARK(timer);
}
// if overlays is false then it draws the message, if true then it draws things such as the grey
@@ -558,14 +598,14 @@ void ChannelView::drawMessages(QPainter &painter)
auto messagesSnapshot = this->getMessagesSnapshot();
size_t start = this->scrollBar.getCurrentValue();
size_t start = size_t(this->scrollBar.getCurrentValue());
if (start >= messagesSnapshot.getLength()) {
return;
}
int y = -(messagesSnapshot[start].get()->getHeight() *
(fmod(this->scrollBar.getCurrentValue(), 1)));
int y = int(-(messagesSnapshot[start].get()->getHeight() *
(fmod(this->scrollBar.getCurrentValue(), 1))));
messages::MessageLayout *end = nullptr;
bool windowFocused = this->window() == QApplication::activeWindow();
@@ -578,7 +618,7 @@ void ChannelView::drawMessages(QPainter &painter)
isLastMessage = this->lastReadMessage.get() == layout;
}
layout->paint(painter, y, i, this->selection, isLastMessage, windowFocused);
layout->paint(painter, DRAW_WIDTH, y, i, this->selection, isLastMessage, windowFocused);
y += layout->getHeight();
@@ -622,6 +662,9 @@ void ChannelView::drawMessages(QPainter &painter)
void ChannelView::wheelEvent(QWheelEvent *event)
{
this->pausedBySelection = false;
this->pausedTemporarily = false;
if (this->scrollBar.isVisible()) {
auto app = getApp();
@@ -691,7 +734,8 @@ void ChannelView::enterEvent(QEvent *)
void ChannelView::leaveEvent(QEvent *)
{
this->paused = false;
this->pausedTemporarily = false;
this->layoutMessages();
}
void ChannelView::mouseMoveEvent(QMouseEvent *event)
@@ -706,7 +750,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
auto app = getApp();
if (app->settings->pauseChatHover.getValue()) {
this->pause(300);
this->pause(CHAT_HOVER_PAUSE_DURATION);
}
auto tooltipWidget = TooltipWidget::getInstance();
@@ -722,8 +766,8 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
}
// is selecting
if (this->selecting) {
this->pause(500);
if (this->isMouseDown) {
this->pause(300);
int index = layout->getSelectionIndex(relativePos);
this->setSelection(this->selection.start, SelectionItem(messageIndex, index));
@@ -732,7 +776,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
}
// message under cursor is collapsed
if (layout->getMessage()->flags & Message::Collapsed) {
if (layout->flags & MessageLayout::Collapsed) {
this->setCursor(Qt::PointingHandCursor);
tooltipWidget->hide();
return;
@@ -754,6 +798,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
tooltipWidget->moveTo(this, event->globalPos());
tooltipWidget->setText(tooltip);
tooltipWidget->show();
tooltipWidget->raise();
}
// check if word has a link
@@ -766,29 +811,14 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
void ChannelView::mousePressEvent(QMouseEvent *event)
{
if (event->modifiers() & (Qt::AltModifier | Qt::ControlModifier)) {
this->unsetCursor();
event->ignore();
return;
}
auto app = getApp();
if (app->settings->linksDoubleClickOnly.getValue()) {
this->pause(200);
}
this->isMouseDown = true;
this->lastPressPosition = event->screenPos();
this->mouseDown.invoke(event);
std::shared_ptr<messages::MessageLayout> layout;
QPoint relativePos;
int messageIndex;
this->mouseDown.invoke(event);
if (!tryGetMessageAt(event->pos(), layout, relativePos, messageIndex)) {
setCursor(Qt::ArrowCursor);
@@ -798,71 +828,87 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
}
// Start selection at the last message at its last index
auto lastMessageIndex = messagesSnapshot.getLength() - 1;
auto lastMessage = messagesSnapshot[lastMessageIndex];
auto lastCharacterIndex = lastMessage->getLastCharacterIndex();
if (event->button() == Qt::LeftButton) {
auto lastMessageIndex = messagesSnapshot.getLength() - 1;
auto lastMessage = messagesSnapshot[lastMessageIndex];
auto lastCharacterIndex = lastMessage->getLastCharacterIndex();
SelectionItem selectionItem(lastMessageIndex, lastCharacterIndex);
this->setSelection(selectionItem, selectionItem);
this->selecting = true;
SelectionItem selectionItem(lastMessageIndex, lastCharacterIndex);
this->setSelection(selectionItem, selectionItem);
}
return;
}
// check if message is collapsed
if (layout->getMessage()->flags & Message::Collapsed) {
if (layout->flags & MessageLayout::Collapsed) {
return;
}
int index = layout->getSelectionIndex(relativePos);
switch (event->button()) {
case Qt::LeftButton: {
if (app->settings->linksDoubleClickOnly.getValue()) {
this->pause(200);
}
auto selectionItem = SelectionItem(messageIndex, index);
this->setSelection(selectionItem, selectionItem);
this->selecting = true;
this->lastPressPosition = event->screenPos();
this->isMouseDown = true;
this->repaint();
int index = layout->getSelectionIndex(relativePos);
auto selectionItem = SelectionItem(messageIndex, index);
this->setSelection(selectionItem, selectionItem);
} break;
case Qt::RightButton: {
this->lastRightPressPosition = event->screenPos();
this->isRightMouseDown = true;
} break;
default:;
}
this->update();
}
void ChannelView::mouseReleaseEvent(QMouseEvent *event)
{
if (event->modifiers() & (Qt::AltModifier | Qt::ControlModifier)) {
this->unsetCursor();
// check if mouse was pressed
if (event->button() == Qt::LeftButton) {
if (this->isMouseDown) {
this->isMouseDown = false;
event->ignore();
if (fabsf(util::distanceBetweenPoints(this->lastPressPosition, event->screenPos())) >
15.f) {
return;
}
} else {
return;
}
} else if (event->button() == Qt::RightButton) {
if (this->isRightMouseDown) {
this->isRightMouseDown = false;
if (fabsf(util::distanceBetweenPoints(this->lastRightPressPosition,
event->screenPos())) > 15.f) {
return;
}
} else {
return;
}
} else {
// not left or right button
return;
}
if (!this->isMouseDown) {
// We didn't grab the mouse press, so we shouldn't be handling the mouse
// release
return;
}
auto app = getApp();
if (this->selecting) {
this->paused = false;
}
this->isMouseDown = false;
this->selecting = false;
float distance = util::distanceBetweenPoints(this->lastPressPosition, event->screenPos());
if (fabsf(distance) > 15.f) {
// It wasn't a proper click, so we don't care about that here
return;
}
// If you clicked and released less than X pixels away, it counts
// as a click!
// find message
this->layoutMessages();
std::shared_ptr<messages::MessageLayout> layout;
QPoint relativePos;
int messageIndex;
// no message found
if (!tryGetMessageAt(event->pos(), layout, relativePos, messageIndex)) {
// No message at clicked position
this->userPopupWidget.hide();
@@ -870,8 +916,9 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
}
// message under cursor is collapsed
if (layout->getMessage()->flags & Message::MessageFlags::Collapsed) {
layout->getMessage()->flags &= ~Message::MessageFlags::Collapsed;
if (layout->flags & MessageLayout::Collapsed) {
layout->flags |= MessageLayout::Expanded;
layout->flags |= MessageLayout::RequiresLayout;
this->layoutMessages();
return;
@@ -883,12 +930,130 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
return;
}
auto &link = hoverLayoutElement->getLink();
if (event->button() != Qt::LeftButton || !app->settings->linksDoubleClickOnly) {
this->handleLinkClick(event, link, layout.get());
// handle the click
this->handleMouseClick(event, hoverLayoutElement, layout.get());
}
void ChannelView::handleMouseClick(QMouseEvent *event,
const messages::MessageLayoutElement *hoveredElement,
messages::MessageLayout *layout)
{
switch (event->button()) {
case Qt::LeftButton: {
if (this->selecting) {
if (this->messagesAddedSinceSelectionPause >
SELECTION_RESUME_SCROLLING_MSG_THRESHOLD) {
this->showingLatestMessages = false;
}
this->pausedBySelection = false;
this->selecting = false;
this->pauseTimeout.stop();
this->pausedTemporarily = false;
this->layoutMessages();
}
auto &link = hoveredElement->getLink();
if (!getApp()->settings->linksDoubleClickOnly) {
this->handleLinkClick(event, link, layout);
this->linkClicked.invoke(link);
}
} break;
case Qt::RightButton: {
this->addContextMenuItems(hoveredElement, layout);
} break;
default:;
}
}
void ChannelView::addContextMenuItems(const messages::MessageLayoutElement *hoveredElement,
messages::MessageLayout *layout)
{
const auto &creator = hoveredElement->getCreator();
auto creatorFlags = creator.getFlags();
static QMenu *menu = new QMenu;
menu->clear();
// Emote actions
if (creatorFlags & (MessageElement::Flags::EmoteImages | MessageElement::Flags::EmojiImage)) {
const auto &emoteElement = static_cast<const messages::EmoteElement &>(creator);
// TODO: We might want to add direct "Open image" variants alongside the Copy
// actions
if (emoteElement.data.image1x != nullptr) {
menu->addAction("Copy 1x link", [url = emoteElement.data.image1x->getUrl()] {
QApplication::clipboard()->setText(url); //
});
}
if (emoteElement.data.image2x != nullptr) {
menu->addAction("Copy 2x link", [url = emoteElement.data.image2x->getUrl()] {
QApplication::clipboard()->setText(url); //
});
}
if (emoteElement.data.image3x != nullptr) {
menu->addAction("Copy 3x link", [url = emoteElement.data.image3x->getUrl()] {
QApplication::clipboard()->setText(url); //
});
}
if ((creatorFlags & MessageElement::Flags::BttvEmote) != 0) {
menu->addSeparator();
QString emotePageLink = emoteElement.data.pageLink;
menu->addAction("Copy BTTV emote link", [emotePageLink] {
QApplication::clipboard()->setText(emotePageLink); //
});
} else if ((creatorFlags & MessageElement::Flags::FfzEmote) != 0) {
menu->addSeparator();
QString emotePageLink = emoteElement.data.pageLink;
menu->addAction("Copy FFZ emote link", [emotePageLink] {
QApplication::clipboard()->setText(emotePageLink); //
});
}
}
this->linkClicked.invoke(link);
// add seperator
if (!menu->actions().empty()) {
menu->addSeparator();
}
// Link copy
if (hoveredElement->getLink().type == Link::Url) {
QString url = hoveredElement->getLink().value;
menu->addAction("Open link in browser", [url] { QDesktopServices::openUrl(QUrl(url)); });
menu->addAction("Copy link", [url] { QApplication::clipboard()->setText(url); });
menu->addSeparator();
}
// Copy actions
menu->addAction("Copy selection",
[this] { QGuiApplication::clipboard()->setText(this->getSelectedText()); });
if (!this->selection.isEmpty()) {
menu->addAction("Copy message", [layout] {
QString copyString;
layout->addSelectionText(copyString);
QGuiApplication::clipboard()->setText(copyString);
});
}
// menu->addAction("Quote message", [layout] {
// QString copyString;
// layout->addSelectionText(copyString);
// // insert into input
// });
menu->move(QCursor::pos());
menu->show();
return;
}
void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
@@ -905,7 +1070,7 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
}
// message under cursor is collapsed
if (layout->getMessage()->flags & Message::Collapsed) {
if (layout->flags & MessageLayout::Collapsed) {
return;
}
@@ -933,6 +1098,10 @@ void ChannelView::hideEvent(QHideEvent *)
void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link,
messages::MessageLayout *layout)
{
if (event->button() != Qt::LeftButton) {
return;
}
switch (link.type) {
case messages::Link::UserInfo: {
auto user = link.value;
@@ -945,24 +1114,7 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link
break;
}
case messages::Link::Url: {
if (event->button() == Qt::RightButton) {
static QMenu *menu = nullptr;
static QString url;
if (menu == nullptr) {
menu = new QMenu;
menu->addAction("Open in browser",
[] { QDesktopServices::openUrl(QUrl(url)); });
menu->addAction("Copy to clipboard",
[] { QApplication::clipboard()->setText(url); });
}
url = link.value;
menu->move(QCursor::pos());
menu->show();
} else {
QDesktopServices::openUrl(QUrl(link.value));
}
QDesktopServices::openUrl(QUrl(link.value));
break;
}
case messages::Link::UserAction: {
+17 -1
View File
@@ -86,7 +86,11 @@ private:
bool updateQueued = false;
bool messageWasAdded = false;
bool lastMessageHasAlternateBackground = false;
bool paused = false;
bool pausedTemporarily = false;
bool pausedBySelection = false;
int messagesAddedSinceSelectionPause = 0;
QTimer pauseTimeout;
boost::optional<messages::MessageElement::Flags> overrideFlags;
messages::MessageLayoutPtr lastReadMessage;
@@ -99,6 +103,16 @@ private:
void drawMessages(QPainter &painter);
void setSelection(const messages::SelectionItem &start, const messages::SelectionItem &end);
messages::MessageElement::Flags getFlags() const;
bool isPaused();
void handleMouseClick(QMouseEvent *event,
const messages::MessageLayoutElement *hoverLayoutElement,
messages::MessageLayout *layout);
void addContextMenuItems(const messages::MessageLayoutElement *hoveredElement,
messages::MessageLayout *layout);
// void beginPause();
// void endPause();
ChannelPtr channel;
@@ -115,7 +129,9 @@ private:
// Mouse event variables
bool isMouseDown = false;
bool isRightMouseDown = false;
QPointF lastPressPosition;
QPointF lastRightPressPosition;
messages::Selection selection;
bool selecting = false;
+13
View File
@@ -0,0 +1,13 @@
//#include "dropoverlay.hpp"
// namespace chatterino {
// namespace widgets {
// namespace helper {
// DropOverlay::DropOverlay()
//{
//}
//} // namespace helper
//} // namespace widgets
//} // namespace chatterino
+11
View File
@@ -0,0 +1,11 @@
//#pragma once
//#include "widgets/helper/splitnode.hpp"
// namespace chatterino {
// namespace widgets {
// namespace helper {
//} // namespace helper
//} // namespace widgets
//} // namespace chatterino
+1 -1
View File
@@ -27,7 +27,7 @@ protected:
private:
QSize preferedSize;
QString text;
FontStyle fontStyle = FontStyle::Medium;
FontStyle fontStyle = FontStyle::ChatMedium;
};
} // namespace widgets
+19 -9
View File
@@ -45,12 +45,20 @@ void NotebookButton::paintEvent(QPaintEvent *)
float h = height(), w = width();
if (icon == IconPlus) {
painter.fillRect(
QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, w - ((h / 12) * 5), (h / 12) * 1),
foreground);
painter.fillRect(
QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, (h / 12) * 1, w - ((h / 12) * 5)),
foreground);
painter.setPen([&] {
QColor tmp = foreground;
if (!this->mouseOver) {
tmp.setAlpha(180);
}
return tmp;
}());
QRect rect = this->rect();
int s = h * 4 / 9;
painter.drawLine(rect.left() + rect.width() / 2 - (s / 2), rect.top() + rect.height() / 2,
rect.left() + rect.width() / 2 + (s / 2), rect.top() + rect.height() / 2);
painter.drawLine(rect.left() + rect.width() / 2, rect.top() + rect.height() / 2 - (s / 2),
rect.left() + rect.width() / 2, rect.top() + rect.height() / 2 + (s / 2));
} else if (icon == IconUser) {
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
@@ -139,10 +147,12 @@ void NotebookButton::dropEvent(QDropEvent *event)
Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget());
if (notebook != nuuls) {
SplitContainer *tab = notebook->addNewPage();
SplitContainer *page = new SplitContainer(notebook);
auto *tab = notebook->addPage(page);
page->setTab(tab);
SplitContainer::draggingSplit->setParent(tab);
tab->addToLayout(SplitContainer::draggingSplit);
SplitContainer::draggingSplit->setParent(page);
page->appendSplit(SplitContainer::draggingSplit);
}
}
}
+220 -531
View File
@@ -13,441 +13,55 @@
#include <QApplication>
#include <QDebug>
#include <QLinearGradient>
#include <QMimeData>
#include <QPainter>
#include <boost/bind.hpp>
namespace chatterino {
namespace widgets {
NotebookTab2::NotebookTab2(Notebook2 *_notebook)
: BaseWidget(_notebook)
, positionChangedAnimation(this, "pos")
, notebook(_notebook)
, menu(this)
NotebookTab::NotebookTab(Notebook *notebook)
: BaseWidget(notebook)
, positionChangedAnimation_(this, "pos")
, notebook_(notebook)
, menu_(this)
{
auto app = getApp();
this->setAcceptDrops(true);
this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
app->settings->showTabCloseButton.connect(boost::bind(&NotebookTab2::hideTabXChanged, this, _1),
this->managedConnections);
this->setMouseTracking(true);
this->menu.addAction("Rename", [this]() {
TextInputDialog d(this);
d.setWindowTitle("Change tab title (Leave empty for default behaviour)");
if (this->useDefaultTitle) {
d.setText("");
} else {
d.setText(this->getTitle());
d.highlightText();
}
if (d.exec() == QDialog::Accepted) {
QString newTitle = d.getText();
if (newTitle.isEmpty()) {
this->useDefaultTitle = true;
// fourtf: xD
// this->page->refreshTitle();
} else {
this->useDefaultTitle = false;
this->setTitle(newTitle);
}
}
});
QAction *enableHighlightsOnNewMessageAction =
new QAction("Enable highlights on new message", &this->menu);
enableHighlightsOnNewMessageAction->setCheckable(true);
this->menu.addAction("Close", [=]() { this->notebook->removePage(this->page); });
this->menu.addAction(enableHighlightsOnNewMessageAction);
QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) {
debug::Log("New value is {}", newValue); //
});
}
void NotebookTab2::themeRefreshEvent()
{
this->update();
}
void NotebookTab2::updateSize()
{
auto app = getApp();
float scale = getScale();
int width;
QFontMetrics metrics(this->font());
if (!app->settings->showTabCloseButton) {
width = (int)((metrics.width(this->title) + 16 /*+ 16*/) * scale);
} else {
width = (int)((metrics.width(this->title) + 8 + 24 /*+ 16*/) * scale);
}
this->resize(std::min((int)(150 * scale), width), (int)(24 * scale));
// if (this->parent() != nullptr) {
// (static_cast<Notebook2 *>(this->parent()))->performLayout(true);
// }
}
const QString &NotebookTab2::getTitle() const
{
return this->title;
}
void NotebookTab2::setTitle(const QString &newTitle)
{
if (this->title != newTitle) {
this->title = newTitle;
this->updateSize();
this->update();
}
}
bool NotebookTab2::isSelected() const
{
return this->selected;
}
void NotebookTab2::setSelected(bool value)
{
this->selected = value;
this->highlightState = HighlightState::None;
this->update();
}
void NotebookTab2::setHighlightState(HighlightState newHighlightStyle)
{
if (this->isSelected()) {
return;
}
if (this->highlightState != HighlightState::Highlighted) {
this->highlightState = newHighlightStyle;
this->update();
}
}
QRect NotebookTab2::getDesiredRect() const
{
return QRect(positionAnimationDesiredPoint, size());
}
void NotebookTab2::hideTabXChanged(bool)
{
this->updateSize();
this->update();
}
void NotebookTab2::moveAnimated(QPoint pos, bool animated)
{
this->positionAnimationDesiredPoint = pos;
QWidget *w = this->window();
if ((w != nullptr && !w->isVisible()) || !animated || !positionChangedAnimationRunning) {
this->move(pos);
this->positionChangedAnimationRunning = true;
return;
}
if (this->positionChangedAnimation.endValue() == pos) {
return;
}
this->positionChangedAnimation.stop();
this->positionChangedAnimation.setDuration(75);
this->positionChangedAnimation.setStartValue(this->pos());
this->positionChangedAnimation.setEndValue(pos);
this->positionChangedAnimation.start();
}
void NotebookTab2::paintEvent(QPaintEvent *)
{
auto app = getApp();
QPainter painter(this);
float scale = this->getScale();
int height = (int)(scale * 24);
// int fullHeight = (int)(scale * 48);
// select the right tab colors
singletons::ThemeManager::TabColors colors;
singletons::ThemeManager::TabColors regular = this->themeManager->tabs.regular;
if (this->selected) {
colors = this->themeManager->tabs.selected;
} else if (this->highlightState == HighlightState::Highlighted) {
colors = this->themeManager->tabs.highlighted;
} else if (this->highlightState == HighlightState::NewMessage) {
colors = this->themeManager->tabs.newMessage;
} else {
colors = this->themeManager->tabs.regular;
}
bool windowFocused = this->window() == QApplication::activeWindow();
// || SettingsDialog::getHandle() == QApplication::activeWindow();
QBrush tabBackground = this->mouseOver ? colors.backgrounds.hover
: (windowFocused ? colors.backgrounds.regular
: colors.backgrounds.unfocused);
if (true) {
painter.fillRect(rect(), this->mouseOver ? regular.backgrounds.hover
: (windowFocused ? regular.backgrounds.regular
: regular.backgrounds.unfocused));
// fill the tab background
painter.fillRect(rect(), tabBackground);
// draw border
// painter.setPen(QPen("#ccc"));
// QPainterPath path(QPointF(0, height));
// path.lineTo(0, 0);
// path.lineTo(this->width() - 1, 0);
// path.lineTo(this->width() - 1, this->height() - 1);
// path.lineTo(0, this->height() - 1);
// painter.drawPath(path);
} else {
// QPainterPath path(QPointF(0, height));
// path.lineTo(8 * scale, 0);
// path.lineTo(this->width() - 8 * scale, 0);
// path.lineTo(this->width(), height);
// painter.fillPath(path, this->mouseOver ? regular.backgrounds.hover
// : (windowFocused ?
// regular.backgrounds.regular
// :
// regular.backgrounds.unfocused));
// // fill the tab background
// painter.fillPath(path, tabBackground);
// painter.setPen(QColor("#FFF"));
// painter.setRenderHint(QPainter::Antialiasing);
// painter.drawPath(path);
// // painter.setBrush(QColor("#000"));
// QLinearGradient gradient(0, height, 0, fullHeight);
// gradient.setColorAt(0, tabBackground.color());
// gradient.setColorAt(1, "#fff");
// QBrush brush(gradient);
// painter.fillRect(0, height, this->width(), fullHeight - height,
// brush);
}
// set the pen color
painter.setPen(colors.text);
// set area for text
int rectW = (!app->settings->showTabCloseButton ? 0 : static_cast<int>(16) * scale);
QRect rect(0, 0, this->width() - rectW, height);
// draw text
if (true) { // legacy
// painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter));
int offset = (int)(scale * 8);
QRect textRect(offset, 0, this->width() - offset - offset, height);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::NoWrap);
painter.drawText(textRect, this->getTitle(), option);
} else {
// QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
// option.setWrapMode(QTextOption::NoWrap);
// int offset = (int)(scale * 16);
// QRect textRect(offset, 0, this->width() - offset - offset, height);
// painter.drawText(textRect, this->getTitle(), option);
}
// draw close x
if (!app->settings->showTabCloseButton && (mouseOver || selected)) {
QRect xRect = this->getXRect();
if (!xRect.isNull()) {
if (mouseOverX) {
painter.fillRect(xRect, QColor(0, 0, 0, 64));
if (mouseDownX) {
painter.fillRect(xRect, QColor(0, 0, 0, 64));
}
}
int a = static_cast<int>(scale * 4);
painter.drawLine(xRect.topLeft() + QPoint(a, a), xRect.bottomRight() + QPoint(-a, -a));
painter.drawLine(xRect.topRight() + QPoint(-a, a), xRect.bottomLeft() + QPoint(a, -a));
}
}
}
void NotebookTab2::mousePressEvent(QMouseEvent *event)
{
this->mouseDown = true;
this->mouseDownX = this->getXRect().contains(event->pos());
this->update();
this->notebook->select(page);
if (this->notebook->getAllowUserTabManagement()) {
switch (event->button()) {
case Qt::RightButton: {
this->menu.popup(event->globalPos());
} break;
}
}
}
void NotebookTab2::mouseReleaseEvent(QMouseEvent *event)
{
this->mouseDown = false;
if (event->button() == Qt::MiddleButton) {
if (this->rect().contains(event->pos())) {
this->notebook->removePage(this->page);
}
} else {
if (getApp()->settings->showTabCloseButton && this->mouseDownX &&
this->getXRect().contains(event->pos())) {
this->mouseDownX = false;
this->notebook->removePage(this->page);
} else {
this->update();
}
}
}
void NotebookTab2::enterEvent(QEvent *)
{
this->mouseOver = true;
this->update();
}
void NotebookTab2::leaveEvent(QEvent *)
{
this->mouseOverX = false;
this->mouseOver = false;
this->update();
}
void NotebookTab2::dragEnterEvent(QDragEnterEvent *)
{
if (this->notebook->getAllowUserTabManagement()) {
this->notebook->select(this->page);
}
}
void NotebookTab2::mouseMoveEvent(QMouseEvent *event)
{
auto app = getApp();
if (app->settings->showTabCloseButton && this->notebook->getAllowUserTabManagement()) //
{
bool overX = this->getXRect().contains(event->pos());
if (overX != this->mouseOverX) {
// Over X state has been changed (we either left or entered it;
this->mouseOverX = overX;
this->update();
}
}
QPoint relPoint = this->mapToParent(event->pos());
if (this->mouseDown && !this->getDesiredRect().contains(relPoint) &&
this->notebook->getAllowUserTabManagement()) //
{
int index;
QWidget *clickedPage = notebook->tabAt(relPoint, index, this->width());
assert(clickedPage);
if (clickedPage != nullptr && clickedPage != this->page) {
this->notebook->rearrangePage(this->page, index);
}
}
}
QRect NotebookTab2::getXRect()
{
if (this->notebook->getAllowUserTabManagement()) {
return QRect();
}
float s = this->getScale();
return QRect(this->width() - static_cast<int>(20 * s), static_cast<int>(4 * s),
static_cast<int>(16 * s), static_cast<int>(16 * s));
}
// 2
NotebookTab::NotebookTab(Notebook *_notebook)
: BaseWidget(_notebook)
, positionChangedAnimation(this, "pos")
, notebook(_notebook)
, menu(this)
{
auto app = getApp();
this->setAcceptDrops(true);
this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
this->positionChangedAnimation_.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
app->settings->showTabCloseButton.connect(boost::bind(&NotebookTab::hideTabXChanged, this, _1),
this->managedConnections);
this->managedConnections_);
this->setMouseTracking(true);
this->menu.addAction("Rename", [this]() {
this->menu_.addAction("Rename", [this]() {
TextInputDialog d(this);
d.setWindowTitle("Change tab title (Leave empty for default behaviour)");
if (this->useDefaultTitle) {
d.setText("");
} else {
d.setText(this->getTitle());
d.highlightText();
}
d.setText(this->getCustomTitle());
d.highlightText();
if (d.exec() == QDialog::Accepted) {
QString newTitle = d.getText();
if (newTitle.isEmpty()) {
this->useDefaultTitle = true;
this->page->refreshTitle();
} else {
this->useDefaultTitle = false;
this->setTitle(newTitle);
}
this->setCustomTitle(newTitle);
}
});
QAction *enableHighlightsOnNewMessageAction =
new QAction("Enable highlights on new message", &this->menu);
enableHighlightsOnNewMessageAction->setCheckable(true);
// QAction *enableHighlightsOnNewMessageAction =
// new QAction("Enable highlights on new message", &this->menu);
// enableHighlightsOnNewMessageAction->setCheckable(true);
this->menu.addAction("Close", [=]() { this->notebook->removePage(this->page); });
this->menu_.addAction("Close", [=]() { this->notebook_->removePage(this->page); });
this->menu.addAction(enableHighlightsOnNewMessageAction);
// this->menu.addAction(enableHighlightsOnNewMessageAction);
QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [](bool newValue) {
debug::Log("New value is {}", newValue); //
});
// QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool
// newValue) {
// debug::Log("New value is {}", newValue); //
// });
}
void NotebookTab::themeRefreshEvent()
@@ -457,49 +71,86 @@ void NotebookTab::themeRefreshEvent()
void NotebookTab::updateSize()
{
auto app = getApp();
float scale = getScale();
int width;
QFontMetrics metrics = getApp()->fonts->getFontMetrics(
FontStyle::UiTabs, float(qreal(this->getScale()) * this->devicePixelRatioF()));
if (!app->settings->showTabCloseButton) {
width = (int)((fontMetrics().width(this->title) + 16 /*+ 16*/) * scale);
if (this->hasXButton()) {
width = int((metrics.width(this->getTitle()) + 32) * scale);
} else {
width = (int)((fontMetrics().width(this->title) + 8 + 24 /*+ 16*/) * scale);
width = int((metrics.width(this->getTitle()) + 16) * scale);
}
this->resize(std::min((int)(150 * scale), width), (int)(24 * scale));
width = std::max<int>(this->height(), std::min(int(150 * scale), width));
if (this->parent() != nullptr) {
(static_cast<Notebook *>(this->parent()))->performLayout(true);
if (this->width() != width) {
this->resize(width, int(NOTEBOOK_TAB_HEIGHT * scale));
this->notebook_->performLayout();
}
}
const QString &NotebookTab::getCustomTitle() const
{
return this->customTitle_;
}
void NotebookTab::setCustomTitle(const QString &newTitle)
{
if (this->customTitle_ != newTitle) {
this->customTitle_ = newTitle;
this->titleUpdated();
}
}
void NotebookTab::resetCustomTitle()
{
this->setCustomTitle(QString());
}
bool NotebookTab::hasCustomTitle() const
{
return !this->customTitle_.isEmpty();
}
void NotebookTab::setDefaultTitle(const QString &title)
{
if (this->defaultTitle_ != title) {
this->defaultTitle_ = title;
if (this->customTitle_.isEmpty()) {
this->titleUpdated();
}
}
}
const QString &NotebookTab::getDefaultTitle() const
{
return this->defaultTitle_;
}
const QString &NotebookTab::getTitle() const
{
return this->title;
return this->customTitle_.isEmpty() ? this->defaultTitle_ : this->customTitle_;
}
void NotebookTab::setTitle(const QString &newTitle)
void NotebookTab::titleUpdated()
{
if (this->title != newTitle) {
this->title = newTitle;
this->updateSize();
this->update();
}
this->updateSize();
this->update();
}
bool NotebookTab::isSelected() const
{
return this->selected;
return this->selected_;
}
void NotebookTab::setSelected(bool value)
{
this->selected = value;
this->selected_ = value;
this->highlightState = HighlightState::None;
this->highlightState_ = HighlightState::None;
this->update();
}
@@ -510,8 +161,8 @@ void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
return;
}
if (this->highlightState != HighlightState::Highlighted) {
this->highlightState = newHighlightStyle;
if (this->highlightState_ != HighlightState::Highlighted) {
this->highlightState_ = newHighlightStyle;
this->update();
}
@@ -519,7 +170,7 @@ void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
QRect NotebookTab::getDesiredRect() const
{
return QRect(positionAnimationDesiredPoint, size());
return QRect(this->positionAnimationDesiredPoint_, size());
}
void NotebookTab::hideTabXChanged(bool)
@@ -530,26 +181,26 @@ void NotebookTab::hideTabXChanged(bool)
void NotebookTab::moveAnimated(QPoint pos, bool animated)
{
this->positionAnimationDesiredPoint = pos;
this->positionAnimationDesiredPoint_ = pos;
QWidget *w = this->window();
if ((w != nullptr && !w->isVisible()) || !animated || !positionChangedAnimationRunning) {
if ((w != nullptr && !w->isVisible()) || !animated || !this->positionChangedAnimationRunning_) {
this->move(pos);
this->positionChangedAnimationRunning = true;
this->positionChangedAnimationRunning_ = true;
return;
}
if (this->positionChangedAnimation.endValue() == pos) {
if (this->positionChangedAnimation_.endValue() == pos) {
return;
}
this->positionChangedAnimation.stop();
this->positionChangedAnimation.setDuration(75);
this->positionChangedAnimation.setStartValue(this->pos());
this->positionChangedAnimation.setEndValue(pos);
this->positionChangedAnimation.start();
this->positionChangedAnimation_.stop();
this->positionChangedAnimation_.setDuration(75);
this->positionChangedAnimation_.setStartValue(this->pos());
this->positionChangedAnimation_.setEndValue(pos);
this->positionChangedAnimation_.start();
}
void NotebookTab::paintEvent(QPaintEvent *)
@@ -558,18 +209,22 @@ void NotebookTab::paintEvent(QPaintEvent *)
QPainter painter(this);
float scale = this->getScale();
int height = (int)(scale * 24);
painter.setFont(getApp()->fonts->getFont(FontStyle::UiTabs, scale * this->devicePixelRatioF()));
QFontMetrics metrics =
app->fonts->getFontMetrics(FontStyle::UiTabs, scale * this->devicePixelRatioF());
int height = int(scale * NOTEBOOK_TAB_HEIGHT);
// int fullHeight = (int)(scale * 48);
// select the right tab colors
singletons::ThemeManager::TabColors colors;
singletons::ThemeManager::TabColors regular = this->themeManager->tabs.regular;
if (this->selected) {
if (this->selected_) {
colors = this->themeManager->tabs.selected;
} else if (this->highlightState == HighlightState::Highlighted) {
} else if (this->highlightState_ == HighlightState::Highlighted) {
colors = this->themeManager->tabs.highlighted;
} else if (this->highlightState == HighlightState::NewMessage) {
} else if (this->highlightState_ == HighlightState::NewMessage) {
colors = this->themeManager->tabs.newMessage;
} else {
colors = this->themeManager->tabs.regular;
@@ -578,127 +233,139 @@ void NotebookTab::paintEvent(QPaintEvent *)
bool windowFocused = this->window() == QApplication::activeWindow();
// || SettingsDialog::getHandle() == QApplication::activeWindow();
QBrush tabBackground = this->mouseOver ? colors.backgrounds.hover
: (windowFocused ? colors.backgrounds.regular
: colors.backgrounds.unfocused);
QBrush tabBackground = this->mouseOver_ ? colors.backgrounds.hover
: (windowFocused ? colors.backgrounds.regular
: colors.backgrounds.unfocused);
if (true) {
painter.fillRect(rect(), this->mouseOver ? regular.backgrounds.hover
: (windowFocused ? regular.backgrounds.regular
: regular.backgrounds.unfocused));
// painter.fillRect(rect(), this->mouseOver_ ? regular.backgrounds.hover
// : (windowFocused ? regular.backgrounds.regular
// :
// regular.backgrounds.unfocused));
// fill the tab background
painter.fillRect(rect(), tabBackground);
// fill the tab background
auto bgRect = rect();
bgRect.setTop(bgRect.top() + 2);
// draw border
// painter.setPen(QPen("#ccc"));
// QPainterPath path(QPointF(0, height));
// path.lineTo(0, 0);
// path.lineTo(this->width() - 1, 0);
// path.lineTo(this->width() - 1, this->height() - 1);
// path.lineTo(0, this->height() - 1);
// painter.drawPath(path);
} else {
// QPainterPath path(QPointF(0, height));
// path.lineTo(8 * scale, 0);
// path.lineTo(this->width() - 8 * scale, 0);
// path.lineTo(this->width(), height);
// painter.fillPath(path, this->mouseOver ? regular.backgrounds.hover
// : (windowFocused ?
// regular.backgrounds.regular
// :
// regular.backgrounds.unfocused));
painter.fillRect(bgRect, tabBackground);
// // fill the tab background
// painter.fillPath(path, tabBackground);
// painter.setPen(QColor("#FFF"));
// painter.setRenderHint(QPainter::Antialiasing);
// painter.drawPath(path);
// // painter.setBrush(QColor("#000"));
// draw border
// painter.setPen(QPen("#fff"));
// QPainterPath path(QPointF(0, height));
// path.lineTo(0, 0);
// path.lineTo(this->width() - 1, 0);
// path.lineTo(this->width() - 1, this->height() - 1);
// path.lineTo(0, this->height() - 1);
// painter.drawPath(path);
// QLinearGradient gradient(0, height, 0, fullHeight);
// gradient.setColorAt(0, tabBackground.color());
// gradient.setColorAt(1, "#fff");
// QBrush brush(gradient);
// painter.fillRect(0, height, this->width(), fullHeight - height,
// brush);
}
// top line
painter.fillRect(QRectF(0, (this->selected_ ? 0.f : 1.f) * scale, this->width(),
(this->selected_ ? 2.f : 1.f) * scale),
this->mouseOver_
? colors.line.hover
: (windowFocused ? colors.line.regular : colors.line.unfocused));
// set the pen color
painter.setPen(colors.text);
// set area for text
int rectW = (!app->settings->showTabCloseButton ? 0 : static_cast<int>(16) * scale);
int rectW = (!app->settings->showTabCloseButton ? 0 : int(16 * scale));
QRect rect(0, 0, this->width() - rectW, height);
// draw text
if (true) { // legacy
// painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter));
int offset = (int)(scale * 8);
QRect textRect(offset, 0, this->width() - offset - offset, height);
int offset = int(scale * 8);
QRect textRect(offset, this->selected_ ? 1 : 2, this->width() - offset - offset, height);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::NoWrap);
painter.drawText(textRect, this->getTitle(), option);
} else {
// QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
// option.setWrapMode(QTextOption::NoWrap);
// int offset = (int)(scale * 16);
// QRect textRect(offset, 0, this->width() - offset - offset, height);
// painter.drawText(textRect, this->getTitle(), option);
if (this->shouldDrawXButton()) {
textRect.setRight(textRect.right() - this->height() / 2);
}
int width = metrics.width(this->getTitle());
Qt::Alignment alignment = width > textRect.width() ? Qt::AlignLeft | Qt::AlignVCenter
: Qt::AlignHCenter | Qt::AlignVCenter;
QTextOption option(alignment);
option.setWrapMode(QTextOption::NoWrap);
painter.drawText(textRect, this->getTitle(), option);
// draw close x
if (app->settings->showTabCloseButton && (mouseOver || selected)) {
if (this->shouldDrawXButton()) {
QRect xRect = this->getXRect();
if (mouseOverX) {
painter.fillRect(xRect, QColor(0, 0, 0, 64));
if (!xRect.isNull()) {
painter.setBrush(QColor("#fff"));
if (mouseDownX) {
if (this->mouseOverX_) {
painter.fillRect(xRect, QColor(0, 0, 0, 64));
if (this->mouseDownX_) {
painter.fillRect(xRect, QColor(0, 0, 0, 64));
}
}
int a = static_cast<int>(scale * 4);
painter.drawLine(xRect.topLeft() + QPoint(a, a), xRect.bottomRight() + QPoint(-a, -a));
painter.drawLine(xRect.topRight() + QPoint(-a, a), xRect.bottomLeft() + QPoint(a, -a));
}
int a = static_cast<int>(scale * 4);
painter.drawLine(xRect.topLeft() + QPoint(a, a), xRect.bottomRight() + QPoint(-a, -a));
painter.drawLine(xRect.topRight() + QPoint(-a, a), xRect.bottomLeft() + QPoint(a, -a));
}
} // namespace widgets
// draw line at bottom
if (!this->selected_) {
painter.fillRect(0, this->height() - 1, this->width(), 1, app->themes->window.background);
}
}
bool NotebookTab::hasXButton()
{
return getApp()->settings->showTabCloseButton && this->notebook_->getAllowUserTabManagement();
}
bool NotebookTab::shouldDrawXButton()
{
return this->hasXButton() && (this->mouseOver_ || this->selected_);
}
void NotebookTab::mousePressEvent(QMouseEvent *event)
{
this->mouseDown = true;
this->mouseDownX = this->getXRect().contains(event->pos());
this->mouseDown_ = true;
this->mouseDownX_ = this->getXRect().contains(event->pos());
this->update();
this->notebook->select(page);
this->notebook_->select(page);
switch (event->button()) {
case Qt::RightButton: {
this->menu.popup(event->globalPos());
} break;
if (this->notebook_->getAllowUserTabManagement()) {
switch (event->button()) {
case Qt::RightButton: {
this->menu_.popup(event->globalPos());
} break;
default:;
}
}
}
void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
{
auto app = getApp();
this->mouseDown_ = false;
this->mouseDown = false;
auto removeThisPage = [this] {
auto reply = QMessageBox::question(this, "Remove this tab",
"Are you sure that you want to remove this tab?",
QMessageBox::Yes | QMessageBox::Cancel);
if (reply == QMessageBox::Yes) {
this->notebook_->removePage(this->page);
}
};
if (event->button() == Qt::MiddleButton) {
if (this->rect().contains(event->pos())) {
this->notebook->removePage(this->page);
removeThisPage();
}
} else {
if (app->settings->showTabCloseButton && this->mouseDownX &&
this->getXRect().contains(event->pos())) {
this->mouseDownX = false;
if (this->hasXButton() && this->mouseDownX_ && this->getXRect().contains(event->pos())) {
this->mouseDownX_ = false;
this->notebook->removePage(this->page);
removeThisPage();
} else {
this->update();
}
@@ -707,34 +374,43 @@ void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
void NotebookTab::enterEvent(QEvent *)
{
this->mouseOver = true;
this->mouseOver_ = true;
this->update();
}
void NotebookTab::leaveEvent(QEvent *)
{
this->mouseOverX = false;
this->mouseOver = false;
this->mouseOverX_ = false;
this->mouseOver_ = false;
this->update();
}
void NotebookTab::dragEnterEvent(QDragEnterEvent *)
void NotebookTab::dragEnterEvent(QDragEnterEvent *event)
{
this->notebook->select(this->page);
if (!event->mimeData()->hasFormat("chatterino/split"))
return;
if (!SplitContainer::isDraggingSplit)
return;
if (this->notebook_->getAllowUserTabManagement()) {
this->notebook_->select(this->page);
}
}
void NotebookTab::mouseMoveEvent(QMouseEvent *event)
{
auto app = getApp();
if (app->settings->showTabCloseButton) {
if (app->settings->showTabCloseButton && this->notebook_->getAllowUserTabManagement()) //
{
bool overX = this->getXRect().contains(event->pos());
if (overX != this->mouseOverX) {
if (overX != this->mouseOverX_) {
// Over X state has been changed (we either left or entered it;
this->mouseOverX = overX;
this->mouseOverX_ = overX;
this->update();
}
@@ -742,15 +418,28 @@ void NotebookTab::mouseMoveEvent(QMouseEvent *event)
QPoint relPoint = this->mapToParent(event->pos());
if (this->mouseDown && !this->getDesiredRect().contains(relPoint)) {
if (this->mouseDown_ && !this->getDesiredRect().contains(relPoint) &&
this->notebook_->getAllowUserTabManagement()) //
{
int index;
SplitContainer *clickedPage = notebook->tabAt(relPoint, index, this->width());
QWidget *clickedPage = this->notebook_->tabAt(relPoint, index, this->width());
if (clickedPage != nullptr && clickedPage != this->page) {
this->notebook->rearrangePage(this->page, index);
this->notebook_->rearrangePage(this->page, index);
}
}
}
QRect NotebookTab::getXRect()
{
// if (!this->notebook->getAllowUserTabManagement()) {
// return QRect();
// }
float s = this->getScale();
return QRect(this->width() - static_cast<int>(20 * s), static_cast<int>(6 * s),
static_cast<int>(16 * s), static_cast<int>(16 * s));
}
} // namespace widgets
} // namespace chatterino
+29 -90
View File
@@ -11,75 +11,12 @@
namespace chatterino {
namespace widgets {
#define NOTEBOOK_TAB_HEIGHT 28
// class Notebook;
class Notebook;
class Notebook2;
class SplitContainer;
class NotebookTab2 : public BaseWidget
{
Q_OBJECT
public:
explicit NotebookTab2(Notebook2 *_notebook);
void updateSize();
QWidget *page;
const QString &getTitle() const;
void setTitle(const QString &newTitle);
bool isSelected() const;
void setSelected(bool value);
void setHighlightState(HighlightState style);
void moveAnimated(QPoint pos, bool animated = true);
QRect getDesiredRect() const;
void hideTabXChanged(bool);
protected:
virtual void themeRefreshEvent() override;
virtual void paintEvent(QPaintEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override;
virtual void dragEnterEvent(QDragEnterEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
private:
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
QPropertyAnimation positionChangedAnimation;
bool positionChangedAnimationRunning = false;
QPoint positionAnimationDesiredPoint;
Notebook2 *notebook;
QString title;
public:
bool useDefaultTitle = true;
private:
bool selected = false;
bool mouseOver = false;
bool mouseDown = false;
bool mouseOverX = false;
bool mouseDownX = false;
HighlightState highlightState = HighlightState::None;
QMenu menu;
QRect getXRect();
};
class NotebookTab : public BaseWidget
{
Q_OBJECT
@@ -89,10 +26,16 @@ public:
void updateSize();
SplitContainer *page;
QWidget *page;
void setCustomTitle(const QString &title);
void resetCustomTitle();
bool hasCustomTitle() const;
const QString &getCustomTitle() const;
void setDefaultTitle(const QString &title);
const QString &getDefaultTitle() const;
const QString &getTitle() const;
void setTitle(const QString &newTitle);
bool isSelected() const;
void setSelected(bool value);
@@ -118,36 +61,32 @@ protected:
virtual void mouseMoveEvent(QMouseEvent *event) override;
private:
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
QPropertyAnimation positionChangedAnimation;
bool positionChangedAnimationRunning = false;
QPoint positionAnimationDesiredPoint;
QPropertyAnimation positionChangedAnimation_;
bool positionChangedAnimationRunning_ = false;
QPoint positionAnimationDesiredPoint_;
Notebook *notebook;
Notebook *notebook_;
QString title;
QString customTitle_;
QString defaultTitle_;
public:
bool useDefaultTitle = true;
bool selected_ = false;
bool mouseOver_ = false;
bool mouseDown_ = false;
bool mouseOverX_ = false;
bool mouseDownX_ = false;
private:
bool selected = false;
bool mouseOver = false;
bool mouseDown = false;
bool mouseOverX = false;
bool mouseDownX = false;
bool hasXButton();
bool shouldDrawXButton();
HighlightState highlightState = HighlightState::None;
HighlightState highlightState_ = HighlightState::None;
QMenu menu;
QMenu menu_;
QRect getXRect()
{
float s = this->getScale();
return QRect(this->width() - static_cast<int>(20 * s), static_cast<int>(4 * s),
static_cast<int>(16 * s), static_cast<int>(16 * s));
}
QRect getXRect();
void titleUpdated();
};
} // namespace widgets
+10 -1
View File
@@ -137,10 +137,19 @@ void ResizingTextEdit::keyPressEvent(QKeyEvent *event)
}
}
void ResizingTextEdit::focusInEvent(QFocusEvent *event)
{
QTextEdit::focusInEvent(event);
if (event->gotFocus()) {
this->focused.invoke();
}
}
void ResizingTextEdit::setCompleter(QCompleter *c)
{
if (this->completer) {
QObject::disconnect(this->completer, 0, this, 0);
QObject::disconnect(this->completer, nullptr, this, nullptr);
}
this->completer = c;
+3
View File
@@ -15,6 +15,7 @@ public:
bool hasHeightForWidth() const override;
pajlada::Signals::Signal<QKeyEvent *> keyPressed;
pajlada::Signals::NoArgSignal focused;
void setCompleter(QCompleter *c);
QCompleter *getCompleter() const;
@@ -23,6 +24,8 @@ protected:
int heightForWidth(int) const override;
void keyPressEvent(QKeyEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
private:
QCompleter *completer = nullptr;
bool completionInProgress = false;
+1 -1
View File
@@ -9,7 +9,7 @@ class ScrollbarHighlight
{
public:
enum Style : char { None, Default, Line };
enum Color : char { Highlight };
enum Color : char { Highlight, Subscription };
ScrollbarHighlight();
ScrollbarHighlight(Color _color, Style _style = Default);
+141 -26
View File
@@ -31,7 +31,6 @@ SplitHeader::SplitHeader(Split *_split)
, split(_split)
{
auto app = getApp();
this->setMouseTracking(true);
util::LayoutCreator<SplitHeader> layoutCreator(this);
auto layout = layoutCreator.emplace<QHBoxLayout>().withoutMargin();
@@ -53,16 +52,26 @@ SplitHeader::SplitHeader(Split *_split)
// channel name label
// auto title = layout.emplace<Label>(this).assign(&this->titleLabel);
auto title = layout.emplace<SignalLabel>().assign(&this->titleLabel);
auto title = layout.emplace<QLabel>().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);
// title->setMouseTracking(true);
// QObject::connect(this->titleLabel, &SignalLabel::mouseDoubleClick, this,
// &SplitHeader::mouseDoubleClickEvent);
// QObject::connect(this->titleLabel, &SignalLabel::mouseMove, this,
// &SplitHeader::mouseMoveEvent);
layout->addStretch(1);
// mode button
auto mode = layout.emplace<RippleEffectLabel>(this).assign(&this->modeButton);
mode->hide();
// QObject::connect(mode.getElement(), &RippleEffectButton::clicked, this, [this]
// {
// //
// });
// moderation mode
auto moderator = layout.emplace<RippleEffectButton>(this).assign(&this->moderationButton);
@@ -84,6 +93,11 @@ SplitHeader::SplitHeader(Split *_split)
this->split->channelChanged.connect([this]() {
this->initializeChannelSignals(); //
});
this->managedConnect(app->accounts->twitch.currentUserChanged,
[this] { this->updateModerationModeIcon(); });
this->setMouseTracking(true);
}
SplitHeader::~SplitHeader()
@@ -91,7 +105,7 @@ SplitHeader::~SplitHeader()
this->onlineStatusChangedConnection.disconnect();
}
void SplitHeader::addDropdownItems(RippleEffectButton *label)
void SplitHeader::addDropdownItems(RippleEffectButton *)
{
// clang-format off
this->dropdownMenu.addAction("Add new split", this->split, &Split::doAddSplit, QKeySequence(tr("Ctrl+T")));
@@ -99,6 +113,7 @@ void SplitHeader::addDropdownItems(RippleEffectButton *label)
// this->dropdownMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
this->dropdownMenu.addAction("Popup", this->split, &Split::doPopup);
this->dropdownMenu.addAction("Open viewer list", this->split, &Split::doOpenViewerList);
this->dropdownMenu.addAction("Search in messages", this->split, &Split::doSearch, QKeySequence(tr("Ctrl+F")));
this->dropdownMenu.addSeparator();
#ifdef USEWEBENGINE
this->dropdownMenu.addAction("Start watching", this, [this]{
@@ -167,7 +182,7 @@ void SplitHeader::updateChannelText()
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel != nullptr) {
const auto &streamStatus = twitchChannel->GetStreamStatus();
const auto streamStatus = twitchChannel->getStreamStatus();
if (streamStatus.live) {
this->isLive = true;
@@ -181,9 +196,13 @@ void SplitHeader::updateChannelText()
"</p>";
if (streamStatus.rerun) {
title += " (rerun)";
} else if (streamStatus.streamType.isEmpty()) {
title += " (" + streamStatus.streamType + ")";
} else {
title += " (live)";
}
} else {
this->tooltip = QString();
}
}
@@ -216,6 +235,49 @@ void SplitHeader::updateModerationModeIcon()
this->moderationButton->setVisible(modButtonVisible);
}
void SplitHeader::updateModes()
{
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(this->split->getChannel().get());
if (tc == nullptr) {
this->modeButton->hide();
return;
}
TwitchChannel::RoomModes roomModes = tc->getRoomModes();
QString text;
if (roomModes.r9k) {
text += "r9k, ";
}
if (roomModes.slowMode) {
text += QString("slow(%1), ").arg(QString::number(roomModes.slowMode));
}
if (roomModes.emoteOnly) {
text += "emote, ";
}
if (roomModes.submode) {
text += "sub, ";
}
if (text.length() > 2) {
text = text.mid(0, text.size() - 2);
}
if (text.isEmpty()) {
this->modeButton->hide();
} else {
static QRegularExpression commaReplacement("^.+?, .+?,( ).+$");
QRegularExpressionMatch match = commaReplacement.match(text);
if (match.hasMatch()) {
text = text.mid(0, match.capturedStart(1)) + '\n' + text.mid(match.capturedEnd(1));
}
this->modeButton->getLabel().setText(text);
this->modeButton->show();
}
}
void SplitHeader::paintEvent(QPaintEvent *)
{
QPainter painter(this);
@@ -227,40 +289,87 @@ void SplitHeader::paintEvent(QPaintEvent *)
void SplitHeader::mousePressEvent(QMouseEvent *event)
{
this->dragging = true;
if (event->button() == Qt::LeftButton) {
this->dragging = true;
this->dragStart = event->pos();
this->dragStart = event->pos();
}
this->doubleClicked = false;
}
void SplitHeader::mouseReleaseEvent(QMouseEvent *event)
{
if (this->dragging && event->button() == Qt::LeftButton) {
QPoint pos = event->globalPos();
if (!showingHelpTooltip) {
this->showingHelpTooltip = true;
QTimer::singleShot(400, this, [this, pos] {
if (this->doubleClicked) {
this->doubleClicked = false;
this->showingHelpTooltip = false;
return;
}
TooltipWidget *widget = new TooltipWidget();
widget->setText("Double click or press <Ctrl+R> to change the channel.\nClick and "
"drag to move the split.");
widget->setAttribute(Qt::WA_DeleteOnClose);
widget->move(pos);
widget->show();
widget->raise();
QTimer::singleShot(3000, widget, [this, widget] {
widget->close();
this->showingHelpTooltip = false;
});
});
}
}
this->dragging = false;
}
void SplitHeader::mouseMoveEvent(QMouseEvent *event)
{
if (!this->dragging && this->isLive) {
auto tooltipWidget = TooltipWidget::getInstance();
tooltipWidget->moveTo(this, event->globalPos());
tooltipWidget->setText(tooltip);
tooltipWidget->show();
}
if (this->dragging) {
if (std::abs(this->dragStart.x() - event->pos().x()) > 12 ||
std::abs(this->dragStart.y() - event->pos().y()) > 12) {
if (std::abs(this->dragStart.x() - event->pos().x()) > int(12 * this->getScale()) ||
std::abs(this->dragStart.y() - event->pos().y()) > int(12 * this->getScale())) {
this->split->drag();
this->dragging = false;
}
}
}
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
BaseWidget::leaveEvent(event);
}
void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
this->split->doChangeChannel();
}
this->doubleClicked = true;
}
void SplitHeader::enterEvent(QEvent *event)
{
if (!this->tooltip.isEmpty()) {
auto tooltipWidget = TooltipWidget::getInstance();
tooltipWidget->moveTo(this, this->mapToGlobal(this->rect().bottomLeft()), false);
tooltipWidget->setText(this->tooltip);
tooltipWidget->show();
tooltipWidget->raise();
}
BaseWidget::enterEvent(event);
}
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
BaseWidget::leaveEvent(event);
}
void SplitHeader::rightButtonClicked()
@@ -283,6 +392,12 @@ void SplitHeader::menuMoveSplit()
void SplitHeader::menuReloadChannelEmotes()
{
auto channel = this->split->getChannel();
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel) {
twitchChannel->reloadChannelEmotes();
}
}
void SplitHeader::menuManualReconnect()
+10 -3
View File
@@ -15,6 +15,7 @@
#include <QWidget>
#include <pajlada/settings/setting.hpp>
#include <pajlada/signals/connection.hpp>
#include <pajlada/signals/signalholder.hpp>
#include <vector>
@@ -23,17 +24,18 @@ namespace widgets {
class Split;
class SplitHeader : public BaseWidget
class SplitHeader : public BaseWidget, pajlada::Signals::SignalHolder
{
Q_OBJECT
public:
explicit SplitHeader(Split *_chatWidget);
virtual ~SplitHeader();
virtual ~SplitHeader() override;
// Update channel text from chat widget
void updateChannelText();
void updateModerationModeIcon();
void updateModes();
protected:
virtual void scaleChangedEvent(float) override;
@@ -41,7 +43,9 @@ protected:
virtual void paintEvent(QPaintEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
@@ -50,12 +54,15 @@ private:
QPoint dragStart;
bool dragging = false;
bool doubleClicked = false;
bool showingHelpTooltip = false;
pajlada::Signals::Connection onlineStatusChangedConnection;
RippleEffectButton *dropdownButton;
// Label *titleLabel;
SignalLabel *titleLabel;
QLabel *titleLabel;
RippleEffectLabel *modeButton;
RippleEffectButton *moderationButton;
QMenu dropdownMenu;
+69 -67
View File
@@ -2,6 +2,8 @@
#include "application.hpp"
#include "controllers/commands/commandcontroller.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
@@ -19,15 +21,15 @@ namespace widgets {
SplitInput::SplitInput(Split *_chatWidget)
: BaseWidget(_chatWidget)
, chatWidget(_chatWidget)
, split(_chatWidget)
{
this->initLayout();
auto completer = new QCompleter(&this->chatWidget->getChannel().get()->completionModel);
auto completer = new QCompleter(&this->split->getChannel().get()->completionModel);
this->ui.textEdit->setCompleter(completer);
this->chatWidget->channelChanged.connect([this] {
auto completer = new QCompleter(&this->chatWidget->getChannel()->completionModel);
this->split->channelChanged.connect([this] {
auto completer = new QCompleter(&this->split->getChannel()->completionModel);
this->ui.textEdit->setCompleter(completer);
});
@@ -65,11 +67,11 @@ void SplitInput::initLayout()
// set edit font
this->ui.textEdit->setFont(
app->fonts->getFont(singletons::FontManager::Type::Medium, this->getScale()));
app->fonts->getFont(singletons::FontManager::Type::ChatMedium, this->getScale()));
this->managedConnections.emplace_back(app->fonts->fontChanged.connect([=]() {
this->ui.textEdit->setFont(
app->fonts->getFont(singletons::FontManager::Type::Medium, this->getScale()));
app->fonts->getFont(singletons::FontManager::Type::ChatMedium, this->getScale()));
}));
// open emote popup
@@ -83,16 +85,16 @@ void SplitInput::initLayout()
});
}
this->emotePopup->resize((int)(300 * this->emotePopup->getScale()),
(int)(500 * this->emotePopup->getScale()));
this->emotePopup->loadChannel(this->chatWidget->getChannel());
this->emotePopup->resize(int(300 * this->emotePopup->getScale()),
int(500 * this->emotePopup->getScale()));
this->emotePopup->loadChannel(this->split->getChannel());
this->emotePopup->show();
});
// clear channelview selection when selecting in the input
QObject::connect(this->ui.textEdit, &QTextEdit::copyAvailable, [this](bool available) {
if (available) {
this->chatWidget->view.clearSelection();
this->split->view.clearSelection();
}
});
@@ -106,13 +108,13 @@ void SplitInput::scaleChangedEvent(float scale)
{
// update the icon size of the emote button
QString text = "<img src=':/images/emote.svg' width='xD' height='xD' />";
text.replace("xD", QString::number((int)12 * scale));
text.replace("xD", QString::number(int(12 * scale)));
this->ui.emoteButton->getLabel().setText(text);
this->ui.emoteButton->setFixedHeight((int)18 * scale);
this->ui.emoteButton->setFixedHeight(int(18 * scale));
// set maximum height
this->setMaximumHeight((int)(150 * this->getScale()));
this->setMaximumHeight(int(150 * this->getScale()));
}
void SplitInput::themeRefreshEvent()
@@ -125,7 +127,7 @@ void SplitInput::themeRefreshEvent()
this->ui.textEdit->setStyleSheet(this->themeManager->splits.input.styleSheet);
this->ui.hbox->setMargin((this->themeManager->isLightTheme() ? 4 : 2) * this->getScale());
this->ui.hbox->setMargin(int((this->themeManager->isLightTheme() ? 4 : 2) * this->getScale()));
}
void SplitInput::installKeyPressedEvent()
@@ -134,7 +136,7 @@ void SplitInput::installKeyPressedEvent()
this->ui.textEdit->keyPressed.connect([this, app](QKeyEvent *event) {
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
auto c = this->chatWidget->getChannel();
auto c = this->split->getChannel();
if (c == nullptr) {
return;
}
@@ -163,15 +165,11 @@ void SplitInput::installKeyPressedEvent()
return;
}
if (event->modifiers() == Qt::AltModifier) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = this->split->getContainer();
int reqX = page->currentX;
int reqY = page->lastRequestedY[reqX] - 1;
qDebug() << "Alt+Down to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
if (page != nullptr) {
page->selectNextSplit(SplitContainer::Above);
}
} else {
if (this->prevMsg.size() && this->prevIndex) {
if (this->prevIndex == (this->prevMsg.size())) {
@@ -191,15 +189,11 @@ void SplitInput::installKeyPressedEvent()
return;
}
if (event->modifiers() == Qt::AltModifier) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = this->split->getContainer();
int reqX = page->currentX;
int reqY = page->lastRequestedY[reqX] + 1;
qDebug() << "Alt+Down to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
if (page != nullptr) {
page->selectNextSplit(SplitContainer::Below);
}
} else {
if (this->prevIndex != (this->prevMsg.size() - 1) &&
this->prevIndex != this->prevMsg.size()) {
@@ -216,49 +210,39 @@ void SplitInput::installKeyPressedEvent()
}
} else if (event->key() == Qt::Key_Left) {
if (event->modifiers() == Qt::AltModifier) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = this->split->getContainer();
int reqX = page->currentX - 1;
int reqY = page->lastRequestedY[reqX];
qDebug() << "Alt+Left to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
if (page != nullptr) {
page->selectNextSplit(SplitContainer::Left);
}
}
} else if (event->key() == Qt::Key_Right) {
if (event->modifiers() == Qt::AltModifier) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = this->split->getContainer();
int reqX = page->currentX + 1;
int reqY = page->lastRequestedY[reqX];
qDebug() << "Alt+Right to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
if (page != nullptr) {
page->selectNextSplit(SplitContainer::Right);
}
}
} else if (event->key() == Qt::Key_Tab) {
if (event->modifiers() == Qt::ControlModifier) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = static_cast<SplitContainer *>(this->split->parentWidget());
Notebook *notebook = static_cast<Notebook *>(page->parentWidget());
notebook->nextTab();
notebook->selectNextTab();
}
} else if (event->key() == Qt::Key_Backtab) {
if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
SplitContainer *page =
static_cast<SplitContainer *>(this->chatWidget->parentWidget());
SplitContainer *page = static_cast<SplitContainer *>(this->split->parentWidget());
Notebook *notebook = static_cast<Notebook *>(page->parentWidget());
notebook->previousTab();
notebook->selectPreviousTab();
}
} else if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) {
if (this->chatWidget->view.hasSelection()) {
this->chatWidget->doCopy();
if (this->split->view.hasSelection()) {
this->split->doCopy();
event->accept();
}
}
@@ -292,13 +276,22 @@ void SplitInput::editTextChanged()
// set textLengthLabel value
QString text = this->ui.textEdit->toPlainText();
this->textChanged.invoke(text);
if (text.startsWith("/r ") && this->split->getChannel()->isTwitchChannel()) //
{
QString lastUser = app->twitch.server->lastUserThatWhisperedMe.get();
if (!lastUser.isEmpty()) {
this->ui.textEdit->setPlainText("/w " + lastUser + text.mid(2));
this->ui.textEdit->moveCursor(QTextCursor::EndOfBlock);
}
} else {
this->textChanged.invoke(text);
text = text.trimmed();
static QRegularExpression spaceRegex("\\s\\s+");
text = text.replace(spaceRegex, " ");
text = text.trimmed();
static QRegularExpression spaceRegex("\\s\\s+");
text = text.replace(spaceRegex, " ");
text = app->commands->execCommand(text, this->chatWidget->getChannel(), true);
text = app->commands->execCommand(text, this->split->getChannel(), true);
}
QString labelText;
@@ -315,14 +308,23 @@ void SplitInput::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(this->rect(), this->themeManager->splits.input.background);
QPen pen(this->themeManager->splits.input.border);
if (this->themeManager->isLightTheme()) {
pen.setWidth((int)(6 * this->getScale()));
int s = int(3 * this->getScale());
QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s));
painter.fillRect(rect, this->themeManager->splits.input.background);
painter.setPen(QColor("#ccc"));
painter.drawRect(rect);
} else {
int s = int(1 * this->getScale());
QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s));
painter.fillRect(rect, this->themeManager->splits.input.background);
painter.setPen(QColor("#333"));
painter.drawRect(rect);
}
painter.setPen(pen);
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
void SplitInput::resizeEvent(QResizeEvent *)
@@ -336,7 +338,7 @@ void SplitInput::resizeEvent(QResizeEvent *)
void SplitInput::mousePressEvent(QMouseEvent *)
{
this->chatWidget->giveFocus(Qt::MouseFocusReason);
this->split->giveFocus(Qt::MouseFocusReason);
}
} // namespace widgets
+1 -7
View File
@@ -40,7 +40,7 @@ protected:
virtual void mousePressEvent(QMouseEvent *event) override;
private:
Split *const chatWidget;
Split *const split;
std::unique_ptr<EmotePopup> emotePopup;
struct {
@@ -52,12 +52,6 @@ private:
} ui;
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
// QHBoxLayout hbox;
// QVBoxLayout vbox;
// QHBoxLayout editContainer;
// ResizingTextEdit textInput;
// QLabel textLengthLabel;
// RippleEffectLabel emotesLabel;
QStringList prevMsg;
QString currMsg;
int prevIndex = 0;
+6
View File
@@ -0,0 +1,6 @@
#include "splitnode.hpp"
SplitNode::SplitNode()
{
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef SPLITNODE_HPP
#define SPLITNODE_HPP
class SplitNode
{
public:
SplitNode();
};
#endif // SPLITNODE_HPP
+62 -9
View File
@@ -9,6 +9,7 @@
#include "application.hpp"
#include "singletons/resourcemanager.hpp"
#include "widgets/split.hpp"
#include "widgets/splitcontainer.hpp"
namespace chatterino {
namespace widgets {
@@ -18,6 +19,7 @@ SplitOverlay::SplitOverlay(Split *parent)
, split(parent)
{
QGridLayout *layout = new QGridLayout(this);
this->_layout = layout;
layout->setMargin(1);
layout->setSpacing(1);
@@ -27,10 +29,11 @@ SplitOverlay::SplitOverlay(Split *parent)
layout->setColumnStretch(3, 1);
QPushButton *move = new QPushButton(getApp()->resources->split.move, QString());
QPushButton *left = new QPushButton(getApp()->resources->split.left, QString());
QPushButton *right = new QPushButton(getApp()->resources->split.right, QString());
QPushButton *up = new QPushButton(getApp()->resources->split.up, QString());
QPushButton *down = new QPushButton(getApp()->resources->split.down, QString());
QPushButton *left = this->_left = new QPushButton(getApp()->resources->split.left, QString());
QPushButton *right = this->_right =
new QPushButton(getApp()->resources->split.right, QString());
QPushButton *up = this->_up = new QPushButton(getApp()->resources->split.up, QString());
QPushButton *down = this->_down = new QPushButton(getApp()->resources->split.down, QString());
move->setGraphicsEffect(new QGraphicsOpacityEffect(this));
left->setGraphicsEffect(new QGraphicsOpacityEffect(this));
@@ -62,14 +65,14 @@ SplitOverlay::SplitOverlay(Split *parent)
up->setFocusPolicy(Qt::NoFocus);
down->setFocusPolicy(Qt::NoFocus);
move->setCursor(Qt::PointingHandCursor);
move->setCursor(Qt::SizeAllCursor);
left->setCursor(Qt::PointingHandCursor);
right->setCursor(Qt::PointingHandCursor);
up->setCursor(Qt::PointingHandCursor);
down->setCursor(Qt::PointingHandCursor);
this->managedConnect(this->scaleChanged, [=](float _scale) {
int a = _scale * 40;
int a = int(_scale * 30);
QSize size(a, a);
move->setIconSize(size);
@@ -78,28 +81,41 @@ SplitOverlay::SplitOverlay(Split *parent)
up->setIconSize(size);
down->setIconSize(size);
});
this->setMouseTracking(true);
this->setCursor(Qt::ArrowCursor);
}
void SplitOverlay::paintEvent(QPaintEvent *event)
void SplitOverlay::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(this->rect(), QColor(0, 0, 0, 90));
painter.fillRect(this->rect(), QColor(0, 0, 0, 150));
QRect rect;
switch (this->hoveredElement) {
case SplitLeft: {
rect = QRect(0, 0, this->width() / 2, this->height());
} break;
case SplitRight: {
rect = QRect(this->width() / 2, 0, this->width() / 2, this->height());
} break;
case SplitUp: {
rect = QRect(0, 0, this->width(), this->height() / 2);
} break;
case SplitDown: {
rect = QRect(0, this->height() / 2, this->width(), this->height() / 2);
} break;
default:;
}
rect.setRight(rect.right() - 1);
rect.setBottom(rect.bottom() - 1);
if (!rect.isNull()) {
painter.setPen(getApp()->themes->splits.dropPreviewBorder);
painter.setBrush(getApp()->themes->splits.dropPreview);
@@ -107,6 +123,29 @@ void SplitOverlay::paintEvent(QPaintEvent *event)
}
}
void SplitOverlay::resizeEvent(QResizeEvent *event)
{
float _scale = this->getScale();
bool wideEnough = event->size().width() > 150 * _scale;
bool highEnough = event->size().height() > 150 * _scale;
this->_left->setVisible(wideEnough);
this->_right->setVisible(wideEnough);
this->_up->setVisible(highEnough);
this->_down->setVisible(highEnough);
}
void SplitOverlay::mouseMoveEvent(QMouseEvent *event)
{
BaseWidget::mouseMoveEvent(event);
// qDebug() << QGuiApplication::queryKeyboardModifiers();
// if ((QGuiApplication::queryKeyboardModifiers() & Qt::AltModifier) == Qt::AltModifier) {
// this->hide();
// }
}
SplitOverlay::ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent, HoveredElement _element)
: QObject(_parent)
, parent(_parent)
@@ -122,7 +161,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched, QEvent *even
dynamic_cast<QGraphicsOpacityEffect *>(((QWidget *)watched)->graphicsEffect());
if (effect != nullptr) {
effect->setOpacity(1);
effect->setOpacity(0.99);
}
this->parent->hoveredElement = this->hoveredElement;
@@ -148,6 +187,20 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched, QEvent *even
return true;
}
} break;
case QEvent::MouseButtonRelease: {
if (this->hoveredElement != HoveredElement::SplitMove) {
SplitContainer *container = this->parent->split->getContainer();
if (container != nullptr) {
auto *_split = new Split(container);
auto dir = SplitContainer::Direction(this->hoveredElement +
SplitContainer::Left - SplitLeft);
container->insertSplit(_split, dir, this->parent->split);
this->parent->hide();
}
}
} break;
default:;
}
return QObject::eventFilter(watched, event);
}
+14 -2
View File
@@ -1,5 +1,8 @@
#pragma once
#include <QGridLayout>
#include <QPushButton>
#include "pajlada/signals/signalholder.hpp"
#include "widgets/basewidget.hpp"
@@ -14,17 +17,26 @@ public:
explicit SplitOverlay(Split *parent = nullptr);
protected:
// bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
private:
enum HoveredElement { None, SplitMove, SplitLeft, SplitRight, SplitUp, SplitDown };
// fourtf: !!! preserve the order of left, up, right and down
enum HoveredElement { None, SplitMove, SplitLeft, SplitUp, SplitRight, SplitDown };
HoveredElement hoveredElement = None;
Split *split;
QGridLayout *_layout;
QPushButton *_left;
QPushButton *_up;
QPushButton *_right;
QPushButton *_down;
class ButtonEventFilter : public QObject
{
HoveredElement hoveredElement;
SplitOverlay *parent;
HoveredElement hoveredElement;
public:
ButtonEventFilter(SplitOverlay *parent, HoveredElement hoveredElement);
+62 -4
View File
@@ -1,17 +1,75 @@
#include "lastruncrashdialog.hpp"
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "singletons/updatemanager.hpp"
#include "util/layoutcreator.hpp"
#include "util/posttothread.hpp"
namespace chatterino {
namespace widgets {
LastRunCrashDialog::LastRunCrashDialog()
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(
new QLabel("The application wasn't terminated properly last time it was executed."));
this->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
this->setWindowTitle("Chatterino");
this->setLayout(layout);
auto &updateManager = singletons::UpdateManager::getInstance();
auto layout = util::LayoutCreator<LastRunCrashDialog>(this).setLayoutType<QVBoxLayout>();
layout.emplace<QLabel>(
"The application wasn't terminated properly the last time it was executed.");
layout->addSpacing(16);
auto update = layout.emplace<QLabel>();
auto buttons = layout.emplace<QDialogButtonBox>();
auto *installUpdateButton = buttons->addButton("Install Update", QDialogButtonBox::NoRole);
installUpdateButton->setEnabled(false);
QObject::connect(installUpdateButton, &QPushButton::clicked, [this, update]() mutable {
auto &updateManager = singletons::UpdateManager::getInstance();
updateManager.installUpdates();
this->setEnabled(false);
update->setText("Downloading updates...");
});
auto *okButton = buttons->addButton("Ignore", QDialogButtonBox::ButtonRole::NoRole);
QObject::connect(okButton, &QPushButton::clicked, [this] { this->accept(); });
// Updates
auto updateUpdateLabel = [update]() mutable {
auto &updateManager = singletons::UpdateManager::getInstance();
switch (updateManager.getStatus()) {
case singletons::UpdateManager::None: {
update->setText("Not checking for updates.");
} break;
case singletons::UpdateManager::Searching: {
update->setText("Checking for updates...");
} break;
case singletons::UpdateManager::UpdateAvailable: {
update->setText("Update available.");
} break;
case singletons::UpdateManager::NoUpdateAvailable: {
update->setText("No update abailable.");
} break;
case singletons::UpdateManager::Error: {
update->setText("Error while searching for update.\nEither the update service is "
"temporarily down or there is an issue with your installation.");
} break;
}
};
updateUpdateLabel();
this->managedConnect(updateManager.statusUpdated, [updateUpdateLabel](auto) mutable {
util::postToThread([updateUpdateLabel]() mutable { updateUpdateLabel(); });
});
}
} // namespace widgets
+2 -1
View File
@@ -1,11 +1,12 @@
#pragma once
#include <QDialog>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
namespace widgets {
class LastRunCrashDialog : public QDialog
class LastRunCrashDialog : public QDialog, pajlada::Signals::SignalHolder
{
public:
LastRunCrashDialog();
+1 -1
View File
@@ -50,7 +50,7 @@ void LogInWithCredentials(const std::string &userID, const std::string &username
pajlada::Settings::Setting<std::string>::set("/accounts/uid" + userID + "/oauthToken",
oauthToken);
getApp()->accounts->Twitch.reloadUsers();
getApp()->accounts->twitch.reloadUsers();
messageBox.exec();
}
+156 -361
View File
@@ -25,7 +25,7 @@
namespace chatterino {
namespace widgets {
Notebook2::Notebook2(QWidget *parent)
Notebook::Notebook(QWidget *parent)
: BaseWidget(parent)
, addButton(this)
{
@@ -38,15 +38,12 @@ Notebook2::Notebook2(QWidget *parent)
QObject::connect(shortcut_prev, &QShortcut::activated, [this] { this->selectPreviousTab(); });
}
NotebookTab2 *Notebook2::addPage(QWidget *page, QString title, bool select)
NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select)
{
auto *tab = new NotebookTab2(this);
auto *tab = new NotebookTab(this);
tab->page = page;
if (!title.isEmpty()) {
tab->setTitle(title);
tab->useDefaultTitle = false;
}
tab->setCustomTitle(title);
Item item;
item.page = page;
@@ -63,10 +60,12 @@ NotebookTab2 *Notebook2::addPage(QWidget *page, QString title, bool select)
this->performLayout();
tab->show();
return tab;
}
void Notebook2::removePage(QWidget *page)
void Notebook::removePage(QWidget *page)
{
for (int i = 0; i < this->items.count(); i++) {
if (this->items[i].page == page) {
@@ -89,16 +88,18 @@ void Notebook2::removePage(QWidget *page)
break;
}
}
this->performLayout(true);
}
void Notebook2::removeCurrentPage()
void Notebook::removeCurrentPage()
{
if (this->selectedPage != nullptr) {
this->removePage(this->selectedPage);
}
}
int Notebook2::indexOf(QWidget *page) const
int Notebook::indexOf(QWidget *page) const
{
for (int i = 0; i < this->items.count(); i++) {
if (this->items[i].page == page) {
@@ -109,7 +110,7 @@ int Notebook2::indexOf(QWidget *page) const
return -1;
}
void Notebook2::select(QWidget *page)
void Notebook::select(QWidget *page)
{
if (page == this->selectedPage) {
return;
@@ -118,20 +119,35 @@ void Notebook2::select(QWidget *page)
if (page != nullptr) {
page->setHidden(false);
NotebookTab2 *tab = this->getTabFromPage(page);
tab->setSelected(true);
tab->raise();
assert(this->containsPage(page));
Item &item = this->findItem(page);
item.tab->setSelected(true);
item.tab->raise();
if (item.selectedWidget == nullptr) {
item.page->setFocus();
} else {
if (containsChild(page, item.selectedWidget)) {
qDebug() << item.selectedWidget;
item.selectedWidget->setFocus(Qt::MouseFocusReason);
} else {
qDebug() << "Notebook: selected child of page doesn't exist anymore";
}
}
}
if (this->selectedPage != nullptr) {
this->selectedPage->setHidden(true);
NotebookTab2 *tab = this->getTabFromPage(selectedPage);
tab->setSelected(false);
Item &item = this->findItem(selectedPage);
item.tab->setSelected(false);
// for (auto split : this->selectedPage->getSplits()) {
// split->updateLastReadMessage();
// }
item.selectedWidget = this->selectedPage->focusWidget();
}
this->selectedPage = page;
@@ -139,7 +155,32 @@ void Notebook2::select(QWidget *page)
this->performLayout();
}
void Notebook2::selectIndex(int index)
bool Notebook::containsPage(QWidget *page)
{
return std::any_of(this->items.begin(), this->items.end(),
[page](const auto &item) { return item.page == page; });
}
Notebook::Item &Notebook::findItem(QWidget *page)
{
auto it = std::find_if(this->items.begin(), this->items.end(),
[page](const auto &item) { return page == item.page; });
assert(it != this->items.end());
return *it;
}
bool Notebook::containsChild(const QObject *obj, const QObject *child)
{
return std::any_of(obj->children().begin(), obj->children().end(), [child](const QObject *o) {
if (o == child) {
return true;
}
return containsChild(o, child);
});
}
void Notebook::selectIndex(int index)
{
if (index < 0 || this->items.count() <= index) {
return;
@@ -148,7 +189,7 @@ void Notebook2::selectIndex(int index)
this->select(this->items[index].page);
}
void Notebook2::selectNextTab()
void Notebook::selectNextTab()
{
if (this->items.size() <= 1) {
return;
@@ -159,7 +200,7 @@ void Notebook2::selectNextTab()
this->select(this->items[index].page);
}
void Notebook2::selectPreviousTab()
void Notebook::selectPreviousTab()
{
if (this->items.size() <= 1) {
return;
@@ -174,22 +215,27 @@ void Notebook2::selectPreviousTab()
this->select(this->items[index].page);
}
int Notebook2::getPageCount() const
int Notebook::getPageCount() const
{
return this->items.count();
}
int Notebook2::getSelectedIndex() const
QWidget *Notebook::getPageAt(int index) const
{
return this->items[index].page;
}
int Notebook::getSelectedIndex() const
{
return this->indexOf(this->selectedPage);
}
QWidget *Notebook2::getSelectedPage() const
QWidget *Notebook::getSelectedPage() const
{
return this->selectedPage;
}
QWidget *Notebook2::tabAt(QPoint point, int &index, int maxWidth)
QWidget *Notebook::tabAt(QPoint point, int &index, int maxWidth)
{
int i = 0;
@@ -211,95 +257,82 @@ QWidget *Notebook2::tabAt(QPoint point, int &index, int maxWidth)
return nullptr;
}
void Notebook2::rearrangePage(QWidget *page, int index)
void Notebook::rearrangePage(QWidget *page, int index)
{
this->items.move(this->indexOf(page), index);
this->performLayout();
this->performLayout(true);
}
bool Notebook2::getAllowUserTabManagement() const
bool Notebook::getAllowUserTabManagement() const
{
return this->allowUserTabManagement;
}
void Notebook2::setAllowUserTabManagement(bool value)
void Notebook::setAllowUserTabManagement(bool value)
{
this->allowUserTabManagement = value;
}
bool Notebook2::getShowAddButton() const
bool Notebook::getShowAddButton() const
{
return this->showAddButton;
}
void Notebook2::setShowAddButton(bool value)
void Notebook::setShowAddButton(bool value)
{
this->showAddButton = value;
this->addButton.setHidden(!value);
}
void Notebook2::scaleChangedEvent(float scale)
void Notebook::scaleChangedEvent(float scale)
{
// float h = 24 * this->getScale();
float h = NOTEBOOK_TAB_HEIGHT * this->getScale();
// this->settingsButton.setFixedSize(h, h);
// this->userButton.setFixedSize(h, h);
// this->addButton.setFixedSize(h, h);
this->addButton.setFixedSize(h, h);
for (auto &i : this->items) {
i.tab->updateSize();
}
}
void Notebook2::resizeEvent(QResizeEvent *)
void Notebook::resizeEvent(QResizeEvent *)
{
this->performLayout();
}
void Notebook2::performLayout(bool animated)
void Notebook::performLayout(bool animated)
{
auto app = getApp();
int xStart = (int)(2 * this->getScale());
int xStart = int(2 * this->getScale());
int x = xStart, y = 0;
float scale = this->getScale();
// bool customFrame = this->parentWindow->hasCustomWindowFrame();
int h = int(NOTEBOOK_TAB_HEIGHT * this->getScale());
// bool customFrame = false;
for (auto *btn : this->customButtons) {
if (!btn->isVisible()) {
continue;
}
// if (!this->showButtons || app->settings->hidePreferencesButton || customFrame) {
// this->settingsButton.hide();
// } else {
// this->settingsButton.show();
// x += settingsButton.width();
// }
// if (!this->showButtons || app->settings->hideUserButton || customFrame) {
// this->userButton.hide();
// } else {
// this->userButton.move(x, 0);
// this->userButton.show();
// x += userButton.width();
// }
btn->setFixedSize(h, h);
btn->move(x, 0);
x += h;
}
// if (customFrame || !this->showButtons ||
// (app->settings->hideUserButton && app->settings->hidePreferencesButton)) {
// x += (int)(scale * 2);
// }
int tabHeight = static_cast<int>(24 * scale);
int tabHeight = static_cast<int>(NOTEBOOK_TAB_HEIGHT * scale);
bool first = true;
for (auto i = this->items.begin(); i != this->items.end(); i++) {
if (!first &&
(i == this->items.end() && this->showAddButton ? tabHeight : 0) + x + i->tab->width() >
width()) //
{
bool wrap =
!first && (((i + 1 == this->items.end() && this->showAddButton) ? tabHeight : 0) + x +
i->tab->width()) > width();
if (wrap) {
y += i->tab->height();
// y += 20;
i->tab->moveAnimated(QPoint(xStart, y), animated);
x = i->tab->width() + xStart;
} else {
@@ -321,7 +354,7 @@ void Notebook2::performLayout(bool animated)
this->update();
}
y += (int)(1 * scale);
y += int(3 * scale);
for (auto &i : this->items) {
i.tab->raise();
@@ -338,16 +371,31 @@ void Notebook2::performLayout(bool animated)
}
}
void Notebook2::paintEvent(QPaintEvent *event)
void Notebook::paintEvent(QPaintEvent *event)
{
BaseWidget::paintEvent(event);
QPainter painter(this);
painter.fillRect(0, this->lineY, this->width(), (int)(1 * this->getScale()),
painter.fillRect(0, this->lineY, this->width(), (int)(3 * this->getScale()),
this->themeManager->tabs.bottomLine);
}
NotebookTab2 *Notebook2::getTabFromPage(QWidget *page)
NotebookButton *Notebook::getAddButton()
{
return &this->addButton;
}
NotebookButton *Notebook::addCustomButton()
{
NotebookButton *btn = new NotebookButton(this);
this->customButtons.push_back(btn);
this->performLayout();
return btn;
}
NotebookTab *Notebook::getTabFromPage(QWidget *page)
{
for (auto &it : this->items) {
if (it.page == page) {
@@ -358,318 +406,65 @@ NotebookTab2 *Notebook2::getTabFromPage(QWidget *page)
return nullptr;
}
// Notebook2::OLD NOTEBOOK
Notebook::Notebook(Window *parent, bool _showButtons)
: BaseWidget(parent)
, parentWindow(parent)
, addButton(this)
, settingsButton(this)
, userButton(this)
, showButtons(_showButtons)
, closeConfirmDialog(this)
SplitNotebook::SplitNotebook(Window *parent)
: Notebook(parent)
{
auto app = getApp();
this->connect(&this->settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
this->connect(&this->userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked()));
this->connect(&this->addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked()));
this->connect(this->getAddButton(), &NotebookButton::clicked,
[this]() { QTimer::singleShot(80, this, [this] { this->addPage(true); }); });
this->settingsButton.icon = NotebookButton::IconSettings;
bool customFrame = parent->hasCustomWindowFrame();
this->userButton.move(24, 0);
this->userButton.icon = NotebookButton::IconUser;
if (!customFrame) {
auto *settingsBtn = this->addCustomButton();
auto *userBtn = this->addCustomButton();
app->settings->hidePreferencesButton.connectSimple([this](auto) { this->performLayout(); });
app->settings->hideUserButton.connectSimple([this](auto) { this->performLayout(); });
settingsBtn->setVisible(!app->settings->hidePreferencesButton.getValue());
userBtn->setVisible(!app->settings->hideUserButton.getValue());
closeConfirmDialog.setText("Are you sure you want to close this tab?");
closeConfirmDialog.setIcon(QMessageBox::Icon::Question);
closeConfirmDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
closeConfirmDialog.setDefaultButton(QMessageBox::Yes);
app->settings->hidePreferencesButton.connect(
[this, settingsBtn](bool hide, auto) {
settingsBtn->setVisible(!hide);
this->performLayout(true);
},
this->uniqueConnections);
this->scaleChangedEvent(this->getScale());
app->settings->hideUserButton.connect(
[this, userBtn](bool hide, auto) {
userBtn->setVisible(!hide);
this->performLayout(true);
},
this->uniqueConnections);
// Window-wide hotkeys
// CTRL+T: Create new split in selected notebook page
CreateWindowShortcut(this, "CTRL+T", [this]() {
if (this->selectedPage == nullptr) {
return;
}
settingsBtn->icon = NotebookButton::IconSettings;
userBtn->icon = NotebookButton::IconUser;
this->selectedPage->addChat(true);
});
QObject::connect(settingsBtn, &NotebookButton::clicked,
[] { getApp()->windows->showSettingsDialog(); });
QObject::connect(userBtn, &NotebookButton::clicked, [this, userBtn] {
getApp()->windows->showAccountSelectPopup(
this->mapToGlobal(userBtn->rect().bottomRight()));
});
}
}
SplitContainer *Notebook::addNewPage(bool select)
SplitContainer *SplitNotebook::addPage(bool select)
{
auto tab = new NotebookTab(this);
auto page = new SplitContainer(this, tab);
SplitContainer *container = new SplitContainer(this);
auto *tab = Notebook::addPage(container, QString(), select);
container->setTab(tab);
tab->setParent(this);
tab->show();
if (select || this->pages.count() == 0) {
this->select(page);
}
this->pages.append(page);
this->performLayout();
return page;
return container;
}
void Notebook::removePage(SplitContainer *page)
SplitContainer *SplitNotebook::getOrAddSelectedPage()
{
if (page->splitCount() > 0 && closeConfirmDialog.exec() != QMessageBox::Yes) {
return;
}
auto *selectedPage = this->getSelectedPage();
int index = this->pages.indexOf(page);
if (this->pages.size() == 1) {
select(nullptr);
} else if (index == this->pages.count() - 1) {
select(this->pages[index - 1]);
} else {
select(this->pages[index + 1]);
}
page->getTab()->deleteLater();
page->deleteLater();
this->pages.removeOne(page);
if (this->pages.empty()) {
this->addNewPage();
}
this->performLayout();
}
void Notebook::removeCurrentPage()
{
if (this->selectedPage == nullptr) {
return;
}
this->removePage(this->selectedPage);
}
SplitContainer *Notebook::getOrAddSelectedPage()
{
if (selectedPage == nullptr) {
this->addNewPage(true);
}
return selectedPage;
}
SplitContainer *Notebook::getSelectedPage()
{
return selectedPage;
}
void Notebook::select(SplitContainer *page)
{
if (page == this->selectedPage) {
return;
}
if (page != nullptr) {
page->setHidden(false);
page->getTab()->setSelected(true);
page->getTab()->raise();
}
if (this->selectedPage != nullptr) {
this->selectedPage->setHidden(true);
this->selectedPage->getTab()->setSelected(false);
for (auto split : this->selectedPage->getSplits()) {
split->updateLastReadMessage();
}
}
this->selectedPage = page;
this->performLayout();
}
void Notebook::selectIndex(int index)
{
if (index < 0 || index >= this->pages.size()) {
return;
}
this->select(this->pages.at(index));
}
int Notebook::tabCount()
{
return this->pages.size();
}
SplitContainer *Notebook::tabAt(QPoint point, int &index, int maxWidth)
{
int i = 0;
for (auto *page : this->pages) {
QRect rect = page->getTab()->getDesiredRect();
rect.setHeight((int)(this->getScale() * 24));
rect.setWidth(std::min(maxWidth, rect.width()));
if (rect.contains(point)) {
index = i;
return page;
}
i++;
}
index = -1;
return nullptr;
}
SplitContainer *Notebook::tabAt(int index)
{
return this->pages[index];
}
void Notebook::rearrangePage(SplitContainer *page, int index)
{
this->pages.move(this->pages.indexOf(page), index);
this->performLayout();
}
void Notebook::nextTab()
{
if (this->pages.size() <= 1) {
return;
}
int index = (this->pages.indexOf(this->selectedPage) + 1) % this->pages.size();
this->select(this->pages[index]);
}
void Notebook::previousTab()
{
if (this->pages.size() <= 1) {
return;
}
int index = (this->pages.indexOf(this->selectedPage) - 1);
if (index < 0) {
index += this->pages.size();
}
this->select(this->pages[index]);
}
void Notebook::performLayout(bool animated)
{
auto app = getApp();
int x = 0, y = 0;
float scale = this->getScale();
bool customFrame = this->parentWindow->hasCustomWindowFrame();
if (!this->showButtons || app->settings->hidePreferencesButton || customFrame) {
this->settingsButton.hide();
} else {
this->settingsButton.show();
x += settingsButton.width();
}
if (!this->showButtons || app->settings->hideUserButton || customFrame) {
this->userButton.hide();
} else {
this->userButton.move(x, 0);
this->userButton.show();
x += userButton.width();
}
if (customFrame || !this->showButtons ||
(app->settings->hideUserButton && app->settings->hidePreferencesButton)) {
x += (int)(scale * 2);
}
int tabHeight = static_cast<int>(24 * scale);
bool first = true;
for (auto &i : this->pages) {
if (!first &&
(i == this->pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
y += i->getTab()->height();
// y += 20;
i->getTab()->moveAnimated(QPoint(0, y), animated);
x = i->getTab()->width();
} else {
i->getTab()->moveAnimated(QPoint(x, y), animated);
x += i->getTab()->width();
}
// x -= (int)(8 * scale);
x += 1;
first = false;
}
// x += (int)(8 * scale);
// x += 1;
this->addButton.move(x, y);
y -= 1;
for (auto &i : this->pages) {
i->getTab()->raise();
}
this->addButton.raise();
if (this->selectedPage != nullptr) {
this->selectedPage->move(0, y + tabHeight);
this->selectedPage->resize(width(), height() - y - tabHeight);
this->selectedPage->raise();
}
}
void Notebook::resizeEvent(QResizeEvent *)
{
this->performLayout(false);
}
void Notebook::scaleChangedEvent(float)
{
float h = 24 * this->getScale();
this->settingsButton.setFixedSize(h, h);
this->userButton.setFixedSize(h, h);
this->addButton.setFixedSize(h, h);
for (auto &i : this->pages) {
i->getTab()->updateSize();
}
}
void Notebook::settingsButtonClicked()
{
auto app = getApp();
app->windows->showSettingsDialog();
}
void Notebook::usersButtonClicked()
{
auto app = getApp();
app->windows->showAccountSelectPopup(
this->mapToGlobal(this->userButton.rect().bottomRight()));
}
void Notebook::addPageButtonClicked()
{
QTimer::singleShot(80, [this] { this->addNewPage(true); });
return selectedPage != nullptr ? (SplitContainer *)selectedPage : this->addPage();
}
} // namespace widgets
+23 -53
View File
@@ -1,5 +1,6 @@
#pragma once
#include "pajlada/signals/signal.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/helper/notebookbutton.hpp"
#include "widgets/helper/notebooktab.hpp"
@@ -14,14 +15,14 @@ namespace widgets {
class Window;
class Notebook2 : public BaseWidget
class Notebook : public BaseWidget
{
Q_OBJECT
public:
explicit Notebook2(QWidget *parent);
explicit Notebook(QWidget *parent);
NotebookTab2 *addPage(QWidget *page, QString title = QString(), bool select = false);
NotebookTab *addPage(QWidget *page, QString title = QString(), bool select = false);
void removePage(QWidget *page);
void removeCurrentPage();
@@ -32,6 +33,7 @@ public:
void selectPreviousTab();
int getPageCount() const;
QWidget *getPageAt(int index) const;
int getSelectedIndex() const;
QWidget *getSelectedPage() const;
@@ -44,83 +46,51 @@ public:
bool getShowAddButton() const;
void setShowAddButton(bool value);
void performLayout(bool animate = false);
protected:
virtual void scaleChangedEvent(float scale) override;
virtual void resizeEvent(QResizeEvent *) override;
virtual void paintEvent(QPaintEvent *) override;
NotebookButton *getAddButton();
NotebookButton *addCustomButton();
private:
struct Item {
NotebookTab2 *tab;
NotebookTab *tab;
QWidget *page;
QWidget *selectedWidget = nullptr;
};
QList<Item> items;
QWidget *selectedPage = nullptr;
bool containsPage(QWidget *page);
Item &findItem(QWidget *page);
static bool containsChild(const QObject *obj, const QObject *child);
NotebookButton addButton;
std::vector<NotebookButton *> customButtons;
bool allowUserTabManagement = false;
bool showAddButton = false;
int lineY = 20;
void performLayout(bool animate = true);
NotebookTab2 *getTabFromPage(QWidget *page);
NotebookTab *getTabFromPage(QWidget *page);
};
class Notebook : public BaseWidget
class SplitNotebook : public Notebook, pajlada::Signals::SignalHolder
{
Q_OBJECT
public:
explicit Notebook(Window *parent, bool _showButtons);
SplitContainer *addNewPage(bool select = false);
void removePage(SplitContainer *page);
void removeCurrentPage();
void select(SplitContainer *page);
void selectIndex(int index);
SplitNotebook(Window *parent);
SplitContainer *addPage(bool select = false);
SplitContainer *getOrAddSelectedPage();
SplitContainer *getSelectedPage();
void performLayout(bool animate = true);
int tabCount();
SplitContainer *tabAt(QPoint point, int &index, int maxWidth = 2000000000);
SplitContainer *tabAt(int index);
void rearrangePage(SplitContainer *page, int index);
void nextTab();
void previousTab();
protected:
void scaleChangedEvent(float scale);
void resizeEvent(QResizeEvent *);
void settingsButtonMouseReleased(QMouseEvent *event);
public slots:
void settingsButtonClicked();
void usersButtonClicked();
void addPageButtonClicked();
private:
Window *parentWindow;
QList<SplitContainer *> pages;
NotebookButton addButton;
NotebookButton settingsButton;
NotebookButton userButton;
SplitContainer *selectedPage = nullptr;
bool showButtons;
QMessageBox closeConfirmDialog;
std::vector<pajlada::Signals::ScopedConnection> uniqueConnections;
};
} // namespace widgets
+50
View File
@@ -0,0 +1,50 @@
#include "notificationpopup.hpp"
#include "widgets/helper/channelview.hpp"
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
namespace chatterino {
namespace widgets {
NotificationPopup::NotificationPopup()
: BaseWindow((QWidget *)nullptr, BaseWindow::FrameLess)
, channel(std::make_shared<Channel>("notifications", Channel::None))
{
this->channelView = new ChannelView(this);
auto *layout = new QVBoxLayout(this);
this->setLayout(layout);
layout->addWidget(this->channelView);
this->channelView->setChannel(this->channel);
this->setScaleIndependantSize(300, 150);
}
void NotificationPopup::updatePosition()
{
Location location = BottomRight;
QDesktopWidget *desktop = QApplication::desktop();
const QRect rect = desktop->availableGeometry();
switch (location) {
case BottomRight: {
this->move(rect.right() - this->width(), rect.bottom() - this->height());
} break;
}
}
void NotificationPopup::addMessage(messages::MessagePtr msg)
{
this->channel->addMessage(msg);
// QTimer::singleShot(5000, this, [this, msg] { this->channel->remove });
}
} // namespace widgets
} // namespace chatterino
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "channel.hpp"
#include "messages/message.hpp"
#include "widgets/basewindow.hpp"
namespace chatterino {
namespace widgets {
class ChannelView;
class NotificationPopup : public BaseWindow
{
public:
enum Location { TopLeft, TopRight, BottomLeft, BottomRight };
NotificationPopup();
void addMessage(messages::MessagePtr msg);
void updatePosition();
private:
ChannelView *channelView;
ChannelPtr channel;
};
} // namespace widgets
} // namespace chatterino
+37 -21
View File
@@ -20,7 +20,7 @@ Scrollbar::Scrollbar(ChannelView *parent)
: BaseWidget(parent)
, currentValueAnimation(this, "currentValue")
{
resize((int)(16 * this->getScale()), 100);
resize(int(16 * this->getScale()), 100);
this->currentValueAnimation.setDuration(150);
this->currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic));
@@ -31,7 +31,7 @@ Scrollbar::Scrollbar(ChannelView *parent)
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() {
resize((int)(16 * this->getScale()), 100);
resize(int(16 * this->getScale()), 100);
timer->deleteLater();
});
@@ -98,7 +98,7 @@ void Scrollbar::setDesiredValue(qreal value, bool animated)
animated &= app->settings->enableSmoothScrolling.getValue();
value = std::max(this->minimum, std::min(this->maximum - this->largeChange, value));
if (this->desiredValue + this->smoothScrollingOffset != value) {
if (std::abs(this->desiredValue + this->smoothScrollingOffset - value) > 0.0001) {
if (animated) {
this->currentValueAnimation.stop();
this->currentValueAnimation.setStartValue(this->currentValue +
@@ -109,14 +109,14 @@ void Scrollbar::setDesiredValue(qreal value, bool animated)
// }
this->currentValueAnimation.setEndValue(value);
this->smoothScrollingOffset = 0;
this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.01;
this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001;
this->currentValueAnimation.start();
} else {
if (this->currentValueAnimation.state() != QPropertyAnimation::Running) {
this->smoothScrollingOffset = 0;
this->desiredValue = value;
this->currentValueAnimation.stop();
this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.01;
this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001;
setCurrentValue(value);
}
}
@@ -196,11 +196,13 @@ void Scrollbar::printCurrentState(const QString &prefix) const
void Scrollbar::paintEvent(QPaintEvent *)
{
auto *app = getApp();
bool mouseOver = this->mouseOverIndex != -1;
int xOffset = mouseOver ? 0 : width() - (int)(4 * this->getScale());
int xOffset = mouseOver ? 0 : width() - int(4 * this->getScale());
QPainter painter(this);
// painter.fillRect(rect(), this->themeManager->ScrollbarBG);
painter.fillRect(rect(), this->themeManager->scrollbars.background);
// painter.fillRect(QRect(xOffset, 0, width(), this->buttonHeight),
// this->themeManager->ScrollbarArrow);
@@ -221,7 +223,7 @@ void Scrollbar::paintEvent(QPaintEvent *)
// draw highlights
auto snapshot = this->highlights.getSnapshot();
int snapshotLength = (int)snapshot.getLength();
size_t snapshotLength = snapshot.getLength();
if (snapshotLength == 0) {
return;
@@ -229,19 +231,33 @@ void Scrollbar::paintEvent(QPaintEvent *)
int w = this->width();
float y = 0;
float dY = (float)(this->height()) / (float)snapshotLength;
int highlightHeight = std::ceil(dY);
float dY = float(this->height()) / float(snapshotLength);
int highlightHeight = int(std::ceil(dY));
for (int i = 0; i < snapshotLength; i++) {
for (size_t i = 0; i < snapshotLength; i++) {
ScrollbarHighlight const &highlight = snapshot[i];
if (!highlight.isNull()) {
if (highlight.getStyle() == ScrollbarHighlight::Default) {
painter.fillRect(w / 8 * 3, (int)y, w / 4, highlightHeight,
this->themeManager->tabs.selected.backgrounds.regular.color());
} else {
painter.fillRect(0, (int)y, w, 1,
this->themeManager->tabs.selected.backgrounds.regular.color());
QColor color = [&] {
switch (highlight.getColor()) {
case ScrollbarHighlight::Highlight:
return app->themes->scrollbars.highlights.highlight;
case ScrollbarHighlight::Subscription:
return app->themes->scrollbars.highlights.subscription;
}
return QColor();
}();
switch (highlight.getStyle()) {
case ScrollbarHighlight::Default: {
painter.fillRect(w / 8 * 3, int(y), w / 4, highlightHeight, color);
} break;
case ScrollbarHighlight::Line: {
painter.fillRect(0, int(y), w, 1, color);
} break;
case ScrollbarHighlight::None:;
}
}
@@ -251,7 +267,7 @@ void Scrollbar::paintEvent(QPaintEvent *)
void Scrollbar::resizeEvent(QResizeEvent *)
{
this->resize((int)(16 * this->getScale()), this->height());
this->resize(int(16 * this->getScale()), this->height());
}
void Scrollbar::mouseMoveEvent(QMouseEvent *event)
@@ -279,7 +295,7 @@ void Scrollbar::mouseMoveEvent(QMouseEvent *event)
} else if (this->mouseDownIndex == 2) {
int delta = event->pos().y() - this->lastMousePosition.y();
setDesiredValue(this->desiredValue + (qreal)delta / this->trackHeight * this->maximum);
setDesiredValue(this->desiredValue + qreal(delta) / this->trackHeight * this->maximum);
}
this->lastMousePosition = event->pos();
@@ -342,8 +358,8 @@ void Scrollbar::updateScroll()
this->trackHeight = height() - this->buttonHeight - this->buttonHeight - MIN_THUMB_HEIGHT - 1;
this->thumbRect = QRect(
0, (int)(this->currentValue / this->maximum * this->trackHeight) + 1 + this->buttonHeight,
width(), (int)(this->largeChange / this->maximum * this->trackHeight) + MIN_THUMB_HEIGHT);
0, int(this->currentValue / this->maximum * this->trackHeight) + 1 + this->buttonHeight,
width(), int(this->largeChange / this->maximum * this->trackHeight) + MIN_THUMB_HEIGHT);
update();
}
+1 -1
View File
@@ -21,7 +21,7 @@ class Scrollbar : public BaseWidget
Q_OBJECT
public:
Scrollbar(ChannelView *parent = 0);
Scrollbar(ChannelView *parent = nullptr);
void addHighlight(ScrollbarHighlight highlight);
void addHighlightsAtStart(const std::vector<ScrollbarHighlight> &highlights);
+6 -6
View File
@@ -17,7 +17,7 @@ namespace chatterino {
namespace widgets {
SelectChannelDialog::SelectChannelDialog()
: BaseWindow((QWidget *)nullptr, true)
: BaseWindow((QWidget *)nullptr, BaseWindow::EnableCustomFrame)
, selectedChannel(Channel::getEmpty())
{
this->setWindowTitle("Select a channel to join");
@@ -26,7 +26,7 @@ SelectChannelDialog::SelectChannelDialog()
util::LayoutCreator<QWidget> layoutWidget(this->getLayoutContainer());
auto layout = layoutWidget.setLayoutType<QVBoxLayout>().withoutMargin();
auto notebook = layout.emplace<Notebook2>(this).assign(&this->ui.notebook);
auto notebook = layout.emplace<Notebook>(this).assign(&this->ui.notebook);
// twitch
{
@@ -35,7 +35,7 @@ SelectChannelDialog::SelectChannelDialog()
// channel_btn
auto channel_btn = vbox.emplace<QRadioButton>("Channel").assign(&this->ui.twitch.channel);
auto channel_lbl = vbox.emplace<QLabel>("Join a twitch channel by it's name.").hidden();
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);
@@ -93,14 +93,14 @@ SelectChannelDialog::SelectChannelDialog()
vbox->addStretch(1);
// tabbing order
QWidget::setTabOrder(*watching_btn, *channel_btn);
QWidget::setTabOrder(*channel_btn, *whispers_btn);
QWidget::setTabOrder(*whispers_btn, *mentions_btn);
QWidget::setTabOrder(*mentions_btn, *watching_btn);
QWidget::setTabOrder(*watching_btn, *channel_btn);
// tab
NotebookTab2 *tab = notebook->addPage(obj.getElement());
tab->setTitle("Twitch");
NotebookTab *tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Twitch");
}
// irc
+1 -1
View File
@@ -38,7 +38,7 @@ private:
};
struct {
Notebook2 *notebook;
Notebook *notebook;
struct {
QRadioButton *channel;
QLineEdit *channelName;
+70 -17
View File
@@ -1,8 +1,12 @@
#include "aboutpage.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/signallabel.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QTextEdit>
#include <QVBoxLayout>
#define PIXMAP_WIDTH 500
@@ -32,29 +36,78 @@ AboutPage::AboutPage()
// palette.setColor(QPalette::Link, "#a5cdff");
// palette.setColor(QPalette::LinkVisited, "#a5cdff");
auto created = layout.emplace<QLabel>();
created->setText(
"Twitch Chat Client created by <a href=\"https://github.com/fourtf\">fourtf</a>");
created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
// created->setPalette(palette);
/*auto xd = layout.emplace<QGroupBox>("Created by...");
{
auto created = xd.emplace<QLabel>();
{
created->setText("Created by <a href=\"https://github.com/fourtf\">fourtf</a><br>"
"with big help from pajlada.");
created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
// created->setPalette(palette);
}
auto github = layout.emplace<QLabel>();
github->setText(
"<a href=\"https://github.com/fourtf/chatterino2\">Chatterino on Github</a>");
github->setTextFormat(Qt::RichText);
github->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
github->setOpenExternalLinks(true);
// github->setPalette(palette);
// auto github = xd.emplace<QLabel>();
// {
// github->setText(
// "<a href=\"https://github.com/fourtf/chatterino2\">Chatterino on
// Github</a>");
// github->setTextFormat(Qt::RichText);
// github->setTextInteractionFlags(Qt::TextBrowserInteraction |
// Qt::LinksAccessibleByKeyboard |
// Qt::LinksAccessibleByKeyboard);
// github->setOpenExternalLinks(true);
// // github->setPalette(palette);
// }
}*/
auto licenses = layout.emplace<QGroupBox>("Open source software used...");
{
auto form = licenses.emplace<QFormLayout>();
addLicense(*form, "Qt Framework", "https://www.qt.io", ":/licenses/qt_lgpl-3.0.txt");
addLicense(*form, "Boost", "https://www.boost.org/", ":/licenses/boost_boost.txt");
addLicense(*form, "Fmt", "http://fmtlib.net/", ":/licenses/fmt_bsd2.txt");
addLicense(*form, "LibCommuni", "https://github.com/communi/libcommuni",
":/licenses/libcommuni_BSD3.txt");
addLicense(*form, "OpenSSL", "https://www.openssl.org/", ":/licenses/openssl.txt");
addLicense(*form, "RapidJson", "http://rapidjson.org/", ":/licenses/rapidjson.txt");
addLicense(*form, "Pajlada/Settings", "https://github.com/pajlada/settings",
":/licenses/pajlada_settings.txt");
addLicense(*form, "Pajlada/Signals", "https://github.com/pajlada/signals",
":/licenses/pajlada_signals.txt");
addLicense(*form, "Websocketpp", "https://www.zaphoyd.com/websocketpp/",
":/licenses/websocketpp.txt");
}
}
layout->addStretch(1);
}
void AboutPage::addLicense(QFormLayout *form, const QString &name, const QString &website,
const QString &licenseLink)
{
auto *a = new QLabel("<a href=\"" + website + "\">" + name + "</a>");
a->setOpenExternalLinks(true);
auto *b = new SignalLabel();
b->setText("<a href=\"" + licenseLink + "\">show license</a>");
b->setCursor(Qt::PointingHandCursor);
QObject::connect(b, &SignalLabel::mouseUp, [licenseLink] {
auto *edit = new QTextEdit;
QFile file(licenseLink);
file.open(QIODevice::ReadOnly);
edit->setText(file.readAll());
edit->setReadOnly(true);
edit->show();
});
form->addRow(a, b);
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino
+4
View File
@@ -3,6 +3,7 @@
#include "widgets/settingspages/settingspage.hpp"
class QLabel;
class QFormLayout;
namespace chatterino {
namespace widgets {
@@ -15,6 +16,9 @@ public:
private:
QLabel *logo;
void addLicense(QFormLayout *form, const QString &name, const QString &website,
const QString &licenseLink);
};
} // namespace settingspages
+40 -18
View File
@@ -2,11 +2,17 @@
#include "application.hpp"
#include "const.hpp"
#include "singletons/accountmanager.hpp"
#include "controllers/accounts/accountcontroller.hpp"
#include "controllers/accounts/accountmodel.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/editablemodelview.hpp"
#include "widgets/logindialog.hpp"
#include <algorithm>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QTableView>
#include <QVBoxLayout>
namespace chatterino {
@@ -16,32 +22,48 @@ namespace settingspages {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/images/accounts.svg")
{
auto *app = getApp();
util::LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
auto buttons = layout.emplace<QDialogButtonBox>();
{
this->addButton = buttons->addButton("Add", QDialogButtonBox::YesRole);
this->removeButton = buttons->addButton("Remove", QDialogButtonBox::NoRole);
}
layout.emplace<AccountSwitchWidget>(this).assign(&this->accSwitchWidget);
helper::EditableModelView *view =
*layout.emplace<helper::EditableModelView>(app->accounts->createModel(nullptr));
// ----
QObject::connect(this->addButton, &QPushButton::clicked, []() {
view->getTableView()->horizontalHeader()->setVisible(false);
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
static auto loginWidget = new LoginWidget();
loginWidget->show();
});
QObject::connect(this->removeButton, &QPushButton::clicked, [this] {
auto selectedUser = this->accSwitchWidget->currentItem()->text();
if (selectedUser == ANONYMOUS_USERNAME_LABEL) {
// Do nothing
return;
}
view->getTableView()->setStyleSheet("background: #333");
getApp()->accounts->Twitch.removeUser(selectedUser);
});
// auto buttons = layout.emplace<QDialogButtonBox>();
// {
// this->addButton = buttons->addButton("Add", QDialogButtonBox::YesRole);
// this->removeButton = buttons->addButton("Remove", QDialogButtonBox::NoRole);
// }
// layout.emplace<AccountSwitchWidget>(this).assign(&this->accSwitchWidget);
// ----
// QObject::connect(this->addButton, &QPushButton::clicked, []() {
// static auto loginWidget = new LoginWidget();
// loginWidget->show();
// });
// QObject::connect(this->removeButton, &QPushButton::clicked, [this] {
// auto selectedUser = this->accSwitchWidget->currentItem()->text();
// if (selectedUser == ANONYMOUS_USERNAME_LABEL) {
// // Do nothing
// return;
// }
// getApp()->accounts->Twitch.removeUser(selectedUser);
// });
}
} // namespace settingspages
+66 -34
View File
@@ -50,20 +50,24 @@ AppearancePage::AppearancePage()
{
auto form = application.emplace<QFormLayout>();
// clang-format off
form->addRow("Theme:", this->createComboBox({THEME_ITEMS}, app->themes->themeName));
form->addRow("Theme color:", this->createThemeColorChanger());
form->addRow("Font:", this->createFontChanger());
auto *theme = this->createComboBox({THEME_ITEMS}, app->themes->themeName);
QObject::connect(theme, &QComboBox::currentTextChanged,
[](const QString &) { getApp()->windows->forceLayoutChannelViews(); });
form->addRow("Tabs:", this->createCheckBox(TAB_X, app->settings->showTabCloseButton));
#ifndef USEWINSDK
form->addRow("", this->createCheckBox(TAB_PREF, app->settings->hidePreferencesButton));
form->addRow("", this->createCheckBox(TAB_USER, app->settings->hideUserButton));
#endif
form->addRow("Theme:", theme);
// form->addRow("Theme color:", this->createThemeColorChanger());
form->addRow("Font:", this->createFontChanger());
form->addRow("Scrolling:", this->createCheckBox(SCROLL_SMOOTH, app->settings->enableSmoothScrolling));
form->addRow("", this->createCheckBox(SCROLL_NEWMSG, app->settings->enableSmoothScrollingNewMessages));
// clang-format on
form->addRow("Tabs:", this->createCheckBox(TAB_X, app->settings->showTabCloseButton));
#ifndef USEWINSDK
form->addRow("", this->createCheckBox(TAB_PREF, app->settings->hidePreferencesButton));
form->addRow("", this->createCheckBox(TAB_USER, app->settings->hideUserButton));
#endif
form->addRow("Scrolling:",
this->createCheckBox(SCROLL_SMOOTH, app->settings->enableSmoothScrolling));
form->addRow("", this->createCheckBox(SCROLL_NEWMSG,
app->settings->enableSmoothScrollingNewMessages));
}
auto messages = layout.emplace<QGroupBox>("Messages").emplace<QVBoxLayout>();
@@ -77,22 +81,25 @@ AppearancePage::AppearancePage()
}
messages.append(this->createCheckBox("Show badges", app->settings->showBadges));
{
auto checkbox =
this->createCheckBox("Seperate messages", app->settings->seperateMessages);
messages.append(checkbox);
QObject::connect(checkbox, &QCheckBox::toggled,
[](bool) { getApp()->windows->repaintVisibleChatWidgets(); });
}
{
auto checkbox = this->createCheckBox("Alternate message background color",
app->settings->alternateMessageBackground);
messages.append(checkbox);
QObject::connect(checkbox, &QCheckBox::toggled, [](bool) {
getApp()->fonts->incGeneration(); // fourtf: hacky solution
getApp()->windows->repaintVisibleChatWidgets();
auto *combo = new QComboBox(this);
combo->addItems({"Never", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"13", "14", "15"});
QObject::connect(combo, &QComboBox::currentTextChanged, [](const QString &str) {
getApp()->settings->collpseMessagesMinLines = str.toInt();
});
auto hbox = messages.emplace<QHBoxLayout>().withoutMargin();
hbox.emplace<QLabel>("Collapse messages longer than");
hbox.append(combo);
hbox.emplace<QLabel>("lines");
}
messages.append(this->createCheckBox("Seperate messages", app->settings->seperateMessages));
messages.append(this->createCheckBox("Alternate message background color",
app->settings->alternateMessageBackground));
messages.append(this->createCheckBox("Show message length while typing",
app->settings->showMessageLength));
@@ -101,6 +108,7 @@ AppearancePage::AppearancePage()
auto emotes = layout.emplace<QGroupBox>("Emotes").setLayoutType<QVBoxLayout>();
{
/*
emotes.append(
this->createCheckBox("Enable Twitch emotes", app->settings->enableTwitchEmotes));
emotes.append(this->createCheckBox("Enable BetterTTV emotes for Twitch",
@@ -108,12 +116,36 @@ AppearancePage::AppearancePage()
emotes.append(this->createCheckBox("Enable FrankerFaceZ emotes for Twitch",
app->settings->enableFfzEmotes));
emotes.append(this->createCheckBox("Enable emojis", app->settings->enableEmojis));
*/
emotes.append(
this->createCheckBox("Enable animations", app->settings->enableGifAnimations));
auto scaleBox = emotes.emplace<QHBoxLayout>();
{
scaleBox.emplace<QLabel>("Emote scale:");
auto emoteScale = scaleBox.emplace<QSlider>(Qt::Horizontal);
emoteScale->setMinimum(5);
emoteScale->setMaximum(50);
auto scaleLabel = scaleBox.emplace<QLabel>("1.0");
scaleLabel->setFixedWidth(100);
QObject::connect(*emoteScale, &QSlider::valueChanged, [scaleLabel](int value) mutable {
float f = (float)value / 10.f;
scaleLabel->setText(QString::number(f));
getApp()->settings->emoteScale.setValue(f);
});
emoteScale->setValue(std::max<int>(
5, std::min<int>(50, (int)(app->settings->emoteScale.getValue() * 10.f))));
scaleLabel->setText(QString::number(app->settings->emoteScale.getValue()));
}
}
layout->addStretch(1);
}
} // namespace settingspages
QLayout *AppearancePage::createThemeColorChanger()
{
@@ -125,7 +157,7 @@ QLayout *AppearancePage::createThemeColorChanger()
// SLIDER
QSlider *slider = new QSlider(Qt::Horizontal);
layout->addWidget(slider);
slider->setValue(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 100);
slider->setValue(int(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 100));
// BUTTON
QPushButton *button = new QPushButton;
@@ -165,12 +197,12 @@ QLayout *AppearancePage::createFontChanger()
layout->addWidget(label);
auto updateFontFamilyLabel = [=](auto) {
label->setText(QString::fromStdString(app->fonts->currentFontFamily.getValue()) + ", " +
QString::number(app->fonts->currentFontSize) + "pt");
label->setText(QString::fromStdString(app->fonts->chatFontFamily.getValue()) + ", " +
QString::number(app->fonts->chatFontSize) + "pt");
};
app->fonts->currentFontFamily.connectSimple(updateFontFamilyLabel, this->managedConnections);
app->fonts->currentFontSize.connectSimple(updateFontFamilyLabel, this->managedConnections);
app->fonts->chatFontFamily.connectSimple(updateFontFamilyLabel, this->managedConnections);
app->fonts->chatFontSize.connectSimple(updateFontFamilyLabel, this->managedConnections);
// BUTTON
QPushButton *button = new QPushButton("Select");
@@ -178,11 +210,11 @@ QLayout *AppearancePage::createFontChanger()
button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
QObject::connect(button, &QPushButton::clicked, [=]() {
QFontDialog dialog(app->fonts->getFont(singletons::FontManager::Medium, 1.));
QFontDialog dialog(app->fonts->getFont(singletons::FontManager::ChatMedium, 1.));
dialog.connect(&dialog, &QFontDialog::fontSelected, [=](const QFont &font) {
app->fonts->currentFontFamily = font.family().toStdString();
app->fonts->currentFontSize = font.pointSize();
app->fonts->chatFontFamily = font.family().toStdString();
app->fonts->chatFontSize = font.pointSize();
});
dialog.show();
@@ -8,7 +8,11 @@
#include <QLabel>
#include <QVBoxLayout>
#ifdef USEWINSDK
#define WINDOW_TOPMOST "Window always on top"
#else
#define WINDOW_TOPMOST "Window always on top (requires restart)"
#endif
#define INPUT_EMPTY "Hide input box when empty"
#define PAUSE_HOVERING "When hovering"
@@ -30,6 +34,12 @@ BehaviourPage::BehaviourPage()
{
form->addRow("Window:", this->createCheckBox(WINDOW_TOPMOST, app->settings->windowTopMost));
form->addRow("Messages:", this->createCheckBox(INPUT_EMPTY, app->settings->hideEmptyInput));
form->addRow(
"", this->createCheckBox("Show which users joined the channel (up to 1000 chatters)",
app->settings->showJoins));
form->addRow(
"", this->createCheckBox("Show which users parted the channel (up to 1000 chatters)",
app->settings->showParts));
form->addRow("Pause chat:",
this->createCheckBox(PAUSE_HOVERING, app->settings->pauseChatHover));
@@ -1,9 +1,9 @@
#include "ignoreuserspage.hpp"
#include "application.hpp"
#include "controllers/accounts/accountcontroller.hpp"
#include "controllers/ignores/ignorecontroller.hpp"
#include "controllers/ignores/ignoremodel.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/editablemodelview.hpp"
@@ -34,7 +34,7 @@ IgnoreUsersPage::IgnoreUsersPage()
// auto group = layout.emplace<QGroupBox>("Ignored users").setLayoutType<QVBoxLayout>();
auto tabs = layout.emplace<QTabWidget>();
tabs->setStyleSheet("color: #000");
// tabs->setStyleSheet("color: #000");
// users
auto users = tabs.appendTab(new QVBoxLayout, "Users");
@@ -91,7 +91,7 @@ void IgnoreUsersPage::onShow()
{
auto app = getApp();
auto user = app->accounts->Twitch.getCurrent();
auto user = app->accounts->twitch.getCurrent();
if (user->isAnon()) {
return;
+37 -36
View File
@@ -1,48 +1,49 @@
#include "logspage.hpp"
//#include "logspage.hpp"
#include "application.hpp"
#include "singletons/pathmanager.hpp"
//#include "application.hpp"
//#include "singletons/pathmanager.hpp"
#include <QFormLayout>
#include <QVBoxLayout>
//#include <QFormLayout>
//#include <QVBoxLayout>
#include "util/layoutcreator.hpp"
//#include "util/layoutcreator.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
// namespace chatterino {
// namespace widgets {
// namespace settingspages {
inline QString CreateLink(const QString &url, bool file = false)
{
if (file) {
return QString("<a href=\"file:///" + url + "\"><span style=\"color: white;\">" + url +
"</span></a>");
}
// inline QString CreateLink(const QString &url, bool file = false)
//{
// if (file) {
// return QString("<a href=\"file:///" + url + "\"><span style=\"color: white;\">" + url +
// "</span></a>");
// }
return QString("<a href=\"" + url + "\"><span style=\"color: white;\">" + url + "</span></a>");
}
// return QString("<a href=\"" + url + "\"><span style=\"color: white;\">" + url +
// "</span></a>");
//}
LogsPage::LogsPage()
: SettingsPage("Logs", "")
{
auto app = getApp();
// LogsPage::LogsPage()
// : SettingsPage("Logs", "")
//{
// auto app = getApp();
util::LayoutCreator<LogsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
// util::LayoutCreator<LogsPage> layoutCreator(this);
// auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
auto logPath = app->paths->logsFolderPath;
// auto logPath = app->paths->logsFolderPath;
auto created = layout.emplace<QLabel>();
created->setText("Logs are saved to " + CreateLink(logPath, true));
created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
layout.append(this->createCheckBox("Enable logging", app->settings->enableLogging));
// auto created = layout.emplace<QLabel>();
// created->setText("Logs are saved to " + CreateLink(logPath, true));
// created->setTextFormat(Qt::RichText);
// created->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard |
// Qt::LinksAccessibleByKeyboard);
// created->setOpenExternalLinks(true);
// layout.append(this->createCheckBox("Enable logging", app->settings->enableLogging));
layout->addStretch(1);
}
// layout->addStretch(1);
//}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino
//} // namespace settingspages
//} // namespace widgets
//} // namespace chatterino
+13 -13
View File
@@ -1,17 +1,17 @@
#pragma once
//#pragma once
#include "widgets/settingspages/settingspage.hpp"
//#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
// namespace chatterino {
// namespace widgets {
// namespace settingspages {
class LogsPage : public SettingsPage
{
public:
LogsPage();
};
// class LogsPage : public SettingsPage
//{
// public:
// LogsPage();
//};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino
//} // namespace settingspages
//} // namespace widgets
//} // namespace chatterino
+38 -11
View File
@@ -2,14 +2,20 @@
#include "application.hpp"
#include "singletons/loggingmanager.hpp"
#include "controllers/taggedusers/taggeduserscontroller.hpp"
#include "controllers/taggedusers/taggedusersmodel.hpp"
#include "singletons/pathmanager.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/editablemodelview.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QTextEdit>
#include <QVBoxLayout>
@@ -33,11 +39,13 @@ ModerationPage::ModerationPage()
auto app = getApp();
util::LayoutCreator<ModerationPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
auto tabs = layoutCreator.emplace<QTabWidget>();
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
{
// Logs (copied from LoggingMananger)
auto created = layout.emplace<QLabel>();
auto created = logs.emplace<QLabel>();
if (app->settings->logPath == "") {
created->setText("Logs are saved to " +
CreateLink(QCoreApplication::applicationDirPath(), true));
@@ -49,9 +57,10 @@ ModerationPage::ModerationPage()
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
layout.append(this->createCheckBox("Enable logging", app->settings->enableLogging));
logs.append(this->createCheckBox("Enable logging", app->settings->enableLogging));
layout->addStretch(1);
<
logs->addStretch(1);
auto selectDir = layout.emplace<QPushButton>("Set custom logpath");
@@ -90,21 +99,25 @@ ModerationPage::ModerationPage()
});
// Logs end
}
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation mode");
{
// clang-format off
auto label = layout.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
auto label = modMode.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
label->setWordWrap(true);
label->setStyleSheet("color: #bbb");
// clang-format on
auto form = layout.emplace<QFormLayout>();
{
form->addRow("Action on timed out messages (unimplemented):",
this->createComboBox({"Disable", "Hide"}, app->settings->timeoutAction));
}
// auto form = modMode.emplace<QFormLayout>();
// {
// form->addRow("Action on timed out messages (unimplemented):",
// this->createComboBox({"Disable", "Hide"},
// app->settings->timeoutAction));
// }
auto modButtons =
layout.emplace<QGroupBox>("Custom moderator buttons").setLayoutType<QVBoxLayout>();
modMode.emplace<QGroupBox>("Custom moderator buttons").setLayoutType<QVBoxLayout>();
{
auto label2 =
modButtons.emplace<QLabel>("One action per line. {user} will be replaced with the "
@@ -122,6 +135,20 @@ ModerationPage::ModerationPage()
app->settings->moderationActions = text->toPlainText();
});
}
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{
helper::EditableModelView *view = *taggedUsers.emplace<helper::EditableModelView>(
app->taggedUsers->createModel(nullptr));
view->setTitles({"Name"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->taggedUsers->users.appendItem(
controllers::taggedusers::TaggedUser(ProviderId::Twitch, "example", "xD"));
});
}*/
}
// ---- misc
+67 -83
View File
@@ -1,6 +1,7 @@
#include "widgets/split.hpp"
#include "application.hpp"
#include "common.hpp"
#include "providers/twitch/emotevalue.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
@@ -39,8 +40,8 @@ using namespace chatterino::messages;
namespace chatterino {
namespace widgets {
pajlada::Signals::Signal<bool> Split::altPressedStatusChanged;
bool Split::altPressesStatus = false;
pajlada::Signals::Signal<Qt::KeyboardModifiers> Split::modifierStatusChanged;
Qt::KeyboardModifiers Split::modifierStatus = Qt::NoModifier;
Split::Split(SplitContainer *parent)
: Split((QWidget *)parent)
@@ -94,7 +95,10 @@ Split::Split(QWidget *parent)
this->input.ui.textEdit->installEventFilter(parent);
this->view.mouseDown.connect([this](QMouseEvent *) { this->giveFocus(Qt::MouseFocusReason); });
this->view.mouseDown.connect([this](QMouseEvent *) {
//
this->giveFocus(Qt::MouseFocusReason);
});
this->view.selectionChanged.connect([this]() {
if (view.hasSelection()) {
this->input.clearSelection();
@@ -128,27 +132,46 @@ Split::Split(QWidget *parent)
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->managedConnect(altPressedStatusChanged, [this](bool status) {
// if (status && this->isMouseOver) {
// this->overlay->show();
// } else {
// this->overlay->hide();
// }
this->managedConnect(modifierStatusChanged, [this](Qt::KeyboardModifiers status) {
if ((status == showSplitOverlayModifiers /*|| status == showAddSplitRegions*/) &&
this->isMouseOver) {
this->overlay->show();
} else {
this->overlay->hide();
}
});
this->input.ui.textEdit->focused.connect([this] { this->focused.invoke(); });
}
Split::~Split()
{
this->usermodeChangedConnection.disconnect();
this->roomModeChangedConnection.disconnect();
this->channelIDChangedConnection.disconnect();
this->indirectChannelChangedConnection.disconnect();
}
ChannelView &Split::getChannelView()
{
return this->view;
}
SplitContainer *Split::getContainer()
{
return this->container;
}
bool Split::isInContainer() const
{
return this->container != nullptr;
}
void Split::setContainer(SplitContainer *_container)
{
this->container = _container;
}
IndirectChannel Split::getIndirectChannel()
{
return this->channel;
@@ -166,6 +189,7 @@ void Split::setChannel(IndirectChannel newChannel)
this->view.setChannel(newChannel.get());
this->usermodeChangedConnection.disconnect();
this->roomModeChangedConnection.disconnect();
this->indirectChannelChangedConnection.disconnect();
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(newChannel.get().get());
@@ -173,6 +197,9 @@ void Split::setChannel(IndirectChannel newChannel)
if (tc != nullptr) {
this->usermodeChangedConnection =
tc->userStateChanged.connect([this] { this->header.updateModerationModeIcon(); });
this->roomModeChangedConnection =
tc->roomModesChanged.connect([this] { this->header.updateModes(); });
}
this->indirectChannelChangedConnection = newChannel.getChannelChanged().connect([this] { //
@@ -181,34 +208,11 @@ void Split::setChannel(IndirectChannel newChannel)
this->header.updateModerationModeIcon();
this->header.updateChannelText();
this->header.updateModes();
this->channelChanged.invoke();
}
void Split::setFlexSizeX(double x)
{
// this->flexSizeX = x;
// this->parentPage->updateFlexValues();
}
double Split::getFlexSizeX()
{
// return this->flexSizeX;
return 1;
}
void Split::setFlexSizeY(double y)
{
// this->flexSizeY = y;
// this->parentPage.updateFlexValues();
}
double Split::getFlexSizeY()
{
// return this->flexSizeY;
return 1;
}
void Split::setModerationMode(bool value)
{
if (value != this->moderationMode) {
@@ -236,7 +240,7 @@ void Split::showChangeChannelPopup(const char *dialogTitle, bool empty,
if (dialog->hasSeletedChannel()) {
this->setChannel(dialog->getSelectedChannel());
if (this->isInContainer()) {
this->container->refreshTitle();
this->container->refreshTabTitle();
}
}
@@ -279,26 +283,19 @@ void Split::paintEvent(QPaintEvent *)
void Split::mouseMoveEvent(QMouseEvent *event)
{
this->handleModifiers(event, event->modifiers());
}
void Split::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() == Qt::LeftButton && event->modifiers() & Qt::AltModifier) {
this->drag();
}
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
}
void Split::keyPressEvent(QKeyEvent *event)
{
this->view.unsetCursor();
this->handleModifiers(event, event->modifiers());
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
}
void Split::keyReleaseEvent(QKeyEvent *event)
{
this->view.unsetCursor();
this->handleModifiers(event, event->modifiers());
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
}
void Split::resizeEvent(QResizeEvent *event)
@@ -311,30 +308,37 @@ void Split::resizeEvent(QResizeEvent *event)
void Split::enterEvent(QEvent *event)
{
this->isMouseOver = true;
if (altPressesStatus) {
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
if (modifierStatus == showSplitOverlayModifiers /*|| modifierStatus == showAddSplitRegions*/) {
this->overlay->show();
}
if (this->container != nullptr) {
this->container->resetMouseStatus();
}
}
void Split::leaveEvent(QEvent *event)
{
this->isMouseOver = false;
this->overlay->hide();
this->handleModifiers(QGuiApplication::queryKeyboardModifiers());
}
void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers)
void Split::focusInEvent(QFocusEvent *event)
{
if (modifiers == Qt::AltModifier) {
if (!altPressesStatus) {
altPressesStatus = true;
altPressedStatusChanged.invoke(true);
}
} else {
if (altPressesStatus) {
altPressesStatus = false;
altPressedStatusChanged.invoke(false);
}
this->setCursor(Qt::ArrowCursor);
this->giveFocus(event->reason());
}
void Split::handleModifiers(Qt::KeyboardModifiers modifiers)
{
if (modifierStatus != modifiers) {
modifierStatus = modifiers;
modifierStatusChanged.invoke(modifiers);
}
}
@@ -342,15 +346,14 @@ void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers)
void Split::doAddSplit()
{
if (this->container) {
this->container->addChat(true);
this->container->appendNewSplit(true);
}
}
void Split::doCloseSplit()
{
if (this->container) {
this->container->removeFromLayout(this);
deleteLater();
this->container->deleteSplit(this);
}
}
@@ -373,7 +376,7 @@ void Split::doPopup()
new Split(static_cast<SplitContainer *>(window.getNotebook().getOrAddSelectedPage()));
split->setChannel(this->getIndirectChannel());
window.getNotebook().getOrAddSelectedPage()->addToLayout(split);
window.getNotebook().getOrAddSelectedPage()->appendSplit(split);
window.show();
}
@@ -536,26 +539,6 @@ static Iter select_randomly(Iter start, Iter end)
return select_randomly(start, end, gen);
}
void Split::doIncFlexX()
{
this->setFlexSizeX(this->getFlexSizeX() * 1.2);
}
void Split::doDecFlexX()
{
this->setFlexSizeX(this->getFlexSizeX() * (1 / 1.2));
}
void Split::doIncFlexY()
{
this->setFlexSizeY(this->getFlexSizeY() * 1.2);
}
void Split::doDecFlexY()
{
this->setFlexSizeY(this->getFlexSizeY() * (1 / 1.2));
}
void Split::drag()
{
auto container = dynamic_cast<SplitContainer *>(this->parentWidget());
@@ -564,7 +547,7 @@ void Split::drag()
SplitContainer::isDraggingSplit = true;
SplitContainer::draggingSplit = this;
auto originalLocation = container->removeFromLayout(this);
auto originalLocation = container->releaseSplit(this);
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
@@ -576,7 +559,8 @@ void Split::drag()
Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
if (dropAction == Qt::IgnoreAction) {
container->addToLayout(this, originalLocation);
container->insertSplit(this,
originalLocation); // SplitContainer::dragOriginalPosition);
}
SplitContainer::isDraggingSplit = false;
+13 -22
View File
@@ -39,9 +39,6 @@ class Split : public BaseWidget, pajlada::Signals::SignalHolder
Q_OBJECT
static pajlada::Signals::Signal<bool> altPressedStatusChanged;
static bool altPressesStatus;
public:
explicit Split(SplitContainer *parent);
explicit Split(QWidget *parent);
@@ -49,21 +46,15 @@ public:
~Split() override;
pajlada::Signals::NoArgSignal channelChanged;
pajlada::Signals::NoArgSignal focused;
ChannelView &getChannelView()
{
return this->view;
}
ChannelView &getChannelView();
SplitContainer *getContainer();
IndirectChannel getIndirectChannel();
ChannelPtr getChannel();
void setChannel(IndirectChannel newChannel);
void setFlexSizeX(double x);
double getFlexSizeX();
void setFlexSizeY(double y);
double getFlexSizeY();
void setModerationMode(bool value);
bool getModerationMode() const;
@@ -79,15 +70,20 @@ public:
bool isInContainer() const;
void setContainer(SplitContainer *container);
static pajlada::Signals::Signal<Qt::KeyboardModifiers> modifierStatusChanged;
static Qt::KeyboardModifiers modifierStatus;
protected:
void paintEvent(QPaintEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
private:
SplitContainer *container;
@@ -99,22 +95,22 @@ private:
SplitInput input;
SplitOverlay *overlay;
double flexSizeX = 1;
double flexSizeY = 1;
bool moderationMode = false;
bool isMouseOver = false;
bool isDragging = false;
pajlada::Signals::Connection channelIDChangedConnection;
pajlada::Signals::Connection usermodeChangedConnection;
pajlada::Signals::Connection roomModeChangedConnection;
pajlada::Signals::Connection indirectChannelChangedConnection;
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user);
void channelNameUpdated(const QString &newChannelName);
void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);
void handleModifiers(Qt::KeyboardModifiers modifiers);
public slots:
// Add new split to the notebook page that this chat widget is in
@@ -151,11 +147,6 @@ public slots:
// Open viewer list of the channel
void doOpenViewerList();
void doIncFlexX();
void doDecFlexX();
void doIncFlexY();
void doDecFlexY();
};
} // namespace widgets
File diff suppressed because it is too large Load Diff
+186 -42
View File
@@ -12,53 +12,198 @@
#include <QVector>
#include <QWidget>
#include <algorithm>
#include <functional>
#include <vector>
#include <pajlada/signals/signal.hpp>
#include <pajlada/signals/signalholder.hpp>
class QJsonObject;
namespace chatterino {
namespace widgets {
class SplitContainer : public BaseWidget
//
// Note: This class is a spaghetti container. There is a lot of spaghetti code inside but it doesn't
// expose any of it publicly.
//
class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder
{
Q_OBJECT
public:
SplitContainer(Notebook *parent, NotebookTab *_tab);
struct Node;
std::pair<int, int> removeFromLayout(Split *widget);
void addToLayout(Split *widget, std::pair<int, int> position = std::pair<int, int>(-1, -1));
// fourtf: !!! preserve the order of left, up, right and down
enum Direction { Left, Above, Right, Below };
const std::vector<Split *> &getSplits() const;
std::vector<std::vector<Split *>> getColumns() const;
struct Position final {
private:
Position() = default;
Position(Node *_relativeNode, Direction _direcion)
: relativeNode(_relativeNode)
, direction(_direcion)
{
}
Node *relativeNode;
Direction direction;
friend struct Node;
friend class SplitContainer;
};
private:
struct DropRect final {
QRect rect;
Position position;
DropRect(const QRect &_rect, const Position &_position)
: rect(_rect)
, position(_position)
{
}
};
struct ResizeRect final {
QRect rect;
Node *node;
bool vertical;
ResizeRect(const QRect &_rect, Node *_node, bool _vertical)
: rect(_rect)
, node(_node)
, vertical(_vertical)
{
}
};
public:
struct Node final {
enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer };
Type getType();
Split *getSplit();
Node *getParent();
qreal getHorizontalFlex();
qreal getVerticalFlex();
const std::vector<std::unique_ptr<Node>> &getChildren();
private:
Type type;
Split *split;
Node *preferedFocusTarget;
Node *parent;
QRectF geometry;
qreal flexH = 1;
qreal flexV = 1;
std::vector<std::unique_ptr<Node>> children;
Node();
Node(Split *_split, Node *_parent);
bool isOrContainsNode(Node *_node);
Node *findNodeContainingSplit(Split *_split);
void insertSplitRelative(Split *_split, Direction _direction);
void nestSplitIntoCollection(Split *_split, Direction _direction);
void _insertNextToThis(Split *_split, Direction _direction);
void setSplit(Split *_split);
Position releaseSplit();
qreal getFlex(bool isVertical);
qreal getSize(bool isVertical);
qreal getChildrensTotalFlex(bool isVertical);
void layout(bool addSpacing, float _scale, std::vector<DropRect> &dropRects,
std::vector<ResizeRect> &resizeRects);
static Type toContainerType(Direction _dir);
friend class SplitContainer;
};
private:
class DropOverlay final : public QWidget
{
public:
DropOverlay(SplitContainer *_parent = nullptr);
void setRects(std::vector<SplitContainer::DropRect> _rects);
pajlada::Signals::NoArgSignal dragEnded;
protected:
void paintEvent(QPaintEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
std::vector<DropRect> rects;
QPoint mouseOverPoint;
SplitContainer *parent;
};
class ResizeHandle final : public QWidget
{
public:
SplitContainer *parent;
Node *node;
void setVertical(bool isVertical);
ResizeHandle(SplitContainer *_parent = nullptr);
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
friend class SplitContainer;
private:
bool vertical;
bool isMouseDown = false;
};
public:
SplitContainer(Notebook *parent);
void appendNewSplit(bool openChannelNameDialog);
void appendSplit(Split *split);
void insertSplit(Split *split, const Position &position);
void insertSplit(Split *split, Direction direction, Split *relativeTo);
void insertSplit(Split *split, Direction direction, Node *relativeTo = nullptr);
Position releaseSplit(Split *split);
Position deleteSplit(Split *split);
void selectNextSplit(Direction direction);
void decodeFromJson(QJsonObject &obj);
int getSplitCount();
const std::vector<Split *> getSplits() const;
void refreshTabTitle();
NotebookTab *getTab() const;
Node *getBaseNode();
void addChat(bool openChannelNameDialog = false);
void setTab(NotebookTab *tab);
void hideResizeHandles();
void resetMouseStatus();
static bool isDraggingSplit;
static Split *draggingSplit;
static std::pair<int, int> dropPosition;
int currentX = 0;
int currentY = 0;
std::map<int, int> lastRequestedY;
void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false);
void requestFocus(int x, int y);
void updateFlexValues();
int splitCount() const;
protected:
bool eventFilter(QObject *object, QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void showEvent(QShowEvent *event) override;
void enterEvent(QEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void leaveEvent(QEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
struct DropRegion {
@@ -72,31 +217,30 @@ private:
}
};
NotebookTab *tab;
void addSplit(Split *split);
struct {
QVBoxLayout parentLayout;
QHBoxLayout hbox;
} ui;
std::vector<Split *> splits;
std::vector<DropRect> dropRects;
std::vector<DropRegion> dropRegions;
NotebookPageDropPreview dropPreview;
DropOverlay overlay;
std::vector<std::unique_ptr<ResizeHandle>> resizeHandles;
QPoint mouseOverPoint;
void setPreviewRect(QPoint mousePos);
void layout();
std::pair<int, int> getChatPosition(const Split *chatWidget);
Node baseNode;
Split *selected;
void setSelected(Split *selected);
void selectSplitRecursive(Node *node, Direction direction);
void focusSplitRecursive(Node *node, Direction direction);
void setPreferedTargetRecursive(Node *node);
Split *createChatWidget();
NotebookTab *tab;
std::vector<Split *> splits;
public:
void refreshTitle();
bool isDragging = false;
void loadSplits();
void save();
void decodeNodeRecusively(QJsonObject &obj, Node *node);
};
} // namespace widgets
+30 -5
View File
@@ -9,11 +9,24 @@
#include <QStyle>
#include <QVBoxLayout>
#ifdef USEWINSDK
#include <Windows.h>
#endif
namespace chatterino {
namespace widgets {
TooltipWidget *TooltipWidget::getInstance()
{
static TooltipWidget *tooltipWidget = nullptr;
if (tooltipWidget == nullptr) {
tooltipWidget = new TooltipWidget();
}
return tooltipWidget;
}
TooltipWidget::TooltipWidget(BaseWidget *parent)
: BaseWindow(parent)
: BaseWindow(parent, BaseWindow::TopMost)
, displayText(new QLabel())
{
auto app = getApp();
@@ -24,9 +37,8 @@ TooltipWidget::TooltipWidget(BaseWidget *parent)
this->setStayInScreenRect(true);
this->setAttribute(Qt::WA_ShowWithoutActivating);
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint |
Qt::X11BypassWindowManagerHint | Qt::BypassWindowManagerHint |
Qt::SubWindow);
this->setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint |
Qt::BypassWindowManagerHint);
displayText->setAlignment(Qt::AlignHCenter);
displayText->setText("tooltip text");
@@ -43,6 +55,19 @@ 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::themeRefreshEvent()
{
this->setStyleSheet("color: #fff; background: #000");
}
void TooltipWidget::scaleChangedEvent(float)
{
this->updateFont();
@@ -53,7 +78,7 @@ void TooltipWidget::updateFont()
auto app = getApp();
this->setFont(
app->fonts->getFont(singletons::FontManager::Type::MediumSmall, this->getScale()));
app->fonts->getFont(singletons::FontManager::Type::ChatMediumSmall, this->getScale()));
}
void TooltipWidget::setText(QString text)
+7 -9
View File
@@ -14,23 +14,21 @@ class TooltipWidget : public BaseWindow
Q_OBJECT
public:
static TooltipWidget *getInstance();
TooltipWidget(BaseWidget *parent = nullptr);
~TooltipWidget();
virtual ~TooltipWidget() override;
void setText(QString text);
static TooltipWidget *getInstance()
{
static TooltipWidget *tooltipWidget = nullptr;
if (tooltipWidget == nullptr) {
tooltipWidget = new TooltipWidget();
}
return tooltipWidget;
}
#ifdef USEWINSDK
void raise();
#endif
protected:
void changeEvent(QEvent *) override;
void leaveEvent(QEvent *) override;
void themeRefreshEvent() override;
void scaleChangedEvent(float) override;
private:
+77 -25
View File
@@ -1,11 +1,13 @@
#include "widgets/window.hpp"
#include "application.hpp"
#include "singletons/accountmanager.hpp"
#include "controllers/accounts/accountcontroller.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "version.hpp"
#include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/helper/shortcut.hpp"
#include "widgets/notebook.hpp"
@@ -24,18 +26,28 @@ namespace chatterino {
namespace widgets {
Window::Window(WindowType _type)
: BaseWindow(nullptr, true)
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
, type(_type)
, dpi(this->getScale())
, notebook(this, !this->hasCustomWindowFrame())
, notebook(this)
{
auto app = getApp();
app->accounts->Twitch.currentUsername.connect([this](const std::string &newUsername, auto) {
if (newUsername.empty()) {
app->accounts->twitch.currentUserChanged.connect([this] {
if (this->userLabel == nullptr) {
return;
}
auto user = getApp()->accounts->twitch.getCurrent();
if (user->isAnon()) {
this->refreshWindowTitle("Not logged in");
this->userLabel->getLabel().setText("anonymous");
} else {
this->refreshWindowTitle(QString::fromStdString(newUsername));
this->refreshWindowTitle(user->getUserName());
this->userLabel->getLabel().setText(user->getUserName());
}
});
@@ -43,18 +55,17 @@ Window::Window(WindowType _type)
this->addTitleBarButton(TitleBarButton::Settings, [app] {
app->windows->showSettingsDialog(); //
});
auto user = this->addTitleBarLabel([app] {
app->windows->showAccountSelectPopup(QCursor::pos()); //
});
app->accounts->Twitch.currentUserChanged.connect(
[=] { user->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); });
this->userLabel = this->addTitleBarLabel([this, app] {
app->windows->showAccountSelectPopup(
this->userLabel->mapToGlobal(this->userLabel->rect().bottomLeft())); //
});
}
if (_type == Window::Main) {
this->resize((int)(600 * this->getScale()), (int)(500 * this->getScale()));
this->resize(int(600 * this->getScale()), int(500 * this->getScale()));
} else {
this->resize((int)(300 * this->getScale()), (int)(500 * this->getScale()));
this->resize(int(300 * this->getScale()), int(500 * this->getScale()));
}
QVBoxLayout *layout = new QVBoxLayout(this);
@@ -66,9 +77,13 @@ Window::Window(WindowType _type)
layout->setMargin(0);
/// Initialize program-wide hotkeys
// CTRL+P: Open Settings Dialog
// CTRL+P: Open settings dialog
CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); });
// CTRL+T: Create new split
CreateWindowShortcut(this, "CTRL+T",
[this] { this->notebook.getOrAddSelectedPage()->appendNewSplit(true); });
// CTRL+Number: Switch to n'th tab
CreateWindowShortcut(this, "CTRL+1", [this] { this->notebook.selectIndex(0); });
CreateWindowShortcut(this, "CTRL+2", [this] { this->notebook.selectIndex(1); });
@@ -81,12 +96,13 @@ Window::Window(WindowType _type)
CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); });
// CTRL+SHIFT+T: New tab
CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(true); });
CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addPage(true); });
// CTRL+SHIFT+W: Close current tab
CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); });
std::vector<QString> cheerMessages;
#ifdef QT_DEBUG
std::vector<QString> cheerMessages, subMessages;
// clang-format off
cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=2000;color=#B22222;display-name=arzenhuz;emotes=185989:33-37;id=1ae336ac-8e1a-4d6b-8b00-9fcee26e8337;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783470139;turbo=0;user-id=111553331;user-type= :arzenhuz!arzenhuz@arzenhuz.tmi.twitch.tv PRIVMSG #pajlada :pajacheer2000 Buy pizza for both pajaH)");
cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=37;color=#3FBF72;display-name=VADIKUS007;emotes=;id=eedd95fd-2a17-4da1-879c-a1e76ffce582;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783184352;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :cheer37)");
@@ -97,15 +113,45 @@ Window::Window(WindowType _type)
cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=1;color=#3FBF72;display-name=VADIKUS007;emotes=;id=c4c5061b-f5c6-464b-8bff-7f1ac816caa7;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782817171;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
cheerMessages.emplace_back(R"(@badges=;bits=1;color=#FF0000;display-name=?????;emotes=;id=979b6b4f-be9a-42fb-a54c-88fcb0aca18d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1515782819084;turbo=0;user-id=70656218;user-type= :stels_tv!stels_tv@stels_tv.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
cheerMessages.emplace_back(R"(@badges=subscriber/3,premium/1;bits=1;color=#FF0000;display-name=kalvarenga;emotes=;id=4744d6f0-de1d-475d-a3ff-38647113265a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782860740;turbo=0;user-id=108393131;user-type= :kalvarenga!kalvarenga@kalvarenga.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
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!)");
subMessages.emplace_back(R"(@badges=staff/1,premium/1;color=#0000FF;display-name=TWW2;emotes=;id=e9176cd8-5e22-4684-ad40-ce53c2561c5e;login=tww2;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=Mr_Woodchuck;msg-param-recipient-id=89614178;msg-param-recipient-name=mr_woodchuck;msg-param-sub-plan-name=House\sof\sNyoro~n;msg-param-sub-plan=1000;room-id=19571752;subscriber=0;system-msg=TWW2\sgifted\sa\sTier\s1\ssub\sto\sMr_Woodchuck!;tmi-sent-ts=1521159445153;turbo=0;user-id=13405587;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada)");
// hyperbolicxd gifted a sub to quote_if_nam
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#00FF7F;display-name=hyperbolicxd;emotes=;id=b20ef4fe-cba8-41d0-a371-6327651dc9cc;login=hyperbolicxd;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=quote_if_nam;msg-param-recipient-id=217259245;msg-param-recipient-user-name=quote_if_nam;msg-param-sender-count=1;msg-param-sub-plan-name=Channel\sSubscription\s(nymn_hs);msg-param-sub-plan=1000;room-id=62300805;subscriber=1;system-msg=hyperbolicxd\sgifted\sa\sTier\s1\ssub\sto\squote_if_nam!\sThis\sis\stheir\sfirst\sGift\sSub\sin\sthe\schannel!;tmi-sent-ts=1528190938558;turbo=0;user-id=111534250;user-type= :tmi.twitch.tv USERNOTICE #pajlada)");
// first time sub
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#0000FF;display-name=byebyeheart;emotes=;id=fe390424-ab89-4c33-bb5a-53c6e5214b9f;login=byebyeheart;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=byebyeheart\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190963670;turbo=0;user-id=131956000;user-type= :tmi.twitch.tv USERNOTICE #pajlada)");
// first time sub
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=vJoeyzz;emotes=;id=b2476df5-fffe-4338-837b-380c5dd90051;login=vjoeyzz;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=vJoeyzz\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190995089;turbo=0;user-id=78945903;user-type= :tmi.twitch.tv USERNOTICE #pajlada)");
// first time sub
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=Lennydog3;emotes=;id=44feb1eb-df60-45f6-904b-7bf0d5375a41;login=lennydog3;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=Lennydog3\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528191098733;turbo=0;user-id=175759335;user-type= :tmi.twitch.tv USERNOTICE #pajlada)");
// resub with message
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#1E90FF;display-name=OscarLord;emotes=;id=376529fd-31a8-4da9-9c0d-92a9470da2cd;login=oscarlord;mod=0;msg-id=resub;msg-param-months=2;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=1000;room-id=39298218;subscriber=1;system-msg=OscarLord\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sOscarLord\ssubscribed\sfor\s2\smonths\sin\sa\srow!;tmi-sent-ts=1528191154801;turbo=0;user-id=162607810;user-type= :tmi.twitch.tv USERNOTICE #pajlada :Hey dk love to watch your streams keep up the good work)");
// resub with message
subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=samewl;emotes=9:22-23;id=599fda87-ca1e-41f2-9af7-6a28208daf1c;login=samewl;mod=0;msg-id=resub;msg-param-months=5;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol);msg-param-sub-plan=Prime;room-id=22484632;subscriber=1;system-msg=samewl\sjust\ssubscribed\swith\sTwitch\sPrime.\ssamewl\ssubscribed\sfor\s5\smonths\sin\sa\srow!;tmi-sent-ts=1528191317948;turbo=0;user-id=70273207;user-type= :tmi.twitch.tv USERNOTICE #pajlada :lot of love sebastian <3)");
// resub without message
subMessages.emplace_back(R"(@badges=subscriber/12;color=#CC00C2;display-name=cspice;emotes=;id=6fc4c3e0-ca61-454a-84b8-5669dee69fc9;login=cspice;mod=0;msg-id=resub;msg-param-months=12;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol):\s$9.99\sSub;msg-param-sub-plan=2000;room-id=22484632;subscriber=1;system-msg=cspice\sjust\ssubscribed\swith\sa\sTier\s2\ssub.\scspice\ssubscribed\sfor\s12\smonths\sin\sa\srow!;tmi-sent-ts=1528192510808;turbo=0;user-id=47894662;user-type= :tmi.twitch.tv USERNOTICE #pajlada)");
// clang-format on
// CreateWindowShortcut(this, "F5", [cheerMessages] {
// auto &ircManager = singletons::IrcManager::getInstance();
// static int index = 0;
// ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]);
// });
CreateWindowShortcut(this, "F5", [=] {
const auto &messages = subMessages;
static int index = 0;
auto app = getApp();
const auto &msg = messages[index++ % messages.size()];
app->twitch.server->addFakeMessage(msg);
});
#endif
this->setWindowTitle("Chatterino 2 Development Build");
this->refreshWindowTitle("");
this->notebook.setAllowUserTabManagement(true);
this->notebook.setShowAddButton(true);
}
Window::WindowType Window::getType()
@@ -128,14 +174,14 @@ void Window::repaintVisibleChatWidgets(Channel *channel)
}
}
Notebook &Window::getNotebook()
SplitNotebook &Window::getNotebook()
{
return this->notebook;
}
void Window::refreshWindowTitle(const QString &username)
{
this->setWindowTitle(username + " - Chatterino for Twitch");
this->setWindowTitle(username + " - Chatterino Beta " CHATTERINO_VERSION);
}
bool Window::event(QEvent *event)
@@ -154,12 +200,18 @@ bool Window::event(QEvent *event)
split->updateLastReadMessage();
}
}
if (SplitContainer *container = dynamic_cast<SplitContainer *>(page)) {
container->hideResizeHandles();
}
} break;
default:;
};
return BaseWindow::event(event);
}
void Window::closeEvent(QCloseEvent *event)
void Window::closeEvent(QCloseEvent *)
{
if (this->type == Window::Main) {
auto app = getApp();
+4 -2
View File
@@ -29,7 +29,7 @@ public:
void repaintVisibleChatWidgets(Channel *channel = nullptr);
Notebook &getNotebook();
SplitNotebook &getNotebook();
void refreshWindowTitle(const QString &username);
@@ -42,12 +42,14 @@ protected:
bool event(QEvent *event) override;
private:
RippleEffectLabel *userLabel = nullptr;
WindowType type;
float dpi;
void loadGeometry();
Notebook notebook;
SplitNotebook notebook;
friend class Notebook;