Merge branch 'master' into git_is_pepega
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
#include "widgets/AccountSwitchPopup.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "BaseSettings.hpp"
|
||||
#include "BaseTheme.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
#include <QChildEvent>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "BaseSettings.hpp"
|
||||
#include "BaseTheme.hpp"
|
||||
#include "boost/algorithm/algorithm.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "util/PostToThread.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "util/WindowsHelper.hpp"
|
||||
@@ -385,7 +384,7 @@ void BaseWindow::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
if (!recursiveCheckMouseTracking(widget))
|
||||
{
|
||||
log("Start moving");
|
||||
qDebug() << "Start moving";
|
||||
this->moving = true;
|
||||
}
|
||||
}
|
||||
@@ -402,7 +401,7 @@ void BaseWindow::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->moving)
|
||||
{
|
||||
log("Stop moving");
|
||||
qDebug() << "Stop moving";
|
||||
this->moving = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "widgets/Notebook.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
|
||||
@@ -282,18 +282,7 @@ void Scrollbar::paintEvent(QPaintEvent *)
|
||||
|
||||
if (!highlight.isNull())
|
||||
{
|
||||
QColor color = [&] {
|
||||
switch (highlight.getColor())
|
||||
{
|
||||
case ScrollbarHighlight::Highlight:
|
||||
return getApp()
|
||||
->themes->scrollbars.highlights.highlight;
|
||||
case ScrollbarHighlight::Subscription:
|
||||
return getApp()
|
||||
->themes->scrollbars.highlights.subscription;
|
||||
}
|
||||
return QColor();
|
||||
}();
|
||||
QColor color = highlight.getColor();
|
||||
|
||||
switch (highlight.getStyle())
|
||||
{
|
||||
|
||||
@@ -351,6 +351,11 @@ void Window::addShortcuts()
|
||||
getApp()->twitch.server->getOrAddChannel(si.channelName));
|
||||
splitContainer->appendSplit(split);
|
||||
});
|
||||
|
||||
createWindowShortcut(this, "CTRL+H", [this] {
|
||||
getSettings()->hideSimilar.setValue(!getSettings()->hideSimilar);
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
}
|
||||
|
||||
void Window::addMenuBar()
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
#include "widgets/dialogs/ColorPickerDialog.hpp"
|
||||
|
||||
#include "providers/colors/ColorProvider.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ColorPickerDialog::ColorPickerDialog(const QColor &initial, QWidget *parent)
|
||||
: BasePopup(BaseWindow::EnableCustomFrame, parent)
|
||||
, color_()
|
||||
, dialogConfirmed_(false)
|
||||
{
|
||||
// This hosts the "business logic" and the dialog button box
|
||||
LayoutCreator<QWidget> layoutWidget(this->getLayoutContainer());
|
||||
auto layout = layoutWidget.setLayoutType<QVBoxLayout>().withoutMargin();
|
||||
|
||||
// This hosts the business logic: color picker and predefined colors
|
||||
LayoutCreator<QWidget> contentCreator(new QWidget());
|
||||
auto contents = contentCreator.setLayoutType<QHBoxLayout>();
|
||||
|
||||
// This hosts the predefined colors (and also the currently selected color)
|
||||
LayoutCreator<QWidget> predefCreator(new QWidget());
|
||||
auto predef = predefCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
// Recently used colors
|
||||
{
|
||||
LayoutCreator<QWidget> gridCreator(new QWidget());
|
||||
this->initRecentColors(gridCreator);
|
||||
|
||||
predef.append(gridCreator.getElement());
|
||||
}
|
||||
|
||||
// Default colors
|
||||
{
|
||||
LayoutCreator<QWidget> gridCreator(new QWidget());
|
||||
this->initDefaultColors(gridCreator);
|
||||
|
||||
predef.append(gridCreator.getElement());
|
||||
}
|
||||
|
||||
// Currently selected color
|
||||
{
|
||||
LayoutCreator<QWidget> curColorCreator(new QWidget());
|
||||
auto curColor = curColorCreator.setLayoutType<QHBoxLayout>();
|
||||
curColor.emplace<QLabel>("Selected:").assign(&this->ui_.selected.label);
|
||||
curColor.emplace<ColorButton>(initial).assign(
|
||||
&this->ui_.selected.color);
|
||||
|
||||
predef.append(curColor.getElement());
|
||||
}
|
||||
|
||||
contents.append(predef.getElement());
|
||||
|
||||
// Color picker
|
||||
{
|
||||
LayoutCreator<QWidget> obj(new QWidget());
|
||||
auto vbox = obj.setLayoutType<QVBoxLayout>();
|
||||
|
||||
// The actual color picker
|
||||
{
|
||||
LayoutCreator<QWidget> cpCreator(new QWidget());
|
||||
this->initColorPicker(cpCreator);
|
||||
|
||||
vbox.append(cpCreator.getElement());
|
||||
}
|
||||
|
||||
// Spin boxes
|
||||
{
|
||||
LayoutCreator<QWidget> sbCreator(new QWidget());
|
||||
this->initSpinBoxes(sbCreator);
|
||||
|
||||
vbox.append(sbCreator.getElement());
|
||||
}
|
||||
|
||||
// HTML color
|
||||
{
|
||||
LayoutCreator<QWidget> htmlCreator(new QWidget());
|
||||
this->initHtmlColor(htmlCreator);
|
||||
|
||||
vbox.append(htmlCreator.getElement());
|
||||
}
|
||||
|
||||
contents.append(obj.getElement());
|
||||
}
|
||||
|
||||
layout.append(contents.getElement());
|
||||
|
||||
// Dialog buttons
|
||||
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->themeChangedEvent();
|
||||
this->selectColor(initial, false);
|
||||
}
|
||||
|
||||
ColorPickerDialog::~ColorPickerDialog()
|
||||
{
|
||||
if (this->htmlColorValidator_)
|
||||
{
|
||||
this->htmlColorValidator_->deleteLater();
|
||||
this->htmlColorValidator_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QColor ColorPickerDialog::selectedColor() const
|
||||
{
|
||||
if (!this->dialogConfirmed_)
|
||||
{
|
||||
// If the Cancel button was clicked, return the invalid color
|
||||
return QColor();
|
||||
}
|
||||
|
||||
return this->color_;
|
||||
}
|
||||
|
||||
void ColorPickerDialog::closeEvent(QCloseEvent *)
|
||||
{
|
||||
this->closed.invoke();
|
||||
}
|
||||
|
||||
void ColorPickerDialog::themeChangedEvent()
|
||||
{
|
||||
BaseWindow::themeChangedEvent();
|
||||
|
||||
QString textCol = this->theme->splits.input.text.name(QColor::HexRgb);
|
||||
QString bgCol = this->theme->splits.input.background.name(QColor::HexRgb);
|
||||
|
||||
// Labels
|
||||
|
||||
QString labelStyle = QString("color: %1;").arg(textCol);
|
||||
|
||||
this->ui_.recent.label->setStyleSheet(labelStyle);
|
||||
this->ui_.def.label->setStyleSheet(labelStyle);
|
||||
this->ui_.selected.label->setStyleSheet(labelStyle);
|
||||
this->ui_.picker.htmlLabel->setStyleSheet(labelStyle);
|
||||
|
||||
for (auto spinBoxLabel : this->ui_.picker.spinBoxLabels)
|
||||
{
|
||||
spinBoxLabel->setStyleSheet(labelStyle);
|
||||
}
|
||||
|
||||
this->ui_.picker.htmlEdit->setStyleSheet(
|
||||
this->theme->splits.input.styleSheet);
|
||||
|
||||
// Styling spin boxes is too much effort
|
||||
}
|
||||
|
||||
void ColorPickerDialog::selectColor(const QColor &color, bool fromColorPicker)
|
||||
{
|
||||
if (color == this->color_)
|
||||
return;
|
||||
|
||||
this->color_ = color;
|
||||
|
||||
// Update UI elements
|
||||
this->ui_.selected.color->setColor(this->color_);
|
||||
|
||||
/*
|
||||
* Somewhat "ugly" hack to prevent feedback loop between widgets. Since
|
||||
* this method is private, I'm okay with this being ugly.
|
||||
*/
|
||||
if (!fromColorPicker)
|
||||
{
|
||||
this->ui_.picker.colorPicker->setCol(this->color_.hslHue(),
|
||||
this->color_.hslSaturation());
|
||||
this->ui_.picker.luminancePicker->setCol(this->color_.hsvHue(),
|
||||
this->color_.hsvSaturation(),
|
||||
this->color_.value());
|
||||
}
|
||||
|
||||
this->ui_.picker.spinBoxes[SpinBox::RED]->setValue(this->color_.red());
|
||||
this->ui_.picker.spinBoxes[SpinBox::GREEN]->setValue(this->color_.green());
|
||||
this->ui_.picker.spinBoxes[SpinBox::BLUE]->setValue(this->color_.blue());
|
||||
this->ui_.picker.spinBoxes[SpinBox::ALPHA]->setValue(this->color_.alpha());
|
||||
|
||||
/*
|
||||
* Here, we are intentionally using HexRgb instead of HexArgb. Most online
|
||||
* sites (or other applications) will likely not include the alpha channel
|
||||
* in their output.
|
||||
*/
|
||||
this->ui_.picker.htmlEdit->setText(this->color_.name(QColor::HexRgb));
|
||||
}
|
||||
|
||||
void ColorPickerDialog::ok()
|
||||
{
|
||||
this->dialogConfirmed_ = true;
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ColorPickerDialog::initRecentColors(LayoutCreator<QWidget> &creator)
|
||||
{
|
||||
auto grid = creator.setLayoutType<QGridLayout>();
|
||||
|
||||
auto label = this->ui_.recent.label = new QLabel("Recently used:");
|
||||
grid->addWidget(label, 0, 0, 1, -1);
|
||||
|
||||
const auto recentColors = ColorProvider::instance().recentColors();
|
||||
auto it = recentColors.begin();
|
||||
size_t ind = 0;
|
||||
while (it != recentColors.end() && ind < MAX_RECENT_COLORS)
|
||||
{
|
||||
this->ui_.recent.colors.push_back(new ColorButton(*it, this));
|
||||
auto *button = this->ui_.recent.colors[ind];
|
||||
|
||||
const int rowInd = (ind / RECENT_COLORS_PER_ROW) + 1;
|
||||
const int columnInd = ind % RECENT_COLORS_PER_ROW;
|
||||
|
||||
grid->addWidget(button, rowInd, columnInd);
|
||||
|
||||
QObject::connect(button, &QPushButton::clicked,
|
||||
[=] { this->selectColor(button->color(), false); });
|
||||
|
||||
++it;
|
||||
++ind;
|
||||
}
|
||||
|
||||
auto spacer =
|
||||
new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
grid->addItem(spacer, (ind / RECENT_COLORS_PER_ROW) + 2, 0, 1, 1,
|
||||
Qt::AlignTop);
|
||||
}
|
||||
|
||||
void ColorPickerDialog::initDefaultColors(LayoutCreator<QWidget> &creator)
|
||||
{
|
||||
auto grid = creator.setLayoutType<QGridLayout>();
|
||||
|
||||
auto label = this->ui_.def.label = new QLabel("Default colors:");
|
||||
grid->addWidget(label, 0, 0, 1, -1);
|
||||
|
||||
const auto defaultColors = ColorProvider::instance().defaultColors();
|
||||
auto it = defaultColors.begin();
|
||||
size_t ind = 0;
|
||||
while (it != defaultColors.end())
|
||||
{
|
||||
this->ui_.def.colors.push_back(new ColorButton(*it, this));
|
||||
auto *button = this->ui_.def.colors[ind];
|
||||
|
||||
const int rowInd = (ind / DEFAULT_COLORS_PER_ROW) + 1;
|
||||
const int columnInd = ind % DEFAULT_COLORS_PER_ROW;
|
||||
|
||||
grid->addWidget(button, rowInd, columnInd);
|
||||
|
||||
QObject::connect(button, &QPushButton::clicked,
|
||||
[=] { this->selectColor(button->color(), false); });
|
||||
|
||||
++it;
|
||||
++ind;
|
||||
}
|
||||
|
||||
auto spacer =
|
||||
new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
grid->addItem(spacer, (ind / DEFAULT_COLORS_PER_ROW) + 2, 0, 1, 1,
|
||||
Qt::AlignTop);
|
||||
}
|
||||
|
||||
void ColorPickerDialog::initColorPicker(LayoutCreator<QWidget> &creator)
|
||||
{
|
||||
auto cpPanel = creator.setLayoutType<QHBoxLayout>();
|
||||
|
||||
/*
|
||||
* For some reason, LayoutCreator::emplace didn't work for these.
|
||||
* (Or maybe I was too dense to make it work.)
|
||||
* After trying to debug for 4 hours or so, I gave up and settled
|
||||
* for this solution.
|
||||
*/
|
||||
auto *colorPicker = new QColorPicker(this);
|
||||
this->ui_.picker.colorPicker = colorPicker;
|
||||
|
||||
auto *luminancePicker = new QColorLuminancePicker(this);
|
||||
this->ui_.picker.luminancePicker = luminancePicker;
|
||||
|
||||
cpPanel.append(colorPicker);
|
||||
cpPanel.append(luminancePicker);
|
||||
|
||||
QObject::connect(colorPicker, SIGNAL(newCol(int, int)), luminancePicker,
|
||||
SLOT(setCol(int, int)));
|
||||
|
||||
QObject::connect(
|
||||
luminancePicker, &QColorLuminancePicker::newHsv,
|
||||
[=](int h, int s, int v) {
|
||||
int alpha = this->ui_.picker.spinBoxes[SpinBox::ALPHA]->value();
|
||||
this->selectColor(QColor::fromHsv(h, s, v, alpha), true);
|
||||
});
|
||||
}
|
||||
|
||||
void ColorPickerDialog::initSpinBoxes(LayoutCreator<QWidget> &creator)
|
||||
{
|
||||
auto spinBoxes = creator.setLayoutType<QGridLayout>();
|
||||
|
||||
auto *red = this->ui_.picker.spinBoxes[SpinBox::RED] =
|
||||
new QColSpinBox(this);
|
||||
auto *green = this->ui_.picker.spinBoxes[SpinBox::GREEN] =
|
||||
new QColSpinBox(this);
|
||||
auto *blue = this->ui_.picker.spinBoxes[SpinBox::BLUE] =
|
||||
new QColSpinBox(this);
|
||||
auto *alpha = this->ui_.picker.spinBoxes[SpinBox::ALPHA] =
|
||||
new QColSpinBox(this);
|
||||
|
||||
// We need pointers to these for theme changes
|
||||
auto *redLbl = this->ui_.picker.spinBoxLabels[SpinBox::RED] =
|
||||
new QLabel("Red:");
|
||||
auto *greenLbl = this->ui_.picker.spinBoxLabels[SpinBox::GREEN] =
|
||||
new QLabel("Green:");
|
||||
auto *blueLbl = this->ui_.picker.spinBoxLabels[SpinBox::BLUE] =
|
||||
new QLabel("Blue:");
|
||||
auto *alphaLbl = this->ui_.picker.spinBoxLabels[SpinBox::ALPHA] =
|
||||
new QLabel("Alpha:");
|
||||
|
||||
spinBoxes->addWidget(redLbl, 0, 0);
|
||||
spinBoxes->addWidget(red, 0, 1);
|
||||
|
||||
spinBoxes->addWidget(greenLbl, 1, 0);
|
||||
spinBoxes->addWidget(green, 1, 1);
|
||||
|
||||
spinBoxes->addWidget(blueLbl, 2, 0);
|
||||
spinBoxes->addWidget(blue, 2, 1);
|
||||
|
||||
spinBoxes->addWidget(alphaLbl, 3, 0);
|
||||
spinBoxes->addWidget(alpha, 3, 1);
|
||||
|
||||
for (size_t i = 0; i < SpinBox::END; ++i)
|
||||
{
|
||||
QObject::connect(
|
||||
this->ui_.picker.spinBoxes[i],
|
||||
QOverload<int>::of(&QSpinBox::valueChanged), [=](int value) {
|
||||
this->selectColor(QColor(red->value(), green->value(),
|
||||
blue->value(), alpha->value()),
|
||||
false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerDialog::initHtmlColor(LayoutCreator<QWidget> &creator)
|
||||
{
|
||||
auto html = creator.setLayoutType<QGridLayout>();
|
||||
|
||||
// Copied from Qt source for QColorShower
|
||||
static QRegularExpression regExp(
|
||||
QStringLiteral("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"));
|
||||
auto *validator = this->htmlColorValidator_ =
|
||||
new QRegularExpressionValidator(regExp, this);
|
||||
|
||||
auto *htmlLabel = this->ui_.picker.htmlLabel = new QLabel("HTML:");
|
||||
auto *htmlEdit = this->ui_.picker.htmlEdit = new QLineEdit(this);
|
||||
|
||||
htmlEdit->setValidator(validator);
|
||||
|
||||
html->addWidget(htmlLabel, 0, 0);
|
||||
html->addWidget(htmlEdit, 0, 1);
|
||||
|
||||
QObject::connect(htmlEdit, &QLineEdit::textEdited,
|
||||
[=](const QString &text) {
|
||||
QColor col(text);
|
||||
if (col.isValid())
|
||||
this->selectColor(col, false);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "widgets/BasePopup.hpp"
|
||||
#include "widgets/helper/ColorButton.hpp"
|
||||
#include "widgets/helper/QColorPicker.hpp"
|
||||
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
/**
|
||||
* @brief A custom color picker dialog.
|
||||
*
|
||||
* This class exists because QColorPickerDialog did not suit our use case.
|
||||
* This dialog provides buttons for recently used and default colors, as well
|
||||
* as a color picker widget identical to the one used in QColorPickerDialog.
|
||||
*/
|
||||
class ColorPickerDialog : public BasePopup
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Create a new color picker dialog that selects the initial color.
|
||||
*
|
||||
* You can connect to the ::closed signal of this instance to get notified
|
||||
* when the dialog is closed.
|
||||
*/
|
||||
ColorPickerDialog(const QColor &initial, QWidget *parent = nullptr);
|
||||
|
||||
~ColorPickerDialog();
|
||||
|
||||
/**
|
||||
* @brief Return the final color selected by the user.
|
||||
*
|
||||
* Note that this method will always return the invalid color if the dialog
|
||||
* is still open, or if the dialog has not been confirmed.
|
||||
*
|
||||
* @return The color selected by the user, if the dialog was confirmed.
|
||||
* The invalid color, if the dialog has not been confirmed.
|
||||
*/
|
||||
QColor selectedColor() const;
|
||||
|
||||
pajlada::Signals::NoArgSignal closed;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
void themeChangedEvent();
|
||||
|
||||
private:
|
||||
struct {
|
||||
struct {
|
||||
QLabel *label;
|
||||
std::vector<ColorButton *> colors;
|
||||
} recent;
|
||||
|
||||
struct {
|
||||
QLabel *label;
|
||||
std::vector<ColorButton *> colors;
|
||||
} def;
|
||||
|
||||
struct {
|
||||
QLabel *label;
|
||||
ColorButton *color;
|
||||
} selected;
|
||||
|
||||
struct {
|
||||
QColorPicker *colorPicker;
|
||||
QColorLuminancePicker *luminancePicker;
|
||||
|
||||
std::array<QLabel *, 4> spinBoxLabels;
|
||||
std::array<QColSpinBox *, 4> spinBoxes;
|
||||
|
||||
QLabel *htmlLabel;
|
||||
QLineEdit *htmlEdit;
|
||||
} picker;
|
||||
} ui_;
|
||||
|
||||
enum SpinBox : size_t { RED = 0, GREEN = 1, BLUE = 2, ALPHA = 3, END };
|
||||
|
||||
static const size_t MAX_RECENT_COLORS = 10;
|
||||
static const size_t RECENT_COLORS_PER_ROW = 5;
|
||||
static const size_t DEFAULT_COLORS_PER_ROW = 5;
|
||||
|
||||
QColor color_;
|
||||
bool dialogConfirmed_;
|
||||
QRegularExpressionValidator *htmlColorValidator_{};
|
||||
|
||||
/**
|
||||
* @brief Update the currently selected color.
|
||||
*
|
||||
* @param color Color to update to.
|
||||
* @param fromColorPicker Whether the color update has been triggered by
|
||||
* one of the color picker widgets. This is needed
|
||||
* to prevent weird widget behavior.
|
||||
*/
|
||||
void selectColor(const QColor &color, bool fromColorPicker);
|
||||
|
||||
/// Called when the dialog is confirmed.
|
||||
void ok();
|
||||
|
||||
// Helper methods for initializing UI elements
|
||||
void initRecentColors(LayoutCreator<QWidget> &creator);
|
||||
void initDefaultColors(LayoutCreator<QWidget> &creator);
|
||||
void initColorPicker(LayoutCreator<QWidget> &creator);
|
||||
void initSpinBoxes(LayoutCreator<QWidget> &creator);
|
||||
void initHtmlColor(LayoutCreator<QWidget> &creator);
|
||||
};
|
||||
} // namespace chatterino
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "widgets/Notebook.hpp"
|
||||
#include "widgets/helper/ChannelView.hpp"
|
||||
|
||||
@@ -31,7 +32,14 @@ namespace {
|
||||
|
||||
if (!map.empty())
|
||||
{
|
||||
for (const auto &emote : map)
|
||||
std::vector<std::pair<EmoteName, EmotePtr>> vec(map.begin(),
|
||||
map.end());
|
||||
std::sort(vec.begin(), vec.end(),
|
||||
[](const std::pair<EmoteName, EmotePtr> &l,
|
||||
const std::pair<EmoteName, EmotePtr> &r) {
|
||||
return l.first.string < r.first.string;
|
||||
});
|
||||
for (const auto &emote : vec)
|
||||
{
|
||||
builder
|
||||
.emplace<EmoteElement>(emote.second,
|
||||
@@ -131,6 +139,10 @@ EmotePopup::EmotePopup(QWidget *parent)
|
||||
this->viewEmojis_ = makeView("Emojis");
|
||||
|
||||
this->loadEmojis();
|
||||
|
||||
createWindowShortcut(this, "CTRL+Tab", [=] { notebook->selectNextTab(); });
|
||||
createWindowShortcut(this, "CTRL+Shift+Tab",
|
||||
[=] { notebook->selectPreviousTab(); });
|
||||
}
|
||||
|
||||
void EmotePopup::loadChannel(ChannelPtr _channel)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace {
|
||||
// messageBox.setIcon(QMessageBox::Information);
|
||||
// messageBox.setText("Successfully logged in with user <b>" +
|
||||
// qS(username) + "</b>!");
|
||||
auto basePath = fS("/accounts/uid{}", userID);
|
||||
std::string basePath = "/accounts/uid" + userID.toStdString();
|
||||
pajlada::Settings::Setting<QString>::set(basePath + "/username",
|
||||
username);
|
||||
pajlada::Settings::Setting<QString>::set(basePath + "/userID", userID);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "IrcMessage"
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "providers/twitch/PartialTwitchUser.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
@@ -55,7 +54,7 @@ void LogsPopup::getLogs()
|
||||
dynamic_cast<TwitchChannel *>(this->channel_.get()))
|
||||
{
|
||||
this->channelName_ = twitchChannel->getName();
|
||||
this->getLogviewerLogs(twitchChannel->roomId());
|
||||
this->getOverrustleLogs();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -63,14 +62,11 @@ void LogsPopup::getLogs()
|
||||
|
||||
if (!this->channelName_.isEmpty())
|
||||
{
|
||||
PartialTwitchUser::byName(this->channelName_)
|
||||
.getId(
|
||||
[=](const QString &roomID) { this->getLogviewerLogs(roomID); },
|
||||
this);
|
||||
this->getOverrustleLogs();
|
||||
return;
|
||||
}
|
||||
|
||||
log("Unable to get logs, no channel name or something specified");
|
||||
qDebug() << "Unable to get logs, no channel name or something specified";
|
||||
}
|
||||
|
||||
void LogsPopup::setMessages(std::vector<MessagePtr> &messages)
|
||||
@@ -81,57 +77,9 @@ void LogsPopup::setMessages(std::vector<MessagePtr> &messages)
|
||||
SearchPopup::setChannel(logsChannel);
|
||||
}
|
||||
|
||||
void LogsPopup::getLogviewerLogs(const QString &roomID)
|
||||
{
|
||||
auto url = QString("https://cbenni.com/api/logs/%1/?nick=%2&before=500")
|
||||
.arg(this->channelName_, this->userName_);
|
||||
|
||||
NetworkRequest(url)
|
||||
.caller(this)
|
||||
.onError([this](NetworkResult) { this->getOverrustleLogs(); })
|
||||
.onSuccess([this, roomID](auto result) -> Outcome {
|
||||
auto data = result.parseJson();
|
||||
std::vector<MessagePtr> messages;
|
||||
|
||||
QJsonValue before = data.value("before");
|
||||
|
||||
for (auto i : before.toArray())
|
||||
{
|
||||
auto messageObject = i.toObject();
|
||||
QString message = messageObject.value("text").toString();
|
||||
|
||||
// Hacky way to fix the timestamp
|
||||
message.insert(1, "historical=1;");
|
||||
message.insert(1, QString("tmi-sent-ts=%10000;")
|
||||
.arg(messageObject["time"].toInt()));
|
||||
message.insert(1, QString("room-id=%1;").arg(roomID));
|
||||
|
||||
MessageParseArgs args;
|
||||
auto ircMessage =
|
||||
Communi::IrcMessage::fromData(message.toUtf8(), nullptr);
|
||||
auto privMsg =
|
||||
static_cast<Communi::IrcPrivateMessage *>(ircMessage);
|
||||
TwitchMessageBuilder builder(this->channel_.get(), privMsg,
|
||||
args);
|
||||
builder.message().searchText = message;
|
||||
|
||||
messages.push_back(builder.build());
|
||||
}
|
||||
|
||||
messages.push_back(
|
||||
MessageBuilder(systemMessage,
|
||||
"Logs provided by https://cbenni.com")
|
||||
.release());
|
||||
this->setMessages(messages);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
void LogsPopup::getOverrustleLogs()
|
||||
{
|
||||
QString url =
|
||||
auto url =
|
||||
QString("https://overrustlelogs.net/api/v1/stalk/%1/%2.json?limit=500")
|
||||
.arg(this->channelName_, this->userName_);
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ private:
|
||||
|
||||
void setMessages(std::vector<MessagePtr> &messages);
|
||||
void getOverrustleLogs();
|
||||
void getLogviewerLogs(const QString &roomID);
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "QualityPopup.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "util/StreamLink.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
@@ -50,7 +49,7 @@ void QualityPopup::okButtonClicked()
|
||||
}
|
||||
catch (const Exception &ex)
|
||||
{
|
||||
log("Exception caught trying to open streamlink: {}", ex.what());
|
||||
qDebug() << "Exception caught trying to open streamlink:" << ex.what();
|
||||
}
|
||||
|
||||
this->close();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define TEXT_VIEWS "Views: "
|
||||
#define TEXT_CREATED "Created: "
|
||||
#define TEXT_USER_ID "ID: "
|
||||
#define TEXT_UNAVAILABLE "(not available)"
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
@@ -382,6 +383,22 @@ void UserInfoPopup::updateUserData()
|
||||
{
|
||||
std::weak_ptr<bool> hack = this->hack_;
|
||||
|
||||
const auto onIdFetchFailed = [this]() {
|
||||
// this can occur when the account doesn't exist.
|
||||
this->ui_.followerCountLabel->setText(TEXT_FOLLOWERS +
|
||||
QString(TEXT_UNAVAILABLE));
|
||||
this->ui_.viewCountLabel->setText(TEXT_VIEWS +
|
||||
QString(TEXT_UNAVAILABLE));
|
||||
this->ui_.createdDateLabel->setText(TEXT_CREATED +
|
||||
QString(TEXT_UNAVAILABLE));
|
||||
|
||||
this->ui_.nameLabel->setText(this->userName_);
|
||||
|
||||
this->ui_.userIDLabel->setText(QString("ID") +
|
||||
QString(TEXT_UNAVAILABLE));
|
||||
this->ui_.userIDLabel->setProperty("copy-text",
|
||||
QString(TEXT_UNAVAILABLE));
|
||||
};
|
||||
const auto onIdFetched = [this, hack](QString id) {
|
||||
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
||||
|
||||
@@ -462,7 +479,8 @@ void UserInfoPopup::updateUserData()
|
||||
this->ui_.ignoreHighlights->setChecked(isIgnoringHighlights);
|
||||
};
|
||||
|
||||
PartialTwitchUser::byName(this->userName_).getId(onIdFetched, this);
|
||||
PartialTwitchUser::byName(this->userName_)
|
||||
.getId(onIdFetched, onIdFetchFailed, this);
|
||||
|
||||
this->ui_.follow->setEnabled(false);
|
||||
this->ui_.ignore->setEnabled(false);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "common/Common.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "debug/Benchmark.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
#include "messages/LimitedQueueSnapshot.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
@@ -29,6 +28,7 @@
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/TooltipPreviewImage.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/Clipboard.hpp"
|
||||
#include "util/DistanceBetweenPoints.hpp"
|
||||
#include "util/IncognitoBrowser.hpp"
|
||||
#include "widgets/Scrollbar.hpp"
|
||||
@@ -65,9 +65,8 @@ namespace {
|
||||
if (!image->isEmpty())
|
||||
{
|
||||
copyMenu->addAction(
|
||||
QString(scale) + "x link", [url = image->url()] {
|
||||
QApplication::clipboard()->setText(url.string);
|
||||
});
|
||||
QString(scale) + "x link",
|
||||
[url = image->url()] { crossPlatformCopy(url.string); });
|
||||
openMenu->addAction(
|
||||
QString(scale) + "x link", [url = image->url()] {
|
||||
QDesktopServices::openUrl(QUrl(url.string));
|
||||
@@ -85,9 +84,8 @@ namespace {
|
||||
openMenu->addSeparator();
|
||||
|
||||
copyMenu->addAction(
|
||||
"Copy " + name + " emote link", [url = emote.homePage] {
|
||||
QApplication::clipboard()->setText(url.string); //
|
||||
});
|
||||
"Copy " + name + " emote link",
|
||||
[url = emote.homePage] { crossPlatformCopy(url.string); });
|
||||
openMenu->addAction(
|
||||
"Open " + name + " emote link", [url = emote.homePage] {
|
||||
QDesktopServices::openUrl(QUrl(url.string)); //
|
||||
@@ -125,9 +123,8 @@ ChannelView::ChannelView(BaseWidget *parent)
|
||||
});
|
||||
|
||||
auto shortcut = new QShortcut(QKeySequence("Ctrl+C"), this);
|
||||
QObject::connect(shortcut, &QShortcut::activated, [this] {
|
||||
QGuiApplication::clipboard()->setText(this->getSelectedText());
|
||||
});
|
||||
QObject::connect(shortcut, &QShortcut::activated,
|
||||
[this] { crossPlatformCopy(this->getSelectedText()); });
|
||||
|
||||
this->clickTimer_ = new QTimer(this);
|
||||
this->clickTimer_->setSingleShot(true);
|
||||
@@ -651,7 +648,8 @@ void ChannelView::messageAppended(MessagePtr &message,
|
||||
|
||||
if (!messageFlags->has(MessageFlag::DoNotTriggerNotification))
|
||||
{
|
||||
if (messageFlags->has(MessageFlag::Highlighted))
|
||||
if (messageFlags->has(MessageFlag::Highlighted) &&
|
||||
!messageFlags->has(MessageFlag::Subscription))
|
||||
{
|
||||
this->tabHighlightRequested.invoke(HighlightState::Highlighted);
|
||||
}
|
||||
@@ -740,9 +738,8 @@ void ChannelView::messageReplaced(size_t index, MessagePtr &replacement)
|
||||
auto snapshot = this->messages_.getSnapshot();
|
||||
if (index >= snapshot.size())
|
||||
{
|
||||
log("Tried to replace out of bounds message. Index: {}. "
|
||||
"Length: {}",
|
||||
index, snapshot.size());
|
||||
qDebug() << "Tried to replace out of bounds message. Index:" << index
|
||||
<< ". Length:" << snapshot.size();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1554,8 +1551,7 @@ void ChannelView::addContextMenuItems(
|
||||
menu->addAction("Open link incognito",
|
||||
[url] { openLinkIncognito(url); });
|
||||
}
|
||||
menu->addAction("Copy link",
|
||||
[url] { QApplication::clipboard()->setText(url); });
|
||||
menu->addAction("Copy link", [url] { crossPlatformCopy(url); });
|
||||
|
||||
menu->addSeparator();
|
||||
}
|
||||
@@ -1563,9 +1559,8 @@ void ChannelView::addContextMenuItems(
|
||||
// Copy actions
|
||||
if (!this->selection_.isEmpty())
|
||||
{
|
||||
menu->addAction("Copy selection", [this] {
|
||||
QGuiApplication::clipboard()->setText(this->getSelectedText());
|
||||
});
|
||||
menu->addAction("Copy selection",
|
||||
[this] { crossPlatformCopy(this->getSelectedText()); });
|
||||
}
|
||||
|
||||
menu->addAction("Copy message", [layout] {
|
||||
@@ -1573,14 +1568,14 @@ void ChannelView::addContextMenuItems(
|
||||
layout->addSelectionText(copyString, 0, INT_MAX,
|
||||
CopyMode::OnlyTextAndEmotes);
|
||||
|
||||
QGuiApplication::clipboard()->setText(copyString);
|
||||
crossPlatformCopy(copyString);
|
||||
});
|
||||
|
||||
menu->addAction("Copy full message", [layout] {
|
||||
QString copyString;
|
||||
layout->addSelectionText(copyString);
|
||||
|
||||
QGuiApplication::clipboard()->setText(copyString);
|
||||
crossPlatformCopy(copyString);
|
||||
});
|
||||
|
||||
// Open in new split.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "widgets/helper/ColorButton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ColorButton::ColorButton(const QColor &color, QWidget *parent)
|
||||
: QPushButton(parent)
|
||||
, color_(color)
|
||||
{
|
||||
this->setColor(color_);
|
||||
}
|
||||
|
||||
const QColor &ColorButton::color() const
|
||||
{
|
||||
return this->color_;
|
||||
}
|
||||
|
||||
void ColorButton::setColor(QColor color)
|
||||
{
|
||||
this->color_ = color;
|
||||
this->setStyleSheet("background-color: " + color.name(QColor::HexArgb));
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ColorButton : public QPushButton
|
||||
{
|
||||
public:
|
||||
ColorButton(const QColor &color, QWidget *parent = nullptr);
|
||||
|
||||
const QColor &color() const;
|
||||
|
||||
void setColor(QColor color);
|
||||
|
||||
private:
|
||||
QColor color_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtWidgets module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "widgets/helper/QColorPicker.hpp"
|
||||
|
||||
#include <qdrawutil.h>
|
||||
|
||||
/*
|
||||
* These classes are literally copied from the Qt source.
|
||||
* Unfortunately, they are private to the QColorDialog class so we cannot use
|
||||
* them directly.
|
||||
* If they become public at any point in the future, it should be possible to
|
||||
* replace every include of this header with the respective includes for the
|
||||
* QColorPicker, QColorLuminancePicker, and QColSpinBox classes.
|
||||
*/
|
||||
namespace chatterino {
|
||||
|
||||
int QColorLuminancePicker::y2val(int y)
|
||||
{
|
||||
int d = height() - 2 * coff - 1;
|
||||
return 255 - (y - coff) * 255 / d;
|
||||
}
|
||||
|
||||
int QColorLuminancePicker::val2y(int v)
|
||||
{
|
||||
int d = height() - 2 * coff - 1;
|
||||
return coff + (255 - v) * d / 255;
|
||||
}
|
||||
|
||||
QColorLuminancePicker::QColorLuminancePicker(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
hue = 100;
|
||||
val = 100;
|
||||
sat = 100;
|
||||
pix = 0;
|
||||
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
}
|
||||
|
||||
QColorLuminancePicker::~QColorLuminancePicker()
|
||||
{
|
||||
delete pix;
|
||||
}
|
||||
|
||||
void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m)
|
||||
{
|
||||
setVal(y2val(m->y()));
|
||||
}
|
||||
|
||||
void QColorLuminancePicker::mousePressEvent(QMouseEvent *m)
|
||||
{
|
||||
setVal(y2val(m->y()));
|
||||
}
|
||||
|
||||
void QColorLuminancePicker::setVal(int v)
|
||||
{
|
||||
if (val == v)
|
||||
return;
|
||||
val = qMax(0, qMin(v, 255));
|
||||
delete pix;
|
||||
pix = 0;
|
||||
repaint();
|
||||
emit newHsv(hue, sat, val);
|
||||
}
|
||||
|
||||
//receives from a hue,sat chooser and relays.
|
||||
void QColorLuminancePicker::setCol(int h, int s)
|
||||
{
|
||||
setCol(h, s, val);
|
||||
emit newHsv(h, s, val);
|
||||
}
|
||||
|
||||
QSize QColorLuminancePicker::sizeHint() const
|
||||
{
|
||||
return QSize(LUMINANCE_PICKER_WIDTH, LUMINANCE_PICKER_HEIGHT);
|
||||
}
|
||||
|
||||
void QColorLuminancePicker::paintEvent(QPaintEvent *)
|
||||
{
|
||||
int w = width() - 5;
|
||||
QRect r(0, foff, w, height() - 2 * foff);
|
||||
int wi = r.width() - 2;
|
||||
int hi = r.height() - 2;
|
||||
if (!pix || pix->height() != hi || pix->width() != wi)
|
||||
{
|
||||
delete pix;
|
||||
QImage img(wi, hi, QImage::Format_RGB32);
|
||||
int y;
|
||||
uint *pixel = (uint *)img.scanLine(0);
|
||||
for (y = 0; y < hi; y++)
|
||||
{
|
||||
uint *end = pixel + wi;
|
||||
std::fill(pixel, end,
|
||||
QColor::fromHsv(hue, sat, y2val(y + coff)).rgb());
|
||||
pixel = end;
|
||||
}
|
||||
pix = new QPixmap(QPixmap::fromImage(img));
|
||||
}
|
||||
QPainter p(this);
|
||||
p.drawPixmap(1, coff, *pix);
|
||||
const QPalette &g = palette();
|
||||
qDrawShadePanel(&p, r, g, true);
|
||||
p.setPen(g.windowText().color());
|
||||
p.setBrush(g.windowText());
|
||||
QPolygon a;
|
||||
int y = val2y(val);
|
||||
a.setPoints(3, w, y, w + 5, y + 5, w + 5, y - 5);
|
||||
p.eraseRect(w, 0, 5, height());
|
||||
p.drawPolygon(a);
|
||||
}
|
||||
|
||||
void QColorLuminancePicker::setCol(int h, int s, int v)
|
||||
{
|
||||
val = v;
|
||||
hue = h;
|
||||
sat = s;
|
||||
delete pix;
|
||||
pix = 0;
|
||||
repaint();
|
||||
}
|
||||
|
||||
QPoint QColorPicker::colPt()
|
||||
{
|
||||
QRect r = contentsRect();
|
||||
return QPoint((360 - hue) * (r.width() - 1) / 360,
|
||||
(255 - sat) * (r.height() - 1) / 255);
|
||||
}
|
||||
|
||||
int QColorPicker::huePt(const QPoint &pt)
|
||||
{
|
||||
QRect r = contentsRect();
|
||||
return 360 - pt.x() * 360 / (r.width() - 1);
|
||||
}
|
||||
|
||||
int QColorPicker::satPt(const QPoint &pt)
|
||||
{
|
||||
QRect r = contentsRect();
|
||||
return 255 - pt.y() * 255 / (r.height() - 1);
|
||||
}
|
||||
|
||||
void QColorPicker::setCol(const QPoint &pt)
|
||||
{
|
||||
setCol(huePt(pt), satPt(pt));
|
||||
}
|
||||
|
||||
QColorPicker::QColorPicker(QWidget *parent)
|
||||
: QFrame(parent)
|
||||
, crossVisible(true)
|
||||
{
|
||||
hue = 0;
|
||||
sat = 0;
|
||||
setCol(150, 255);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
}
|
||||
|
||||
QColorPicker::~QColorPicker()
|
||||
{
|
||||
}
|
||||
|
||||
void QColorPicker::setCrossVisible(bool visible)
|
||||
{
|
||||
if (crossVisible != visible)
|
||||
{
|
||||
crossVisible = visible;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QSize QColorPicker::sizeHint() const
|
||||
{
|
||||
return QSize(COLOR_PICKER_WIDTH, COLOR_PICKER_HEIGHT);
|
||||
}
|
||||
|
||||
void QColorPicker::setCol(int h, int s)
|
||||
{
|
||||
int nhue = qMin(qMax(0, h), 359);
|
||||
int nsat = qMin(qMax(0, s), 255);
|
||||
if (nhue == hue && nsat == sat)
|
||||
return;
|
||||
QRect r(colPt(), QSize(20, 20));
|
||||
hue = nhue;
|
||||
sat = nsat;
|
||||
r = r.united(QRect(colPt(), QSize(20, 20)));
|
||||
r.translate(contentsRect().x() - 9, contentsRect().y() - 9);
|
||||
repaint(r);
|
||||
}
|
||||
|
||||
void QColorPicker::mouseMoveEvent(QMouseEvent *m)
|
||||
{
|
||||
QPoint p = m->pos() - contentsRect().topLeft();
|
||||
setCol(p);
|
||||
emit newCol(hue, sat);
|
||||
}
|
||||
|
||||
void QColorPicker::mousePressEvent(QMouseEvent *m)
|
||||
{
|
||||
QPoint p = m->pos() - contentsRect().topLeft();
|
||||
setCol(p);
|
||||
emit newCol(hue, sat);
|
||||
}
|
||||
|
||||
void QColorPicker::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
drawFrame(&p);
|
||||
QRect r = contentsRect();
|
||||
p.drawPixmap(r.topLeft(), pix);
|
||||
if (crossVisible)
|
||||
{
|
||||
QPoint pt = colPt() + r.topLeft();
|
||||
p.setPen(Qt::black);
|
||||
p.fillRect(pt.x() - 9, pt.y(), 20, 2, Qt::black);
|
||||
p.fillRect(pt.x(), pt.y() - 9, 2, 20, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
void QColorPicker::resizeEvent(QResizeEvent *ev)
|
||||
{
|
||||
QFrame::resizeEvent(ev);
|
||||
int w = width() - frameWidth() * 2;
|
||||
int h = height() - frameWidth() * 2;
|
||||
QImage img(w, h, QImage::Format_RGB32);
|
||||
int x, y;
|
||||
uint *pixel = (uint *)img.scanLine(0);
|
||||
for (y = 0; y < h; y++)
|
||||
{
|
||||
const uint *end = pixel + w;
|
||||
x = 0;
|
||||
while (pixel < end)
|
||||
{
|
||||
QPoint p(x, y);
|
||||
QColor c;
|
||||
c.setHsv(huePt(p), satPt(p), 200);
|
||||
*pixel = c.rgb();
|
||||
++pixel;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
pix = QPixmap::fromImage(img);
|
||||
}
|
||||
|
||||
QColSpinBox::QColSpinBox(QWidget *parent)
|
||||
: QSpinBox(parent)
|
||||
{
|
||||
this->setRange(0, 255);
|
||||
}
|
||||
|
||||
void QColSpinBox::setValue(int i)
|
||||
{
|
||||
const QSignalBlocker blocker(this);
|
||||
QSpinBox::setValue(i);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtWidgets module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
/*
|
||||
* These classes are literally copied from the Qt source.
|
||||
* Unfortunately, they are private to the QColorDialog class so we cannot use
|
||||
* them directly.
|
||||
* If they become public at any point in the future, it should be possible to
|
||||
* replace every include of this header with the respective includes for the
|
||||
* QColorPicker, QColorLuminancePicker, and QColSpinBox classes.
|
||||
*/
|
||||
class QColorPicker : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QColorPicker(QWidget *parent);
|
||||
~QColorPicker();
|
||||
void setCrossVisible(bool visible);
|
||||
|
||||
public slots:
|
||||
void setCol(int h, int s);
|
||||
|
||||
signals:
|
||||
void newCol(int h, int s);
|
||||
|
||||
protected:
|
||||
QSize sizeHint() const override;
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void mouseMoveEvent(QMouseEvent *) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
|
||||
private:
|
||||
int hue;
|
||||
int sat;
|
||||
QPoint colPt();
|
||||
int huePt(const QPoint &pt);
|
||||
int satPt(const QPoint &pt);
|
||||
void setCol(const QPoint &pt);
|
||||
QPixmap pix;
|
||||
bool crossVisible;
|
||||
};
|
||||
|
||||
static const int COLOR_PICKER_WIDTH = 220;
|
||||
static const int COLOR_PICKER_HEIGHT = 200;
|
||||
|
||||
class QColorLuminancePicker : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QColorLuminancePicker(QWidget *parent = 0);
|
||||
~QColorLuminancePicker();
|
||||
|
||||
public slots:
|
||||
void setCol(int h, int s, int v);
|
||||
void setCol(int h, int s);
|
||||
|
||||
signals:
|
||||
void newHsv(int h, int s, int v);
|
||||
|
||||
protected:
|
||||
QSize sizeHint() const override;
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void mouseMoveEvent(QMouseEvent *) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
|
||||
private:
|
||||
enum { foff = 3, coff = 4 }; //frame and contents offset
|
||||
int val;
|
||||
int hue;
|
||||
int sat;
|
||||
int y2val(int y);
|
||||
int val2y(int val);
|
||||
void setVal(int v);
|
||||
QPixmap *pix;
|
||||
};
|
||||
|
||||
static const int LUMINANCE_PICKER_WIDTH = 25;
|
||||
static const int LUMINANCE_PICKER_HEIGHT = COLOR_PICKER_HEIGHT;
|
||||
|
||||
class QColSpinBox : public QSpinBox
|
||||
{
|
||||
public:
|
||||
QColSpinBox(QWidget *parent);
|
||||
|
||||
void setValue(int i);
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,24 +1,27 @@
|
||||
#include "widgets/helper/ScrollbarHighlight.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "widgets/Scrollbar.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ScrollbarHighlight::ScrollbarHighlight()
|
||||
: color_(Color::Highlight)
|
||||
: color_(std::make_shared<QColor>())
|
||||
, style_(Style::None)
|
||||
{
|
||||
}
|
||||
|
||||
ScrollbarHighlight::ScrollbarHighlight(Color color, Style style)
|
||||
ScrollbarHighlight::ScrollbarHighlight(const std::shared_ptr<QColor> color,
|
||||
Style style)
|
||||
: color_(color)
|
||||
, style_(style)
|
||||
{
|
||||
}
|
||||
|
||||
ScrollbarHighlight::Color ScrollbarHighlight::getColor() const
|
||||
QColor ScrollbarHighlight::getColor() const
|
||||
{
|
||||
return this->color_;
|
||||
return *this->color_;
|
||||
}
|
||||
|
||||
ScrollbarHighlight::Style ScrollbarHighlight::getStyle() const
|
||||
|
||||
@@ -6,17 +6,25 @@ class ScrollbarHighlight
|
||||
{
|
||||
public:
|
||||
enum Style : char { None, Default, Line };
|
||||
enum Color : char { Highlight, Subscription };
|
||||
|
||||
/**
|
||||
* @brief Constructs an invalid ScrollbarHighlight.
|
||||
*
|
||||
* A highlight constructed this way will not show on the scrollbar.
|
||||
* For these, use the static ScrollbarHighlight::newSubscription and
|
||||
* ScrollbarHighlight::newHighlight methods.
|
||||
*/
|
||||
ScrollbarHighlight();
|
||||
ScrollbarHighlight(Color color, Style style = Default);
|
||||
|
||||
Color getColor() const;
|
||||
ScrollbarHighlight(const std::shared_ptr<QColor> color,
|
||||
Style style = Default);
|
||||
|
||||
QColor getColor() const;
|
||||
Style getStyle() const;
|
||||
bool isNull() const;
|
||||
|
||||
private:
|
||||
Color color_;
|
||||
std::shared_ptr<QColor> color_;
|
||||
Style style_;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/Modes.hpp"
|
||||
#include "common/Version.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "util/RemoveScrollAreaBackground.hpp"
|
||||
#include "widgets/helper/SignalLabel.hpp"
|
||||
@@ -102,8 +101,6 @@ AboutPage::AboutPage()
|
||||
":/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");
|
||||
@@ -166,7 +163,7 @@ AboutPage::AboutPage()
|
||||
|
||||
if (contributorParts.size() != 4)
|
||||
{
|
||||
log("Missing parts in line '{}'", line);
|
||||
qDebug() << "Missing parts in line" << line;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -547,6 +547,33 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
||||
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
|
||||
});
|
||||
|
||||
layout.addTitle("Similarity");
|
||||
layout.addDescription(
|
||||
"Hides or grays out similar messages from the same user");
|
||||
layout.addCheckbox("Similarity enabled", s.similarityEnabled);
|
||||
layout.addCheckbox("Gray out similar messages", s.colorSimilarDisabled);
|
||||
layout.addCheckbox("Hide similar messages (Ctrl + H)", s.hideSimilar);
|
||||
layout.addCheckbox("Hide or gray out my own similar messages",
|
||||
s.hideSimilarMyself);
|
||||
layout.addCheckbox("Shown similar messages trigger highlights",
|
||||
s.shownSimilarTriggerHighlights);
|
||||
layout.addDropdown<float>(
|
||||
"Similarity percentage", {"0.5", "0.75", "0.9"}, s.similarityPercentage,
|
||||
[](auto val) { return QString::number(val); },
|
||||
[](auto args) { return fuzzyToFloat(args.value, 0.9f); });
|
||||
s.hideSimilar.connect(
|
||||
[]() { getApp()->windows->forceLayoutChannelViews(); }, false);
|
||||
layout.addDropdown<int>(
|
||||
"Similar messages max delay in seconds",
|
||||
{"5", "10", "15", "30", "60", "120"}, s.hideSimilarMaxDelay,
|
||||
[](auto val) { return QString::number(val); },
|
||||
[](auto args) { return fuzzyToInt(args.value, 5); });
|
||||
layout.addDropdown<int>(
|
||||
"Similar messages max previous messages to check",
|
||||
{"1", "2", "3", "4", "5"}, s.hideSimilarMaxMessagesToCheck,
|
||||
[](auto val) { return QString::number(val); },
|
||||
[](auto args) { return fuzzyToInt(args.value, 3); });
|
||||
|
||||
// invisible element for width
|
||||
auto inv = new BaseWidget(this);
|
||||
inv->setScaleIndependantWidth(500);
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include "controllers/highlights/HighlightController.hpp"
|
||||
#include "controllers/highlights/HighlightModel.hpp"
|
||||
#include "controllers/highlights/UserHighlightModel.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "util/StandardItemHelper.hpp"
|
||||
#include "widgets/helper/EditableModelView.hpp"
|
||||
#include "widgets/dialogs/ColorPickerDialog.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
@@ -46,8 +46,10 @@ HighlightingPage::HighlightingPage()
|
||||
// HIGHLIGHTS
|
||||
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
|
||||
{
|
||||
highlights.emplace<QLabel>("Messages can be highlighted if "
|
||||
"they match a certain pattern.");
|
||||
highlights.emplace<QLabel>(
|
||||
"Messages can be highlighted if they match a certain "
|
||||
"pattern.\n"
|
||||
"NOTE: User highlights will override phrase highlights.");
|
||||
|
||||
EditableModelView *view =
|
||||
highlights
|
||||
@@ -57,7 +59,8 @@ HighlightingPage::HighlightingPage()
|
||||
|
||||
view->addRegexHelpLink();
|
||||
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
|
||||
"Enable\nregex", "Case-\nsensitive"});
|
||||
"Enable\nregex", "Case-\nsensitive",
|
||||
"Custom\nsound", "Color"});
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::Fixed);
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
@@ -72,14 +75,22 @@ HighlightingPage::HighlightingPage()
|
||||
|
||||
view->addButtonPressed.connect([] {
|
||||
getApp()->highlights->phrases.appendItem(HighlightPhrase{
|
||||
"my phrase", true, false, false, false});
|
||||
"my phrase", true, false, false, false, "",
|
||||
*ColorProvider::instance().color(
|
||||
ColorType::SelfHighlight)});
|
||||
});
|
||||
|
||||
QObject::connect(view->getTableView(), &QTableView::clicked,
|
||||
[this, view](const QModelIndex &clicked) {
|
||||
this->tableCellClicked(clicked, view);
|
||||
});
|
||||
}
|
||||
|
||||
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
|
||||
{
|
||||
pingUsers.emplace<QLabel>(
|
||||
"Messages from a certain user can be highlighted.");
|
||||
"Messages from a certain user can be highlighted.\n"
|
||||
"NOTE: User highlights will override phrase highlights.");
|
||||
EditableModelView *view =
|
||||
pingUsers
|
||||
.emplace<EditableModelView>(
|
||||
@@ -90,9 +101,10 @@ HighlightingPage::HighlightingPage()
|
||||
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
|
||||
// set to "false" by default & the column is hidden
|
||||
view->setTitles({"Username", "Flash\ntaskbar", "Play\nsound",
|
||||
"Enable\nregex"});
|
||||
"Enable\nregex", "Case-\nsensitive",
|
||||
"Custom\nsound", "Color"});
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::Fixed);
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
@@ -108,8 +120,15 @@ HighlightingPage::HighlightingPage()
|
||||
view->addButtonPressed.connect([] {
|
||||
getApp()->highlights->highlightedUsers.appendItem(
|
||||
HighlightPhrase{"highlighted user", true, false, false,
|
||||
false});
|
||||
false, "",
|
||||
*ColorProvider::instance().color(
|
||||
ColorType::SelfHighlight)});
|
||||
});
|
||||
|
||||
QObject::connect(view->getTableView(), &QTableView::clicked,
|
||||
[this, view](const QModelIndex &clicked) {
|
||||
this->tableCellClicked(clicked, view);
|
||||
});
|
||||
}
|
||||
|
||||
auto disabledUsers =
|
||||
@@ -148,17 +167,33 @@ HighlightingPage::HighlightingPage()
|
||||
// MISC
|
||||
auto customSound = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
{
|
||||
customSound.append(this->createCheckBox(
|
||||
"Custom sound", getSettings()->customHighlightSound));
|
||||
auto fallbackSound = customSound.append(this->createCheckBox(
|
||||
"Fallback sound (played when no other sound is set)",
|
||||
getSettings()->customHighlightSound));
|
||||
|
||||
auto getSelectFileText = [] {
|
||||
const QString value = getSettings()->pathHighlightSound;
|
||||
return value.isEmpty() ? "Select custom fallback sound"
|
||||
: QUrl::fromLocalFile(value).fileName();
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
customSound.emplace<QPushButton>(getSelectFileText());
|
||||
|
||||
QObject::connect(
|
||||
selectFile.getElement(), &QPushButton::clicked, this,
|
||||
[=]() mutable {
|
||||
auto fileName = QFileDialog::getOpenFileName(
|
||||
this, tr("Open Sound"), "",
|
||||
tr("Audio Files (*.mp3 *.wav)"));
|
||||
|
||||
getSettings()->pathHighlightSound = fileName;
|
||||
selectFile.getElement()->setText(getSelectFileText());
|
||||
|
||||
// Set check box according to updated value
|
||||
fallbackSound->setCheckState(
|
||||
fileName.isEmpty() ? Qt::Unchecked : Qt::Checked);
|
||||
});
|
||||
}
|
||||
|
||||
layout.append(createCheckBox(ALWAYS_PLAY,
|
||||
@@ -172,4 +207,71 @@ HighlightingPage::HighlightingPage()
|
||||
this->disabledUsersChangedTimer_.setSingleShot(true);
|
||||
}
|
||||
|
||||
void HighlightingPage::tableCellClicked(const QModelIndex &clicked,
|
||||
EditableModelView *view)
|
||||
{
|
||||
using Column = HighlightModel::Column;
|
||||
|
||||
if (clicked.column() == Column::SoundPath)
|
||||
{
|
||||
auto fileUrl = QFileDialog::getOpenFileUrl(
|
||||
this, tr("Open Sound"), QUrl(), tr("Audio Files (*.mp3 *.wav)"));
|
||||
view->getModel()->setData(clicked, fileUrl, Qt::UserRole);
|
||||
view->getModel()->setData(clicked, fileUrl.fileName(), Qt::DisplayRole);
|
||||
|
||||
// Enable custom sound check box if user set a sound
|
||||
if (!fileUrl.isEmpty())
|
||||
{
|
||||
QModelIndex checkBox = clicked.siblingAtColumn(Column::PlaySound);
|
||||
view->getModel()->setData(checkBox, Qt::Checked,
|
||||
Qt::CheckStateRole);
|
||||
}
|
||||
}
|
||||
else if (clicked.column() == Column::Color)
|
||||
{
|
||||
auto initial =
|
||||
view->getModel()->data(clicked, Qt::DecorationRole).value<QColor>();
|
||||
|
||||
auto dialog = new ColorPickerDialog(initial, this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->show();
|
||||
dialog->closed.connect([=] {
|
||||
QColor selected = dialog->selectedColor();
|
||||
|
||||
if (selected.isValid())
|
||||
{
|
||||
view->getModel()->setData(clicked, selected,
|
||||
Qt::DecorationRole);
|
||||
|
||||
// Hacky (?) way to figure out what tab the cell was clicked in
|
||||
const bool fromMessages = (dynamic_cast<HighlightModel *>(
|
||||
view->getModel()) != nullptr);
|
||||
|
||||
if (fromMessages)
|
||||
{
|
||||
/*
|
||||
* For preset highlights in the "Messages" tab, we need to
|
||||
* manually update the color map.
|
||||
*/
|
||||
auto instance = ColorProvider::instance();
|
||||
switch (clicked.row())
|
||||
{
|
||||
case 0:
|
||||
instance.updateColor(ColorType::SelfHighlight,
|
||||
selected);
|
||||
break;
|
||||
case 1:
|
||||
instance.updateColor(ColorType::Whisper, selected);
|
||||
break;
|
||||
case 2:
|
||||
instance.updateColor(ColorType::Subscription,
|
||||
selected);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "widgets/helper/EditableModelView.hpp"
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
@@ -17,6 +18,8 @@ public:
|
||||
|
||||
private:
|
||||
QTimer disabledUsersChangedTimer_;
|
||||
|
||||
void tableCellClicked(const QModelIndex &clicked, EditableModelView *view);
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -26,6 +26,10 @@ KeyboardSettingsPage::KeyboardSettingsPage()
|
||||
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->addRow(
|
||||
new QLabel("Ctrl + H"),
|
||||
new QLabel(
|
||||
"Hide/Show similar messages (Enable in General under Similarity)"));
|
||||
|
||||
form->addItem(new QSpacerItem(16, 16));
|
||||
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "common/Env.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "providers/twitch/EmoteValue.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||
@@ -13,6 +12,7 @@
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/NuulsUploader.hpp"
|
||||
#include "util/Clipboard.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "util/StreamLink.hpp"
|
||||
#include "widgets/Notebook.hpp"
|
||||
@@ -537,7 +537,7 @@ void Split::openInStreamlink()
|
||||
}
|
||||
catch (const Exception &ex)
|
||||
{
|
||||
log("Error in doOpenStreamlink: {}", ex.what());
|
||||
qDebug() << "Error in doOpenStreamlink:" << ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ void Split::openSubPage()
|
||||
|
||||
void Split::copyToClipboard()
|
||||
{
|
||||
QApplication::clipboard()->setText(this->view_->getSelectedText());
|
||||
crossPlatformCopy(this->view_->getSelectedText());
|
||||
}
|
||||
|
||||
void Split::showSearch()
|
||||
|
||||
@@ -577,10 +577,15 @@ void SplitHeader::updateModerationModeIcon()
|
||||
auto channel = this->split_->getChannel();
|
||||
auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
|
||||
|
||||
if (twitchChannel != nullptr && twitchChannel->hasModRights())
|
||||
if (twitchChannel != nullptr &&
|
||||
(twitchChannel->hasModRights() || moderationMode))
|
||||
{
|
||||
this->moderationButton_->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->moderationButton_->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void SplitHeader::paintEvent(QPaintEvent *)
|
||||
|
||||
Reference in New Issue
Block a user