Normalize line endings in already existing files

This commit is contained in:
Leon Richardt
2019-09-08 22:27:57 +02:00
parent 8064f8a49e
commit b06eb9df83
157 changed files with 15175 additions and 15175 deletions
+294 -294
View File
@@ -1,294 +1,294 @@
#include "AttachedWindow.hpp"
#include "Application.hpp"
#include "util/DebugCount.hpp"
#include "widgets/splits/Split.hpp"
#include <QTimer>
#include <QVBoxLayout>
#ifdef USEWINSDK
# include "util/WindowsHelper.hpp"
# include "Windows.h"
// don't even think about reordering these
# include "Psapi.h"
# pragma comment(lib, "Dwmapi.lib")
#endif
namespace chatterino {
#ifdef USEWINSDK
static thread_local std::vector<HWND> taskbarHwnds;
BOOL CALLBACK enumWindows(HWND hwnd, LPARAM)
{
constexpr int length = 16;
auto className = std::make_unique<WCHAR[]>(length);
GetClassName(hwnd, className.get(), length);
// qDebug() << QString::fromWCharArray(className.get(), length);
if (lstrcmp(className.get(), L"Shell_TrayWnd") == 0 ||
lstrcmp(className.get(), L"Shell_Secondary") == 0)
{
taskbarHwnds.push_back(hwnd);
}
return true;
}
#endif
AttachedWindow::AttachedWindow(void *_target, int _yOffset)
: QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window)
, target_(_target)
, yOffset_(_yOffset)
{
QLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
this->setLayout(layout);
auto *split = new Split(this);
this->ui_.split = split;
split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
layout->addWidget(split);
DebugCount::increase("attached window");
}
AttachedWindow::~AttachedWindow()
{
for (auto it = items.begin(); it != items.end(); it++)
{
if (it->window == this)
{
items.erase(it);
break;
}
}
DebugCount::decrease("attached window");
}
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
{
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();
window->fullscreen_ = args.fullscreen;
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);
}
}
if (show)
{
window->updateWindowRect(window->target_);
window->show();
}
return window;
}
void AttachedWindow::detach(const QString &winId)
{
for (Item &item : items)
{
if (item.winId == winId)
{
item.window->deleteLater();
}
}
}
void AttachedWindow::setChannel(ChannelPtr channel)
{
this->ui_.split->setChannel(channel);
}
void AttachedWindow::showEvent(QShowEvent *)
{
this->attachToHwnd(this->target_);
}
void AttachedWindow::attachToHwnd(void *_attachedPtr)
{
#ifdef USEWINSDK
if (this->attached_)
{
return;
}
this->attached_ = true;
//auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
// FAST TIMER - used to resize/reorder windows
this->timer_.setInterval(1);
QObject::connect(&this->timer_, &QTimer::timeout, [this, 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(), int(filenameLength));
if (!qfilename.endsWith("chrome.exe") &&
!qfilename.endsWith("firefox.exe"))
{
qDebug() << "NM Illegal caller" << qfilename;
this->timer_.stop();
this->deleteLater();
return;
}
this->validProcessName_ = true;
}
this->updateWindowRect(attached);
});
this->timer_.start();
// SLOW TIMER - used to hide taskbar behind fullscreen window
this->slowTimer_.setInterval(2000);
QObject::connect(&this->slowTimer_, &QTimer::timeout, [this, attached] {
if (this->fullscreen_)
{
taskbarHwnds.clear();
::EnumWindows(&enumWindows, 0);
for (auto taskbarHwnd : taskbarHwnds)
{
::SetWindowPos(taskbarHwnd,
GetNextWindow(attached, GW_HWNDNEXT), 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
});
this->slowTimer_.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
if (HWND next = ::GetNextWindow(attached, GW_HWNDPREV))
{
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
float scale = 1.f;
if (auto dpi = getWindowDpi(attached))
{
scale = dpi.get() / 96.f;
for (auto w : this->ui_.split->findChildren<BaseWidget *>())
{
w->setOverrideScale(scale);
}
this->ui_.split->setOverrideScale(scale);
}
if (this->height_ != -1)
{
this->ui_.split->setFixedWidth(int(this->width_ * scale));
// offset
int o = this->fullscreen_ ? 0 : 8;
::MoveWindow(hwnd, int(rect.right - this->width_ * scale - o),
int(rect.bottom - this->height_ * scale - o) + 4,
int(this->width_ * scale) - 5,
int(this->height_ * scale) - 5, true);
}
// if (this->fullscreen_)
// {
// ::BringWindowToTop(attached);
// }
// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8,
// rect.bottom - rect.top - 82 - 8, false);
#endif
}
// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message,
// long *result)
//{
// MSG *msg = reinterpret_cast
// case WM_NCCALCSIZE: {
// }
//}
std::vector<AttachedWindow::Item> AttachedWindow::items;
} // namespace chatterino
#include "AttachedWindow.hpp"
#include "Application.hpp"
#include "util/DebugCount.hpp"
#include "widgets/splits/Split.hpp"
#include <QTimer>
#include <QVBoxLayout>
#ifdef USEWINSDK
# include "util/WindowsHelper.hpp"
# include "Windows.h"
// don't even think about reordering these
# include "Psapi.h"
# pragma comment(lib, "Dwmapi.lib")
#endif
namespace chatterino {
#ifdef USEWINSDK
static thread_local std::vector<HWND> taskbarHwnds;
BOOL CALLBACK enumWindows(HWND hwnd, LPARAM)
{
constexpr int length = 16;
auto className = std::make_unique<WCHAR[]>(length);
GetClassName(hwnd, className.get(), length);
// qDebug() << QString::fromWCharArray(className.get(), length);
if (lstrcmp(className.get(), L"Shell_TrayWnd") == 0 ||
lstrcmp(className.get(), L"Shell_Secondary") == 0)
{
taskbarHwnds.push_back(hwnd);
}
return true;
}
#endif
AttachedWindow::AttachedWindow(void *_target, int _yOffset)
: QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window)
, target_(_target)
, yOffset_(_yOffset)
{
QLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
this->setLayout(layout);
auto *split = new Split(this);
this->ui_.split = split;
split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
layout->addWidget(split);
DebugCount::increase("attached window");
}
AttachedWindow::~AttachedWindow()
{
for (auto it = items.begin(); it != items.end(); it++)
{
if (it->window == this)
{
items.erase(it);
break;
}
}
DebugCount::decrease("attached window");
}
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
{
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();
window->fullscreen_ = args.fullscreen;
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);
}
}
if (show)
{
window->updateWindowRect(window->target_);
window->show();
}
return window;
}
void AttachedWindow::detach(const QString &winId)
{
for (Item &item : items)
{
if (item.winId == winId)
{
item.window->deleteLater();
}
}
}
void AttachedWindow::setChannel(ChannelPtr channel)
{
this->ui_.split->setChannel(channel);
}
void AttachedWindow::showEvent(QShowEvent *)
{
this->attachToHwnd(this->target_);
}
void AttachedWindow::attachToHwnd(void *_attachedPtr)
{
#ifdef USEWINSDK
if (this->attached_)
{
return;
}
this->attached_ = true;
//auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
// FAST TIMER - used to resize/reorder windows
this->timer_.setInterval(1);
QObject::connect(&this->timer_, &QTimer::timeout, [this, 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(), int(filenameLength));
if (!qfilename.endsWith("chrome.exe") &&
!qfilename.endsWith("firefox.exe"))
{
qDebug() << "NM Illegal caller" << qfilename;
this->timer_.stop();
this->deleteLater();
return;
}
this->validProcessName_ = true;
}
this->updateWindowRect(attached);
});
this->timer_.start();
// SLOW TIMER - used to hide taskbar behind fullscreen window
this->slowTimer_.setInterval(2000);
QObject::connect(&this->slowTimer_, &QTimer::timeout, [this, attached] {
if (this->fullscreen_)
{
taskbarHwnds.clear();
::EnumWindows(&enumWindows, 0);
for (auto taskbarHwnd : taskbarHwnds)
{
::SetWindowPos(taskbarHwnd,
GetNextWindow(attached, GW_HWNDNEXT), 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
});
this->slowTimer_.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
if (HWND next = ::GetNextWindow(attached, GW_HWNDPREV))
{
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
float scale = 1.f;
if (auto dpi = getWindowDpi(attached))
{
scale = dpi.get() / 96.f;
for (auto w : this->ui_.split->findChildren<BaseWidget *>())
{
w->setOverrideScale(scale);
}
this->ui_.split->setOverrideScale(scale);
}
if (this->height_ != -1)
{
this->ui_.split->setFixedWidth(int(this->width_ * scale));
// offset
int o = this->fullscreen_ ? 0 : 8;
::MoveWindow(hwnd, int(rect.right - this->width_ * scale - o),
int(rect.bottom - this->height_ * scale - o) + 4,
int(this->width_ * scale) - 5,
int(this->height_ * scale) - 5, true);
}
// if (this->fullscreen_)
// {
// ::BringWindowToTop(attached);
// }
// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8,
// rect.bottom - rect.top - 82 - 8, false);
#endif
}
// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message,
// long *result)
//{
// MSG *msg = reinterpret_cast
// case WM_NCCALCSIZE: {
// }
//}
std::vector<AttachedWindow::Item> AttachedWindow::items;
} // namespace chatterino
+68 -68
View File
@@ -1,68 +1,68 @@
#pragma once
#include <QTimer>
#include <QWidget>
namespace chatterino {
class Split;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
class AttachedWindow : public QWidget
{
AttachedWindow(void *_target, int _yOffset);
public:
struct GetArgs {
QString winId;
int yOffset = -1;
int width = -1;
int height = -1;
bool fullscreen = false;
};
virtual ~AttachedWindow() override;
static AttachedWindow *get(void *target_, const GetArgs &args);
static void detach(const QString &winId);
void setChannel(ChannelPtr channel);
protected:
virtual void showEvent(QShowEvent *) override;
// virtual void nativeEvent(const QByteArray &eventType, void *message,
// long *result) override;
private:
struct {
Split *split;
} ui_;
struct Item {
void *hwnd;
AttachedWindow *window;
QString winId;
};
static std::vector<Item> items;
void attachToHwnd(void *attached);
void updateWindowRect(void *attached);
void *target_;
int yOffset_;
int currentYOffset_;
int width_ = 360;
int height_ = -1;
bool fullscreen_ = false;
#ifdef USEWINSDK
bool validProcessName_ = false;
bool attached_ = false;
#endif
QTimer timer_;
QTimer slowTimer_;
};
} // namespace chatterino
#pragma once
#include <QTimer>
#include <QWidget>
namespace chatterino {
class Split;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
class AttachedWindow : public QWidget
{
AttachedWindow(void *_target, int _yOffset);
public:
struct GetArgs {
QString winId;
int yOffset = -1;
int width = -1;
int height = -1;
bool fullscreen = false;
};
virtual ~AttachedWindow() override;
static AttachedWindow *get(void *target_, const GetArgs &args);
static void detach(const QString &winId);
void setChannel(ChannelPtr channel);
protected:
virtual void showEvent(QShowEvent *) override;
// virtual void nativeEvent(const QByteArray &eventType, void *message,
// long *result) override;
private:
struct {
Split *split;
} ui_;
struct Item {
void *hwnd;
AttachedWindow *window;
QString winId;
};
static std::vector<Item> items;
void attachToHwnd(void *attached);
void updateWindowRect(void *attached);
void *target_;
int yOffset_;
int currentYOffset_;
int width_ = 360;
int height_ = -1;
bool fullscreen_ = false;
#ifdef USEWINSDK
bool validProcessName_ = false;
bool attached_ = false;
#endif
QTimer timer_;
QTimer slowTimer_;
};
} // namespace chatterino
+35 -35
View File
@@ -1,35 +1,35 @@
#include "StreamView.hpp"
#include "common/Channel.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/ChannelView.hpp"
#include "widgets/splits/Split.hpp"
#ifdef USEWEBENGINE
# include <QtWebEngineWidgets>
#endif
namespace chatterino {
StreamView::StreamView(ChannelPtr channel, const QUrl &url)
{
LayoutCreator<StreamView> layoutCreator(this);
#ifdef USEWEBENGINE
auto web =
layoutCreator.emplace<QWebEngineView>(this).assign(&this->stream);
web->setUrl(url);
web->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled,
true);
#endif
auto chat = layoutCreator.emplace<ChannelView>();
chat->setFixedWidth(300);
chat->setChannel(channel);
this->layout()->setSpacing(0);
this->layout()->setMargin(0);
}
} // namespace chatterino
#include "StreamView.hpp"
#include "common/Channel.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/ChannelView.hpp"
#include "widgets/splits/Split.hpp"
#ifdef USEWEBENGINE
# include <QtWebEngineWidgets>
#endif
namespace chatterino {
StreamView::StreamView(ChannelPtr channel, const QUrl &url)
{
LayoutCreator<StreamView> layoutCreator(this);
#ifdef USEWEBENGINE
auto web =
layoutCreator.emplace<QWebEngineView>(this).assign(&this->stream);
web->setUrl(url);
web->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled,
true);
#endif
auto chat = layoutCreator.emplace<ChannelView>();
chat->setFixedWidth(300);
chat->setChannel(channel);
this->layout()->setSpacing(0);
this->layout()->setMargin(0);
}
} // namespace chatterino
+25 -25
View File
@@ -1,25 +1,25 @@
#pragma once
#include <QUrl>
#include <QWidget>
#include <memory>
class QWebEngineView;
namespace chatterino {
class Channel;
class StreamView : public QWidget
{
public:
StreamView(std::shared_ptr<Channel> channel, const QUrl &url);
private:
#ifdef USEWEBENGINE
QWebEngineView *stream;
#endif
};
} // namespace chatterino
#pragma once
#include <QUrl>
#include <QWidget>
#include <memory>
class QWebEngineView;
namespace chatterino {
class Channel;
class StreamView : public QWidget
{
public:
StreamView(std::shared_ptr<Channel> channel, const QUrl &url);
private:
#ifdef USEWEBENGINE
QWebEngineView *stream;
#endif
};
} // namespace chatterino
+92 -92
View File
@@ -1,92 +1,92 @@
#include "LastRunCrashDialog.hpp"
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "singletons/Updates.hpp"
#include "util/LayoutCreator.hpp"
#include "util/PostToThread.hpp"
namespace chatterino {
LastRunCrashDialog::LastRunCrashDialog()
{
this->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
this->setWindowTitle("Chatterino");
auto layout =
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 = 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 = UpdateManager::getInstance();
// switch (updateManager.getStatus()) {
// case UpdateManager::None: {
// update->setText("Not checking for updates.");
// } break;
// case UpdateManager::Searching: {
// update->setText("Checking for updates...");
// } break;
// case UpdateManager::UpdateAvailable: {
// update->setText("Update available.");
// } break;
// case UpdateManager::NoUpdateAvailable: {
// update->setText("No update abailable.");
// } break;
// case UpdateManager::SearchFailed: {
// update->setText("Error while searching for update.\nEither
// the update service is "
// "temporarily down or there is an issue
// with your installation.");
// } break;
// case UpdateManager::Downloading: {
// update->setText(
// "Downloading the update. Chatterino will close once
// the download is done.");
// } break;
// case UpdateManager::DownloadFailed: {
// update->setText("Download failed.");
// } break;
// case UpdateManager::WriteFileFailed: {
// update->setText("Writing the update file to the hard drive
// failed.");
// } break;
// }
// };
// updateUpdateLabel();
// this->managedConnect(updateManager.statusUpdated,
// [updateUpdateLabel](auto) mutable {
// postToThread([updateUpdateLabel]() mutable { updateUpdateLabel();
// });
// });
}
} // namespace chatterino
#include "LastRunCrashDialog.hpp"
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "singletons/Updates.hpp"
#include "util/LayoutCreator.hpp"
#include "util/PostToThread.hpp"
namespace chatterino {
LastRunCrashDialog::LastRunCrashDialog()
{
this->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
this->setWindowTitle("Chatterino");
auto layout =
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 = 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 = UpdateManager::getInstance();
// switch (updateManager.getStatus()) {
// case UpdateManager::None: {
// update->setText("Not checking for updates.");
// } break;
// case UpdateManager::Searching: {
// update->setText("Checking for updates...");
// } break;
// case UpdateManager::UpdateAvailable: {
// update->setText("Update available.");
// } break;
// case UpdateManager::NoUpdateAvailable: {
// update->setText("No update abailable.");
// } break;
// case UpdateManager::SearchFailed: {
// update->setText("Error while searching for update.\nEither
// the update service is "
// "temporarily down or there is an issue
// with your installation.");
// } break;
// case UpdateManager::Downloading: {
// update->setText(
// "Downloading the update. Chatterino will close once
// the download is done.");
// } break;
// case UpdateManager::DownloadFailed: {
// update->setText("Download failed.");
// } break;
// case UpdateManager::WriteFileFailed: {
// update->setText("Writing the update file to the hard drive
// failed.");
// } break;
// }
// };
// updateUpdateLabel();
// this->managedConnect(updateManager.statusUpdated,
// [updateUpdateLabel](auto) mutable {
// postToThread([updateUpdateLabel]() mutable { updateUpdateLabel();
// });
// });
}
} // namespace chatterino
+14 -14
View File
@@ -1,14 +1,14 @@
#pragma once
#include <QDialog>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
class LastRunCrashDialog : public QDialog, pajlada::Signals::SignalHolder
{
public:
LastRunCrashDialog();
};
} // namespace chatterino
#pragma once
#include <QDialog>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
class LastRunCrashDialog : public QDialog, pajlada::Signals::SignalHolder
{
public:
LastRunCrashDialog();
};
} // namespace chatterino
+54 -54
View File
@@ -1,54 +1,54 @@
#include "NotificationPopup.hpp"
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "widgets/helper/ChannelView.hpp"
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
namespace chatterino {
NotificationPopup::NotificationPopup()
: BaseWindow((QWidget *)nullptr, BaseWindow::Frameless)
, channel_(std::make_shared<Channel>("notifications", Channel::Type::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(MessagePtr msg)
{
this->channel_->addMessage(msg);
// QTimer::singleShot(5000, this, [this, msg] { this->channel->remove });
}
} // namespace chatterino
#include "NotificationPopup.hpp"
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "widgets/helper/ChannelView.hpp"
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
namespace chatterino {
NotificationPopup::NotificationPopup()
: BaseWindow((QWidget *)nullptr, BaseWindow::Frameless)
, channel_(std::make_shared<Channel>("notifications", Channel::Type::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(MessagePtr msg)
{
this->channel_->addMessage(msg);
// QTimer::singleShot(5000, this, [this, msg] { this->channel->remove });
}
} // namespace chatterino
+29 -29
View File
@@ -1,29 +1,29 @@
#pragma once
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class ChannelView;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
struct Message;
using MessagePtr = std::shared_ptr<const Message>;
class NotificationPopup : public BaseWindow
{
public:
enum Location { TopLeft, TopRight, BottomLeft, BottomRight };
NotificationPopup();
void addMessage(MessagePtr msg);
void updatePosition();
private:
ChannelView *channelView_;
ChannelPtr channel_;
};
} // namespace chatterino
#pragma once
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class ChannelView;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
struct Message;
using MessagePtr = std::shared_ptr<const Message>;
class NotificationPopup : public BaseWindow
{
public:
enum Location { TopLeft, TopRight, BottomLeft, BottomRight };
NotificationPopup();
void addMessage(MessagePtr msg);
void updatePosition();
private:
ChannelView *channelView_;
ChannelPtr channel_;
};
} // namespace chatterino
+358 -358
View File
@@ -1,358 +1,358 @@
#include "SelectChannelDialog.hpp"
#include "Application.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Theme.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/helper/NotebookTab.hpp"
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#define TAB_TWITCH 0
namespace chatterino {
SelectChannelDialog::SelectChannelDialog(QWidget *parent)
: BaseWindow(parent, BaseWindow::EnableCustomFrame)
, selectedChannel_(Channel::getEmpty())
{
this->setWindowTitle("Select a channel to join");
this->tabFilter_.dialog = this;
LayoutCreator<QWidget> layoutWidget(this->getLayoutContainer());
auto layout = layoutWidget.setLayoutType<QVBoxLayout>().withoutMargin();
auto notebook = layout.emplace<Notebook>(this).assign(&this->ui_.notebook);
// twitch
{
LayoutCreator<QWidget> obj(new QWidget());
auto vbox = obj.setLayoutType<QVBoxLayout>();
// 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 its name.").hidden();
channel_lbl->setWordWrap(true);
auto channel_edit = vbox.emplace<QLineEdit>().hidden().assign(
&this->ui_.twitch.channelName);
QObject::connect(channel_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable {
if (enabled)
{
channel_edit->setFocus();
channel_edit->setSelection(
0, channel_edit->text().length());
}
channel_edit->setVisible(enabled);
channel_lbl->setVisible(enabled);
});
channel_btn->installEventFilter(&this->tabFilter_);
channel_edit->installEventFilter(&this->tabFilter_);
// whispers_btn
auto whispers_btn = vbox.emplace<QRadioButton>("Whispers")
.assign(&this->ui_.twitch.whispers);
auto whispers_lbl =
vbox.emplace<QLabel>("Shows the whispers that you receive while "
"chatterino is running.")
.hidden();
whispers_lbl->setWordWrap(true);
whispers_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
whispers_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { whispers_lbl->setVisible(enabled); });
// mentions_btn
auto mentions_btn = vbox.emplace<QRadioButton>("Mentions")
.assign(&this->ui_.twitch.mentions);
auto mentions_lbl =
vbox.emplace<QLabel>("Shows all the messages that highlight you "
"from any channel.")
.hidden();
mentions_lbl->setWordWrap(true);
mentions_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
mentions_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { mentions_lbl->setVisible(enabled); });
// watching_btn
auto watching_btn = vbox.emplace<QRadioButton>("Watching")
.assign(&this->ui_.twitch.watching);
auto watching_lbl =
vbox.emplace<QLabel>("Requires the chatterino browser extension.")
.hidden();
watching_lbl->setWordWrap(true);
watching_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
watching_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { watching_lbl->setVisible(enabled); });
vbox->addStretch(1);
// tabbing order
QWidget::setTabOrder(watching_btn.getElement(),
channel_btn.getElement());
QWidget::setTabOrder(channel_btn.getElement(),
whispers_btn.getElement());
QWidget::setTabOrder(whispers_btn.getElement(),
mentions_btn.getElement());
QWidget::setTabOrder(mentions_btn.getElement(),
watching_btn.getElement());
// tab
auto tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Twitch");
}
// irc
/*
{
LayoutCreator<QWidget> obj(new QWidget());
auto vbox = obj.setLayoutType<QVBoxLayout>();
auto form = vbox.emplace<QFormLayout>();
form->addRow(new QLabel("User name:"), new QLineEdit());
form->addRow(new QLabel("First nick choice:"), new QLineEdit());
form->addRow(new QLabel("Second nick choice:"), new QLineEdit());
form->addRow(new QLabel("Third nick choice:"), new QLineEdit());
auto tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Irc");
}
*/
layout->setStretchFactor(notebook.getElement(), 1);
auto buttons =
layout.emplace<QHBoxLayout>().emplace<QDialogButtonBox>(this);
{
auto *button_ok = buttons->addButton(QDialogButtonBox::Ok);
QObject::connect(button_ok, &QPushButton::clicked,
[=](bool) { this->ok(); });
auto *button_cancel = buttons->addButton(QDialogButtonBox::Cancel);
QObject::connect(button_cancel, &QAbstractButton::clicked,
[=](bool) { this->close(); });
}
this->setScaleIndependantSize(300, 310);
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
// Shortcuts
auto *shortcut_ok = new QShortcut(QKeySequence("Return"), this);
QObject::connect(shortcut_ok, &QShortcut::activated, [=] { this->ok(); });
auto *shortcut_cancel = new QShortcut(QKeySequence("Esc"), this);
QObject::connect(shortcut_cancel, &QShortcut::activated,
[=] { this->close(); });
}
void SelectChannelDialog::ok()
{
this->hasSelectedChannel_ = true;
this->close();
}
void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
{
auto channel = _channel.get();
assert(channel);
this->selectedChannel_ = channel;
switch (_channel.getType())
{
case Channel::Type::Twitch:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
this->ui_.twitch.channelName->setText(channel->getName());
}
break;
case Channel::Type::TwitchWatching:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.watching->setFocus();
}
break;
case Channel::Type::TwitchMentions:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.mentions->setFocus();
}
break;
case Channel::Type::TwitchWhispers:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.whispers->setFocus();
}
break;
default:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
}
}
this->hasSelectedChannel_ = false;
}
IndirectChannel SelectChannelDialog::getSelectedChannel() const
{
if (!this->hasSelectedChannel_)
{
return this->selectedChannel_;
}
auto app = getApp();
switch (this->ui_.notebook->getSelectedIndex())
{
case TAB_TWITCH:
{
if (this->ui_.twitch.channel->isChecked())
{
return app->twitch.server->getOrAddChannel(
this->ui_.twitch.channelName->text().trimmed());
}
else if (this->ui_.twitch.watching->isChecked())
{
return app->twitch.server->watchingChannel;
}
else if (this->ui_.twitch.mentions->isChecked())
{
return app->twitch.server->mentionsChannel;
}
else if (this->ui_.twitch.whispers->isChecked())
{
return app->twitch.server->whispersChannel;
}
}
}
return this->selectedChannel_;
}
bool SelectChannelDialog::hasSeletedChannel() const
{
return this->hasSelectedChannel_;
}
bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched,
QEvent *event)
{
auto *widget = (QWidget *)watched;
if (event->type() == QEvent::FocusIn)
{
widget->grabKeyboard();
auto *radio = dynamic_cast<QRadioButton *>(watched);
if (radio)
{
radio->setChecked(true);
}
return true;
}
else if (event->type() == QEvent::FocusOut)
{
widget->releaseKeyboard();
return false;
}
else if (event->type() == QEvent::KeyPress)
{
QKeyEvent *event_key = static_cast<QKeyEvent *>(event);
if ((event_key->key() == Qt::Key_Tab ||
event_key->key() == Qt::Key_Down) &&
event_key->modifiers() == Qt::NoModifier)
{
if (widget == this->dialog->ui_.twitch.channelName)
{
this->dialog->ui_.twitch.whispers->setFocus();
return true;
}
else
{
widget->nextInFocusChain()->setFocus();
}
return true;
}
else if (((event_key->key() == Qt::Key_Tab ||
event_key->key() == Qt::Key_Backtab) &&
event_key->modifiers() == Qt::ShiftModifier) ||
((event_key->key() == Qt::Key_Up) &&
event_key->modifiers() == Qt::NoModifier))
{
if (widget == this->dialog->ui_.twitch.channelName)
{
this->dialog->ui_.twitch.watching->setFocus();
return true;
}
else if (widget == this->dialog->ui_.twitch.whispers)
{
this->dialog->ui_.twitch.channel->setFocus();
return true;
}
widget->previousInFocusChain()->setFocus();
return true;
}
else
{
return false;
}
}
else if (event->type() == QEvent::KeyRelease)
{
QKeyEvent *event_key = static_cast<QKeyEvent *>(event);
if ((event_key->key() == Qt::Key_Backtab ||
event_key->key() == Qt::Key_Down) &&
event_key->modifiers() == Qt::NoModifier)
{
return true;
}
}
return false;
}
void SelectChannelDialog::closeEvent(QCloseEvent *)
{
this->closed.invoke();
}
void SelectChannelDialog::themeChangedEvent()
{
BaseWindow::themeChangedEvent();
if (this->theme->isLightTheme())
{
this->setStyleSheet(
"QRadioButton { color: #000 } QLabel { color: #000 }");
}
else
{
this->setStyleSheet(
"QRadioButton { color: #fff } QLabel { color: #fff }");
}
}
} // namespace chatterino
#include "SelectChannelDialog.hpp"
#include "Application.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Theme.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/helper/NotebookTab.hpp"
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#define TAB_TWITCH 0
namespace chatterino {
SelectChannelDialog::SelectChannelDialog(QWidget *parent)
: BaseWindow(parent, BaseWindow::EnableCustomFrame)
, selectedChannel_(Channel::getEmpty())
{
this->setWindowTitle("Select a channel to join");
this->tabFilter_.dialog = this;
LayoutCreator<QWidget> layoutWidget(this->getLayoutContainer());
auto layout = layoutWidget.setLayoutType<QVBoxLayout>().withoutMargin();
auto notebook = layout.emplace<Notebook>(this).assign(&this->ui_.notebook);
// twitch
{
LayoutCreator<QWidget> obj(new QWidget());
auto vbox = obj.setLayoutType<QVBoxLayout>();
// 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 its name.").hidden();
channel_lbl->setWordWrap(true);
auto channel_edit = vbox.emplace<QLineEdit>().hidden().assign(
&this->ui_.twitch.channelName);
QObject::connect(channel_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable {
if (enabled)
{
channel_edit->setFocus();
channel_edit->setSelection(
0, channel_edit->text().length());
}
channel_edit->setVisible(enabled);
channel_lbl->setVisible(enabled);
});
channel_btn->installEventFilter(&this->tabFilter_);
channel_edit->installEventFilter(&this->tabFilter_);
// whispers_btn
auto whispers_btn = vbox.emplace<QRadioButton>("Whispers")
.assign(&this->ui_.twitch.whispers);
auto whispers_lbl =
vbox.emplace<QLabel>("Shows the whispers that you receive while "
"chatterino is running.")
.hidden();
whispers_lbl->setWordWrap(true);
whispers_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
whispers_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { whispers_lbl->setVisible(enabled); });
// mentions_btn
auto mentions_btn = vbox.emplace<QRadioButton>("Mentions")
.assign(&this->ui_.twitch.mentions);
auto mentions_lbl =
vbox.emplace<QLabel>("Shows all the messages that highlight you "
"from any channel.")
.hidden();
mentions_lbl->setWordWrap(true);
mentions_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
mentions_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { mentions_lbl->setVisible(enabled); });
// watching_btn
auto watching_btn = vbox.emplace<QRadioButton>("Watching")
.assign(&this->ui_.twitch.watching);
auto watching_lbl =
vbox.emplace<QLabel>("Requires the chatterino browser extension.")
.hidden();
watching_lbl->setWordWrap(true);
watching_btn->installEventFilter(&this->tabFilter_);
QObject::connect(
watching_btn.getElement(), &QRadioButton::toggled,
[=](bool enabled) mutable { watching_lbl->setVisible(enabled); });
vbox->addStretch(1);
// tabbing order
QWidget::setTabOrder(watching_btn.getElement(),
channel_btn.getElement());
QWidget::setTabOrder(channel_btn.getElement(),
whispers_btn.getElement());
QWidget::setTabOrder(whispers_btn.getElement(),
mentions_btn.getElement());
QWidget::setTabOrder(mentions_btn.getElement(),
watching_btn.getElement());
// tab
auto tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Twitch");
}
// irc
/*
{
LayoutCreator<QWidget> obj(new QWidget());
auto vbox = obj.setLayoutType<QVBoxLayout>();
auto form = vbox.emplace<QFormLayout>();
form->addRow(new QLabel("User name:"), new QLineEdit());
form->addRow(new QLabel("First nick choice:"), new QLineEdit());
form->addRow(new QLabel("Second nick choice:"), new QLineEdit());
form->addRow(new QLabel("Third nick choice:"), new QLineEdit());
auto tab = notebook->addPage(obj.getElement());
tab->setCustomTitle("Irc");
}
*/
layout->setStretchFactor(notebook.getElement(), 1);
auto buttons =
layout.emplace<QHBoxLayout>().emplace<QDialogButtonBox>(this);
{
auto *button_ok = buttons->addButton(QDialogButtonBox::Ok);
QObject::connect(button_ok, &QPushButton::clicked,
[=](bool) { this->ok(); });
auto *button_cancel = buttons->addButton(QDialogButtonBox::Cancel);
QObject::connect(button_cancel, &QAbstractButton::clicked,
[=](bool) { this->close(); });
}
this->setScaleIndependantSize(300, 310);
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
// Shortcuts
auto *shortcut_ok = new QShortcut(QKeySequence("Return"), this);
QObject::connect(shortcut_ok, &QShortcut::activated, [=] { this->ok(); });
auto *shortcut_cancel = new QShortcut(QKeySequence("Esc"), this);
QObject::connect(shortcut_cancel, &QShortcut::activated,
[=] { this->close(); });
}
void SelectChannelDialog::ok()
{
this->hasSelectedChannel_ = true;
this->close();
}
void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
{
auto channel = _channel.get();
assert(channel);
this->selectedChannel_ = channel;
switch (_channel.getType())
{
case Channel::Type::Twitch:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
this->ui_.twitch.channelName->setText(channel->getName());
}
break;
case Channel::Type::TwitchWatching:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.watching->setFocus();
}
break;
case Channel::Type::TwitchMentions:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.mentions->setFocus();
}
break;
case Channel::Type::TwitchWhispers:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.whispers->setFocus();
}
break;
default:
{
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
}
}
this->hasSelectedChannel_ = false;
}
IndirectChannel SelectChannelDialog::getSelectedChannel() const
{
if (!this->hasSelectedChannel_)
{
return this->selectedChannel_;
}
auto app = getApp();
switch (this->ui_.notebook->getSelectedIndex())
{
case TAB_TWITCH:
{
if (this->ui_.twitch.channel->isChecked())
{
return app->twitch.server->getOrAddChannel(
this->ui_.twitch.channelName->text().trimmed());
}
else if (this->ui_.twitch.watching->isChecked())
{
return app->twitch.server->watchingChannel;
}
else if (this->ui_.twitch.mentions->isChecked())
{
return app->twitch.server->mentionsChannel;
}
else if (this->ui_.twitch.whispers->isChecked())
{
return app->twitch.server->whispersChannel;
}
}
}
return this->selectedChannel_;
}
bool SelectChannelDialog::hasSeletedChannel() const
{
return this->hasSelectedChannel_;
}
bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched,
QEvent *event)
{
auto *widget = (QWidget *)watched;
if (event->type() == QEvent::FocusIn)
{
widget->grabKeyboard();
auto *radio = dynamic_cast<QRadioButton *>(watched);
if (radio)
{
radio->setChecked(true);
}
return true;
}
else if (event->type() == QEvent::FocusOut)
{
widget->releaseKeyboard();
return false;
}
else if (event->type() == QEvent::KeyPress)
{
QKeyEvent *event_key = static_cast<QKeyEvent *>(event);
if ((event_key->key() == Qt::Key_Tab ||
event_key->key() == Qt::Key_Down) &&
event_key->modifiers() == Qt::NoModifier)
{
if (widget == this->dialog->ui_.twitch.channelName)
{
this->dialog->ui_.twitch.whispers->setFocus();
return true;
}
else
{
widget->nextInFocusChain()->setFocus();
}
return true;
}
else if (((event_key->key() == Qt::Key_Tab ||
event_key->key() == Qt::Key_Backtab) &&
event_key->modifiers() == Qt::ShiftModifier) ||
((event_key->key() == Qt::Key_Up) &&
event_key->modifiers() == Qt::NoModifier))
{
if (widget == this->dialog->ui_.twitch.channelName)
{
this->dialog->ui_.twitch.watching->setFocus();
return true;
}
else if (widget == this->dialog->ui_.twitch.whispers)
{
this->dialog->ui_.twitch.channel->setFocus();
return true;
}
widget->previousInFocusChain()->setFocus();
return true;
}
else
{
return false;
}
}
else if (event->type() == QEvent::KeyRelease)
{
QKeyEvent *event_key = static_cast<QKeyEvent *>(event);
if ((event_key->key() == Qt::Key_Backtab ||
event_key->key() == Qt::Key_Down) &&
event_key->modifiers() == Qt::NoModifier)
{
return true;
}
}
return false;
}
void SelectChannelDialog::closeEvent(QCloseEvent *)
{
this->closed.invoke();
}
void SelectChannelDialog::themeChangedEvent()
{
BaseWindow::themeChangedEvent();
if (this->theme->isLightTheme())
{
this->setStyleSheet(
"QRadioButton { color: #000 } QLabel { color: #000 }");
}
else
{
this->setStyleSheet(
"QRadioButton { color: #fff } QLabel { color: #fff }");
}
}
} // namespace chatterino
+61 -61
View File
@@ -1,61 +1,61 @@
#pragma once
#include "Application.hpp"
#include "common/Channel.hpp"
#include "widgets/BaseWindow.hpp"
#include <pajlada/signals/signal.hpp>
#include <QLabel>
#include <QRadioButton>
namespace chatterino {
class Notebook;
class SelectChannelDialog final : public BaseWindow
{
public:
SelectChannelDialog(QWidget *parent = nullptr);
void setSelectedChannel(IndirectChannel selectedChannel_);
IndirectChannel getSelectedChannel() const;
bool hasSeletedChannel() const;
pajlada::Signals::NoArgSignal closed;
protected:
virtual void closeEvent(QCloseEvent *) override;
virtual void themeChangedEvent() override;
private:
class EventFilter : public QObject
{
public:
SelectChannelDialog *dialog;
protected:
virtual bool eventFilter(QObject *watched, QEvent *event) override;
};
struct {
Notebook *notebook;
struct {
QRadioButton *channel;
QLineEdit *channelName;
QRadioButton *whispers;
QRadioButton *mentions;
QRadioButton *watching;
} twitch;
} ui_;
EventFilter tabFilter_;
ChannelPtr selectedChannel_;
bool hasSelectedChannel_ = false;
void ok();
friend class EventFilter;
};
} // namespace chatterino
#pragma once
#include "Application.hpp"
#include "common/Channel.hpp"
#include "widgets/BaseWindow.hpp"
#include <pajlada/signals/signal.hpp>
#include <QLabel>
#include <QRadioButton>
namespace chatterino {
class Notebook;
class SelectChannelDialog final : public BaseWindow
{
public:
SelectChannelDialog(QWidget *parent = nullptr);
void setSelectedChannel(IndirectChannel selectedChannel_);
IndirectChannel getSelectedChannel() const;
bool hasSeletedChannel() const;
pajlada::Signals::NoArgSignal closed;
protected:
virtual void closeEvent(QCloseEvent *) override;
virtual void themeChangedEvent() override;
private:
class EventFilter : public QObject
{
public:
SelectChannelDialog *dialog;
protected:
virtual bool eventFilter(QObject *watched, QEvent *event) override;
};
struct {
Notebook *notebook;
struct {
QRadioButton *channel;
QLineEdit *channelName;
QRadioButton *whispers;
QRadioButton *mentions;
QRadioButton *watching;
} twitch;
} ui_;
EventFilter tabFilter_;
ChannelPtr selectedChannel_;
bool hasSelectedChannel_ = false;
void ok();
friend class EventFilter;
};
} // namespace chatterino
+92 -92
View File
@@ -1,92 +1,92 @@
#include "UpdateDialog.hpp"
#include "singletons/Updates.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Label.hpp"
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
namespace chatterino {
UpdateDialog::UpdateDialog()
: BaseWindow(nullptr,
BaseWindow::Flags(BaseWindow::Frameless | BaseWindow::TopMost |
BaseWindow::EnableCustomFrame))
{
auto layout =
LayoutCreator<UpdateDialog>(this).setLayoutType<QVBoxLayout>();
layout.emplace<Label>("You shouldn't be seeing this dialog.")
.assign(&this->ui_.label);
auto buttons = layout.emplace<QDialogButtonBox>();
auto install = buttons->addButton("Install", QDialogButtonBox::AcceptRole);
this->ui_.installButton = install;
auto dismiss = buttons->addButton("Dismiss", QDialogButtonBox::RejectRole);
QObject::connect(install, &QPushButton::clicked, this, [this] {
Updates::getInstance().installUpdates();
this->close();
});
QObject::connect(dismiss, &QPushButton::clicked, this, [this] {
this->buttonClicked.invoke(Dismiss);
this->close();
});
this->updateStatusChanged(Updates::getInstance().getStatus());
this->connections_.managedConnect(
Updates::getInstance().statusUpdated,
[this](auto status) { this->updateStatusChanged(status); });
this->setScaleIndependantHeight(150);
}
void UpdateDialog::updateStatusChanged(Updates::Status status)
{
this->ui_.installButton->setVisible(status == Updates::UpdateAvailable);
switch (status)
{
case Updates::UpdateAvailable:
{
this->ui_.label->setText(
QString("An update (%1) is available.\n\nDo you want to "
"download and install it?")
.arg(Updates::getInstance().getOnlineVersion()));
this->updateGeometry();
}
break;
case Updates::SearchFailed:
{
this->ui_.label->setText("Failed to load version information.");
}
break;
case Updates::Downloading:
{
this->ui_.label->setText(
"Downloading updates.\n\nChatterino will restart "
"automatically when the download is done.");
}
break;
case Updates::DownloadFailed:
{
this->ui_.label->setText("Failed to download the update.");
}
break;
case Updates::WriteFileFailed:
{
this->ui_.label->setText("Failed to save the update to disk.");
}
break;
default:;
}
}
} // namespace chatterino
#include "UpdateDialog.hpp"
#include "singletons/Updates.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Label.hpp"
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
namespace chatterino {
UpdateDialog::UpdateDialog()
: BaseWindow(nullptr,
BaseWindow::Flags(BaseWindow::Frameless | BaseWindow::TopMost |
BaseWindow::EnableCustomFrame))
{
auto layout =
LayoutCreator<UpdateDialog>(this).setLayoutType<QVBoxLayout>();
layout.emplace<Label>("You shouldn't be seeing this dialog.")
.assign(&this->ui_.label);
auto buttons = layout.emplace<QDialogButtonBox>();
auto install = buttons->addButton("Install", QDialogButtonBox::AcceptRole);
this->ui_.installButton = install;
auto dismiss = buttons->addButton("Dismiss", QDialogButtonBox::RejectRole);
QObject::connect(install, &QPushButton::clicked, this, [this] {
Updates::getInstance().installUpdates();
this->close();
});
QObject::connect(dismiss, &QPushButton::clicked, this, [this] {
this->buttonClicked.invoke(Dismiss);
this->close();
});
this->updateStatusChanged(Updates::getInstance().getStatus());
this->connections_.managedConnect(
Updates::getInstance().statusUpdated,
[this](auto status) { this->updateStatusChanged(status); });
this->setScaleIndependantHeight(150);
}
void UpdateDialog::updateStatusChanged(Updates::Status status)
{
this->ui_.installButton->setVisible(status == Updates::UpdateAvailable);
switch (status)
{
case Updates::UpdateAvailable:
{
this->ui_.label->setText(
QString("An update (%1) is available.\n\nDo you want to "
"download and install it?")
.arg(Updates::getInstance().getOnlineVersion()));
this->updateGeometry();
}
break;
case Updates::SearchFailed:
{
this->ui_.label->setText("Failed to load version information.");
}
break;
case Updates::Downloading:
{
this->ui_.label->setText(
"Downloading updates.\n\nChatterino will restart "
"automatically when the download is done.");
}
break;
case Updates::DownloadFailed:
{
this->ui_.label->setText("Failed to download the update.");
}
break;
case Updates::WriteFileFailed:
{
this->ui_.label->setText("Failed to save the update to disk.");
}
break;
default:;
}
}
} // namespace chatterino
+32 -32
View File
@@ -1,32 +1,32 @@
#pragma once
#include "pajlada/signals/signalholder.hpp"
#include "singletons/Updates.hpp"
#include "widgets/BaseWindow.hpp"
#include "widgets/Label.hpp"
class QPushButton;
namespace chatterino {
class UpdateDialog : public BaseWindow
{
public:
enum Button { Dismiss, Install };
UpdateDialog();
pajlada::Signals::Signal<Button> buttonClicked;
private:
void updateStatusChanged(Updates::Status status);
struct {
Label *label = nullptr;
QPushButton *installButton = nullptr;
} ui_;
pajlada::Signals::SignalHolder connections_;
};
} // namespace chatterino
#pragma once
#include "pajlada/signals/signalholder.hpp"
#include "singletons/Updates.hpp"
#include "widgets/BaseWindow.hpp"
#include "widgets/Label.hpp"
class QPushButton;
namespace chatterino {
class UpdateDialog : public BaseWindow
{
public:
enum Button { Dismiss, Install };
UpdateDialog();
pajlada::Signals::Signal<Button> buttonClicked;
private:
void updateStatusChanged(Updates::Status status);
struct {
Label *label = nullptr;
QPushButton *installButton = nullptr;
} ui_;
pajlada::Signals::SignalHolder connections_;
};
} // namespace chatterino
File diff suppressed because it is too large Load Diff
+73 -73
View File
@@ -1,73 +1,73 @@
#pragma once
#include "widgets/BaseWindow.hpp"
#include <pajlada/signals/signal.hpp>
class QCheckBox;
namespace chatterino {
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
class Label;
class UserInfoPopup final : public BaseWindow
{
Q_OBJECT
public:
UserInfoPopup();
void setData(const QString &name, const ChannelPtr &channel);
protected:
virtual void themeChangedEvent() override;
virtual void scaleChangedEvent(float scale) override;
private:
void installEvents();
void updateUserData();
void loadAvatar(const QUrl &url);
bool isMod_;
bool isBroadcaster_;
QString userName_;
QString userId_;
ChannelPtr channel_;
pajlada::Signals::NoArgSignal userStateChanged_;
std::shared_ptr<bool> hack_;
struct {
Button *avatarButton = nullptr;
// RippleEffectLabel2 *viewLogs = nullptr;
Label *nameLabel = nullptr;
Label *viewCountLabel = nullptr;
Label *followerCountLabel = nullptr;
Label *createdDateLabel = nullptr;
Label *userIDLabel = nullptr;
QCheckBox *follow = nullptr;
QCheckBox *ignore = nullptr;
QCheckBox *ignoreHighlights = nullptr;
} ui_;
class TimeoutWidget : public BaseWidget
{
public:
enum Action { Ban, Unban, Timeout };
TimeoutWidget();
pajlada::Signals::Signal<std::pair<Action, int>> buttonClicked;
protected:
void paintEvent(QPaintEvent *event) override;
};
};
} // namespace chatterino
#pragma once
#include "widgets/BaseWindow.hpp"
#include <pajlada/signals/signal.hpp>
class QCheckBox;
namespace chatterino {
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
class Label;
class UserInfoPopup final : public BaseWindow
{
Q_OBJECT
public:
UserInfoPopup();
void setData(const QString &name, const ChannelPtr &channel);
protected:
virtual void themeChangedEvent() override;
virtual void scaleChangedEvent(float scale) override;
private:
void installEvents();
void updateUserData();
void loadAvatar(const QUrl &url);
bool isMod_;
bool isBroadcaster_;
QString userName_;
QString userId_;
ChannelPtr channel_;
pajlada::Signals::NoArgSignal userStateChanged_;
std::shared_ptr<bool> hack_;
struct {
Button *avatarButton = nullptr;
// RippleEffectLabel2 *viewLogs = nullptr;
Label *nameLabel = nullptr;
Label *viewCountLabel = nullptr;
Label *followerCountLabel = nullptr;
Label *createdDateLabel = nullptr;
Label *userIDLabel = nullptr;
QCheckBox *follow = nullptr;
QCheckBox *ignore = nullptr;
QCheckBox *ignoreHighlights = nullptr;
} ui_;
class TimeoutWidget : public BaseWidget
{
public:
enum Action { Ban, Unban, Timeout };
TimeoutWidget();
pajlada::Signals::Signal<std::pair<Action, int>> buttonClicked;
protected:
void paintEvent(QPaintEvent *event) override;
};
};
} // namespace chatterino
+11 -11
View File
@@ -1,11 +1,11 @@
#include "WelcomeDialog.hpp"
namespace chatterino {
WelcomeDialog::WelcomeDialog()
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
{
this->setWindowTitle("Chatterino quick setup");
}
} // namespace chatterino
#include "WelcomeDialog.hpp"
namespace chatterino {
WelcomeDialog::WelcomeDialog()
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
{
this->setWindowTitle("Chatterino quick setup");
}
} // namespace chatterino
+13 -13
View File
@@ -1,13 +1,13 @@
#pragma once
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class WelcomeDialog : public BaseWindow
{
public:
WelcomeDialog();
};
} // namespace chatterino
#pragma once
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class WelcomeDialog : public BaseWindow
{
public:
WelcomeDialog();
};
} // namespace chatterino
+65 -65
View File
@@ -1,65 +1,65 @@
#include "ComboBoxItemDelegate.hpp"
#include <QComboBox>
namespace chatterino {
ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
ComboBoxItemDelegate::~ComboBoxItemDelegate()
{
}
QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QVariant data = index.data(Qt::UserRole + 1);
if (data.type() != QVariant::StringList)
{
return QStyledItemDelegate::createEditor(parent, option, index);
}
QComboBox *combo = new QComboBox(parent);
combo->addItems(data.toStringList());
return combo;
}
void ComboBoxItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
if (QComboBox *cb = qobject_cast<QComboBox *>(editor))
{
// get the index of the text in the combobox that matches the current
// value of the itenm
QString currentText = index.data(Qt::EditRole).toString();
int cbIndex = cb->findText(currentText);
// if it is valid, adjust the combobox
if (cbIndex >= 0)
{
cb->setCurrentIndex(cbIndex);
}
}
else
{
QStyledItemDelegate::setEditorData(editor, index);
}
}
void ComboBoxItemDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
if (QComboBox *cb = qobject_cast<QComboBox *>(editor))
// save the current text of the combo box as the current value of the
// item
model->setData(index, cb->currentText(), Qt::EditRole);
else
QStyledItemDelegate::setModelData(editor, model, index);
}
} // namespace chatterino
#include "ComboBoxItemDelegate.hpp"
#include <QComboBox>
namespace chatterino {
ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
ComboBoxItemDelegate::~ComboBoxItemDelegate()
{
}
QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QVariant data = index.data(Qt::UserRole + 1);
if (data.type() != QVariant::StringList)
{
return QStyledItemDelegate::createEditor(parent, option, index);
}
QComboBox *combo = new QComboBox(parent);
combo->addItems(data.toStringList());
return combo;
}
void ComboBoxItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
if (QComboBox *cb = qobject_cast<QComboBox *>(editor))
{
// get the index of the text in the combobox that matches the current
// value of the itenm
QString currentText = index.data(Qt::EditRole).toString();
int cbIndex = cb->findText(currentText);
// if it is valid, adjust the combobox
if (cbIndex >= 0)
{
cb->setCurrentIndex(cbIndex);
}
}
else
{
QStyledItemDelegate::setEditorData(editor, index);
}
}
void ComboBoxItemDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
if (QComboBox *cb = qobject_cast<QComboBox *>(editor))
// save the current text of the combo box as the current value of the
// item
model->setData(index, cb->currentText(), Qt::EditRole);
else
QStyledItemDelegate::setModelData(editor, model, index);
}
} // namespace chatterino
+25 -25
View File
@@ -1,25 +1,25 @@
#pragma once
#include <QStyledItemDelegate>
namespace chatterino {
// stolen from https://wiki.qt.io/Combo_Boxes_in_Item_Views
class ComboBoxItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxItemDelegate(QObject *parent = nullptr);
~ComboBoxItemDelegate();
virtual QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
virtual void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
};
} // namespace chatterino
#pragma once
#include <QStyledItemDelegate>
namespace chatterino {
// stolen from https://wiki.qt.io/Combo_Boxes_in_Item_Views
class ComboBoxItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxItemDelegate(QObject *parent = nullptr);
~ComboBoxItemDelegate();
virtual QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
virtual void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
};
} // namespace chatterino
+28 -28
View File
@@ -1,28 +1,28 @@
#include "DebugPopup.hpp"
#include "util/DebugCount.hpp"
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QLabel>
#include <QTimer>
namespace chatterino {
DebugPopup::DebugPopup()
{
auto *layout = new QHBoxLayout(this);
auto *text = new QLabel(this);
auto *timer = new QTimer(this);
timer->setInterval(300);
QObject::connect(timer, &QTimer::timeout,
[text] { text->setText(DebugCount::getDebugText()); });
timer->start();
text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
layout->addWidget(text);
}
} // namespace chatterino
#include "DebugPopup.hpp"
#include "util/DebugCount.hpp"
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QLabel>
#include <QTimer>
namespace chatterino {
DebugPopup::DebugPopup()
{
auto *layout = new QHBoxLayout(this);
auto *text = new QLabel(this);
auto *timer = new QTimer(this);
timer->setInterval(300);
QObject::connect(timer, &QTimer::timeout,
[text] { text->setText(DebugCount::getDebugText()); });
timer->start();
text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
layout->addWidget(text);
}
} // namespace chatterino
+13 -13
View File
@@ -1,13 +1,13 @@
#pragma once
#include <QWidget>
namespace chatterino {
class DebugPopup : public QWidget
{
public:
DebugPopup();
};
} // namespace chatterino
#pragma once
#include <QWidget>
namespace chatterino {
class DebugPopup : public QWidget
{
public:
DebugPopup();
};
} // namespace chatterino
+97 -97
View File
@@ -1,97 +1,97 @@
#include "EditableModelView.hpp"
#include <QAbstractTableModel>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
namespace chatterino {
EditableModelView::EditableModelView(QAbstractTableModel *model)
: tableView_(new QTableView(this))
, model_(model)
{
this->model_->setParent(this);
this->tableView_->setModel(model);
this->tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->tableView_->setSelectionBehavior(QAbstractItemView::SelectRows);
this->tableView_->verticalHeader()->hide();
// create layout
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setMargin(0);
// create button layout
QHBoxLayout *buttons = new QHBoxLayout(this);
this->buttons_ = buttons;
vbox->addLayout(buttons);
// add
QPushButton *add = new QPushButton("Add");
buttons->addWidget(add);
QObject::connect(add, &QPushButton::clicked,
[this] { this->addButtonPressed.invoke(); });
// remove
QPushButton *remove = new QPushButton("Remove");
buttons->addWidget(remove);
QObject::connect(remove, &QPushButton::clicked, [this] {
QModelIndexList list;
while ((list = this->getTableView()->selectionModel()->selectedRows(0))
.length() > 0)
{
model_->removeRow(list[0].row());
}
});
buttons->addStretch();
// add tableview
vbox->addWidget(this->tableView_);
// finish button layout
buttons->addStretch(1);
}
void EditableModelView::setTitles(std::initializer_list<QString> titles)
{
int i = 0;
for (const QString &title : titles)
{
if (this->model_->columnCount() == i)
{
break;
}
this->model_->setHeaderData(i++, Qt::Horizontal, title,
Qt::DisplayRole);
}
}
QTableView *EditableModelView::getTableView()
{
return this->tableView_;
}
QAbstractTableModel *EditableModelView::getModel()
{
return this->model_;
}
void EditableModelView::addCustomButton(QWidget *widget)
{
this->buttons_->addWidget(widget);
}
void EditableModelView::addRegexHelpLink()
{
auto regexHelpLabel =
new QLabel("<a href='https://chatterino.com/help/regex'><span "
"style='color:#99f'>regex info</span></a>");
regexHelpLabel->setOpenExternalLinks(true);
this->addCustomButton(regexHelpLabel);
}
} // namespace chatterino
#include "EditableModelView.hpp"
#include <QAbstractTableModel>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
namespace chatterino {
EditableModelView::EditableModelView(QAbstractTableModel *model)
: tableView_(new QTableView(this))
, model_(model)
{
this->model_->setParent(this);
this->tableView_->setModel(model);
this->tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->tableView_->setSelectionBehavior(QAbstractItemView::SelectRows);
this->tableView_->verticalHeader()->hide();
// create layout
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setMargin(0);
// create button layout
QHBoxLayout *buttons = new QHBoxLayout(this);
this->buttons_ = buttons;
vbox->addLayout(buttons);
// add
QPushButton *add = new QPushButton("Add");
buttons->addWidget(add);
QObject::connect(add, &QPushButton::clicked,
[this] { this->addButtonPressed.invoke(); });
// remove
QPushButton *remove = new QPushButton("Remove");
buttons->addWidget(remove);
QObject::connect(remove, &QPushButton::clicked, [this] {
QModelIndexList list;
while ((list = this->getTableView()->selectionModel()->selectedRows(0))
.length() > 0)
{
model_->removeRow(list[0].row());
}
});
buttons->addStretch();
// add tableview
vbox->addWidget(this->tableView_);
// finish button layout
buttons->addStretch(1);
}
void EditableModelView::setTitles(std::initializer_list<QString> titles)
{
int i = 0;
for (const QString &title : titles)
{
if (this->model_->columnCount() == i)
{
break;
}
this->model_->setHeaderData(i++, Qt::Horizontal, title,
Qt::DisplayRole);
}
}
QTableView *EditableModelView::getTableView()
{
return this->tableView_;
}
QAbstractTableModel *EditableModelView::getModel()
{
return this->model_;
}
void EditableModelView::addCustomButton(QWidget *widget)
{
this->buttons_->addWidget(widget);
}
void EditableModelView::addRegexHelpLink()
{
auto regexHelpLabel =
new QLabel("<a href='https://chatterino.com/help/regex'><span "
"style='color:#99f'>regex info</span></a>");
regexHelpLabel->setOpenExternalLinks(true);
this->addCustomButton(regexHelpLabel);
}
} // namespace chatterino
+34 -34
View File
@@ -1,34 +1,34 @@
#pragma once
#include <QWidget>
#include <pajlada/signals/signal.hpp>
class QAbstractTableModel;
class QTableView;
class QHBoxLayout;
namespace chatterino {
class EditableModelView : public QWidget
{
public:
EditableModelView(QAbstractTableModel *model);
void setTitles(std::initializer_list<QString> titles);
QTableView *getTableView();
QAbstractTableModel *getModel();
pajlada::Signals::NoArgSignal addButtonPressed;
void addCustomButton(QWidget *widget);
void addRegexHelpLink();
private:
QTableView *tableView_{};
QAbstractTableModel *model_{};
QHBoxLayout *buttons_{};
};
} // namespace chatterino
#pragma once
#include <QWidget>
#include <pajlada/signals/signal.hpp>
class QAbstractTableModel;
class QTableView;
class QHBoxLayout;
namespace chatterino {
class EditableModelView : public QWidget
{
public:
EditableModelView(QAbstractTableModel *model);
void setTitles(std::initializer_list<QString> titles);
QTableView *getTableView();
QAbstractTableModel *getModel();
pajlada::Signals::NoArgSignal addButtonPressed;
void addCustomButton(QWidget *widget);
void addRegexHelpLink();
private:
QTableView *tableView_{};
QAbstractTableModel *model_{};
QHBoxLayout *buttons_{};
};
} // namespace chatterino
+48 -48
View File
@@ -1,48 +1,48 @@
#pragma once
#include "widgets/BaseWidget.hpp"
#include <QPainter>
namespace chatterino {
class Line : public BaseWidget
{
public:
Line(bool vertical)
: BaseWidget(nullptr)
, vertical_(vertical)
{
if (this->vertical_)
{
this->setScaleIndependantWidth(8);
}
else
{
this->setScaleIndependantHeight(8);
}
}
virtual void paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(QColor("#999"));
if (this->vertical_)
{
painter.drawLine(this->width() / 2, 0, this->width() / 2,
this->height());
}
else
{
painter.drawLine(0, this->height() / 2, this->width(),
this->height() / 2);
}
}
private:
bool vertical_;
};
} // namespace chatterino
#pragma once
#include "widgets/BaseWidget.hpp"
#include <QPainter>
namespace chatterino {
class Line : public BaseWidget
{
public:
Line(bool vertical)
: BaseWidget(nullptr)
, vertical_(vertical)
{
if (this->vertical_)
{
this->setScaleIndependantWidth(8);
}
else
{
this->setScaleIndependantHeight(8);
}
}
virtual void paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(QColor("#999"));
if (this->vertical_)
{
painter.drawLine(this->width() / 2, 0, this->width() / 2,
this->height());
}
else
{
painter.drawLine(0, this->height() / 2, this->width(),
this->height() / 2);
}
}
private:
bool vertical_;
};
} // namespace chatterino
+116 -116
View File
@@ -1,116 +1,116 @@
#include "SearchPopup.hpp"
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "widgets/helper/ChannelView.hpp"
namespace chatterino {
namespace {
ChannelPtr filter(const QString &text, const QString &channelName,
const LimitedQueueSnapshot<MessagePtr> &snapshot)
{
ChannelPtr channel(new Channel(channelName, Channel::Type::None));
for (size_t i = 0; i < snapshot.size(); i++)
{
MessagePtr message = snapshot[i];
if (text.isEmpty() ||
message->searchText.indexOf(text, 0, Qt::CaseInsensitive) != -1)
{
channel->addMessage(message);
}
}
return channel;
}
} // namespace
SearchPopup::SearchPopup()
{
this->initLayout();
this->resize(400, 600);
}
void SearchPopup::setChannel(const ChannelPtr &channel)
{
this->channelName_ = channel->getName();
this->snapshot_ = channel->getMessageSnapshot();
this->search();
this->updateWindowTitle();
}
void SearchPopup::updateWindowTitle()
{
this->setWindowTitle("Searching in " + this->channelName_ + "s history");
}
void SearchPopup::search()
{
this->channelView_->setChannel(filter(this->searchInput_->text(),
this->channelName_, this->snapshot_));
}
void SearchPopup::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
{
this->close();
return;
}
BaseWidget::keyPressEvent(e);
}
void SearchPopup::initLayout()
{
// VBOX
{
QVBoxLayout *layout1 = new QVBoxLayout(this);
layout1->setMargin(0);
layout1->setSpacing(0);
// HBOX
{
QHBoxLayout *layout2 = new QHBoxLayout(this);
layout2->setMargin(8);
layout2->setSpacing(8);
// SEARCH INPUT
{
this->searchInput_ = new QLineEdit(this);
layout2->addWidget(this->searchInput_);
QObject::connect(this->searchInput_, &QLineEdit::returnPressed,
[this] { this->search(); });
}
// SEARCH BUTTON
{
QPushButton *searchButton = new QPushButton(this);
searchButton->setText("Search");
layout2->addWidget(searchButton);
QObject::connect(searchButton, &QPushButton::clicked,
[this] { this->search(); });
}
layout1->addLayout(layout2);
}
// CHANNELVIEW
{
this->channelView_ = new ChannelView(this);
layout1->addWidget(this->channelView_);
}
this->setLayout(layout1);
}
}
} // namespace chatterino
#include "SearchPopup.hpp"
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "widgets/helper/ChannelView.hpp"
namespace chatterino {
namespace {
ChannelPtr filter(const QString &text, const QString &channelName,
const LimitedQueueSnapshot<MessagePtr> &snapshot)
{
ChannelPtr channel(new Channel(channelName, Channel::Type::None));
for (size_t i = 0; i < snapshot.size(); i++)
{
MessagePtr message = snapshot[i];
if (text.isEmpty() ||
message->searchText.indexOf(text, 0, Qt::CaseInsensitive) != -1)
{
channel->addMessage(message);
}
}
return channel;
}
} // namespace
SearchPopup::SearchPopup()
{
this->initLayout();
this->resize(400, 600);
}
void SearchPopup::setChannel(const ChannelPtr &channel)
{
this->channelName_ = channel->getName();
this->snapshot_ = channel->getMessageSnapshot();
this->search();
this->updateWindowTitle();
}
void SearchPopup::updateWindowTitle()
{
this->setWindowTitle("Searching in " + this->channelName_ + "s history");
}
void SearchPopup::search()
{
this->channelView_->setChannel(filter(this->searchInput_->text(),
this->channelName_, this->snapshot_));
}
void SearchPopup::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
{
this->close();
return;
}
BaseWidget::keyPressEvent(e);
}
void SearchPopup::initLayout()
{
// VBOX
{
QVBoxLayout *layout1 = new QVBoxLayout(this);
layout1->setMargin(0);
layout1->setSpacing(0);
// HBOX
{
QHBoxLayout *layout2 = new QHBoxLayout(this);
layout2->setMargin(8);
layout2->setSpacing(8);
// SEARCH INPUT
{
this->searchInput_ = new QLineEdit(this);
layout2->addWidget(this->searchInput_);
QObject::connect(this->searchInput_, &QLineEdit::returnPressed,
[this] { this->search(); });
}
// SEARCH BUTTON
{
QPushButton *searchButton = new QPushButton(this);
searchButton->setText("Search");
layout2->addWidget(searchButton);
QObject::connect(searchButton, &QPushButton::clicked,
[this] { this->search(); });
}
layout1->addLayout(layout2);
}
// CHANNELVIEW
{
this->channelView_ = new ChannelView(this);
layout1->addWidget(this->channelView_);
}
this->setLayout(layout1);
}
}
} // namespace chatterino
+35 -35
View File
@@ -1,35 +1,35 @@
#pragma once
#include "ForwardDecl.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "widgets/BaseWindow.hpp"
#include <memory>
class QLineEdit;
namespace chatterino {
class SearchPopup : public BaseWindow
{
public:
SearchPopup();
virtual void setChannel(const ChannelPtr &channel);
protected:
void keyPressEvent(QKeyEvent *e) override;
virtual void updateWindowTitle();
private:
void initLayout();
void search();
LimitedQueueSnapshot<MessagePtr> snapshot_;
QLineEdit *searchInput_{};
ChannelView *channelView_{};
QString channelName_{};
};
} // namespace chatterino
#pragma once
#include "ForwardDecl.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "widgets/BaseWindow.hpp"
#include <memory>
class QLineEdit;
namespace chatterino {
class SearchPopup : public BaseWindow
{
public:
SearchPopup();
virtual void setChannel(const ChannelPtr &channel);
protected:
void keyPressEvent(QKeyEvent *e) override;
virtual void updateWindowTitle();
private:
void initLayout();
void search();
LimitedQueueSnapshot<MessagePtr> snapshot_;
QLineEdit *searchInput_{};
ChannelView *channelView_{};
QString channelName_{};
};
} // namespace chatterino
+222 -222
View File
@@ -1,222 +1,222 @@
#include "AboutPage.hpp"
#include "debug/Log.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/helper/SignalLabel.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QTextEdit>
#include <QTextStream>
#include <QVBoxLayout>
#define PIXMAP_WIDTH 500
namespace chatterino {
AboutPage::AboutPage()
: SettingsPage("About", ":/settings/about.svg")
{
LayoutCreator<AboutPage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
{
QPixmap pixmap;
pixmap.load(":/settings/aboutlogo.png");
auto logo = layout.emplace<QLabel>().assign(&this->logo_);
logo->setPixmap(pixmap);
logo->setFixedSize(PIXMAP_WIDTH,
PIXMAP_WIDTH * pixmap.height() / pixmap.width());
logo->setScaledContents(true);
// this does nothing
// QPalette palette;
// palette.setColor(QPalette::Text, Qt::white);
// palette.setColor(QPalette::Link, "#a5cdff");
// palette.setColor(QPalette::LinkVisited, "#a5cdff");
/*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 = 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.getElement(), "Qt Framework", "https://www.qt.io",
":/licenses/qt_lgpl-3.0.txt");
addLicense(form.getElement(), "Boost", "https://www.boost.org/",
":/licenses/boost_boost.txt");
addLicense(form.getElement(), "Fmt", "http://fmtlib.net/",
":/licenses/fmt_bsd2.txt");
addLicense(form.getElement(), "LibCommuni",
"https://github.com/communi/libcommuni",
":/licenses/libcommuni_BSD3.txt");
addLicense(form.getElement(), "OpenSSL", "https://www.openssl.org/",
":/licenses/openssl.txt");
addLicense(form.getElement(), "RapidJson", "http://rapidjson.org/",
":/licenses/rapidjson.txt");
addLicense(form.getElement(), "Pajlada/Settings",
"https://github.com/pajlada/settings",
":/licenses/pajlada_settings.txt");
addLicense(form.getElement(), "Pajlada/Signals",
"https://github.com/pajlada/signals",
":/licenses/pajlada_signals.txt");
addLicense(form.getElement(), "Websocketpp",
"https://www.zaphoyd.com/websocketpp/",
":/licenses/websocketpp.txt");
}
auto attributions = layout.emplace<QGroupBox>("Attributions...");
{
auto l = attributions.emplace<QVBoxLayout>();
// clang-format off
l.emplace<QLabel>("EmojiOne 2 and 3 emojis provided by <a href=\"https://www.emojione.com/\">EmojiOne</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twemoji emojis provided by <a href=\"https://github.com/twitter/twemoji\">Twitter's Twemoji</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Facebook emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Apple emojis provided by <a href=\"https://apple.com\">Apple</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Google emojis provided by <a href=\"https://google.com\">Google</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Messenger emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Emoji datasource provided by <a href=\"https://www.iamcal.com/\">Cal Henderson</a>"
"(<a href=\"https://github.com/iamcal/emoji-data/blob/master/LICENSE\">show license</a>)")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twitch emote data provided by <a href=\"https://twitchemotes.com/\">twitchemotes.com</a> through the <a href=\"https://github.com/Chatterino/api\">Chatterino API</a>")->setOpenExternalLinks(true);
// clang-format on
}
// Contributors
auto contributors = layout.emplace<QGroupBox>("Contributors");
{
auto l = contributors.emplace<QVBoxLayout>();
QFile contributorsFile(":/contributors.txt");
contributorsFile.open(QFile::ReadOnly);
QTextStream stream(&contributorsFile);
stream.setCodec("UTF-8");
QString line;
while (stream.readLineInto(&line))
{
if (line.isEmpty() || line.startsWith('#'))
{
continue;
}
QStringList contributorParts = line.split("|");
if (contributorParts.size() != 4)
{
log("Missing parts in line '{}'", line);
continue;
}
QString username = contributorParts[0].trimmed();
QString url = contributorParts[1].trimmed();
QString avatarUrl = contributorParts[2].trimmed();
QString role = contributorParts[3].trimmed();
auto *usernameLabel =
new QLabel("<a href=\"" + url + "\">" + username + "</a>");
usernameLabel->setOpenExternalLinks(true);
auto *roleLabel = new QLabel(role);
auto contributorBox2 = l.emplace<QHBoxLayout>();
const auto addAvatar = [&avatarUrl, &contributorBox2] {
if (!avatarUrl.isEmpty())
{
QPixmap avatarPixmap;
avatarPixmap.load(avatarUrl);
auto avatar = contributorBox2.emplace<QLabel>();
avatar->setPixmap(avatarPixmap);
avatar->setFixedSize(64, 64);
avatar->setScaledContents(true);
}
};
const auto addLabels = [&contributorBox2, &usernameLabel,
&roleLabel] {
auto labelBox = new QVBoxLayout();
contributorBox2->addLayout(labelBox);
labelBox->addWidget(usernameLabel);
labelBox->addWidget(roleLabel);
};
addLabels();
addAvatar();
}
}
}
auto buildInfo = QStringList();
buildInfo += "Qt " QT_VERSION_STR;
#ifdef USEWINSDK
buildInfo += "Windows SDK";
#endif
#ifdef _MSC_FULL_VER
buildInfo += "MSVC " + QString::number(_MSC_FULL_VER, 10);
#endif
auto buildText = QString("Built with " + buildInfo.join(", "));
layout.emplace<QLabel>(buildText);
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 QLabel("<a href=\"" + licenseLink + "\">show license</a>");
QObject::connect(b, &QLabel::linkActivated, [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 chatterino
#include "AboutPage.hpp"
#include "debug/Log.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/helper/SignalLabel.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QTextEdit>
#include <QTextStream>
#include <QVBoxLayout>
#define PIXMAP_WIDTH 500
namespace chatterino {
AboutPage::AboutPage()
: SettingsPage("About", ":/settings/about.svg")
{
LayoutCreator<AboutPage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
{
QPixmap pixmap;
pixmap.load(":/settings/aboutlogo.png");
auto logo = layout.emplace<QLabel>().assign(&this->logo_);
logo->setPixmap(pixmap);
logo->setFixedSize(PIXMAP_WIDTH,
PIXMAP_WIDTH * pixmap.height() / pixmap.width());
logo->setScaledContents(true);
// this does nothing
// QPalette palette;
// palette.setColor(QPalette::Text, Qt::white);
// palette.setColor(QPalette::Link, "#a5cdff");
// palette.setColor(QPalette::LinkVisited, "#a5cdff");
/*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 = 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.getElement(), "Qt Framework", "https://www.qt.io",
":/licenses/qt_lgpl-3.0.txt");
addLicense(form.getElement(), "Boost", "https://www.boost.org/",
":/licenses/boost_boost.txt");
addLicense(form.getElement(), "Fmt", "http://fmtlib.net/",
":/licenses/fmt_bsd2.txt");
addLicense(form.getElement(), "LibCommuni",
"https://github.com/communi/libcommuni",
":/licenses/libcommuni_BSD3.txt");
addLicense(form.getElement(), "OpenSSL", "https://www.openssl.org/",
":/licenses/openssl.txt");
addLicense(form.getElement(), "RapidJson", "http://rapidjson.org/",
":/licenses/rapidjson.txt");
addLicense(form.getElement(), "Pajlada/Settings",
"https://github.com/pajlada/settings",
":/licenses/pajlada_settings.txt");
addLicense(form.getElement(), "Pajlada/Signals",
"https://github.com/pajlada/signals",
":/licenses/pajlada_signals.txt");
addLicense(form.getElement(), "Websocketpp",
"https://www.zaphoyd.com/websocketpp/",
":/licenses/websocketpp.txt");
}
auto attributions = layout.emplace<QGroupBox>("Attributions...");
{
auto l = attributions.emplace<QVBoxLayout>();
// clang-format off
l.emplace<QLabel>("EmojiOne 2 and 3 emojis provided by <a href=\"https://www.emojione.com/\">EmojiOne</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twemoji emojis provided by <a href=\"https://github.com/twitter/twemoji\">Twitter's Twemoji</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Facebook emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Apple emojis provided by <a href=\"https://apple.com\">Apple</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Google emojis provided by <a href=\"https://google.com\">Google</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Messenger emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Emoji datasource provided by <a href=\"https://www.iamcal.com/\">Cal Henderson</a>"
"(<a href=\"https://github.com/iamcal/emoji-data/blob/master/LICENSE\">show license</a>)")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twitch emote data provided by <a href=\"https://twitchemotes.com/\">twitchemotes.com</a> through the <a href=\"https://github.com/Chatterino/api\">Chatterino API</a>")->setOpenExternalLinks(true);
// clang-format on
}
// Contributors
auto contributors = layout.emplace<QGroupBox>("Contributors");
{
auto l = contributors.emplace<QVBoxLayout>();
QFile contributorsFile(":/contributors.txt");
contributorsFile.open(QFile::ReadOnly);
QTextStream stream(&contributorsFile);
stream.setCodec("UTF-8");
QString line;
while (stream.readLineInto(&line))
{
if (line.isEmpty() || line.startsWith('#'))
{
continue;
}
QStringList contributorParts = line.split("|");
if (contributorParts.size() != 4)
{
log("Missing parts in line '{}'", line);
continue;
}
QString username = contributorParts[0].trimmed();
QString url = contributorParts[1].trimmed();
QString avatarUrl = contributorParts[2].trimmed();
QString role = contributorParts[3].trimmed();
auto *usernameLabel =
new QLabel("<a href=\"" + url + "\">" + username + "</a>");
usernameLabel->setOpenExternalLinks(true);
auto *roleLabel = new QLabel(role);
auto contributorBox2 = l.emplace<QHBoxLayout>();
const auto addAvatar = [&avatarUrl, &contributorBox2] {
if (!avatarUrl.isEmpty())
{
QPixmap avatarPixmap;
avatarPixmap.load(avatarUrl);
auto avatar = contributorBox2.emplace<QLabel>();
avatar->setPixmap(avatarPixmap);
avatar->setFixedSize(64, 64);
avatar->setScaledContents(true);
}
};
const auto addLabels = [&contributorBox2, &usernameLabel,
&roleLabel] {
auto labelBox = new QVBoxLayout();
contributorBox2->addLayout(labelBox);
labelBox->addWidget(usernameLabel);
labelBox->addWidget(roleLabel);
};
addLabels();
addAvatar();
}
}
}
auto buildInfo = QStringList();
buildInfo += "Qt " QT_VERSION_STR;
#ifdef USEWINSDK
buildInfo += "Windows SDK";
#endif
#ifdef _MSC_FULL_VER
buildInfo += "MSVC " + QString::number(_MSC_FULL_VER, 10);
#endif
auto buildText = QString("Built with " + buildInfo.join(", "));
layout.emplace<QLabel>(buildText);
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 QLabel("<a href=\"" + licenseLink + "\">show license</a>");
QObject::connect(b, &QLabel::linkActivated, [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 chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QFormLayout;
namespace chatterino {
class AboutPage : public SettingsPage
{
public:
AboutPage();
private:
void addLicense(QFormLayout *form, const QString &name_,
const QString &website, const QString &licenseLink);
QLabel *logo_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QFormLayout;
namespace chatterino {
class AboutPage : public SettingsPage
{
public:
AboutPage();
private:
void addLicense(QFormLayout *form, const QString &name_,
const QString &website, const QString &licenseLink);
QLabel *logo_;
};
} // namespace chatterino
+69 -69
View File
@@ -1,69 +1,69 @@
#include "AccountsPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/accounts/AccountModel.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/dialogs/LoginDialog.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QTableView>
#include <QVBoxLayout>
#include <algorithm>
namespace chatterino {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/settings/accounts.svg")
{
auto *app = getApp();
LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
EditableModelView *view =
layout.emplace<EditableModelView>(app->accounts->createModel(nullptr))
.getElement();
view->getTableView()->horizontalHeader()->setVisible(false);
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
static auto loginWidget = new LoginWidget();
loginWidget->show();
loginWidget->raise();
});
view->getTableView()->setStyleSheet("background: #333");
// 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 chatterino
#include "AccountsPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/accounts/AccountModel.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/dialogs/LoginDialog.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QTableView>
#include <QVBoxLayout>
#include <algorithm>
namespace chatterino {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/settings/accounts.svg")
{
auto *app = getApp();
LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
EditableModelView *view =
layout.emplace<EditableModelView>(app->accounts->createModel(nullptr))
.getElement();
view->getTableView()->horizontalHeader()->setVisible(false);
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
static auto loginWidget = new LoginWidget();
loginWidget->show();
loginWidget->raise();
});
view->getTableView()->setStyleSheet("background: #333");
// 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 chatterino
+21 -21
View File
@@ -1,21 +1,21 @@
#pragma once
#include "widgets/AccountSwitchWidget.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
#include <QPushButton>
namespace chatterino {
class AccountsPage : public SettingsPage
{
public:
AccountsPage();
private:
QPushButton *addButton_;
QPushButton *removeButton_;
AccountSwitchWidget *accountSwitchWidget_;
};
} // namespace chatterino
#pragma once
#include "widgets/AccountSwitchWidget.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
#include <QPushButton>
namespace chatterino {
class AccountsPage : public SettingsPage
{
public:
AccountsPage();
private:
QPushButton *addButton_;
QPushButton *removeButton_;
AccountSwitchWidget *accountSwitchWidget_;
};
} // namespace chatterino
+88 -88
View File
@@ -1,88 +1,88 @@
#include "CommandPage.hpp"
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTableView>
#include <QTextEdit>
#include "Application.hpp"
#include "controllers/commands/CommandController.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
//#include "widgets/helper/ComboBoxItemDelegate.hpp"
#include "util/CombinePath.hpp"
#include <QLabel>
#include <QTextEdit>
// clang-format off
#define TEXT "{1} => first word &nbsp;&nbsp;&nbsp; {1+} => first word and after &nbsp;&nbsp;&nbsp; {{ => { &nbsp;&nbsp;&nbsp; <a href='https://chatterino.com/help/commands'>more info</a>"
// clang-format on
namespace chatterino {
namespace {
QString c1settingsPath()
{
return combinePath(qgetenv("appdata"),
"Chatterino\\Custom\\Commands.txt");
}
} // namespace
CommandPage::CommandPage()
: SettingsPage("Commands", ":/settings/commands.svg")
{
auto app = getApp();
LayoutCreator<CommandPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
EditableModelView *view =
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
.getElement();
view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->commands->items_.appendItem(
Command{"/command", "I made a new command HeyGuys"});
});
if (QFile(c1settingsPath()).exists())
{
auto button = new QPushButton("Import commands from Chatterino 1");
view->addCustomButton(button);
QObject::connect(button, &QPushButton::clicked, this, [] {
QFile c1settings = c1settingsPath();
c1settings.open(QIODevice::ReadOnly);
for (auto line : QString(c1settings.readAll())
.split(QRegularExpression("[\r\n]"),
QString::SkipEmptyParts))
{
if (int index = line.indexOf(' '); index != -1)
{
getApp()->commands->items_.insertItem(
Command(line.mid(0, index), line.mid(index + 1)));
}
}
});
}
layout.append(
this->createCheckBox("Also match the trigger at the end of the message",
getSettings()->allowCommandsAtEnd));
QLabel *text = layout.emplace<QLabel>(TEXT).getElement();
text->setWordWrap(true);
text->setStyleSheet("color: #bbb");
text->setOpenExternalLinks(true);
// ---- end of layout
this->commandsEditTimer_.setSingleShot(true);
}
} // namespace chatterino
#include "CommandPage.hpp"
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTableView>
#include <QTextEdit>
#include "Application.hpp"
#include "controllers/commands/CommandController.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
//#include "widgets/helper/ComboBoxItemDelegate.hpp"
#include "util/CombinePath.hpp"
#include <QLabel>
#include <QTextEdit>
// clang-format off
#define TEXT "{1} => first word &nbsp;&nbsp;&nbsp; {1+} => first word and after &nbsp;&nbsp;&nbsp; {{ => { &nbsp;&nbsp;&nbsp; <a href='https://chatterino.com/help/commands'>more info</a>"
// clang-format on
namespace chatterino {
namespace {
QString c1settingsPath()
{
return combinePath(qgetenv("appdata"),
"Chatterino\\Custom\\Commands.txt");
}
} // namespace
CommandPage::CommandPage()
: SettingsPage("Commands", ":/settings/commands.svg")
{
auto app = getApp();
LayoutCreator<CommandPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
EditableModelView *view =
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
.getElement();
view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->commands->items_.appendItem(
Command{"/command", "I made a new command HeyGuys"});
});
if (QFile(c1settingsPath()).exists())
{
auto button = new QPushButton("Import commands from Chatterino 1");
view->addCustomButton(button);
QObject::connect(button, &QPushButton::clicked, this, [] {
QFile c1settings = c1settingsPath();
c1settings.open(QIODevice::ReadOnly);
for (auto line : QString(c1settings.readAll())
.split(QRegularExpression("[\r\n]"),
QString::SkipEmptyParts))
{
if (int index = line.indexOf(' '); index != -1)
{
getApp()->commands->items_.insertItem(
Command(line.mid(0, index), line.mid(index + 1)));
}
}
});
}
layout.append(
this->createCheckBox("Also match the trigger at the end of the message",
getSettings()->allowCommandsAtEnd));
QLabel *text = layout.emplace<QLabel>(TEXT).getElement();
text->setWordWrap(true);
text->setStyleSheet("color: #bbb");
text->setOpenExternalLinks(true);
// ---- end of layout
this->commandsEditTimer_.setSingleShot(true);
}
} // namespace chatterino
+19 -19
View File
@@ -1,19 +1,19 @@
#pragma once
#include <QTextEdit>
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class CommandPage : public SettingsPage
{
public:
CommandPage();
private:
QTimer commandsEditTimer_;
};
} // namespace chatterino
#pragma once
#include <QTextEdit>
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class CommandPage : public SettingsPage
{
public:
CommandPage();
private:
QTimer commandsEditTimer_;
};
} // namespace chatterino
+74 -74
View File
@@ -1,74 +1,74 @@
#include "ExternalToolsPage.hpp"
#include "Application.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include <QGroupBox>
#define STREAMLINK_QUALITY \
"Choose", "Source", "High", "Medium", "Low", "Audio only"
namespace chatterino {
ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", ":/settings/externaltools.svg")
{
LayoutCreator<ExternalToolsPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
{
auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>();
auto description = new QLabel(
"Streamlink is a command-line utility that pipes video streams "
"from various "
"services into a video player, such as VLC. Make sure to edit the "
"configuration file before you use it!");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
auto links = new QLabel(
createNamedLink("https://streamlink.github.io/", "Website") + " " +
createNamedLink(
"https://github.com/streamlink/streamlink/releases/latest",
"Download"));
links->setTextFormat(Qt::RichText);
links->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
links->setOpenExternalLinks(true);
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);
auto customPathCb =
this->createCheckBox("Use custom path (Enable if using "
"non-standard streamlink installation path)",
getSettings()->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);
auto customPath = this->createLineEdit(getSettings()->streamlinkPath);
customPath->setPlaceholderText(
"Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
groupLayout->addRow(
"Preferred quality:",
this->createComboBox({STREAMLINK_QUALITY},
getSettings()->preferredQuality));
groupLayout->addRow(
"Additional options:",
this->createLineEdit(getSettings()->streamlinkOpts));
getSettings()->streamlinkUseCustomPath.connect(
[=](const auto &value, auto) {
customPath->setEnabled(value); //
},
this->managedConnections_);
}
layout->addStretch(1);
}
} // namespace chatterino
#include "ExternalToolsPage.hpp"
#include "Application.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include <QGroupBox>
#define STREAMLINK_QUALITY \
"Choose", "Source", "High", "Medium", "Low", "Audio only"
namespace chatterino {
ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", ":/settings/externaltools.svg")
{
LayoutCreator<ExternalToolsPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
{
auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>();
auto description = new QLabel(
"Streamlink is a command-line utility that pipes video streams "
"from various "
"services into a video player, such as VLC. Make sure to edit the "
"configuration file before you use it!");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
auto links = new QLabel(
createNamedLink("https://streamlink.github.io/", "Website") + " " +
createNamedLink(
"https://github.com/streamlink/streamlink/releases/latest",
"Download"));
links->setTextFormat(Qt::RichText);
links->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
links->setOpenExternalLinks(true);
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);
auto customPathCb =
this->createCheckBox("Use custom path (Enable if using "
"non-standard streamlink installation path)",
getSettings()->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);
auto customPath = this->createLineEdit(getSettings()->streamlinkPath);
customPath->setPlaceholderText(
"Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
groupLayout->addRow(
"Preferred quality:",
this->createComboBox({STREAMLINK_QUALITY},
getSettings()->preferredQuality));
groupLayout->addRow(
"Additional options:",
this->createLineEdit(getSettings()->streamlinkOpts));
getSettings()->streamlinkUseCustomPath.connect(
[=](const auto &value, auto) {
customPath->setEnabled(value); //
},
this->managedConnections_);
}
layout->addStretch(1);
}
} // namespace chatterino
+13 -13
View File
@@ -1,13 +1,13 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class ExternalToolsPage : public SettingsPage
{
public:
ExternalToolsPage();
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class ExternalToolsPage : public SettingsPage
{
public:
ExternalToolsPage();
};
} // namespace chatterino
File diff suppressed because it is too large Load Diff
+191 -191
View File
@@ -1,191 +1,191 @@
#pragma once
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include "Application.hpp"
#include "boost/variant.hpp"
#include "pajlada/signals/signal.hpp"
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QCheckBox;
class QComboBox;
namespace chatterino {
class TitleLabel : public QLabel
{
Q_OBJECT
public:
TitleLabel(const QString &text)
: QLabel(text)
{
}
};
class DescriptionLabel : public QLabel
{
Q_OBJECT
public:
DescriptionLabel(const QString &text)
: QLabel(text)
{
}
};
struct DropdownArgs {
QString value;
int index;
QComboBox *combobox;
};
class ComboBox : public QComboBox
{
Q_OBJECT
void wheelEvent(QWheelEvent *event) override
{
}
};
class SettingsLayout : public QVBoxLayout
{
Q_OBJECT
public:
TitleLabel *addTitle(const QString &text);
/// @param inverse Inverses true to false and vice versa
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
bool inverse = false);
ComboBox *addDropdown(const QString &text, const QStringList &items);
ComboBox *addDropdown(const QString &text, const QStringList &items,
pajlada::Settings::Setting<QString> &setting,
bool editable = false);
template <typename OnClick>
QPushButton *makeButton(const QString &text, OnClick onClick)
{
auto button = new QPushButton(text);
this->groups_.back().widgets.push_back({button, {text}});
QObject::connect(button, &QPushButton::clicked, onClick);
return button;
}
template <typename OnClick>
QPushButton *addButton(const QString &text, OnClick onClick)
{
auto button = makeButton(text, onClick);
auto layout = new QHBoxLayout();
layout->addWidget(button);
layout->addStretch(1);
this->addLayout(layout);
return button;
}
template <typename T>
ComboBox *addDropdown(
const QString &text, const QStringList &items,
pajlada::Settings::Setting<T> &setting,
std::function<boost::variant<int, QString>(T)> getValue,
std::function<T(DropdownArgs)> setValue, bool editable = true)
{
auto items2 = items;
auto selected = getValue(setting.getValue());
if (selected.which() == 1)
{
// QString
if (!editable && !items2.contains(boost::get<QString>(selected)))
items2.insert(0, boost::get<QString>(selected));
}
auto combo = this->addDropdown(text, items2);
if (editable)
combo->setEditable(true);
if (selected.which() == 0)
{
// int
auto value = boost::get<int>(selected);
if (value >= 0 && value < items2.size())
combo->setCurrentIndex(value);
}
else if (selected.which() == 1)
{
// QString
combo->setEditText(boost::get<QString>(selected));
}
setting.connect(
[getValue = std::move(getValue), combo](const T &value, auto) {
auto var = getValue(value);
if (var.which() == 0)
combo->setCurrentIndex(boost::get<int>(var));
else
{
combo->setCurrentText(boost::get<QString>(var));
combo->setEditText(boost::get<QString>(var));
}
},
this->managedConnections_);
QObject::connect(
combo,
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
// &QComboBox::editTextChanged,
[combo, &setting,
setValue = std::move(setValue)](const QString &newValue) {
setting = setValue(
DropdownArgs{newValue, combo->currentIndex(), combo});
getApp()->windows->forceLayoutChannelViews();
});
return combo;
}
DescriptionLabel *addDescription(const QString &text);
void addSeperator();
bool filterElements(const QString &query);
private:
struct Widget {
QWidget *element;
QStringList keywords;
};
struct Group {
QString name;
QWidget *title{};
std::vector<Widget> widgets;
};
std::vector<Group> groups_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
class GeneralPage : public SettingsPage
{
Q_OBJECT
public:
GeneralPage();
bool filterElements(const QString &query);
private:
void initLayout(SettingsLayout &layout);
void initExtra();
QString getFont(const DropdownArgs &args) const;
DescriptionLabel *cachePath_{};
SettingsLayout *settingsLayout_{};
};
} // namespace chatterino
#pragma once
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include "Application.hpp"
#include "boost/variant.hpp"
#include "pajlada/signals/signal.hpp"
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QCheckBox;
class QComboBox;
namespace chatterino {
class TitleLabel : public QLabel
{
Q_OBJECT
public:
TitleLabel(const QString &text)
: QLabel(text)
{
}
};
class DescriptionLabel : public QLabel
{
Q_OBJECT
public:
DescriptionLabel(const QString &text)
: QLabel(text)
{
}
};
struct DropdownArgs {
QString value;
int index;
QComboBox *combobox;
};
class ComboBox : public QComboBox
{
Q_OBJECT
void wheelEvent(QWheelEvent *event) override
{
}
};
class SettingsLayout : public QVBoxLayout
{
Q_OBJECT
public:
TitleLabel *addTitle(const QString &text);
/// @param inverse Inverses true to false and vice versa
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
bool inverse = false);
ComboBox *addDropdown(const QString &text, const QStringList &items);
ComboBox *addDropdown(const QString &text, const QStringList &items,
pajlada::Settings::Setting<QString> &setting,
bool editable = false);
template <typename OnClick>
QPushButton *makeButton(const QString &text, OnClick onClick)
{
auto button = new QPushButton(text);
this->groups_.back().widgets.push_back({button, {text}});
QObject::connect(button, &QPushButton::clicked, onClick);
return button;
}
template <typename OnClick>
QPushButton *addButton(const QString &text, OnClick onClick)
{
auto button = makeButton(text, onClick);
auto layout = new QHBoxLayout();
layout->addWidget(button);
layout->addStretch(1);
this->addLayout(layout);
return button;
}
template <typename T>
ComboBox *addDropdown(
const QString &text, const QStringList &items,
pajlada::Settings::Setting<T> &setting,
std::function<boost::variant<int, QString>(T)> getValue,
std::function<T(DropdownArgs)> setValue, bool editable = true)
{
auto items2 = items;
auto selected = getValue(setting.getValue());
if (selected.which() == 1)
{
// QString
if (!editable && !items2.contains(boost::get<QString>(selected)))
items2.insert(0, boost::get<QString>(selected));
}
auto combo = this->addDropdown(text, items2);
if (editable)
combo->setEditable(true);
if (selected.which() == 0)
{
// int
auto value = boost::get<int>(selected);
if (value >= 0 && value < items2.size())
combo->setCurrentIndex(value);
}
else if (selected.which() == 1)
{
// QString
combo->setEditText(boost::get<QString>(selected));
}
setting.connect(
[getValue = std::move(getValue), combo](const T &value, auto) {
auto var = getValue(value);
if (var.which() == 0)
combo->setCurrentIndex(boost::get<int>(var));
else
{
combo->setCurrentText(boost::get<QString>(var));
combo->setEditText(boost::get<QString>(var));
}
},
this->managedConnections_);
QObject::connect(
combo,
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
// &QComboBox::editTextChanged,
[combo, &setting,
setValue = std::move(setValue)](const QString &newValue) {
setting = setValue(
DropdownArgs{newValue, combo->currentIndex(), combo});
getApp()->windows->forceLayoutChannelViews();
});
return combo;
}
DescriptionLabel *addDescription(const QString &text);
void addSeperator();
bool filterElements(const QString &query);
private:
struct Widget {
QWidget *element;
QStringList keywords;
};
struct Group {
QString name;
QWidget *title{};
std::vector<Widget> widgets;
};
std::vector<Group> groups_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
class GeneralPage : public SettingsPage
{
Q_OBJECT
public:
GeneralPage();
bool filterElements(const QString &query);
private:
void initLayout(SettingsLayout &layout);
void initExtra();
QString getFont(const DropdownArgs &args) const;
DescriptionLabel *cachePath_{};
SettingsLayout *settingsLayout_{};
};
} // namespace chatterino
+175 -175
View File
@@ -1,175 +1,175 @@
#include "HighlightingPage.hpp"
#include "Application.hpp"
#include "controllers/highlights/HighlightBlacklistModel.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightModel.hpp"
#include "controllers/highlights/UserHighlightModel.hpp"
#include "debug/Log.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QHeaderView>
#include <QListWidget>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTabWidget>
#include <QTableView>
#include <QTextEdit>
#define ENABLE_HIGHLIGHTS "Enable Highlighting"
#define HIGHLIGHT_MSG "Highlight messages containing your name"
#define PLAY_SOUND "Play sound when your name is mentioned"
#define FLASH_TASKBAR "Flash taskbar when your name is mentioned"
#define ALWAYS_PLAY "Play highlight sound even when Chatterino is focused"
namespace chatterino {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/settings/notifications.svg")
{
auto app = getApp();
LayoutCreator<HighlightingPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
// GENERAL
// layout.append(this->createCheckBox(ENABLE_HIGHLIGHTS,
// getSettings()->enableHighlights));
// TABS
auto tabs = layout.emplace<QTabWidget>();
{
// HIGHLIGHTS
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
{
highlights.emplace<QLabel>("Messages can be highlighted if "
"they match a certain pattern.");
EditableModelView *view =
highlights
.emplace<EditableModelView>(
app->highlights->createModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex", "Case-\nsensitive"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->phrases.appendItem(HighlightPhrase{
"my phrase", true, false, false, false});
});
}
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
{
pingUsers.emplace<QLabel>(
"Messages from a certain user can be highlighted.");
EditableModelView *view =
pingUsers
.emplace<EditableModelView>(
app->highlights->createUserModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->getTableView()->horizontalHeader()->hideSection(4);
// Case-sensitivity doesn't make sense for user names so it is
// set to "false" by default & no checkbox is shown
view->setTitles({"Username", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->highlightedUsers.appendItem(
HighlightPhrase{"highlighted user", true, false, false,
false});
});
}
auto disabledUsers =
tabs.appendTab(new QVBoxLayout, "Excluded Users");
{
disabledUsers.emplace<QLabel>(
"This is a list of users (e.g. bots) whose messages should "
"<u>not</u> be highlighted.");
EditableModelView *view =
disabledUsers
.emplace<EditableModelView>(
app->highlights->createBlacklistModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->blacklistedUsers.appendItem(
HighlightBlacklistUser{"blacklisted user", false});
});
}
}
// MISC
auto customSound = layout.emplace<QHBoxLayout>().withoutMargin();
{
customSound.append(this->createCheckBox(
"Custom sound", getSettings()->customHighlightSound));
auto selectFile =
customSound.emplace<QPushButton>("Select custom sound file");
QObject::connect(selectFile.getElement(), &QPushButton::clicked,
this, [this] {
auto fileName = QFileDialog::getOpenFileName(
this, tr("Open Sound"), "",
tr("Audio Files (*.mp3 *.wav)"));
getSettings()->pathHighlightSound = fileName;
});
}
layout.append(createCheckBox(ALWAYS_PLAY,
getSettings()->highlightAlwaysPlaySound));
layout.append(createCheckBox(
"Flash taskbar only stops highlighting when chatterino is focused",
getSettings()->longAlerts));
}
// ---- misc
this->disabledUsersChangedTimer_.setSingleShot(true);
}
} // namespace chatterino
#include "HighlightingPage.hpp"
#include "Application.hpp"
#include "controllers/highlights/HighlightBlacklistModel.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightModel.hpp"
#include "controllers/highlights/UserHighlightModel.hpp"
#include "debug/Log.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QHeaderView>
#include <QListWidget>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTabWidget>
#include <QTableView>
#include <QTextEdit>
#define ENABLE_HIGHLIGHTS "Enable Highlighting"
#define HIGHLIGHT_MSG "Highlight messages containing your name"
#define PLAY_SOUND "Play sound when your name is mentioned"
#define FLASH_TASKBAR "Flash taskbar when your name is mentioned"
#define ALWAYS_PLAY "Play highlight sound even when Chatterino is focused"
namespace chatterino {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/settings/notifications.svg")
{
auto app = getApp();
LayoutCreator<HighlightingPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
// GENERAL
// layout.append(this->createCheckBox(ENABLE_HIGHLIGHTS,
// getSettings()->enableHighlights));
// TABS
auto tabs = layout.emplace<QTabWidget>();
{
// HIGHLIGHTS
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
{
highlights.emplace<QLabel>("Messages can be highlighted if "
"they match a certain pattern.");
EditableModelView *view =
highlights
.emplace<EditableModelView>(
app->highlights->createModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex", "Case-\nsensitive"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->phrases.appendItem(HighlightPhrase{
"my phrase", true, false, false, false});
});
}
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
{
pingUsers.emplace<QLabel>(
"Messages from a certain user can be highlighted.");
EditableModelView *view =
pingUsers
.emplace<EditableModelView>(
app->highlights->createUserModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->getTableView()->horizontalHeader()->hideSection(4);
// Case-sensitivity doesn't make sense for user names so it is
// set to "false" by default & no checkbox is shown
view->setTitles({"Username", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->highlightedUsers.appendItem(
HighlightPhrase{"highlighted user", true, false, false,
false});
});
}
auto disabledUsers =
tabs.appendTab(new QVBoxLayout, "Excluded Users");
{
disabledUsers.emplace<QLabel>(
"This is a list of users (e.g. bots) whose messages should "
"<u>not</u> be highlighted.");
EditableModelView *view =
disabledUsers
.emplace<EditableModelView>(
app->highlights->createBlacklistModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->blacklistedUsers.appendItem(
HighlightBlacklistUser{"blacklisted user", false});
});
}
}
// MISC
auto customSound = layout.emplace<QHBoxLayout>().withoutMargin();
{
customSound.append(this->createCheckBox(
"Custom sound", getSettings()->customHighlightSound));
auto selectFile =
customSound.emplace<QPushButton>("Select custom sound file");
QObject::connect(selectFile.getElement(), &QPushButton::clicked,
this, [this] {
auto fileName = QFileDialog::getOpenFileName(
this, tr("Open Sound"), "",
tr("Audio Files (*.mp3 *.wav)"));
getSettings()->pathHighlightSound = fileName;
});
}
layout.append(createCheckBox(ALWAYS_PLAY,
getSettings()->highlightAlwaysPlaySound));
layout.append(createCheckBox(
"Flash taskbar only stops highlighting when chatterino is focused",
getSettings()->longAlerts));
}
// ---- misc
this->disabledUsersChangedTimer_.setSingleShot(true);
}
} // namespace chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QAbstractTableModel>
#include <QTimer>
class QPushButton;
class QListWidget;
namespace chatterino {
class HighlightingPage : public SettingsPage
{
public:
HighlightingPage();
private:
QTimer disabledUsersChangedTimer_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QAbstractTableModel>
#include <QTimer>
class QPushButton;
class QListWidget;
namespace chatterino {
class HighlightingPage : public SettingsPage
{
public:
HighlightingPage();
private:
QTimer disabledUsersChangedTimer_;
};
} // namespace chatterino
+133 -133
View File
@@ -1,133 +1,133 @@
#include "IgnoresPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/ignores/IgnoreModel.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QCheckBox>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
// clang-format off
#define INFO "/ignore <user> in chat ignores a user.\n/unignore <user> in chat unignores a user.\nYou can also click on a user to open the usercard."
// clang-format on
namespace chatterino {
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
QStringListModel &model);
IgnoresPage::IgnoresPage()
: SettingsPage("Ignores", ":/settings/ignore.svg")
{
LayoutCreator<IgnoresPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
auto tabs = layout.emplace<QTabWidget>();
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Messages"));
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"),
this->userListModel_);
}
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
{
layout.emplace<QLabel>("Messages can be ignored if "
"they match a certain pattern.");
EditableModelView *view =
layout
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
.getElement();
view->setTitles(
{"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addRegexHelpLink();
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->ignores->phrases.appendItem(
IgnorePhrase{"my pattern", false, false,
getSettings()->ignoredPhraseReplace.getValue(), true});
});
}
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
QStringListModel &userModel)
{
auto label = users.emplace<QLabel>(INFO);
label->setWordWrap(true);
users.append(page.createCheckBox("Enable twitch ignored users",
getSettings()->enableTwitchIgnoredUsers));
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
{
anyways.emplace<QLabel>("Show messages from ignored users anyways:");
auto combo = anyways.emplace<QComboBox>().getElement();
combo->addItems(
{"Never", "If you are Moderator", "If you are Broadcaster"});
auto &setting = getSettings()->showIgnoredUsersMessages;
setting.connect(
[combo](const int value) { combo->setCurrentIndex(value); });
QObject::connect(combo,
QOverload<int>::of(&QComboBox::currentIndexChanged),
[&setting](int index) {
if (index != -1)
setting = index;
});
anyways->addStretch(1);
}
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
{
auto add = addremove.emplace<QPushButton>("Ignore user");
auto remove = addremove.emplace<QPushButton>("Unignore User");
addremove->addStretch(1);
}*/
users.emplace<QLabel>("List of ignored users:");
users.emplace<QListView>()->setModel(&userModel);
}
void IgnoresPage::onShow()
{
auto app = getApp();
auto user = app->accounts->twitch.getCurrent();
if (user->isAnon())
{
return;
}
QStringList users;
for (const auto &ignoredUser : user->getIgnores())
{
users << ignoredUser.name;
}
users.sort(Qt::CaseInsensitive);
this->userListModel_.setStringList(users);
}
} // namespace chatterino
#include "IgnoresPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/ignores/IgnoreModel.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QCheckBox>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
// clang-format off
#define INFO "/ignore <user> in chat ignores a user.\n/unignore <user> in chat unignores a user.\nYou can also click on a user to open the usercard."
// clang-format on
namespace chatterino {
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
QStringListModel &model);
IgnoresPage::IgnoresPage()
: SettingsPage("Ignores", ":/settings/ignore.svg")
{
LayoutCreator<IgnoresPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
auto tabs = layout.emplace<QTabWidget>();
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Messages"));
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"),
this->userListModel_);
}
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
{
layout.emplace<QLabel>("Messages can be ignored if "
"they match a certain pattern.");
EditableModelView *view =
layout
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
.getElement();
view->setTitles(
{"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addRegexHelpLink();
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->ignores->phrases.appendItem(
IgnorePhrase{"my pattern", false, false,
getSettings()->ignoredPhraseReplace.getValue(), true});
});
}
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
QStringListModel &userModel)
{
auto label = users.emplace<QLabel>(INFO);
label->setWordWrap(true);
users.append(page.createCheckBox("Enable twitch ignored users",
getSettings()->enableTwitchIgnoredUsers));
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
{
anyways.emplace<QLabel>("Show messages from ignored users anyways:");
auto combo = anyways.emplace<QComboBox>().getElement();
combo->addItems(
{"Never", "If you are Moderator", "If you are Broadcaster"});
auto &setting = getSettings()->showIgnoredUsersMessages;
setting.connect(
[combo](const int value) { combo->setCurrentIndex(value); });
QObject::connect(combo,
QOverload<int>::of(&QComboBox::currentIndexChanged),
[&setting](int index) {
if (index != -1)
setting = index;
});
anyways->addStretch(1);
}
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
{
auto add = addremove.emplace<QPushButton>("Ignore user");
auto remove = addremove.emplace<QPushButton>("Unignore User");
addremove->addStretch(1);
}*/
users.emplace<QLabel>("List of ignored users:");
users.emplace<QListView>()->setModel(&userModel);
}
void IgnoresPage::onShow()
{
auto app = getApp();
auto user = app->accounts->twitch.getCurrent();
if (user->isAnon())
{
return;
}
QStringList users;
for (const auto &ignoredUser : user->getIgnores())
{
users << ignoredUser.name;
}
users.sort(Qt::CaseInsensitive);
this->userListModel_.setStringList(users);
}
} // namespace chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QStringListModel>
class QVBoxLayout;
namespace chatterino {
class IgnoresPage : public SettingsPage
{
public:
IgnoresPage();
void onShow() final;
private:
QStringListModel userListModel_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QStringListModel>
class QVBoxLayout;
namespace chatterino {
class IgnoresPage : public SettingsPage
{
public:
IgnoresPage();
void onShow() final;
private:
QStringListModel userListModel_;
};
} // namespace chatterino
@@ -1,50 +1,50 @@
#include "KeyboardSettingsPage.hpp"
#include "util/LayoutCreator.hpp"
#include <QFormLayout>
#include <QLabel>
namespace chatterino {
KeyboardSettingsPage::KeyboardSettingsPage()
: SettingsPage("Keybindings", ":/settings/keybinds.svg")
{
auto layout =
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
auto form = layout.emplace<QFormLayout>().withoutMargin();
form->addRow(new QLabel("Hold Ctrl"), new QLabel("Show resize handles"));
form->addRow(new QLabel("Hold Ctrl + Alt"),
new QLabel("Show split overlay"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + T"), new QLabel("Create new split"));
form->addRow(new QLabel("Ctrl + W"), new QLabel("Close current split"));
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
form->addRow(new QLabel("Ctrl + Shift + W"),
new QLabel("Close current tab"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
new QLabel("Select tab 1/2/3/..."));
form->addRow(new QLabel("Ctrl + Tab"), new QLabel("Select next tab"));
form->addRow(new QLabel("Ctrl + Shift + Tab"),
new QLabel("Select previous tab"));
form->addRow(new QLabel("Alt + Left/Up/Right/Down"),
new QLabel("Select split left/up/right/down"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + R"), new QLabel("Change channel"));
form->addRow(new QLabel("Ctrl + F"),
new QLabel("Search in current channel"));
form->addRow(new QLabel("Ctrl + E"), new QLabel("Open Emote menu"));
form->addRow(new QLabel("Ctrl + P"), new QLabel("Open Settings menu"));
form->addRow(new QLabel("F5"),
new QLabel("Reload subscriber and channel emotes"));
}
} // namespace chatterino
#include "KeyboardSettingsPage.hpp"
#include "util/LayoutCreator.hpp"
#include <QFormLayout>
#include <QLabel>
namespace chatterino {
KeyboardSettingsPage::KeyboardSettingsPage()
: SettingsPage("Keybindings", ":/settings/keybinds.svg")
{
auto layout =
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
auto form = layout.emplace<QFormLayout>().withoutMargin();
form->addRow(new QLabel("Hold Ctrl"), new QLabel("Show resize handles"));
form->addRow(new QLabel("Hold Ctrl + Alt"),
new QLabel("Show split overlay"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + T"), new QLabel("Create new split"));
form->addRow(new QLabel("Ctrl + W"), new QLabel("Close current split"));
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
form->addRow(new QLabel("Ctrl + Shift + W"),
new QLabel("Close current tab"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
new QLabel("Select tab 1/2/3/..."));
form->addRow(new QLabel("Ctrl + Tab"), new QLabel("Select next tab"));
form->addRow(new QLabel("Ctrl + Shift + Tab"),
new QLabel("Select previous tab"));
form->addRow(new QLabel("Alt + Left/Up/Right/Down"),
new QLabel("Select split left/up/right/down"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + R"), new QLabel("Change channel"));
form->addRow(new QLabel("Ctrl + F"),
new QLabel("Search in current channel"));
form->addRow(new QLabel("Ctrl + E"), new QLabel("Open Emote menu"));
form->addRow(new QLabel("Ctrl + P"), new QLabel("Open Settings menu"));
form->addRow(new QLabel("F5"),
new QLabel("Reload subscriber and channel emotes"));
}
} // namespace chatterino
@@ -1,13 +1,13 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class KeyboardSettingsPage : public SettingsPage
{
public:
KeyboardSettingsPage();
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class KeyboardSettingsPage : public SettingsPage
{
public:
KeyboardSettingsPage();
};
} // namespace chatterino
+216 -216
View File
@@ -1,216 +1,216 @@
#include "ModerationPage.hpp"
#include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp"
#include "singletons/Paths.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
namespace chatterino {
qint64 dirSize(QString dirPath)
{
qint64 size = 0;
QDir dir(dirPath);
// calculate total size of current directories' files
QDir::Filters fileFilters = QDir::Files | QDir::System | QDir::Hidden;
for (QString filePath : dir.entryList(fileFilters))
{
QFileInfo fi(dir, filePath);
size += fi.size();
}
// add size of child directories recursively
QDir::Filters dirFilters =
QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden;
for (QString childDirPath : dir.entryList(dirFilters))
size += dirSize(dirPath + QDir::separator() + childDirPath);
return size;
}
QString formatSize(qint64 size)
{
QStringList units = {"Bytes", "KB", "MB", "GB", "TB", "PB"};
int i;
double outputSize = size;
for (i = 0; i < units.size() - 1; i++)
{
if (outputSize < 1024)
break;
outputSize = outputSize / 1024;
}
return QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]);
}
QString fetchLogDirectorySize()
{
QString logPathDirectory = getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
: getSettings()->logPath;
qint64 logsSize = dirSize(logPathDirectory);
QString logsSizeLabel = "Your logs currently take up ";
logsSizeLabel += formatSize(logsSize);
logsSizeLabel += " of space";
return logsSizeLabel;
}
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/settings/moderation.svg")
{
auto app = getApp();
LayoutCreator<ModerationPage> layoutCreator(this);
auto tabs = layoutCreator.emplace<QTabWidget>();
this->tabWidget_ = tabs.getElement();
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
{
logs.append(this->createCheckBox("Enable logging",
getSettings()->enableLogging));
auto logsPathLabel = logs.emplace<QLabel>();
// Logs (copied from LoggingMananger)
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
auto) mutable {
QString pathOriginal =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
QString pathShortened =
"Logs are saved at <a href=\"file:///" + pathOriginal +
"\"><span style=\"color: white;\">" +
shortenString(pathOriginal, 50) + "</span></a>";
logsPathLabel->setText(pathShortened);
logsPathLabel->setToolTip(pathOriginal);
});
logsPathLabel->setTextFormat(Qt::RichText);
logsPathLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard);
logsPathLabel->setOpenExternalLinks(true);
auto buttons = logs.emplace<QHBoxLayout>().withoutMargin();
// Select and Reset
auto selectDir = buttons.emplace<QPushButton>("Select log directory ");
auto resetDir = buttons.emplace<QPushButton>("Reset");
getSettings()->logPath.connect(
[element = resetDir.getElement()](const QString &path) {
element->setEnabled(!path.isEmpty());
});
buttons->addStretch();
logs->addStretch(1);
// Show how big (size-wise) the logs are
auto logsPathSizeLabel = logs.emplace<QLabel>();
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
// Select event
QObject::connect(
selectDir.getElement(), &QPushButton::clicked, this,
[this, logsPathSizeLabel]() mutable {
auto dirName = QFileDialog::getExistingDirectory(this);
getSettings()->logPath = dirName;
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
});
buttons->addSpacing(16);
// Reset custom logpath
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
[logsPathSizeLabel]() mutable {
getSettings()->logPath = "";
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(QtConcurrent::run(
[] { return fetchLogDirectorySize(); }));
});
} // logs end
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
{
// clang-format off
auto label = modMode.emplace<QLabel>(
"Moderation mode is enabled by clicking <img width='18' height='18' src=':/buttons/modModeDisabled.png'> in a channel that you moderate.<br><br>"
"Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.<br>"
"For deleting messages use /delete {msg-id}.");
label->setWordWrap(true);
label->setStyleSheet("color: #bbb");
// clang-format on
// auto form = modMode.emplace<QFormLayout>();
// {
// form->addRow("Action on timed out messages
// (unimplemented):",
// this->createComboBox({"Disable", "Hide"},
// getSettings()->timeoutAction));
// }
EditableModelView *view =
modMode
.emplace<EditableModelView>(
app->moderationActions->createModel(nullptr))
.getElement();
view->setTitles({"Actions"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(
ModerationAction("/timeout {user} 300"));
});
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{
EditableModelView *view = *taggedUsers.emplace<EditableModelView>(
app->taggedUsers->createModel(nullptr));
view->setTitles({"Name"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->taggedUsers->users.appendItem(
TaggedUser(ProviderId::Twitch, "example", "xD"));
});
}*/
}
// ---- misc
this->itemsChangedTimer_.setSingleShot(true);
}
void ModerationPage::selectModerationActions()
{
this->tabWidget_->setCurrentIndex(1);
}
} // namespace chatterino
#include "ModerationPage.hpp"
#include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp"
#include "singletons/Paths.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
namespace chatterino {
qint64 dirSize(QString dirPath)
{
qint64 size = 0;
QDir dir(dirPath);
// calculate total size of current directories' files
QDir::Filters fileFilters = QDir::Files | QDir::System | QDir::Hidden;
for (QString filePath : dir.entryList(fileFilters))
{
QFileInfo fi(dir, filePath);
size += fi.size();
}
// add size of child directories recursively
QDir::Filters dirFilters =
QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden;
for (QString childDirPath : dir.entryList(dirFilters))
size += dirSize(dirPath + QDir::separator() + childDirPath);
return size;
}
QString formatSize(qint64 size)
{
QStringList units = {"Bytes", "KB", "MB", "GB", "TB", "PB"};
int i;
double outputSize = size;
for (i = 0; i < units.size() - 1; i++)
{
if (outputSize < 1024)
break;
outputSize = outputSize / 1024;
}
return QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]);
}
QString fetchLogDirectorySize()
{
QString logPathDirectory = getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
: getSettings()->logPath;
qint64 logsSize = dirSize(logPathDirectory);
QString logsSizeLabel = "Your logs currently take up ";
logsSizeLabel += formatSize(logsSize);
logsSizeLabel += " of space";
return logsSizeLabel;
}
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/settings/moderation.svg")
{
auto app = getApp();
LayoutCreator<ModerationPage> layoutCreator(this);
auto tabs = layoutCreator.emplace<QTabWidget>();
this->tabWidget_ = tabs.getElement();
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
{
logs.append(this->createCheckBox("Enable logging",
getSettings()->enableLogging));
auto logsPathLabel = logs.emplace<QLabel>();
// Logs (copied from LoggingMananger)
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
auto) mutable {
QString pathOriginal =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
QString pathShortened =
"Logs are saved at <a href=\"file:///" + pathOriginal +
"\"><span style=\"color: white;\">" +
shortenString(pathOriginal, 50) + "</span></a>";
logsPathLabel->setText(pathShortened);
logsPathLabel->setToolTip(pathOriginal);
});
logsPathLabel->setTextFormat(Qt::RichText);
logsPathLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard);
logsPathLabel->setOpenExternalLinks(true);
auto buttons = logs.emplace<QHBoxLayout>().withoutMargin();
// Select and Reset
auto selectDir = buttons.emplace<QPushButton>("Select log directory ");
auto resetDir = buttons.emplace<QPushButton>("Reset");
getSettings()->logPath.connect(
[element = resetDir.getElement()](const QString &path) {
element->setEnabled(!path.isEmpty());
});
buttons->addStretch();
logs->addStretch(1);
// Show how big (size-wise) the logs are
auto logsPathSizeLabel = logs.emplace<QLabel>();
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
// Select event
QObject::connect(
selectDir.getElement(), &QPushButton::clicked, this,
[this, logsPathSizeLabel]() mutable {
auto dirName = QFileDialog::getExistingDirectory(this);
getSettings()->logPath = dirName;
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
});
buttons->addSpacing(16);
// Reset custom logpath
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
[logsPathSizeLabel]() mutable {
getSettings()->logPath = "";
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(QtConcurrent::run(
[] { return fetchLogDirectorySize(); }));
});
} // logs end
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
{
// clang-format off
auto label = modMode.emplace<QLabel>(
"Moderation mode is enabled by clicking <img width='18' height='18' src=':/buttons/modModeDisabled.png'> in a channel that you moderate.<br><br>"
"Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.<br>"
"For deleting messages use /delete {msg-id}.");
label->setWordWrap(true);
label->setStyleSheet("color: #bbb");
// clang-format on
// auto form = modMode.emplace<QFormLayout>();
// {
// form->addRow("Action on timed out messages
// (unimplemented):",
// this->createComboBox({"Disable", "Hide"},
// getSettings()->timeoutAction));
// }
EditableModelView *view =
modMode
.emplace<EditableModelView>(
app->moderationActions->createModel(nullptr))
.getElement();
view->setTitles({"Actions"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(
ModerationAction("/timeout {user} 300"));
});
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{
EditableModelView *view = *taggedUsers.emplace<EditableModelView>(
app->taggedUsers->createModel(nullptr));
view->setTitles({"Name"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->taggedUsers->users.appendItem(
TaggedUser(ProviderId::Twitch, "example", "xD"));
});
}*/
}
// ---- misc
this->itemsChangedTimer_.setSingleShot(true);
}
void ModerationPage::selectModerationActions()
{
this->tabWidget_->setCurrentIndex(1);
}
} // namespace chatterino
+24 -24
View File
@@ -1,24 +1,24 @@
#pragma once
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
class QTabWidget;
class QPushButton;
namespace chatterino {
class ModerationPage : public SettingsPage
{
public:
ModerationPage();
void selectModerationActions();
private:
QTimer itemsChangedTimer_;
QTabWidget *tabWidget_{};
};
} // namespace chatterino
#pragma once
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
class QTabWidget;
class QPushButton;
namespace chatterino {
class ModerationPage : public SettingsPage
{
public:
ModerationPage();
void selectModerationActions();
private:
QTimer itemsChangedTimer_;
QTabWidget *tabWidget_{};
};
} // namespace chatterino
+176 -176
View File
@@ -1,176 +1,176 @@
#include "SettingsPage.hpp"
#include "Application.hpp"
#include "singletons/WindowManager.hpp"
#include <QDebug>
#include <QPainter>
#include <util/FunctionEventFilter.hpp>
namespace chatterino {
bool filterItemsRec(QObject *object, const QString &query)
{
bool any{};
for (auto &&child : object->children())
{
auto setOpacity = [&](auto *widget, bool condition) {
any |= condition;
widget->greyedOut = !condition;
widget->update();
};
if (auto x = dynamic_cast<SCheckBox *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SLabel *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SComboBox *>(child); x)
{
setOpacity(x, [=]() {
for (int i = 0; i < x->count(); i++)
{
if (x->itemText(i).contains(query, Qt::CaseInsensitive))
return true;
}
return false;
}());
}
else if (auto x = dynamic_cast<QTabWidget *>(child); x)
{
for (int i = 0; i < x->count(); i++)
{
bool tabAny{};
if (x->tabText(i).contains(query, Qt::CaseInsensitive))
{
tabAny = true;
}
auto widget = x->widget(i);
tabAny |= filterItemsRec(widget, query);
any |= tabAny;
}
}
else
{
any |= filterItemsRec(child, query);
}
}
return any;
}
SettingsPage::SettingsPage(const QString &name, const QString &iconResource)
: name_(name)
, iconResource_(iconResource)
{
}
bool SettingsPage::filterElements(const QString &query)
{
return filterItemsRec(this, query) || query.isEmpty() ||
this->name_.contains(query, Qt::CaseInsensitive);
}
const QString &SettingsPage::getName()
{
return this->name_;
}
const QString &SettingsPage::getIconResource()
{
return this->iconResource_;
}
SettingsDialogTab *SettingsPage::tab() const
{
return this->tab_;
}
void SettingsPage::setTab(SettingsDialogTab *tab)
{
this->tab_ = tab;
}
void SettingsPage::cancel()
{
this->onCancel_.invoke();
}
QCheckBox *SettingsPage::createCheckBox(
const QString &text, pajlada::Settings::Setting<bool> &setting)
{
QCheckBox *checkbox = new SCheckBox(text);
// update when setting changes
setting.connect(
[checkbox](const bool &value, auto) {
checkbox->setChecked(value); //
},
this->managedConnections_);
// update setting on toggle
QObject::connect(checkbox, &QCheckBox::toggled, this,
[&setting](bool state) {
setting = state;
getApp()->windows->forceLayoutChannelViews();
});
return checkbox;
}
QComboBox *SettingsPage::createComboBox(
const QStringList &items, pajlada::Settings::Setting<QString> &setting)
{
QComboBox *combo = new SComboBox();
// update setting on toogle
combo->addItems(items);
// update when setting changes
setting.connect(
[combo](const QString &value, auto) { combo->setCurrentText(value); },
this->managedConnections_);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const QString &newValue) { setting = newValue; });
return combo;
}
QLineEdit *SettingsPage::createLineEdit(
pajlada::Settings::Setting<QString> &setting)
{
QLineEdit *edit = new QLineEdit();
edit->setText(setting);
// update when setting changes
QObject::connect(
edit, &QLineEdit::textChanged,
[&setting](const QString &newValue) { setting = newValue; });
return edit;
}
QSpinBox *SettingsPage::createSpinBox(pajlada::Settings::Setting<int> &setting,
int min, int max)
{
QSpinBox *w = new QSpinBox;
w->setMinimum(min);
w->setMaximum(max);
setting.connect([w](const int &value, auto) { w->setValue(value); });
QObject::connect(w, QOverload<int>::of(&QSpinBox::valueChanged),
[&setting](int value) { setting.setValue(value); });
return w;
}
} // namespace chatterino
#include "SettingsPage.hpp"
#include "Application.hpp"
#include "singletons/WindowManager.hpp"
#include <QDebug>
#include <QPainter>
#include <util/FunctionEventFilter.hpp>
namespace chatterino {
bool filterItemsRec(QObject *object, const QString &query)
{
bool any{};
for (auto &&child : object->children())
{
auto setOpacity = [&](auto *widget, bool condition) {
any |= condition;
widget->greyedOut = !condition;
widget->update();
};
if (auto x = dynamic_cast<SCheckBox *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SLabel *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SComboBox *>(child); x)
{
setOpacity(x, [=]() {
for (int i = 0; i < x->count(); i++)
{
if (x->itemText(i).contains(query, Qt::CaseInsensitive))
return true;
}
return false;
}());
}
else if (auto x = dynamic_cast<QTabWidget *>(child); x)
{
for (int i = 0; i < x->count(); i++)
{
bool tabAny{};
if (x->tabText(i).contains(query, Qt::CaseInsensitive))
{
tabAny = true;
}
auto widget = x->widget(i);
tabAny |= filterItemsRec(widget, query);
any |= tabAny;
}
}
else
{
any |= filterItemsRec(child, query);
}
}
return any;
}
SettingsPage::SettingsPage(const QString &name, const QString &iconResource)
: name_(name)
, iconResource_(iconResource)
{
}
bool SettingsPage::filterElements(const QString &query)
{
return filterItemsRec(this, query) || query.isEmpty() ||
this->name_.contains(query, Qt::CaseInsensitive);
}
const QString &SettingsPage::getName()
{
return this->name_;
}
const QString &SettingsPage::getIconResource()
{
return this->iconResource_;
}
SettingsDialogTab *SettingsPage::tab() const
{
return this->tab_;
}
void SettingsPage::setTab(SettingsDialogTab *tab)
{
this->tab_ = tab;
}
void SettingsPage::cancel()
{
this->onCancel_.invoke();
}
QCheckBox *SettingsPage::createCheckBox(
const QString &text, pajlada::Settings::Setting<bool> &setting)
{
QCheckBox *checkbox = new SCheckBox(text);
// update when setting changes
setting.connect(
[checkbox](const bool &value, auto) {
checkbox->setChecked(value); //
},
this->managedConnections_);
// update setting on toggle
QObject::connect(checkbox, &QCheckBox::toggled, this,
[&setting](bool state) {
setting = state;
getApp()->windows->forceLayoutChannelViews();
});
return checkbox;
}
QComboBox *SettingsPage::createComboBox(
const QStringList &items, pajlada::Settings::Setting<QString> &setting)
{
QComboBox *combo = new SComboBox();
// update setting on toogle
combo->addItems(items);
// update when setting changes
setting.connect(
[combo](const QString &value, auto) { combo->setCurrentText(value); },
this->managedConnections_);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const QString &newValue) { setting = newValue; });
return combo;
}
QLineEdit *SettingsPage::createLineEdit(
pajlada::Settings::Setting<QString> &setting)
{
QLineEdit *edit = new QLineEdit();
edit->setText(setting);
// update when setting changes
QObject::connect(
edit, &QLineEdit::textChanged,
[&setting](const QString &newValue) { setting = newValue; });
return edit;
}
QSpinBox *SettingsPage::createSpinBox(pajlada::Settings::Setting<int> &setting,
int min, int max)
{
QSpinBox *w = new QSpinBox;
w->setMinimum(min);
w->setMaximum(max);
setting.connect([w](const int &value, auto) { w->setValue(value); });
QObject::connect(w, QOverload<int>::of(&QSpinBox::valueChanged),
[&setting](int value) { setting.setValue(value); });
return w;
}
} // namespace chatterino
+84 -84
View File
@@ -1,84 +1,84 @@
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <pajlada/signals/signal.hpp>
#include "singletons/Settings.hpp"
#define SETTINGS_PAGE_WIDGET_BOILERPLATE(type, parent) \
class type : public parent \
{ \
using parent::parent; \
\
public: \
bool greyedOut{}; \
\
protected: \
void paintEvent(QPaintEvent *e) override \
{ \
parent::paintEvent(e); \
\
if (this->greyedOut) \
{ \
QPainter painter(this); \
QColor color = QColor("#222222"); \
color.setAlphaF(0.7); \
painter.fillRect(this->rect(), color); \
} \
} \
};
namespace chatterino {
// S* widgets are the same as their Q* counterparts,
// but they can be greyed out and will be if you search.
SETTINGS_PAGE_WIDGET_BOILERPLATE(SCheckBox, QCheckBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SLabel, QLabel)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SComboBox, QComboBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SPushButton, QPushButton)
class SettingsDialogTab;
class SettingsPage : public QFrame
{
Q_OBJECT
public:
SettingsPage(const QString &name, const QString &iconResource);
const QString &getName();
const QString &getIconResource();
virtual bool filterElements(const QString &query);
SettingsDialogTab *tab() const;
void setTab(SettingsDialogTab *tab);
void cancel();
QCheckBox *createCheckBox(const QString &text,
pajlada::Settings::Setting<bool> &setting);
QComboBox *createComboBox(const QStringList &items,
pajlada::Settings::Setting<QString> &setting);
QLineEdit *createLineEdit(pajlada::Settings::Setting<QString> &setting);
QSpinBox *createSpinBox(pajlada::Settings::Setting<int> &setting,
int min = 0, int max = 2500);
virtual void onShow()
{
}
protected:
QString name_;
QString iconResource_;
SettingsDialogTab *tab_;
pajlada::Signals::NoArgSignal onCancel_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
} // namespace chatterino
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <pajlada/signals/signal.hpp>
#include "singletons/Settings.hpp"
#define SETTINGS_PAGE_WIDGET_BOILERPLATE(type, parent) \
class type : public parent \
{ \
using parent::parent; \
\
public: \
bool greyedOut{}; \
\
protected: \
void paintEvent(QPaintEvent *e) override \
{ \
parent::paintEvent(e); \
\
if (this->greyedOut) \
{ \
QPainter painter(this); \
QColor color = QColor("#222222"); \
color.setAlphaF(0.7); \
painter.fillRect(this->rect(), color); \
} \
} \
};
namespace chatterino {
// S* widgets are the same as their Q* counterparts,
// but they can be greyed out and will be if you search.
SETTINGS_PAGE_WIDGET_BOILERPLATE(SCheckBox, QCheckBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SLabel, QLabel)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SComboBox, QComboBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SPushButton, QPushButton)
class SettingsDialogTab;
class SettingsPage : public QFrame
{
Q_OBJECT
public:
SettingsPage(const QString &name, const QString &iconResource);
const QString &getName();
const QString &getIconResource();
virtual bool filterElements(const QString &query);
SettingsDialogTab *tab() const;
void setTab(SettingsDialogTab *tab);
void cancel();
QCheckBox *createCheckBox(const QString &text,
pajlada::Settings::Setting<bool> &setting);
QComboBox *createComboBox(const QStringList &items,
pajlada::Settings::Setting<QString> &setting);
QLineEdit *createLineEdit(pajlada::Settings::Setting<QString> &setting);
QSpinBox *createSpinBox(pajlada::Settings::Setting<int> &setting,
int min = 0, int max = 2500);
virtual void onShow()
{
}
protected:
QString name_;
QString iconResource_;
SettingsDialogTab *tab_;
pajlada::Signals::NoArgSignal onCancel_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
} // namespace chatterino
+254 -254
View File
@@ -1,254 +1,254 @@
#include "SplitOverlay.hpp"
#include <QEvent>
#include <QGraphicsBlurEffect>
#include <QGraphicsEffect>
#include <QGraphicsOpacityEffect>
#include <QGridLayout>
#include <QPainter>
#include <QPushButton>
#include "Application.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Theme.hpp"
#include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp"
namespace chatterino {
SplitOverlay::SplitOverlay(Split *parent)
: BaseWidget(parent)
, split_(parent)
{
QGridLayout *layout = new QGridLayout(this);
this->layout_ = layout;
layout->setMargin(1);
layout->setSpacing(1);
layout->setRowStretch(1, 1);
layout->setRowStretch(3, 1);
layout->setColumnStretch(1, 1);
layout->setColumnStretch(3, 1);
auto *move = new QPushButton(getApp()->resources->split.move, QString());
auto *left = this->left_ =
new QPushButton(getApp()->resources->split.left, QString());
auto *right = this->right_ =
new QPushButton(getApp()->resources->split.right, QString());
auto *up = this->up_ =
new QPushButton(getApp()->resources->split.up, QString());
auto *down = this->down_ =
new QPushButton(getApp()->resources->split.down, QString());
move->setGraphicsEffect(new QGraphicsOpacityEffect(this));
left->setGraphicsEffect(new QGraphicsOpacityEffect(this));
right->setGraphicsEffect(new QGraphicsOpacityEffect(this));
up->setGraphicsEffect(new QGraphicsOpacityEffect(this));
down->setGraphicsEffect(new QGraphicsOpacityEffect(this));
move->setFlat(true);
left->setFlat(true);
right->setFlat(true);
up->setFlat(true);
down->setFlat(true);
layout->addWidget(move, 2, 2);
layout->addWidget(left, 2, 0);
layout->addWidget(right, 2, 4);
layout->addWidget(up, 0, 2);
layout->addWidget(down, 4, 2);
move->installEventFilter(new ButtonEventFilter(this, SplitMove));
left->installEventFilter(new ButtonEventFilter(this, SplitLeft));
right->installEventFilter(new ButtonEventFilter(this, SplitRight));
up->installEventFilter(new ButtonEventFilter(this, SplitUp));
down->installEventFilter(new ButtonEventFilter(this, SplitDown));
move->setFocusPolicy(Qt::NoFocus);
left->setFocusPolicy(Qt::NoFocus);
right->setFocusPolicy(Qt::NoFocus);
up->setFocusPolicy(Qt::NoFocus);
down->setFocusPolicy(Qt::NoFocus);
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 = int(_scale * 30);
QSize size(a, a);
move->setIconSize(size);
left->setIconSize(size);
right->setIconSize(size);
up->setIconSize(size);
down->setIconSize(size);
});
this->setMouseTracking(true);
this->setCursor(Qt::ArrowCursor);
}
void SplitOverlay::paintEvent(QPaintEvent *)
{
QPainter painter(this);
if (this->theme->isLightTheme())
{
painter.fillRect(this->rect(), QColor(255, 255, 255, 200));
}
else
{
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);
painter.drawRect(rect);
}
}
void SplitOverlay::resizeEvent(QResizeEvent *event)
{
float _scale = this->scale();
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)
, hoveredElement(_element)
{
}
bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
QEvent *event)
{
switch (event->type())
{
case QEvent::Enter:
{
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.99);
}
this->parent->hoveredElement_ = this->hoveredElement;
this->parent->update();
}
break;
case QEvent::Leave:
{
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.7);
}
this->parent->hoveredElement_ = HoveredElement::None;
this->parent->update();
}
break;
case QEvent::MouseButtonPress:
{
if (this->hoveredElement == HoveredElement::SplitMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
this->parent->split_->drag();
}
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);
}
} // namespace chatterino
#include "SplitOverlay.hpp"
#include <QEvent>
#include <QGraphicsBlurEffect>
#include <QGraphicsEffect>
#include <QGraphicsOpacityEffect>
#include <QGridLayout>
#include <QPainter>
#include <QPushButton>
#include "Application.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Theme.hpp"
#include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp"
namespace chatterino {
SplitOverlay::SplitOverlay(Split *parent)
: BaseWidget(parent)
, split_(parent)
{
QGridLayout *layout = new QGridLayout(this);
this->layout_ = layout;
layout->setMargin(1);
layout->setSpacing(1);
layout->setRowStretch(1, 1);
layout->setRowStretch(3, 1);
layout->setColumnStretch(1, 1);
layout->setColumnStretch(3, 1);
auto *move = new QPushButton(getApp()->resources->split.move, QString());
auto *left = this->left_ =
new QPushButton(getApp()->resources->split.left, QString());
auto *right = this->right_ =
new QPushButton(getApp()->resources->split.right, QString());
auto *up = this->up_ =
new QPushButton(getApp()->resources->split.up, QString());
auto *down = this->down_ =
new QPushButton(getApp()->resources->split.down, QString());
move->setGraphicsEffect(new QGraphicsOpacityEffect(this));
left->setGraphicsEffect(new QGraphicsOpacityEffect(this));
right->setGraphicsEffect(new QGraphicsOpacityEffect(this));
up->setGraphicsEffect(new QGraphicsOpacityEffect(this));
down->setGraphicsEffect(new QGraphicsOpacityEffect(this));
move->setFlat(true);
left->setFlat(true);
right->setFlat(true);
up->setFlat(true);
down->setFlat(true);
layout->addWidget(move, 2, 2);
layout->addWidget(left, 2, 0);
layout->addWidget(right, 2, 4);
layout->addWidget(up, 0, 2);
layout->addWidget(down, 4, 2);
move->installEventFilter(new ButtonEventFilter(this, SplitMove));
left->installEventFilter(new ButtonEventFilter(this, SplitLeft));
right->installEventFilter(new ButtonEventFilter(this, SplitRight));
up->installEventFilter(new ButtonEventFilter(this, SplitUp));
down->installEventFilter(new ButtonEventFilter(this, SplitDown));
move->setFocusPolicy(Qt::NoFocus);
left->setFocusPolicy(Qt::NoFocus);
right->setFocusPolicy(Qt::NoFocus);
up->setFocusPolicy(Qt::NoFocus);
down->setFocusPolicy(Qt::NoFocus);
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 = int(_scale * 30);
QSize size(a, a);
move->setIconSize(size);
left->setIconSize(size);
right->setIconSize(size);
up->setIconSize(size);
down->setIconSize(size);
});
this->setMouseTracking(true);
this->setCursor(Qt::ArrowCursor);
}
void SplitOverlay::paintEvent(QPaintEvent *)
{
QPainter painter(this);
if (this->theme->isLightTheme())
{
painter.fillRect(this->rect(), QColor(255, 255, 255, 200));
}
else
{
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);
painter.drawRect(rect);
}
}
void SplitOverlay::resizeEvent(QResizeEvent *event)
{
float _scale = this->scale();
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)
, hoveredElement(_element)
{
}
bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
QEvent *event)
{
switch (event->type())
{
case QEvent::Enter:
{
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.99);
}
this->parent->hoveredElement_ = this->hoveredElement;
this->parent->update();
}
break;
case QEvent::Leave:
{
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.7);
}
this->parent->hoveredElement_ = HoveredElement::None;
this->parent->update();
}
break;
case QEvent::MouseButtonPress:
{
if (this->hoveredElement == HoveredElement::SplitMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
this->parent->split_->drag();
}
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);
}
} // namespace chatterino
+58 -58
View File
@@ -1,58 +1,58 @@
#pragma once
#include <QGridLayout>
#include <QPushButton>
#include "pajlada/signals/signalholder.hpp"
#include "widgets/BaseWidget.hpp"
namespace chatterino {
class Split;
class SplitOverlay : public BaseWidget, pajlada::Signals::SignalHolder
{
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:
// fourtf: !!! preserve the order of left, up, right and down
enum HoveredElement {
None,
SplitMove,
SplitLeft,
SplitUp,
SplitRight,
SplitDown
};
class ButtonEventFilter : public QObject
{
SplitOverlay *parent;
HoveredElement hoveredElement;
public:
ButtonEventFilter(SplitOverlay *parent, HoveredElement hoveredElement);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
HoveredElement hoveredElement_ = None;
Split *split_;
QGridLayout *layout_;
QPushButton *left_;
QPushButton *up_;
QPushButton *right_;
QPushButton *down_;
friend class ButtonEventFilter;
};
} // namespace chatterino
#pragma once
#include <QGridLayout>
#include <QPushButton>
#include "pajlada/signals/signalholder.hpp"
#include "widgets/BaseWidget.hpp"
namespace chatterino {
class Split;
class SplitOverlay : public BaseWidget, pajlada::Signals::SignalHolder
{
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:
// fourtf: !!! preserve the order of left, up, right and down
enum HoveredElement {
None,
SplitMove,
SplitLeft,
SplitUp,
SplitRight,
SplitDown
};
class ButtonEventFilter : public QObject
{
SplitOverlay *parent;
HoveredElement hoveredElement;
public:
ButtonEventFilter(SplitOverlay *parent, HoveredElement hoveredElement);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
HoveredElement hoveredElement_ = None;
Split *split_;
QGridLayout *layout_;
QPushButton *left_;
QPushButton *up_;
QPushButton *right_;
QPushButton *down_;
friend class ButtonEventFilter;
};
} // namespace chatterino