mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Merge branch 'master' into apa-bits
This commit is contained in:
commit
9f1eb654fd
94 changed files with 1178 additions and 1640 deletions
|
@ -461,4 +461,4 @@ git_hash = $$str_member($$git_commit, 0, 8)
|
|||
# https://stackoverflow.com/questions/3348711/add-a-define-to-qmake-with-a-value/18343449#18343449
|
||||
DEFINES += CHATTERINO_GIT_COMMIT=\\\"$$git_commit\\\"
|
||||
DEFINES += CHATTERINO_GIT_RELEASE=\\\"$$git_release\\\"
|
||||
DEFINES += CHATTERINO_GIT_HASH=\\\"$$git_hash\\\"
|
||||
DEFINES += CHATTERINO_GIT_HASH=\\\"$$git_hash\\\"
|
|
@ -3,7 +3,7 @@ Below I have tried to list all environment variables that can be used to modify
|
|||
|
||||
### CHATTERINO2_RECENT_MESSAGES_URL
|
||||
Used to change the URL that Chatterino2 uses when trying to load historic Twitch chat messages (if the setting is enabled).
|
||||
Default value: `https://recent-messages.robotty.de/api/v2/recent-messages/%1?clearchatToNotice=true`
|
||||
Default value: `https://recent-messages.robotty.de/api/v2/recent-messages/%1?clearchatToNotice=true` (an [open-source service](https://github.com/robotty/recent-messages) written and currently run by [@RAnders00](https://github.com/RAnders00))
|
||||
Arguments:
|
||||
- `%1` = Name of the Twitch channel
|
||||
|
||||
|
|
|
@ -98,12 +98,12 @@ void AB_SETTINGS_CLASS::restoreSnapshot()
|
|||
|
||||
float AB_SETTINGS_CLASS::getClampedUiScale() const
|
||||
{
|
||||
return clamp<float>(this->uiScale.getValue(), 0.1, 10);
|
||||
return clamp<float>(this->uiScale.getValue(), 0.2f, 10);
|
||||
}
|
||||
|
||||
void AB_SETTINGS_CLASS::setClampedUiScale(float value)
|
||||
{
|
||||
this->uiScale.setValue(clamp<float>(value, 0.1, 10));
|
||||
this->uiScale.setValue(clamp<float>(value, 0.2f, 10));
|
||||
}
|
||||
|
||||
#ifndef AB_CUSTOM_SETTINGS
|
||||
|
|
|
@ -161,6 +161,8 @@ Fonts::FontData Fonts::createFontData(FontStyle type, float scale)
|
|||
{FontStyle::Tiny, {8, "Monospace", false, QFont::Normal}},
|
||||
{FontStyle::UiMedium,
|
||||
{int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
{FontStyle::UiMediumBold,
|
||||
{int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Bold}},
|
||||
{FontStyle::UiTabs,
|
||||
{int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@ enum class FontStyle : uint8_t {
|
|||
ChatVeryLarge,
|
||||
|
||||
UiMedium,
|
||||
UiMediumBold,
|
||||
UiTabs,
|
||||
|
||||
// don't remove this value
|
||||
|
|
|
@ -61,7 +61,12 @@ BaseWindow::BaseWindow(QWidget *parent, Flags _flags)
|
|||
this->init();
|
||||
|
||||
getSettings()->uiScale.connect(
|
||||
[this]() { postToThread([this] { this->updateScale(); }); },
|
||||
[this]() {
|
||||
postToThread([this] {
|
||||
this->updateScale();
|
||||
this->updateScale();
|
||||
});
|
||||
},
|
||||
this->connections_);
|
||||
|
||||
this->updateScale();
|
||||
|
@ -72,6 +77,30 @@ BaseWindow::BaseWindow(QWidget *parent, Flags _flags)
|
|||
// QTimer::this->scaleChangedEvent(this->getScale());
|
||||
|
||||
this->resize(300, 150);
|
||||
|
||||
#ifdef USEWINSDK
|
||||
this->useNextBounds_.setSingleShot(true);
|
||||
QObject::connect(&this->useNextBounds_, &QTimer::timeout, this,
|
||||
[this]() { this->currentBounds_ = this->nextBounds_; });
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::setInitialBounds(const QRect &bounds)
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
this->initalBounds_ = bounds;
|
||||
#else
|
||||
this->setGeometry(bounds);
|
||||
#endif
|
||||
}
|
||||
|
||||
QRect BaseWindow::getBounds()
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
return this->currentBounds_;
|
||||
#else
|
||||
return this->geometry();
|
||||
#endif
|
||||
}
|
||||
|
||||
float BaseWindow::scale() const
|
||||
|
@ -574,6 +603,11 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
|
|||
returnValue = this->handleSIZE(msg);
|
||||
break;
|
||||
|
||||
case WM_MOVE:
|
||||
returnValue = this->handleMOVE(msg);
|
||||
*result = 0;
|
||||
break;
|
||||
|
||||
case WM_NCHITTEST:
|
||||
returnValue = this->handleNCHITTEST(msg, result);
|
||||
break;
|
||||
|
@ -704,12 +738,23 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg)
|
|||
this->updateScale();
|
||||
}
|
||||
|
||||
if (!this->shown_ && this->isVisible() && this->hasCustomWindowFrame())
|
||||
if (!this->shown_ && this->isVisible())
|
||||
{
|
||||
this->shown_ = true;
|
||||
if (this->hasCustomWindowFrame())
|
||||
{
|
||||
this->shown_ = true;
|
||||
|
||||
const MARGINS shadow = {8, 8, 8, 8};
|
||||
DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow);
|
||||
const MARGINS shadow = {8, 8, 8, 8};
|
||||
DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow);
|
||||
}
|
||||
if (!this->initalBounds_.isNull())
|
||||
{
|
||||
::SetWindowPos(msg->hwnd, nullptr, this->initalBounds_.x(),
|
||||
this->initalBounds_.y(), this->initalBounds_.width(),
|
||||
this->initalBounds_.height(),
|
||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
this->currentBounds_ = this->initalBounds_;
|
||||
}
|
||||
}
|
||||
|
||||
this->calcButtonsSizes();
|
||||
|
@ -770,6 +815,18 @@ bool BaseWindow::handleSIZE(MSG *msg)
|
|||
{
|
||||
this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0);
|
||||
}
|
||||
|
||||
this->isNotMinimizedOrMaximized_ = msg->wParam == SIZE_RESTORED;
|
||||
|
||||
if (this->isNotMinimizedOrMaximized_)
|
||||
{
|
||||
RECT rect;
|
||||
::GetWindowRect(msg->hwnd, &rect);
|
||||
this->currentBounds_ =
|
||||
QRect(QPoint(rect.left, rect.top),
|
||||
QPoint(rect.right - 1, rect.bottom - 1));
|
||||
}
|
||||
this->useNextBounds_.stop();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -778,6 +835,22 @@ bool BaseWindow::handleSIZE(MSG *msg)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool BaseWindow::handleMOVE(MSG *msg)
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
if (this->isNotMinimizedOrMaximized_)
|
||||
{
|
||||
RECT rect;
|
||||
::GetWindowRect(msg->hwnd, &rect);
|
||||
this->nextBounds_ = QRect(QPoint(rect.left, rect.top),
|
||||
QPoint(rect.right - 1, rect.bottom - 1));
|
||||
|
||||
this->useNextBounds_.start(10);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
|
|
|
@ -34,6 +34,9 @@ public:
|
|||
|
||||
explicit BaseWindow(QWidget *parent = nullptr, Flags flags_ = None);
|
||||
|
||||
void setInitialBounds(const QRect &bounds);
|
||||
QRect getBounds();
|
||||
|
||||
QWidget *getLayoutContainer();
|
||||
bool hasCustomWindowFrame();
|
||||
TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style,
|
||||
|
@ -95,6 +98,7 @@ private:
|
|||
bool handleSHOWWINDOW(MSG *msg);
|
||||
bool handleNCCALCSIZE(MSG *msg, long *result);
|
||||
bool handleSIZE(MSG *msg);
|
||||
bool handleMOVE(MSG *msg);
|
||||
bool handleNCHITTEST(MSG *msg, long *result);
|
||||
|
||||
bool enableCustomFrame_;
|
||||
|
@ -116,6 +120,14 @@ private:
|
|||
std::vector<Button *> buttons;
|
||||
} ui_;
|
||||
|
||||
#ifdef USEWINSDK
|
||||
QRect initalBounds_;
|
||||
QRect currentBounds_;
|
||||
QRect nextBounds_;
|
||||
QTimer useNextBounds_;
|
||||
bool isNotMinimizedOrMaximized_{};
|
||||
#endif
|
||||
|
||||
pajlada::Signals::SignalHolder connections_;
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ void Label::paintEvent(QPaintEvent *)
|
|||
? Qt::AlignLeft | Qt::AlignVCenter
|
||||
: Qt::AlignCenter;
|
||||
|
||||
painter.setBrush(this->palette().windowText());
|
||||
|
||||
QTextOption option(alignment);
|
||||
option.setWrapMode(QTextOption::NoWrap);
|
||||
painter.drawText(textRect, this->text_, option);
|
||||
|
|
|
@ -38,14 +38,14 @@ const QPixmap &Button::getPixmap() const
|
|||
return this->pixmap_;
|
||||
}
|
||||
|
||||
void Button::setDim(bool value)
|
||||
void Button::setDim(Dim value)
|
||||
{
|
||||
this->dimPixmap_ = value;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
bool Button::getDim() const
|
||||
Button::Dim Button::getDim() const
|
||||
{
|
||||
return this->dimPixmap_;
|
||||
}
|
||||
|
@ -76,7 +76,12 @@ bool Button::getEnableMargin() const
|
|||
|
||||
qreal Button::getCurrentDimAmount() const
|
||||
{
|
||||
return this->dimPixmap_ && !this->mouseOver_ ? 0.7 : 1;
|
||||
if (this->dimPixmap_ == Dim::None || this->mouseOver_)
|
||||
return 1;
|
||||
else if (this->dimPixmap_ == Dim::Some)
|
||||
return 0.7;
|
||||
else
|
||||
return 0.15;
|
||||
}
|
||||
|
||||
void Button::setBorderColor(const QColor &color)
|
||||
|
@ -114,13 +119,13 @@ void Button::paintEvent(QPaintEvent *)
|
|||
|
||||
if (!this->pixmap_.isNull())
|
||||
{
|
||||
if (!this->mouseOver_ && this->dimPixmap_ && this->enabled_)
|
||||
{
|
||||
painter.setOpacity(this->getCurrentDimAmount());
|
||||
}
|
||||
painter.setOpacity(this->getCurrentDimAmount());
|
||||
|
||||
QRect rect = this->rect();
|
||||
int s = this->enableMargin_ ? int(6 * this->scale()) : 0;
|
||||
|
||||
int margin = this->height() < 22 * this->scale() ? 3 : 6;
|
||||
|
||||
int s = this->enableMargin_ ? int(margin * this->scale()) : 0;
|
||||
|
||||
rect.moveLeft(s);
|
||||
rect.setRight(rect.right() - s - s);
|
||||
|
|
|
@ -28,14 +28,16 @@ class Button : public BaseWidget
|
|||
};
|
||||
|
||||
public:
|
||||
enum class Dim { None, Some, Lots };
|
||||
|
||||
Button(BaseWidget *parent = nullptr);
|
||||
|
||||
void setMouseEffectColor(boost::optional<QColor> color);
|
||||
void setPixmap(const QPixmap &pixmap_);
|
||||
const QPixmap &getPixmap() const;
|
||||
|
||||
void setDim(bool value);
|
||||
bool getDim() const;
|
||||
void setDim(Dim value);
|
||||
Dim getDim() const;
|
||||
qreal getCurrentDimAmount() const;
|
||||
|
||||
void setEnable(bool value);
|
||||
|
@ -76,7 +78,7 @@ private:
|
|||
|
||||
QColor borderColor_{};
|
||||
QPixmap pixmap_{};
|
||||
bool dimPixmap_{true};
|
||||
Dim dimPixmap_{Dim::Some};
|
||||
bool enableMargin_{true};
|
||||
QPoint mousePos_{};
|
||||
double hoverMultiplier_{0.0};
|
||||
|
|
|
@ -12,7 +12,8 @@ header_header = \
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class Resources2 : public Singleton {
|
||||
class Resources2 : public Singleton
|
||||
{
|
||||
public:
|
||||
Resources2();
|
||||
|
||||
|
|
BIN
resources/buttons/copyDark.png
Normal file
BIN
resources/buttons/copyDark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
13
resources/buttons/copyDark.svg
Normal file
13
resources/buttons/copyDark.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<!-- Created using Krita: http://krita.org -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:krita="http://krita.org/namespaces/svg/krita"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
width="368.64pt"
|
||||
height="368.64pt"
|
||||
viewBox="0 0 368.64 368.64">
|
||||
<defs/>
|
||||
<path id="shape0" transform="translate(0.477297082745412, 59.3121166947556)" fill="#ffffff" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" d="M42.034 0.254558L43.0522 255.449L43.6262 256.827L44.1855 258.216L44.7441 259.606L45.3161 260.985L45.9154 262.341L46.5562 263.662L47.2523 264.937L48.018 266.154L48.867 267.3L49.8136 268.365L50.8717 269.336L52.0553 270.202L53.3785 270.951L54.8553 271.571L56.4997 272.05L58.3257 272.378L268.973 272.505L269.227 294.397L268.9 295.844L268.498 297.271L268.017 298.668L267.453 300.026L266.799 301.334L266.053 302.584L265.207 303.766L264.259 304.87L263.202 305.887L262.031 306.807L260.742 307.62L259.33 308.318L257.791 308.89L256.118 309.328L13.2689 309.328L11.4859 308.872L9.86178 308.304L8.38902 307.628L7.05996 306.85L5.86701 305.976L4.80256 305.011L3.85899 303.959L3.02871 302.828L2.3041 301.621L1.67756 300.345L1.14149 299.004L0.68828 297.605L0.310317 296.152L0 294.651L0.310203 14.5104L0.847067 13.1388L1.38231 11.7663L1.93671 10.4058L2.53105 9.0706L3.18609 7.77381L3.92263 6.52858L4.76143 5.34805L5.72327 4.24536L6.82893 3.23365L8.09918 2.32608L9.55481 1.53579L11.2166 0.87591L13.1053 0.3596L15.2417 0L42.034 0.254558"/><path id="shape1" transform="translate(61.0200045914134, 1.04007141771575)" fill="#f2f2f2" fill-rule="evenodd" d="M270.415 114.679L270.415 288.287L269.98 289.646L269.61 291.035L269.286 292.446L268.992 293.865L268.712 295.284L268.428 296.691L268.123 298.076L267.781 299.428L267.386 300.735L266.919 301.989L266.364 303.177L265.704 304.288L264.923 305.314L264.003 306.241L262.928 307.061L261.681 307.762L260.244 308.333L258.602 308.764L256.737 309.043L254.632 309.161L16.8749 309.798L14.0545 309.696L11.5994 309.354L9.48192 308.797L7.67441 308.049L6.14912 307.134L4.87835 306.076L3.8344 304.9L2.98956 303.63L2.31611 302.29L1.78637 300.905L1.37262 299.499L1.04715 298.095L0.78226 296.719L0.550247 295.394L0.323402 294.145L0.0740195 292.997L0 16.0599L0.288877 13.6625L0.719498 11.5587L1.28146 9.72701L1.96438 8.14583L2.75785 6.79365L3.65147 5.64893L4.63485 4.69013L5.69758 3.89572L6.82928 3.24416L8.01954 2.7139L9.25797 2.28343L10.5342 1.93118L11.8377 1.63564L13.1583 1.37525L14.4854 1.12849L15.8087 0.87381L17.1178 0.58968L18.4022 0.254558L138.172 0L138.045 97.4959L138.594 99.0696L139.153 100.628L139.731 102.16L140.334 103.656L140.97 105.104L141.646 106.495L142.369 107.817L143.147 109.06L143.987 110.213L144.896 111.266L145.883 112.208L146.954 113.028L148.116 113.716L149.377 114.261L150.744 114.653L152.225 114.88L153.827 114.933L191.897 114.85L249.265 114.725L270.415 114.679"/><path id="shape2" transform="translate(210.201627880834, 1.9091882641311)" fill="#ffffff" fill-rule="evenodd" d="M0.0636396 0L121.743 103.223L14.828 103.478C6.29306 101.355 0.80087 96.9823 0.0636396 89.0955C-0.0212132 88.5863 -0.0212132 58.8879 0.0636396 0Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
BIN
resources/buttons/copyLight.png
Normal file
BIN
resources/buttons/copyLight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,004 B |
13
resources/buttons/copyLight.svg
Normal file
13
resources/buttons/copyLight.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<!-- Created using Krita: http://krita.org -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:krita="http://krita.org/namespaces/svg/krita"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
width="368.64pt"
|
||||
height="368.64pt"
|
||||
viewBox="0 0 368.64 368.64">
|
||||
<defs/>
|
||||
<path id="shape0" transform="translate(0.477297082745412, 59.3121166947556)" fill="#000000" fill-rule="evenodd" d="M42.034 0.254558L43.0522 255.449L43.6262 256.827L44.1855 258.216L44.7441 259.606L45.3161 260.985L45.9154 262.341L46.5562 263.662L47.2523 264.937L48.018 266.154L48.867 267.3L49.8136 268.365L50.8717 269.336L52.0553 270.202L53.3785 270.951L54.8553 271.571L56.4997 272.05L58.3257 272.378L268.973 272.505L269.227 294.397L268.9 295.844L268.498 297.271L268.017 298.668L267.453 300.026L266.799 301.334L266.053 302.584L265.207 303.766L264.259 304.87L263.202 305.887L262.031 306.807L260.742 307.62L259.33 308.318L257.791 308.89L256.118 309.328L13.2689 309.328L11.4859 308.872L9.86178 308.304L8.38902 307.628L7.05996 306.85L5.86701 305.976L4.80256 305.011L3.85899 303.959L3.02871 302.828L2.3041 301.621L1.67756 300.345L1.14149 299.004L0.68828 297.605L0.310317 296.152L0 294.651L0.310203 14.5104L0.847067 13.1388L1.38231 11.7663L1.93671 10.4058L2.53105 9.0706L3.18609 7.77381L3.92263 6.52858L4.76143 5.34805L5.72327 4.24536L6.82893 3.23365L8.09918 2.32608L9.55481 1.53579L11.2166 0.87591L13.1053 0.3596L15.2417 0L42.034 0.254558"/><path id="shape1" transform="translate(61.0200045914134, 1.04007141771575)" fill="#000000" fill-rule="evenodd" d="M270.415 114.679L270.415 288.287L269.98 289.646L269.61 291.035L269.286 292.446L268.992 293.865L268.712 295.284L268.428 296.691L268.123 298.076L267.781 299.428L267.386 300.735L266.919 301.989L266.364 303.177L265.704 304.288L264.923 305.314L264.003 306.241L262.928 307.061L261.681 307.762L260.244 308.333L258.602 308.764L256.737 309.043L254.632 309.161L16.8749 309.798L14.0545 309.696L11.5994 309.354L9.48192 308.797L7.67441 308.049L6.14912 307.134L4.87835 306.076L3.8344 304.9L2.98956 303.63L2.31611 302.29L1.78637 300.905L1.37262 299.499L1.04715 298.095L0.78226 296.719L0.550247 295.394L0.323402 294.145L0.0740195 292.997L0 16.0599L0.288877 13.6625L0.719498 11.5587L1.28146 9.72701L1.96438 8.14583L2.75785 6.79365L3.65147 5.64893L4.63485 4.69013L5.69758 3.89572L6.82928 3.24416L8.01954 2.7139L9.25797 2.28343L10.5342 1.93118L11.8377 1.63564L13.1583 1.37525L14.4854 1.12849L15.8087 0.87381L17.1178 0.58968L18.4022 0.254558L138.172 0L138.045 97.4959L138.594 99.0696L139.153 100.628L139.731 102.16L140.334 103.656L140.97 105.104L141.646 106.495L142.369 107.817L143.147 109.06L143.987 110.213L144.896 111.266L145.883 112.208L146.954 113.028L148.116 113.716L149.377 114.261L150.744 114.653L152.225 114.88L153.827 114.933L191.897 114.85L249.265 114.725L270.415 114.679"/><path id="shape2" transform="translate(210.201627880834, 1.9091882641311)" fill="#000000" fill-rule="evenodd" d="M0.0636396 0L121.743 103.223L14.828 103.478C6.29306 101.355 0.80087 96.9823 0.0636396 89.0955C-0.0212132 88.5863 -0.0212132 58.8879 0.0636396 0Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/buttons/search.png
Normal file
BIN
resources/buttons/search.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
|
@ -18,10 +18,10 @@ def isNotIgnored(file):
|
|||
|
||||
return file.as_posix() not in ignored_files
|
||||
|
||||
all_files = list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*'))))
|
||||
image_files = list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*.png'))))
|
||||
all_files = sorted(list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*')))))
|
||||
image_files = sorted(list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*.png')))))
|
||||
|
||||
with open('./resources_autogenerated.qrc', 'w') as out:
|
||||
out.write(resources_header)
|
||||
|
|
|
@ -8,7 +8,7 @@ QCheckBox::indicator {
|
|||
}
|
||||
|
||||
chatterino--ComboBox {
|
||||
width: 100px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
QScrollArea {
|
||||
|
@ -27,19 +27,19 @@ QComboBox QFrame {
|
|||
|
||||
chatterino--SettingsPage {
|
||||
background: #222;
|
||||
border: 1px solid #555;
|
||||
/*border: 1px solid #555;
|
||||
border-left: none;*/
|
||||
}
|
||||
|
||||
chatterino--PageHeader {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
chatterino--TitleLabel {
|
||||
font-family: "Segoe UI light";
|
||||
font-size: 24px;
|
||||
color: #4FC3F7;
|
||||
}
|
||||
|
||||
chatterino--TitleLabel2 {
|
||||
font-family: "Segoe UI light";
|
||||
font-size: 24px;
|
||||
color: #bbb;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
chatterino--DescriptionLabel {
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
<RCC>
|
||||
<qresource prefix="/"> <file>chatterino.icns</file>
|
||||
<file>contributors.txt</file>
|
||||
<file>emoji.json</file>
|
||||
<file>emojidata.txt</file>
|
||||
<file>error.png</file>
|
||||
<file>icon.ico</file>
|
||||
<file>icon.png</file>
|
||||
<file>pajaDank.png</file>
|
||||
<file>tlds.txt</file>
|
||||
<qresource prefix="/"> <file>.gitignore</file>
|
||||
<file>avatars/fourtf.png</file>
|
||||
<file>avatars/pajlada.png</file>
|
||||
<file>buttons/addSplit.png</file>
|
||||
<file>buttons/addSplitDark.png</file>
|
||||
<file>buttons/ban.png</file>
|
||||
<file>buttons/banRed.png</file>
|
||||
<file>buttons/copyDark.png</file>
|
||||
<file>buttons/copyDark.svg</file>
|
||||
<file>buttons/copyDarkTheme.png</file>
|
||||
<file>buttons/copyLight.png</file>
|
||||
<file>buttons/copyLight.svg</file>
|
||||
<file>buttons/emote.svg</file>
|
||||
<file>buttons/emoteDark.svg</file>
|
||||
<file>buttons/menuDark.png</file>
|
||||
|
@ -23,14 +20,24 @@
|
|||
<file>buttons/modModeDisabled2.png</file>
|
||||
<file>buttons/modModeEnabled.png</file>
|
||||
<file>buttons/modModeEnabled2.png</file>
|
||||
<file>buttons/search.png</file>
|
||||
<file>buttons/timeout.png</file>
|
||||
<file>buttons/trashCan.png</file>
|
||||
<file>buttons/trashcan.svg</file>
|
||||
<file>buttons/unban.png</file>
|
||||
<file>buttons/unmod.png</file>
|
||||
<file>buttons/update.png</file>
|
||||
<file>buttons/updateError.png</file>
|
||||
<file>chatterino.desktop</file>
|
||||
<file>chatterino.icns</file>
|
||||
<file>contributors.txt</file>
|
||||
<file>emoji.json</file>
|
||||
<file>emojidata.txt</file>
|
||||
<file>error.png</file>
|
||||
<file>examples/moving.gif</file>
|
||||
<file>examples/splitting.gif</file>
|
||||
<file>icon.ico</file>
|
||||
<file>icon.png</file>
|
||||
<file>licenses/boost_boost.txt</file>
|
||||
<file>licenses/emoji-data-source.txt</file>
|
||||
<file>licenses/fmt_bsd2.txt</file>
|
||||
|
@ -41,6 +48,7 @@
|
|||
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||
<file>licenses/rapidjson.txt</file>
|
||||
<file>licenses/websocketpp.txt</file>
|
||||
<file>pajaDank.png</file>
|
||||
<file>qss/settings.qss</file>
|
||||
<file>settings/about.svg</file>
|
||||
<file>settings/aboutlogo.png</file>
|
||||
|
@ -63,6 +71,7 @@
|
|||
<file>split/move.png</file>
|
||||
<file>split/right.png</file>
|
||||
<file>split/up.png</file>
|
||||
<file>tlds.txt</file>
|
||||
<file>twitch/admin.png</file>
|
||||
<file>twitch/automod.png</file>
|
||||
<file>twitch/broadcaster.png</file>
|
||||
|
@ -76,4 +85,4 @@
|
|||
<file>twitch/verified.png</file>
|
||||
<file>twitch/vip.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
</RCC>
|
|
@ -24,6 +24,7 @@
|
|||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/Toasts.hpp"
|
||||
#include "singletons/Updates.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/IsBigEndian.hpp"
|
||||
#include "util/PostToThread.hpp"
|
||||
|
@ -99,6 +100,9 @@ int Application::run(QApplication &qtApp)
|
|||
|
||||
this->windows->getMainWindow().show();
|
||||
|
||||
getSettings()->betaUpdates.connect(
|
||||
[] { Updates::getInstance().checkForUpdates(); }, false);
|
||||
|
||||
return qtApp.exec();
|
||||
}
|
||||
|
||||
|
|
10
src/ForwardDecl.hpp
Normal file
10
src/ForwardDecl.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
class Channel;
|
||||
class ChannelView;
|
||||
using ChannelPtr = std::shared_ptr<Channel>;
|
||||
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
} // namespace chatterino
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <fmt/format.h>
|
||||
# include <irccommand.h>
|
||||
|
|
|
@ -10,6 +10,9 @@ Resources2::Resources2()
|
|||
this->buttons.addSplitDark = QPixmap(":/buttons/addSplitDark.png");
|
||||
this->buttons.ban = QPixmap(":/buttons/ban.png");
|
||||
this->buttons.banRed = QPixmap(":/buttons/banRed.png");
|
||||
this->buttons.copyDark = QPixmap(":/buttons/copyDark.png");
|
||||
this->buttons.copyDarkTheme = QPixmap(":/buttons/copyDarkTheme.png");
|
||||
this->buttons.copyLight = QPixmap(":/buttons/copyLight.png");
|
||||
this->buttons.menuDark = QPixmap(":/buttons/menuDark.png");
|
||||
this->buttons.menuLight = QPixmap(":/buttons/menuLight.png");
|
||||
this->buttons.mod = QPixmap(":/buttons/mod.png");
|
||||
|
@ -17,6 +20,7 @@ Resources2::Resources2()
|
|||
this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png");
|
||||
this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png");
|
||||
this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.png");
|
||||
this->buttons.search = QPixmap(":/buttons/search.png");
|
||||
this->buttons.timeout = QPixmap(":/buttons/timeout.png");
|
||||
this->buttons.trashCan = QPixmap(":/buttons/trashCan.png");
|
||||
this->buttons.unban = QPixmap(":/buttons/unban.png");
|
||||
|
@ -46,4 +50,4 @@ Resources2::Resources2()
|
|||
this->twitch.vip = QPixmap(":/twitch/vip.png");
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino
|
|
@ -17,6 +17,9 @@ public:
|
|||
QPixmap addSplitDark;
|
||||
QPixmap ban;
|
||||
QPixmap banRed;
|
||||
QPixmap copyDark;
|
||||
QPixmap copyDarkTheme;
|
||||
QPixmap copyLight;
|
||||
QPixmap menuDark;
|
||||
QPixmap menuLight;
|
||||
QPixmap mod;
|
||||
|
@ -24,6 +27,7 @@ public:
|
|||
QPixmap modModeDisabled2;
|
||||
QPixmap modModeEnabled;
|
||||
QPixmap modModeEnabled2;
|
||||
QPixmap search;
|
||||
QPixmap timeout;
|
||||
QPixmap trashCan;
|
||||
QPixmap unban;
|
||||
|
@ -60,4 +64,4 @@ public:
|
|||
} twitch;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino
|
|
@ -24,6 +24,17 @@ QJsonObject NetworkResult::parseJson() const
|
|||
return jsonDoc.object();
|
||||
}
|
||||
|
||||
QJsonArray NetworkResult::parseJsonArray() const
|
||||
{
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(this->data_));
|
||||
if (jsonDoc.isNull())
|
||||
{
|
||||
return QJsonArray{};
|
||||
}
|
||||
|
||||
return jsonDoc.array();
|
||||
}
|
||||
|
||||
rapidjson::Document NetworkResult::parseRapidJson() const
|
||||
{
|
||||
rapidjson::Document ret(rapidjson::kObjectType);
|
||||
|
|
|
@ -10,7 +10,13 @@ class NetworkResult
|
|||
public:
|
||||
NetworkResult(const QByteArray &data);
|
||||
|
||||
/// Parses the result as json and returns the root as an object.
|
||||
/// Returns empty object if parsing failed.
|
||||
QJsonObject parseJson() const;
|
||||
/// Parses the result as json and returns the root as an array.
|
||||
/// Returns empty object if parsing failed.
|
||||
QJsonArray parseJsonArray() const;
|
||||
/// Parses the result as json and returns the document.
|
||||
rapidjson::Document parseRapidJson() const;
|
||||
const QByteArray &getData() const;
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ public:
|
|||
return this->vector_;
|
||||
}
|
||||
|
||||
const std::vector<TVectorItem> cloneVector() const
|
||||
std::vector<TVectorItem> cloneVector() const
|
||||
{
|
||||
std::shared_lock lock(this->mutex_);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <QtGlobal>
|
||||
|
||||
#define CHATTERINO_VERSION "2.1.2"
|
||||
#define CHATTERINO_VERSION "2.1.4-beta-1"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# define CHATTERINO_OS "win"
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace chatterino {
|
|||
|
||||
// commandmodel
|
||||
HighlightModel::HighlightModel(QObject *parent)
|
||||
: SignalVectorModel<HighlightPhrase>(4, parent)
|
||||
: SignalVectorModel<HighlightPhrase>(5, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,13 @@ HighlightModel::HighlightModel(QObject *parent)
|
|||
HighlightPhrase HighlightModel::getItemFromRow(
|
||||
std::vector<QStandardItem *> &row, const HighlightPhrase &original)
|
||||
{
|
||||
// key, alert, sound, regex
|
||||
// key, alert, sound, regex, case-sensitivity
|
||||
|
||||
return HighlightPhrase{row[0]->data(Qt::DisplayRole).toString(),
|
||||
row[1]->data(Qt::CheckStateRole).toBool(),
|
||||
row[2]->data(Qt::CheckStateRole).toBool(),
|
||||
row[3]->data(Qt::CheckStateRole).toBool()};
|
||||
row[3]->data(Qt::CheckStateRole).toBool(),
|
||||
row[4]->data(Qt::CheckStateRole).toBool()};
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
|
@ -32,6 +33,7 @@ void HighlightModel::getRowFromItem(const HighlightPhrase &item,
|
|||
setBoolItem(row[1], item.getAlert());
|
||||
setBoolItem(row[2], item.getSound());
|
||||
setBoolItem(row[3], item.isRegex());
|
||||
setBoolItem(row[4], item.isCaseSensitive());
|
||||
}
|
||||
|
||||
void HighlightModel::afterInit()
|
||||
|
|
|
@ -15,21 +15,24 @@ public:
|
|||
bool operator==(const HighlightPhrase &other) const
|
||||
{
|
||||
return std::tie(this->pattern_, this->sound_, this->alert_,
|
||||
this->isRegex_) == std::tie(other.pattern_,
|
||||
other.sound_, other.alert_,
|
||||
other.isRegex_);
|
||||
this->isRegex_, this->caseSensitive_) ==
|
||||
std::tie(other.pattern_, other.sound_, other.alert_,
|
||||
other.isRegex_, other.caseSensitive_);
|
||||
}
|
||||
|
||||
HighlightPhrase(const QString &pattern, bool alert, bool sound,
|
||||
bool isRegex)
|
||||
bool isRegex, bool caseSensitive)
|
||||
: pattern_(pattern)
|
||||
, alert_(alert)
|
||||
, sound_(sound)
|
||||
, isRegex_(isRegex)
|
||||
, regex_(isRegex_ ? pattern
|
||||
: "\\b" + QRegularExpression::escape(pattern) + "\\b",
|
||||
QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::UseUnicodePropertiesOption)
|
||||
, caseSensitive_(caseSensitive)
|
||||
, regex_(
|
||||
isRegex_ ? pattern
|
||||
: "\\b" + QRegularExpression::escape(pattern) + "\\b",
|
||||
QRegularExpression::UseUnicodePropertiesOption |
|
||||
(caseSensitive_ ? QRegularExpression::NoPatternOption
|
||||
: QRegularExpression::CaseInsensitiveOption))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -60,11 +63,17 @@ public:
|
|||
return this->isValid() && this->regex_.match(subject).hasMatch();
|
||||
}
|
||||
|
||||
bool isCaseSensitive() const
|
||||
{
|
||||
return this->caseSensitive_;
|
||||
}
|
||||
|
||||
private:
|
||||
QString pattern_;
|
||||
bool alert_;
|
||||
bool sound_;
|
||||
bool isRegex_;
|
||||
bool caseSensitive_;
|
||||
QRegularExpression regex_;
|
||||
};
|
||||
} // namespace chatterino
|
||||
|
@ -82,6 +91,7 @@ struct Serialize<chatterino::HighlightPhrase> {
|
|||
chatterino::rj::set(ret, "alert", value.getAlert(), a);
|
||||
chatterino::rj::set(ret, "sound", value.getSound(), a);
|
||||
chatterino::rj::set(ret, "regex", value.isRegex(), a);
|
||||
chatterino::rj::set(ret, "case", value.isCaseSensitive(), a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -93,20 +103,24 @@ struct Deserialize<chatterino::HighlightPhrase> {
|
|||
{
|
||||
if (!value.IsObject())
|
||||
{
|
||||
return chatterino::HighlightPhrase(QString(), true, false, false);
|
||||
return chatterino::HighlightPhrase(QString(), true, false, false,
|
||||
false);
|
||||
}
|
||||
|
||||
QString _pattern;
|
||||
bool _alert = true;
|
||||
bool _sound = false;
|
||||
bool _isRegex = false;
|
||||
bool _caseSensitive = false;
|
||||
|
||||
chatterino::rj::getSafe(value, "pattern", _pattern);
|
||||
chatterino::rj::getSafe(value, "alert", _alert);
|
||||
chatterino::rj::getSafe(value, "sound", _sound);
|
||||
chatterino::rj::getSafe(value, "regex", _isRegex);
|
||||
chatterino::rj::getSafe(value, "case", _caseSensitive);
|
||||
|
||||
return chatterino::HighlightPhrase(_pattern, _alert, _sound, _isRegex);
|
||||
return chatterino::HighlightPhrase(_pattern, _alert, _sound,
|
||||
_isRegex, _caseSensitive);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace chatterino {
|
|||
|
||||
// commandmodel
|
||||
UserHighlightModel::UserHighlightModel(QObject *parent)
|
||||
: SignalVectorModel<HighlightPhrase>(4, parent)
|
||||
: SignalVectorModel<HighlightPhrase>(5, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,8 @@ HighlightPhrase UserHighlightModel::getItemFromRow(
|
|||
return HighlightPhrase{row[0]->data(Qt::DisplayRole).toString(),
|
||||
row[1]->data(Qt::CheckStateRole).toBool(),
|
||||
row[2]->data(Qt::CheckStateRole).toBool(),
|
||||
row[3]->data(Qt::CheckStateRole).toBool()};
|
||||
row[3]->data(Qt::CheckStateRole).toBool(),
|
||||
row[4]->data(Qt::CheckStateRole).toBool()};
|
||||
}
|
||||
|
||||
// row into vector item
|
||||
|
@ -32,6 +33,7 @@ void UserHighlightModel::getRowFromItem(const HighlightPhrase &item,
|
|||
setBoolItem(row[1], item.getAlert());
|
||||
setBoolItem(row[2], item.getSound());
|
||||
setBoolItem(row[3], item.isRegex());
|
||||
setBoolItem(row[4], item.isCaseSensitive());
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
std::size_t size()
|
||||
std::size_t size() const
|
||||
{
|
||||
return this->length_;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
|
|||
QSize(int(container.getScale() * image->width() * emoteScale),
|
||||
int(container.getScale() * image->height() * emoteScale));
|
||||
|
||||
container.addElement((new ImageLayoutElement(*this, image, size))
|
||||
container.addElement(this->makeImageLayoutElement(image, size)
|
||||
->setLink(this->getLink()));
|
||||
}
|
||||
else
|
||||
|
@ -159,6 +159,27 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
|
|||
}
|
||||
}
|
||||
|
||||
MessageLayoutElement *EmoteElement::makeImageLayoutElement(
|
||||
const ImagePtr &image, const QSize &size)
|
||||
{
|
||||
return new ImageLayoutElement(*this, image, size);
|
||||
}
|
||||
|
||||
// MOD BADGE
|
||||
ModBadgeElement::ModBadgeElement(const EmotePtr &data,
|
||||
MessageElementFlags flags_)
|
||||
: EmoteElement(data, flags_)
|
||||
{
|
||||
}
|
||||
|
||||
MessageLayoutElement *ModBadgeElement::makeImageLayoutElement(
|
||||
const ImagePtr &image, const QSize &size)
|
||||
{
|
||||
static const QColor modBadgeBackgroundColor("#34AE0A");
|
||||
return new ImageWithBackgroundLayoutElement(*this, image, size,
|
||||
modBadgeBackgroundColor);
|
||||
}
|
||||
|
||||
// BADGE
|
||||
BadgeElement::BadgeElement(const EmotePtr &emote, MessageElementFlags flags)
|
||||
: MessageElement(flags)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
namespace chatterino {
|
||||
class Channel;
|
||||
struct MessageLayoutContainer;
|
||||
class MessageLayoutElement;
|
||||
|
||||
class Image;
|
||||
using ImagePtr = std::shared_ptr<Image>;
|
||||
|
@ -209,11 +210,26 @@ public:
|
|||
MessageElementFlags flags_) override;
|
||||
EmotePtr getEmote() const;
|
||||
|
||||
protected:
|
||||
virtual MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
|
||||
const QSize &size);
|
||||
|
||||
private:
|
||||
std::unique_ptr<TextElement> textElement_;
|
||||
EmotePtr emote_;
|
||||
};
|
||||
|
||||
// Behaves like an emote element, except it creates a different image layout element that draws the mod badge background
|
||||
class ModBadgeElement : public EmoteElement
|
||||
{
|
||||
public:
|
||||
ModBadgeElement(const EmotePtr &data, MessageElementFlags flags_);
|
||||
|
||||
protected:
|
||||
MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
|
||||
const QSize &size) override;
|
||||
};
|
||||
|
||||
class BadgeElement : public MessageElement
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -168,6 +168,33 @@ int ImageLayoutElement::getXFromIndex(int index)
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// IMAGE WITH BACKGROUND
|
||||
//
|
||||
ImageWithBackgroundLayoutElement::ImageWithBackgroundLayoutElement(
|
||||
MessageElement &creator, ImagePtr image, const QSize &size, QColor color)
|
||||
: ImageLayoutElement(creator, image, size)
|
||||
, color_(color)
|
||||
{
|
||||
}
|
||||
|
||||
void ImageWithBackgroundLayoutElement::paint(QPainter &painter)
|
||||
{
|
||||
if (this->image_ == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto pixmap = this->image_->pixmapOrLoad();
|
||||
if (pixmap && !this->image_->animated())
|
||||
{
|
||||
painter.fillRect(QRectF(this->getRect()), this->color_);
|
||||
|
||||
// fourtf: make it use qreal values
|
||||
painter.drawPixmap(QRectF(this->getRect()), *pixmap, QRectF());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TEXT
|
||||
//
|
||||
|
|
|
@ -72,10 +72,22 @@ protected:
|
|||
int getMouseOverIndex(const QPoint &abs) const override;
|
||||
int getXFromIndex(int index) override;
|
||||
|
||||
private:
|
||||
ImagePtr image_;
|
||||
};
|
||||
|
||||
class ImageWithBackgroundLayoutElement : public ImageLayoutElement
|
||||
{
|
||||
public:
|
||||
ImageWithBackgroundLayoutElement(MessageElement &creator, ImagePtr image,
|
||||
const QSize &size, QColor color);
|
||||
|
||||
protected:
|
||||
void paint(QPainter &painter) override;
|
||||
|
||||
private:
|
||||
QColor color_;
|
||||
};
|
||||
|
||||
// TEXT
|
||||
class TextLayoutElement : public MessageLayoutElement
|
||||
{
|
||||
|
|
|
@ -21,13 +21,25 @@ namespace {
|
|||
return {urlTemplate.replace("{{id}}", id.string)
|
||||
.replace("{{image}}", emoteScale)};
|
||||
}
|
||||
|
||||
Url getEmoteLinkV3(const EmoteId &id, const QString &emoteScale)
|
||||
{
|
||||
static const QString urlTemplate(
|
||||
"https://cdn.betterttv.net/emote/%1/%2");
|
||||
|
||||
return {urlTemplate.arg(id.string, emoteScale)};
|
||||
}
|
||||
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
|
||||
{
|
||||
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
|
||||
static std::mutex mutex;
|
||||
|
||||
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
|
||||
const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes)
|
||||
const QJsonArray &jsonEmotes, const EmoteMap ¤tEmotes)
|
||||
{
|
||||
auto emotes = EmoteMap();
|
||||
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
||||
auto urlTemplate =
|
||||
qS("https:") + jsonRoot.value("urlTemplate").toString();
|
||||
|
||||
for (auto jsonEmote : jsonEmotes)
|
||||
{
|
||||
|
@ -37,10 +49,9 @@ namespace {
|
|||
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
|
||||
ImageSet{Image::fromUrl(getEmoteLinkV3(id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "3x"), 0.25)},
|
||||
Tooltip{name.string + "<br />Global BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
|
||||
|
@ -50,38 +61,36 @@ namespace {
|
|||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
|
||||
{
|
||||
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
|
||||
static std::mutex mutex;
|
||||
|
||||
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
{
|
||||
auto emotes = EmoteMap();
|
||||
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
||||
auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString();
|
||||
|
||||
for (auto jsonEmote_ : jsonEmotes)
|
||||
{
|
||||
auto jsonEmote = jsonEmote_.toObject();
|
||||
auto innerParse = [&jsonRoot, &emotes](const char *key) {
|
||||
auto jsonEmotes = jsonRoot.value(key).toArray();
|
||||
for (auto jsonEmote_ : jsonEmotes)
|
||||
{
|
||||
auto jsonEmote = jsonEmote_.toObject();
|
||||
|
||||
auto id = EmoteId{jsonEmote.value("id").toString()};
|
||||
auto name = EmoteName{jsonEmote.value("code").toString()};
|
||||
// emoteObject.value("imageType").toString();
|
||||
auto id = EmoteId{jsonEmote.value("id").toString()};
|
||||
auto name = EmoteName{jsonEmote.value("code").toString()};
|
||||
// emoteObject.value("imageType").toString();
|
||||
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
|
||||
Tooltip{name.string + "<br />Channel BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLinkV3(id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "3x"), 0.25),
|
||||
},
|
||||
Tooltip{name.string + "<br />Channel BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
|
||||
emotes[name] = cachedOrMake(std::move(emote), id);
|
||||
}
|
||||
emotes[name] = cachedOrMake(std::move(emote), id);
|
||||
}
|
||||
};
|
||||
|
||||
innerParse("channelEmotes");
|
||||
innerParse("sharedEmotes");
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
|
@ -116,7 +125,7 @@ void BttvEmotes::loadEmotes()
|
|||
.timeout(30000)
|
||||
.onSuccess([this](auto result) -> Outcome {
|
||||
auto emotes = this->global_.get();
|
||||
auto pair = parseGlobalEmotes(result.parseJson(), *emotes);
|
||||
auto pair = parseGlobalEmotes(result.parseJsonArray(), *emotes);
|
||||
if (pair.first)
|
||||
this->global_.set(
|
||||
std::make_shared<EmoteMap>(std::move(pair.second)));
|
||||
|
@ -125,10 +134,10 @@ void BttvEmotes::loadEmotes()
|
|||
.execute();
|
||||
}
|
||||
|
||||
void BttvEmotes::loadChannel(const QString &channelName,
|
||||
void BttvEmotes::loadChannel(const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> callback)
|
||||
{
|
||||
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName)
|
||||
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelId)
|
||||
.timeout(3000)
|
||||
.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
||||
auto pair = parseChannelEmotes(result.parseJson());
|
||||
|
|
|
@ -14,9 +14,9 @@ class EmoteMap;
|
|||
class BttvEmotes final
|
||||
{
|
||||
static constexpr const char *globalEmoteApiUrl =
|
||||
"https://api.betterttv.net/2/emotes";
|
||||
"https://api.betterttv.net/3/cached/emotes/global";
|
||||
static constexpr const char *bttvChannelEmoteApiUrl =
|
||||
"https://api.betterttv.net/2/channels/";
|
||||
"https://api.betterttv.net/3/cached/users/twitch/";
|
||||
|
||||
public:
|
||||
BttvEmotes();
|
||||
|
@ -24,7 +24,7 @@ public:
|
|||
std::shared_ptr<const EmoteMap> emotes() const;
|
||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||
void loadEmotes();
|
||||
static void loadChannel(const QString &channelName,
|
||||
static void loadChannel(const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> callback);
|
||||
|
||||
private:
|
||||
|
|
|
@ -79,7 +79,38 @@ namespace {
|
|||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
|
||||
boost::optional<EmotePtr> parseModBadge(const QJsonObject &jsonRoot)
|
||||
{
|
||||
boost::optional<EmotePtr> modBadge;
|
||||
|
||||
auto room = jsonRoot.value("room").toObject();
|
||||
auto modUrls = room.value("mod_urls").toObject();
|
||||
if (!modUrls.isEmpty())
|
||||
{
|
||||
auto modBadge1x = getEmoteLink(modUrls, "1");
|
||||
auto modBadge2x = getEmoteLink(modUrls, "2");
|
||||
auto modBadge3x = getEmoteLink(modUrls, "4");
|
||||
|
||||
auto modBadgeImageSet = ImageSet{
|
||||
Image::fromUrl(modBadge1x, 1),
|
||||
modBadge2x.string.isEmpty() ? Image::getEmpty()
|
||||
: Image::fromUrl(modBadge2x, 0.5),
|
||||
modBadge3x.string.isEmpty() ? Image::getEmpty()
|
||||
: Image::fromUrl(modBadge3x, 0.25),
|
||||
};
|
||||
|
||||
modBadge = std::make_shared<Emote>(Emote{
|
||||
{""},
|
||||
modBadgeImageSet,
|
||||
Tooltip{"Twitch Channel Moderator"},
|
||||
modBadge1x,
|
||||
});
|
||||
}
|
||||
return modBadge;
|
||||
}
|
||||
|
||||
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
{
|
||||
auto jsonSets = jsonRoot.value("sets").toObject();
|
||||
auto emotes = EmoteMap();
|
||||
|
@ -110,7 +141,7 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
return emotes;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -138,7 +169,7 @@ void FfzEmotes::loadEmotes()
|
|||
QString url("https://api.frankerfacez.com/v1/set/global");
|
||||
|
||||
NetworkRequest(url)
|
||||
|
||||
|
||||
.timeout(30000)
|
||||
.onSuccess([this](auto result) -> Outcome {
|
||||
auto emotes = this->emotes();
|
||||
|
@ -151,21 +182,28 @@ void FfzEmotes::loadEmotes()
|
|||
.execute();
|
||||
}
|
||||
|
||||
void FfzEmotes::loadChannel(const QString &channelName,
|
||||
std::function<void(EmoteMap &&)> callback)
|
||||
void FfzEmotes::loadChannel(
|
||||
const QString &channelId, std::function<void(EmoteMap &&)> emoteCallback,
|
||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback)
|
||||
{
|
||||
log("[FFZEmotes] Reload FFZ Channel Emotes for channel {}\n", channelName);
|
||||
log("[FFZEmotes] Reload FFZ Channel Emotes for channel {}\n", channelId);
|
||||
|
||||
NetworkRequest("https://api.frankerfacez.com/v1/room/id/" + channelId)
|
||||
|
||||
NetworkRequest("https://api.frankerfacez.com/v1/room/" + channelName)
|
||||
|
||||
.timeout(20000)
|
||||
.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
||||
auto pair = parseChannelEmotes(result.parseJson());
|
||||
if (pair.first)
|
||||
callback(std::move(pair.second));
|
||||
return pair.first;
|
||||
.onSuccess([emoteCallback = std::move(emoteCallback),
|
||||
modBadgeCallback =
|
||||
std::move(modBadgeCallback)](auto result) -> Outcome {
|
||||
auto json = result.parseJson();
|
||||
auto emoteMap = parseChannelEmotes(json);
|
||||
auto modBadge = parseModBadge(json);
|
||||
|
||||
emoteCallback(std::move(emoteMap));
|
||||
modBadgeCallback(std::move(modBadge));
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([channelName](int result) {
|
||||
.onError([channelId](int result) {
|
||||
if (result == 203)
|
||||
{
|
||||
// User does not have any FFZ emotes
|
||||
|
@ -176,12 +214,12 @@ void FfzEmotes::loadChannel(const QString &channelName,
|
|||
{
|
||||
// TODO: Auto retry in case of a timeout, with a delay
|
||||
log("Fetching FFZ emotes for channel {} failed due to timeout",
|
||||
channelName);
|
||||
channelId);
|
||||
return true;
|
||||
}
|
||||
|
||||
log("Error fetching FFZ emotes for channel {}, error {}",
|
||||
channelName, result);
|
||||
log("Error fetching FFZ emotes for channel {}, error {}", channelId,
|
||||
result);
|
||||
|
||||
return true;
|
||||
})
|
||||
|
|
|
@ -22,8 +22,10 @@ public:
|
|||
std::shared_ptr<const EmoteMap> emotes() const;
|
||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||
void loadEmotes();
|
||||
static void loadChannel(const QString &channelName,
|
||||
std::function<void(EmoteMap &&)> callback);
|
||||
static void loadChannel(
|
||||
const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> emoteCallback,
|
||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback);
|
||||
|
||||
private:
|
||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
#include "FfzModBadge.hpp"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QJsonObject>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
FfzModBadge::FfzModBadge(const QString &channelName)
|
||||
: channelName_(channelName)
|
||||
{
|
||||
}
|
||||
|
||||
void FfzModBadge::loadCustomModBadge()
|
||||
{
|
||||
static QString partialUrl("https://cdn.frankerfacez.com/room-badge/mod/");
|
||||
|
||||
QString url = partialUrl + channelName_ + "/1";
|
||||
NetworkRequest(url)
|
||||
|
||||
.onSuccess([this, url](auto result) -> Outcome {
|
||||
auto data = result.getData();
|
||||
|
||||
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QImageReader reader(&buffer);
|
||||
if (reader.imageCount() == 0)
|
||||
return Failure;
|
||||
|
||||
QPixmap badgeOverlay = QPixmap::fromImageReader(&reader);
|
||||
QPixmap badgePixmap(18, 18);
|
||||
|
||||
// the default mod badge green color
|
||||
badgePixmap.fill(QColor("#34AE0A"));
|
||||
QPainter painter(&badgePixmap);
|
||||
QRectF rect(0, 0, 18, 18);
|
||||
painter.drawPixmap(rect, badgeOverlay, rect);
|
||||
|
||||
auto emote = Emote{{""},
|
||||
ImageSet{Image::fromPixmap(badgePixmap)},
|
||||
Tooltip{"Twitch Channel Moderator"},
|
||||
Url{url}};
|
||||
|
||||
this->badge_ = std::make_shared<Emote>(emote);
|
||||
// getBadge.execute();
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
EmotePtr FfzModBadge::badge() const
|
||||
{
|
||||
return this->badge_;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Emote;
|
||||
using EmotePtr = std::shared_ptr<const Emote>;
|
||||
|
||||
class FfzModBadge
|
||||
{
|
||||
public:
|
||||
FfzModBadge(const QString &channelName);
|
||||
|
||||
void loadCustomModBadge();
|
||||
|
||||
EmotePtr badge() const;
|
||||
|
||||
private:
|
||||
const QString channelName_;
|
||||
EmotePtr badge_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -30,15 +30,18 @@ AbstractIrcServer::AbstractIrcServer()
|
|||
this->readConnection_.reset(new IrcConnection);
|
||||
this->readConnection_->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::messageReceived,
|
||||
[this](auto msg) { this->messageReceived(msg); });
|
||||
QObject::connect(
|
||||
this->readConnection_.get(), &Communi::IrcConnection::messageReceived,
|
||||
[this](auto msg) { this->readConnectionMessageReceived(msg); });
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::privateMessageReceived,
|
||||
[this](auto msg) { this->privateMessageReceived(msg); });
|
||||
QObject::connect(
|
||||
this->readConnection_.get(), &Communi::IrcConnection::connected,
|
||||
[this] { this->onConnected(this->readConnection_.get()); });
|
||||
[this] { this->onReadConnected(this->readConnection_.get()); });
|
||||
QObject::connect(
|
||||
this->writeConnection_.get(), &Communi::IrcConnection::connected,
|
||||
[this] { this->onWriteConnected(this->writeConnection_.get()); });
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::disconnected,
|
||||
[this] { this->onDisconnected(); });
|
||||
|
@ -227,7 +230,7 @@ std::shared_ptr<Channel> AbstractIrcServer::getChannelOrEmpty(
|
|||
return Channel::getEmpty();
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onConnected(IrcConnection *connection)
|
||||
void AbstractIrcServer::onReadConnected(IrcConnection *connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->channelMutex);
|
||||
|
||||
|
@ -262,6 +265,10 @@ void AbstractIrcServer::onConnected(IrcConnection *connection)
|
|||
this->falloffCounter_ = 1;
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onWriteConnected(IrcConnection *connection)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onDisconnected()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->channelMutex);
|
||||
|
@ -310,7 +317,7 @@ void AbstractIrcServer::addFakeMessage(const QString &data)
|
|||
}
|
||||
else
|
||||
{
|
||||
this->messageReceived(fakeMessage);
|
||||
this->readConnectionMessageReceived(fakeMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,7 +326,8 @@ void AbstractIrcServer::privateMessageReceived(
|
|||
{
|
||||
}
|
||||
|
||||
void AbstractIrcServer::messageReceived(Communi::IrcMessage *message)
|
||||
void AbstractIrcServer::readConnectionMessageReceived(
|
||||
Communi::IrcMessage *message)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -49,10 +49,11 @@ protected:
|
|||
const QString &channelName) = 0;
|
||||
|
||||
virtual void privateMessageReceived(Communi::IrcPrivateMessage *message);
|
||||
virtual void messageReceived(Communi::IrcMessage *message);
|
||||
virtual void readConnectionMessageReceived(Communi::IrcMessage *message);
|
||||
virtual void writeConnectionMessageReceived(Communi::IrcMessage *message);
|
||||
|
||||
virtual void onConnected(IrcConnection *connection);
|
||||
virtual void onReadConnected(IrcConnection *connection);
|
||||
virtual void onWriteConnected(IrcConnection *connection);
|
||||
virtual void onDisconnected();
|
||||
virtual void onSocketError();
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ ChatroomChannel::ChatroomChannel(const QString &channelName,
|
|||
}
|
||||
}
|
||||
|
||||
void ChatroomChannel::refreshChannelEmotes()
|
||||
void ChatroomChannel::refreshBTTVChannelEmotes()
|
||||
{
|
||||
if (this->chatroomOwnerId.isEmpty())
|
||||
{
|
||||
|
@ -36,17 +36,28 @@ void ChatroomChannel::refreshChannelEmotes()
|
|||
this->bttvEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
FfzEmotes::loadChannel(username, [this, weak](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
if (auto shared = weak.lock())
|
||||
{
|
||||
this->chatroomOwnerName = username;
|
||||
}
|
||||
});
|
||||
}
|
||||
void ChatroomChannel::refreshFFZChannelEmotes()
|
||||
{
|
||||
if (this->chatroomOwnerId.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
FfzEmotes::loadChannel(
|
||||
this->chatroomOwnerId,
|
||||
[this](auto &&emoteMap) {
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
},
|
||||
[this](auto &&modBadge) {
|
||||
this->ffzCustomModBadge_.set(std::move(modBadge));
|
||||
});
|
||||
}
|
||||
|
||||
const QString &ChatroomChannel::getDisplayName() const
|
||||
{
|
||||
|
|
|
@ -13,7 +13,8 @@ protected:
|
|||
explicit ChatroomChannel(const QString &channelName,
|
||||
TwitchBadges &globalTwitchBadges,
|
||||
BttvEmotes &globalBttv, FfzEmotes &globalFfz);
|
||||
virtual void refreshChannelEmotes() override;
|
||||
virtual void refreshBTTVChannelEmotes() override;
|
||||
virtual void refreshFFZChannelEmotes() override;
|
||||
virtual const QString &getDisplayName() const override;
|
||||
|
||||
QString chatroomOwnerId;
|
||||
|
|
|
@ -582,49 +582,6 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
|
|||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleWriteConnectionNoticeMessage(
|
||||
Communi::IrcNoticeMessage *message)
|
||||
{
|
||||
static std::unordered_set<std::string> readConnectionOnlyIDs{
|
||||
"host_on",
|
||||
"host_off",
|
||||
"host_target_went_offline",
|
||||
"emote_only_on",
|
||||
"emote_only_off",
|
||||
"slow_on",
|
||||
"slow_off",
|
||||
"subs_on",
|
||||
"subs_off",
|
||||
"r9k_on",
|
||||
"r9k_off",
|
||||
|
||||
// Display for user who times someone out. This implies you're a
|
||||
// moderator, at which point you will be connected to PubSub and receive
|
||||
// a better message from there
|
||||
"timeout_success",
|
||||
"ban_success",
|
||||
|
||||
// Channel suspended notices
|
||||
"msg_channel_suspended",
|
||||
};
|
||||
|
||||
QVariant v = message->tag("msg-id");
|
||||
if (v.isValid())
|
||||
{
|
||||
std::string msgID = v.toString().toStdString();
|
||||
|
||||
if (readConnectionOnlyIDs.find(msgID) != readConnectionOnlyIDs.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log("Showing notice message from write connection with message id '{}'",
|
||||
msgID);
|
||||
}
|
||||
|
||||
this->handleNoticeMessage(message);
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
auto app = getApp();
|
||||
|
|
|
@ -46,8 +46,6 @@ public:
|
|||
Communi::IrcNoticeMessage *message);
|
||||
void handleNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
|
||||
void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
|
||||
void handleJoinMessage(Communi::IrcMessage *message);
|
||||
void handlePartMessage(Communi::IrcMessage *message);
|
||||
|
||||
|
|
|
@ -699,13 +699,13 @@ PubSub::PubSub()
|
|||
};
|
||||
|
||||
this->moderationActionHandlers["denied_automod_message"] =
|
||||
[this](const auto &data, const auto &roomID) {
|
||||
[](const auto &data, const auto &roomID) {
|
||||
// This message got denied by a moderator
|
||||
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||
};
|
||||
|
||||
this->moderationActionHandlers["approved_automod_message"] =
|
||||
[this](const auto &data, const auto &roomID) {
|
||||
[](const auto &data, const auto &roomID) {
|
||||
// This message got approved by a moderator
|
||||
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||
};
|
||||
|
|
|
@ -87,7 +87,6 @@ TwitchChannel::TwitchChannel(const QString &name,
|
|||
, globalFfz_(ffz)
|
||||
, bttvEmotes_(std::make_shared<EmoteMap>())
|
||||
, ffzEmotes_(std::make_shared<EmoteMap>())
|
||||
, ffzCustomModBadge_(name)
|
||||
, mod_(false)
|
||||
{
|
||||
log("[TwitchChannel:{}] Opened", name);
|
||||
|
@ -113,6 +112,8 @@ TwitchChannel::TwitchChannel(const QString &name,
|
|||
this->refreshLiveStatus();
|
||||
this->refreshBadges();
|
||||
this->refreshCheerEmotes();
|
||||
this->refreshFFZChannelEmotes();
|
||||
this->refreshBTTVChannelEmotes();
|
||||
});
|
||||
|
||||
// timers
|
||||
|
@ -135,9 +136,7 @@ TwitchChannel::TwitchChannel(const QString &name,
|
|||
void TwitchChannel::initialize()
|
||||
{
|
||||
this->refreshChatters();
|
||||
this->refreshChannelEmotes();
|
||||
this->refreshBadges();
|
||||
this->ffzCustomModBadge_.loadCustomModBadge();
|
||||
}
|
||||
|
||||
bool TwitchChannel::isEmpty() const
|
||||
|
@ -150,19 +149,30 @@ bool TwitchChannel::canSendMessage() const
|
|||
return !this->isEmpty();
|
||||
}
|
||||
|
||||
void TwitchChannel::refreshChannelEmotes()
|
||||
void TwitchChannel::refreshBTTVChannelEmotes()
|
||||
{
|
||||
BttvEmotes::loadChannel(
|
||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
this->roomId(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->bttvEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
}
|
||||
|
||||
void TwitchChannel::refreshFFZChannelEmotes()
|
||||
{
|
||||
FfzEmotes::loadChannel(
|
||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
this->roomId(),
|
||||
[this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
},
|
||||
[this, weak = weakOf<Channel>(this)](auto &&modBadge) {
|
||||
if (auto shared = weak.lock())
|
||||
{
|
||||
this->ffzCustomModBadge_.set(std::move(modBadge));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -818,10 +828,7 @@ boost::optional<EmotePtr> TwitchChannel::twitchBadge(
|
|||
|
||||
boost::optional<EmotePtr> TwitchChannel::ffzCustomModBadge() const
|
||||
{
|
||||
if (auto badge = this->ffzCustomModBadge_.badge())
|
||||
return badge;
|
||||
|
||||
return boost::none;
|
||||
return this->ffzCustomModBadge_.get();
|
||||
}
|
||||
|
||||
boost::optional<CheerEmote> TwitchChannel::cheerEmote(const QString &string)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "common/Outcome.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "common/UsernameSet.hpp"
|
||||
#include "providers/ffz/FfzModBadge.hpp"
|
||||
#include "providers/twitch/TwitchEmotes.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
@ -85,7 +84,8 @@ public:
|
|||
std::shared_ptr<const EmoteMap> bttvEmotes() const;
|
||||
std::shared_ptr<const EmoteMap> ffzEmotes() const;
|
||||
|
||||
virtual void refreshChannelEmotes();
|
||||
virtual void refreshBTTVChannelEmotes();
|
||||
virtual void refreshFFZChannelEmotes();
|
||||
|
||||
// Badges
|
||||
boost::optional<EmotePtr> ffzCustomModBadge() const;
|
||||
|
@ -150,13 +150,13 @@ protected:
|
|||
FfzEmotes &globalFfz_;
|
||||
Atomic<std::shared_ptr<const EmoteMap>> bttvEmotes_;
|
||||
Atomic<std::shared_ptr<const EmoteMap>> ffzEmotes_;
|
||||
Atomic<boost::optional<EmotePtr>> ffzCustomModBadge_;
|
||||
|
||||
private:
|
||||
// Badges
|
||||
UniqueAccess<std::map<QString, std::map<QString, EmotePtr>>>
|
||||
badgeSets_; // "subscribers": { "0": ... "3": ... "6": ...
|
||||
UniqueAccess<std::vector<CheerEmoteSet>> cheerEmoteSets_;
|
||||
FfzModBadge ffzCustomModBadge_;
|
||||
|
||||
bool mod_ = false;
|
||||
bool vip_ = false;
|
||||
|
|
|
@ -952,7 +952,7 @@ void TwitchMessageBuilder::parseHighlights()
|
|||
{
|
||||
HighlightPhrase selfHighlight(
|
||||
currentUsername, getSettings()->enableSelfHighlightTaskbar,
|
||||
getSettings()->enableSelfHighlightSound, false);
|
||||
getSettings()->enableSelfHighlightSound, false, false);
|
||||
activeHighlights.emplace_back(std::move(selfHighlight));
|
||||
}
|
||||
|
||||
|
@ -1171,7 +1171,7 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
|||
{
|
||||
if (auto customModBadge = this->twitchChannel->ffzCustomModBadge())
|
||||
{
|
||||
this->emplace<BadgeElement>(
|
||||
this->emplace<ModBadgeElement>(
|
||||
customModBadge.get(),
|
||||
MessageElementFlag::BadgeChannelAuthority)
|
||||
->setTooltip((*customModBadge)->tooltip.string);
|
||||
|
|
|
@ -128,9 +128,8 @@ void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
|||
IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
|
||||
}
|
||||
|
||||
void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
||||
void TwitchServer::readConnectionMessageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
// this->readConnection
|
||||
if (message->type() == Communi::IrcMessage::Type::Private)
|
||||
{
|
||||
// We already have a handler for private messages
|
||||
|
@ -141,39 +140,11 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
|||
|
||||
auto &handler = IrcMessageHandler::getInstance();
|
||||
|
||||
if (command == "ROOMSTATE")
|
||||
{
|
||||
handler.handleRoomStateMessage(message);
|
||||
}
|
||||
else if (command == "CLEARCHAT")
|
||||
{
|
||||
handler.handleClearChatMessage(message);
|
||||
}
|
||||
else if (command == "CLEARMSG")
|
||||
{
|
||||
handler.handleClearMessageMessage(message);
|
||||
}
|
||||
else if (command == "USERSTATE")
|
||||
{
|
||||
handler.handleUserStateMessage(message);
|
||||
}
|
||||
else if (command == "WHISPER")
|
||||
{
|
||||
handler.handleWhisperMessage(message);
|
||||
}
|
||||
else if (command == "USERNOTICE")
|
||||
{
|
||||
handler.handleUserNoticeMessage(message, *this);
|
||||
}
|
||||
else if (command == "MODE")
|
||||
// Below commands enabled through the twitch.tv/membership CAP REQ
|
||||
if (command == "MODE")
|
||||
{
|
||||
handler.handleModeMessage(message);
|
||||
}
|
||||
else if (command == "NOTICE")
|
||||
{
|
||||
handler.handleNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
else if (command == "JOIN")
|
||||
{
|
||||
handler.handleJoinMessage(message);
|
||||
|
@ -186,24 +157,58 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
|||
|
||||
void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
switch (message->type())
|
||||
{
|
||||
case Communi::IrcMessage::Type::Notice:
|
||||
{
|
||||
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
break;
|
||||
const QString &command = message->command();
|
||||
|
||||
default:;
|
||||
auto &handler = IrcMessageHandler::getInstance();
|
||||
|
||||
// Below commands enabled through the twitch.tv/commands CAP REQ
|
||||
if (command == "USERSTATE")
|
||||
{
|
||||
handler.handleUserStateMessage(message);
|
||||
}
|
||||
else if (command == "WHISPER")
|
||||
{
|
||||
handler.handleWhisperMessage(message);
|
||||
}
|
||||
else if (command == "USERNOTICE")
|
||||
{
|
||||
handler.handleUserNoticeMessage(message, *this);
|
||||
}
|
||||
else if (command == "ROOMSTATE")
|
||||
{
|
||||
handler.handleRoomStateMessage(message);
|
||||
}
|
||||
else if (command == "CLEARCHAT")
|
||||
{
|
||||
handler.handleClearChatMessage(message);
|
||||
}
|
||||
else if (command == "CLEARMSG")
|
||||
{
|
||||
handler.handleClearMessageMessage(message);
|
||||
}
|
||||
else if (command == "NOTICE")
|
||||
{
|
||||
handler.handleNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchServer::onConnected(IrcConnection *connection)
|
||||
void TwitchServer::onReadConnected(IrcConnection *connection)
|
||||
{
|
||||
// connection in thise case is the read connection
|
||||
connection->sendRaw(
|
||||
"CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership");
|
||||
AbstractIrcServer::onReadConnected(connection);
|
||||
|
||||
// twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
|
||||
// twitch.tv/membership enables the JOIN/PART/MODE/NAMES commands. See https://dev.twitch.tv/docs/irc/membership/
|
||||
connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/membership");
|
||||
}
|
||||
|
||||
void TwitchServer::onWriteConnected(IrcConnection *connection)
|
||||
{
|
||||
AbstractIrcServer::onWriteConnected(connection);
|
||||
|
||||
// twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
|
||||
// twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
|
||||
connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/commands");
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel> TwitchServer::getCustomChannel(
|
||||
|
|
|
@ -51,11 +51,13 @@ protected:
|
|||
|
||||
virtual void privateMessageReceived(
|
||||
Communi::IrcPrivateMessage *message) override;
|
||||
virtual void messageReceived(Communi::IrcMessage *message) override;
|
||||
virtual void readConnectionMessageReceived(
|
||||
Communi::IrcMessage *message) override;
|
||||
virtual void writeConnectionMessageReceived(
|
||||
Communi::IrcMessage *message) override;
|
||||
|
||||
virtual void onConnected(IrcConnection *connection) override;
|
||||
virtual void onReadConnected(IrcConnection *connection) override;
|
||||
virtual void onWriteConnected(IrcConnection *connection) override;
|
||||
|
||||
virtual std::shared_ptr<Channel> getCustomChannel(
|
||||
const QString &channelname) override;
|
||||
|
|
|
@ -198,11 +198,13 @@ public:
|
|||
QStringSetting streamlinkOpts = {"/external/streamlink/options", ""};
|
||||
|
||||
/// Misc
|
||||
BoolSetting betaUpdates = {"/misc/beta", false};
|
||||
IntSetting startUpNotification = {"/misc/startUpNotification", 0};
|
||||
QStringSetting currentVersion = {"/misc/currentVersion", ""};
|
||||
BoolSetting loadTwitchMessageHistoryOnConnect = {
|
||||
"/misc/twitch/loadMessageHistoryOnConnect", true};
|
||||
IntSetting emotesTooltipPreview = {"/misc/emotesTooltipPreview", 0};
|
||||
BoolSetting openLinksIncognito = {"/misc/openLinksIncognito", 0};
|
||||
|
||||
QStringSetting cachePath = {"/cache/path", ""};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Updates.hpp"
|
||||
|
||||
#include "Settings.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "common/Version.hpp"
|
||||
|
@ -13,6 +14,12 @@
|
|||
#include <QProcess>
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
QString currentBranch()
|
||||
{
|
||||
return getSettings()->betaUpdates ? "beta" : "stable";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Updates::Updates()
|
||||
: currentVersion_(CHATTERINO_VERSION)
|
||||
|
@ -195,8 +202,8 @@ void Updates::installUpdates()
|
|||
void Updates::checkForUpdates()
|
||||
{
|
||||
QString url =
|
||||
"https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS
|
||||
"/stable";
|
||||
"https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/" +
|
||||
currentBranch();
|
||||
|
||||
NetworkRequest(url)
|
||||
.timeout(60000)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "util/Clamp.hpp"
|
||||
#include "widgets/AccountSwitchPopupWidget.hpp"
|
||||
#include "widgets/AccountSwitchPopup.hpp"
|
||||
#include "widgets/Notebook.hpp"
|
||||
#include "widgets/Window.hpp"
|
||||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
|
@ -63,7 +63,7 @@ void WindowManager::showSettingsDialog(SettingsDialogPreference preference)
|
|||
void WindowManager::showAccountSelectPopup(QPoint point)
|
||||
{
|
||||
// static QWidget *lastFocusedWidget = nullptr;
|
||||
static AccountSwitchPopupWidget *w = new AccountSwitchPopupWidget();
|
||||
static AccountSwitchPopup *w = new AccountSwitchPopup();
|
||||
|
||||
if (w->hasFocus())
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ void WindowManager::showAccountSelectPopup(QPoint point)
|
|||
w->refresh();
|
||||
|
||||
QPoint buttonPos = point;
|
||||
w->move(buttonPos.x(), buttonPos.y());
|
||||
w->move(buttonPos.x() - 30, buttonPos.y());
|
||||
|
||||
w->show();
|
||||
w->setFocus();
|
||||
|
@ -338,7 +338,7 @@ void WindowManager::initialize(Settings &settings, Paths &paths)
|
|||
// Have to offset x by one because qt moves the window 1px too
|
||||
// far to the left:w
|
||||
|
||||
window.setGeometry(x + 1, y, width, height);
|
||||
window.setInitialBounds({x, y, width, height});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,10 +465,12 @@ void WindowManager::save()
|
|||
}
|
||||
|
||||
// window geometry
|
||||
window_obj.insert("x", window->x());
|
||||
window_obj.insert("y", window->y());
|
||||
window_obj.insert("width", window->width());
|
||||
window_obj.insert("height", window->height());
|
||||
auto rect = window->getBounds();
|
||||
|
||||
window_obj.insert("x", rect.x());
|
||||
window_obj.insert("y", rect.y());
|
||||
window_obj.insert("width", rect.width());
|
||||
window_obj.insert("height", rect.height());
|
||||
|
||||
// window tabs
|
||||
QJsonArray tabs_arr;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "widgets/AccountSwitchPopupWidget.hpp"
|
||||
#include "widgets/AccountSwitchPopup.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
|
||||
|
@ -10,10 +10,10 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
AccountSwitchPopupWidget::AccountSwitchPopupWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
AccountSwitchPopup::AccountSwitchPopup(QWidget *parent)
|
||||
: BaseWindow(parent,
|
||||
BaseWindow::Flags(BaseWindow::TopMost | BaseWindow::Frameless))
|
||||
{
|
||||
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
#ifdef Q_OS_LINUX
|
||||
this->setWindowFlag(Qt::Popup);
|
||||
#endif
|
||||
|
@ -38,22 +38,22 @@ AccountSwitchPopupWidget::AccountSwitchPopupWidget(QWidget *parent)
|
|||
SettingsDialog::showDialog(SettingsDialogPreference::Accounts); //
|
||||
});
|
||||
|
||||
this->setLayout(vbox);
|
||||
this->getLayoutContainer()->setLayout(vbox);
|
||||
|
||||
// this->setStyleSheet("background: #333");
|
||||
this->setScaleIndependantSize(200, 200);
|
||||
}
|
||||
|
||||
void AccountSwitchPopupWidget::refresh()
|
||||
void AccountSwitchPopup::refresh()
|
||||
{
|
||||
this->ui_.accountSwitchWidget->refresh();
|
||||
}
|
||||
|
||||
void AccountSwitchPopupWidget::focusOutEvent(QFocusEvent *)
|
||||
void AccountSwitchPopup::focusOutEvent(QFocusEvent *)
|
||||
{
|
||||
this->hide();
|
||||
}
|
||||
|
||||
void AccountSwitchPopupWidget::paintEvent(QPaintEvent *)
|
||||
void AccountSwitchPopup::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/AccountSwitchWidget.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class AccountSwitchPopupWidget : public QWidget
|
||||
class AccountSwitchPopup : public BaseWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AccountSwitchPopupWidget(QWidget *parent = nullptr);
|
||||
AccountSwitchPopup(QWidget *parent = nullptr);
|
||||
|
||||
void refresh();
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/InitUpdateButton.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "widgets/AccountSwitchPopupWidget.hpp"
|
||||
#include "widgets/AccountSwitchPopup.hpp"
|
||||
#include "widgets/Notebook.hpp"
|
||||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
#include "widgets/dialogs/UpdateDialog.hpp"
|
||||
|
@ -377,13 +377,13 @@ void Window::onAccountSelected()
|
|||
{
|
||||
auto user = getApp()->accounts->twitch.getCurrent();
|
||||
|
||||
#ifdef CHATTERINO_NIGHTLY_VERSION_STRING
|
||||
auto windowTitleEnd =
|
||||
QString("Chatterino Nightly " CHATTERINO_VERSION
|
||||
" (" UGLYMACROHACK(CHATTERINO_NIGHTLY_VERSION_STRING) ")");
|
||||
#else
|
||||
//#ifdef CHATTERINO_NIGHTLY_VERSION_STRING
|
||||
// auto windowTitleEnd =
|
||||
// QString("Chatterino Nightly " CHATTERINO_VERSION
|
||||
// " (" UGLYMACROHACK(CHATTERINO_NIGHTLY_VERSION_STRING) ")");
|
||||
//#else
|
||||
auto windowTitleEnd = QString("Chatterino " CHATTERINO_VERSION);
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
this->setWindowTitle(windowTitleEnd);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#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"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
|
@ -20,34 +21,30 @@ namespace chatterino {
|
|||
LogsPopup::LogsPopup()
|
||||
: channel_(Channel::getEmpty())
|
||||
{
|
||||
this->initLayout();
|
||||
this->resize(400, 600);
|
||||
}
|
||||
|
||||
void LogsPopup::initLayout()
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
this->channelView_ = new ChannelView(this);
|
||||
layout->addWidget(this->channelView_);
|
||||
|
||||
this->setLayout(layout);
|
||||
}
|
||||
|
||||
void LogsPopup::setChannelName(QString channelName)
|
||||
{
|
||||
this->channelName_ = channelName;
|
||||
}
|
||||
|
||||
void LogsPopup::setChannel(std::shared_ptr<Channel> channel)
|
||||
void LogsPopup::setChannel(const ChannelPtr &channel)
|
||||
{
|
||||
this->channel_ = channel;
|
||||
this->updateWindowTitle();
|
||||
}
|
||||
|
||||
void LogsPopup::setTargetUserName(QString userName)
|
||||
void LogsPopup::setChannelName(const QString &channelName)
|
||||
{
|
||||
this->channelName_ = channelName;
|
||||
this->updateWindowTitle();
|
||||
}
|
||||
|
||||
void LogsPopup::setTargetUserName(const QString &userName)
|
||||
{
|
||||
this->userName_ = userName;
|
||||
this->updateWindowTitle();
|
||||
}
|
||||
|
||||
void LogsPopup::updateWindowTitle()
|
||||
{
|
||||
this->setWindowTitle(this->userName_ + "'s logs in #" + this->channelName_);
|
||||
}
|
||||
|
||||
void LogsPopup::getLogs()
|
||||
|
@ -60,8 +57,6 @@ void LogsPopup::getLogs()
|
|||
this->channelName_ = twitchChannel->getName();
|
||||
this->getLogviewerLogs(twitchChannel->roomId());
|
||||
|
||||
this->setWindowTitle(this->userName_ + "'s logs in #" +
|
||||
this->channelName_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +78,7 @@ void LogsPopup::setMessages(std::vector<MessagePtr> &messages)
|
|||
ChannelPtr logsChannel(new Channel("logs", Channel::Type::Misc));
|
||||
|
||||
logsChannel->addMessagesAtStart(messages);
|
||||
this->channelView_->setChannel(logsChannel);
|
||||
SearchPopup::setChannel(logsChannel);
|
||||
}
|
||||
|
||||
void LogsPopup::getLogviewerLogs(const QString &roomID)
|
||||
|
@ -121,6 +116,8 @@ void LogsPopup::getLogviewerLogs(const QString &roomID)
|
|||
static_cast<Communi::IrcPrivateMessage *>(ircMessage);
|
||||
TwitchMessageBuilder builder(this->channel_.get(), privMsg,
|
||||
args);
|
||||
builder.message().searchText = message;
|
||||
|
||||
messages.push_back(builder.build());
|
||||
}
|
||||
|
||||
|
@ -165,6 +162,7 @@ void LogsPopup::getOverrustleLogs()
|
|||
for (auto i : dataMessages)
|
||||
{
|
||||
QJsonObject singleMessage = i.toObject();
|
||||
auto text = singleMessage.value("text").toString();
|
||||
QTime timeStamp =
|
||||
QDateTime::fromSecsSinceEpoch(
|
||||
singleMessage.value("timestamp").toInt())
|
||||
|
@ -175,9 +173,9 @@ void LogsPopup::getOverrustleLogs()
|
|||
builder.emplace<TextElement>(this->userName_,
|
||||
MessageElementFlag::Username,
|
||||
MessageColor::System);
|
||||
builder.emplace<TextElement>(
|
||||
singleMessage.value("text").toString(),
|
||||
MessageElementFlag::Text, MessageColor::Text);
|
||||
builder.emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::Text);
|
||||
builder.message().searchText = text;
|
||||
messages.push_back(builder.release());
|
||||
}
|
||||
}
|
||||
|
@ -191,4 +189,5 @@ void LogsPopup::getOverrustleLogs()
|
|||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,37 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
#include "widgets/helper/SearchPopup.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Channel;
|
||||
class ChannelView;
|
||||
|
||||
class Channel;
|
||||
using ChannelPtr = std::shared_ptr<Channel>;
|
||||
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
|
||||
class LogsPopup : public BaseWindow
|
||||
class LogsPopup : public SearchPopup
|
||||
{
|
||||
public:
|
||||
LogsPopup();
|
||||
|
||||
void setChannelName(QString channelName);
|
||||
void setChannel(std::shared_ptr<Channel> channel);
|
||||
void setTargetUserName(QString userName);
|
||||
void setChannel(const ChannelPtr &channel) override;
|
||||
void setChannelName(const QString &channelName);
|
||||
void setTargetUserName(const QString &userName);
|
||||
|
||||
void getLogs();
|
||||
|
||||
protected:
|
||||
void updateWindowTitle() override;
|
||||
|
||||
private:
|
||||
ChannelView *channelView_ = nullptr;
|
||||
ChannelPtr channel_;
|
||||
|
||||
QString userName_;
|
||||
QString channelName_;
|
||||
|
||||
void initLayout();
|
||||
void setMessages(std::vector<MessagePtr> &messages);
|
||||
void getOverrustleLogs();
|
||||
void getLogviewerLogs(const QString &roomID);
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "widgets/helper/Button.hpp"
|
||||
#include "widgets/helper/SettingsDialogTab.hpp"
|
||||
#include "widgets/settingspages/AboutPage.hpp"
|
||||
#include "widgets/settingspages/AccountsPage.hpp"
|
||||
#include "widgets/settingspages/AdvancedPage.hpp"
|
||||
#include "widgets/settingspages/BrowserExtensionPage.hpp"
|
||||
#include "widgets/settingspages/CommandPage.hpp"
|
||||
#include "widgets/settingspages/EmotesPage.hpp"
|
||||
#include "widgets/settingspages/ExternalToolsPage.hpp"
|
||||
#include "widgets/settingspages/FeelPage.hpp"
|
||||
#include "widgets/settingspages/GeneralPage.hpp"
|
||||
#include "widgets/settingspages/HighlightingPage.hpp"
|
||||
#include "widgets/settingspages/IgnoresPage.hpp"
|
||||
#include "widgets/settingspages/KeyboardSettingsPage.hpp"
|
||||
#include "widgets/settingspages/LogsPage.hpp"
|
||||
#include "widgets/settingspages/LookPage.hpp"
|
||||
#include "widgets/settingspages/ModerationPage.hpp"
|
||||
#include "widgets/settingspages/NotificationPage.hpp"
|
||||
#include "widgets/settingspages/SpecialChannelsPage.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
@ -30,6 +26,8 @@ SettingsDialog *SettingsDialog::handle = nullptr;
|
|||
SettingsDialog::SettingsDialog()
|
||||
: BaseWindow(nullptr, BaseWindow::DisableCustomScaling)
|
||||
{
|
||||
this->setWindowTitle("Chatterino Settings");
|
||||
|
||||
this->initUi();
|
||||
this->addTabs();
|
||||
|
||||
|
@ -43,39 +41,55 @@ SettingsDialog::SettingsDialog()
|
|||
|
||||
void SettingsDialog::initUi()
|
||||
{
|
||||
LayoutCreator<SettingsDialog> layoutCreator(this);
|
||||
auto outerBox = LayoutCreator<SettingsDialog>(this)
|
||||
.setLayoutType<QVBoxLayout>()
|
||||
.withoutSpacing();
|
||||
|
||||
// tab pages
|
||||
layoutCreator.setLayoutType<QHBoxLayout>()
|
||||
.withoutSpacing()
|
||||
.emplace<QWidget>()
|
||||
// TOP
|
||||
auto title = outerBox.emplace<PageHeader>();
|
||||
auto edit = LayoutCreator<PageHeader>(title.getElement())
|
||||
.setLayoutType<QHBoxLayout>()
|
||||
.withoutMargin()
|
||||
.emplace<QLineEdit>()
|
||||
.assign(&this->ui_.search);
|
||||
edit->setPlaceholderText("Find in settings...");
|
||||
|
||||
QObject::connect(edit.getElement(), &QLineEdit::textChanged, this,
|
||||
&SettingsDialog::filterElements);
|
||||
|
||||
// CENTER
|
||||
auto centerBox =
|
||||
outerBox.emplace<QHBoxLayout>().withoutMargin().withoutSpacing();
|
||||
|
||||
// left side (tabs)
|
||||
centerBox.emplace<QWidget>()
|
||||
.assign(&this->ui_.tabContainerContainer)
|
||||
.emplace<QVBoxLayout>()
|
||||
.setLayoutType<QVBoxLayout>()
|
||||
.withoutMargin()
|
||||
.assign(&this->ui_.tabContainer);
|
||||
|
||||
this->ui_.tabContainerContainer->layout()->setContentsMargins(8, 8, 0, 39);
|
||||
|
||||
this->layout()->setSpacing(0);
|
||||
|
||||
// right side layout
|
||||
auto right = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
// right side (pages)
|
||||
auto right =
|
||||
centerBox.emplace<QVBoxLayout>().withoutMargin().withoutSpacing();
|
||||
{
|
||||
right.emplace<QStackedLayout>()
|
||||
.assign(&this->ui_.pageStack)
|
||||
.withoutMargin();
|
||||
|
||||
auto buttons = right.emplace<QDialogButtonBox>(Qt::Horizontal);
|
||||
{
|
||||
this->ui_.okButton =
|
||||
buttons->addButton("Ok", QDialogButtonBox::YesRole);
|
||||
this->ui_.cancelButton =
|
||||
buttons->addButton("Cancel", QDialogButtonBox::NoRole);
|
||||
}
|
||||
}
|
||||
|
||||
this->ui_.pageStack->setMargin(0);
|
||||
|
||||
outerBox->addSpacing(12);
|
||||
|
||||
// BOTTOM
|
||||
auto buttons = outerBox.emplace<QDialogButtonBox>(Qt::Horizontal);
|
||||
{
|
||||
this->ui_.okButton =
|
||||
buttons->addButton("Ok", QDialogButtonBox::YesRole);
|
||||
this->ui_.cancelButton =
|
||||
buttons->addButton("Cancel", QDialogButtonBox::NoRole);
|
||||
}
|
||||
|
||||
// ---- misc
|
||||
this->ui_.tabContainerContainer->setObjectName("tabWidget");
|
||||
this->ui_.pageStack->setObjectName("pages");
|
||||
|
@ -86,6 +100,50 @@ void SettingsDialog::initUi()
|
|||
&SettingsDialog::onCancelClicked);
|
||||
}
|
||||
|
||||
void SettingsDialog::filterElements(const QString &text)
|
||||
{
|
||||
// filter elements and hide pages
|
||||
for (auto &&page : this->pages_)
|
||||
{
|
||||
// filterElements returns true if anything on the page matches the search query
|
||||
page->tab()->setVisible(page->filterElements(text));
|
||||
}
|
||||
|
||||
// find next visible page
|
||||
if (this->lastSelectedByUser_ && this->lastSelectedByUser_->isVisible())
|
||||
{
|
||||
this->selectTab(this->lastSelectedByUser_, false);
|
||||
}
|
||||
else if (!this->selectedTab_->isVisible())
|
||||
{
|
||||
for (auto &&tab : this->tabs_)
|
||||
{
|
||||
if (tab->isVisible())
|
||||
{
|
||||
this->selectTab(tab, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicate spaces
|
||||
bool shouldShowSpace = false;
|
||||
|
||||
for (int i = 0; i < this->ui_.tabContainer->count(); i++)
|
||||
{
|
||||
auto item = this->ui_.tabContainer->itemAt(i);
|
||||
if (auto x = dynamic_cast<QSpacerItem *>(item); x)
|
||||
{
|
||||
x->changeSize(10, shouldShowSpace ? int(16 * this->scale()) : 0);
|
||||
shouldShowSpace = false;
|
||||
}
|
||||
else if (item->widget())
|
||||
{
|
||||
shouldShowSpace |= item->widget()->isVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDialog *SettingsDialog::getHandle()
|
||||
{
|
||||
return SettingsDialog::handle;
|
||||
|
@ -96,7 +154,7 @@ void SettingsDialog::addTabs()
|
|||
this->ui_.tabContainer->setMargin(0);
|
||||
this->ui_.tabContainer->setSpacing(0);
|
||||
|
||||
this->ui_.tabContainer->addSpacing(16);
|
||||
this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20);
|
||||
|
||||
this->addTab(new GeneralPage);
|
||||
|
||||
|
@ -104,32 +162,21 @@ void SettingsDialog::addTabs()
|
|||
|
||||
this->addTab(new AccountsPage);
|
||||
|
||||
// this->ui_.tabContainer->addSpacing(16);
|
||||
|
||||
// this->addTab(new LookPage);
|
||||
// this->addTab(new FeelPage);
|
||||
|
||||
this->ui_.tabContainer->addSpacing(16);
|
||||
|
||||
this->addTab(new CommandPage);
|
||||
// this->addTab(new EmotesPage);
|
||||
this->addTab(new HighlightingPage);
|
||||
this->addTab(new IgnoresPage);
|
||||
|
||||
this->ui_.tabContainer->addSpacing(16);
|
||||
|
||||
this->addTab(new KeyboardSettingsPage);
|
||||
// this->addTab(new LogsPage);
|
||||
this->addTab(this->ui_.moderationPage = new ModerationPage);
|
||||
this->addTab(new NotificationPage);
|
||||
// this->addTab(new SpecialChannelsPage);
|
||||
// this->addTab(new BrowserExtensionPage);
|
||||
this->addTab(new ExternalToolsPage);
|
||||
this->addTab(new AdvancedPage);
|
||||
|
||||
this->ui_.tabContainer->addStretch(1);
|
||||
this->addTab(new AboutPage, Qt::AlignBottom);
|
||||
this->ui_.tabContainer->addSpacing(16);
|
||||
}
|
||||
|
||||
void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment)
|
||||
|
@ -140,6 +187,7 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment)
|
|||
this->ui_.pageStack->addWidget(page);
|
||||
this->ui_.tabContainer->addWidget(tab, 0, alignment);
|
||||
this->tabs_.push_back(tab);
|
||||
this->pages_.push_back(page);
|
||||
|
||||
if (this->tabs_.size() == 1)
|
||||
{
|
||||
|
@ -147,7 +195,7 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment)
|
|||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::selectTab(SettingsDialogTab *tab)
|
||||
void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser)
|
||||
{
|
||||
this->ui_.pageStack->setCurrentWidget(tab->getSettingsPage());
|
||||
|
||||
|
@ -159,10 +207,12 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab)
|
|||
|
||||
tab->setSelected(true);
|
||||
tab->setStyleSheet("background: #222; color: #4FC3F7;"
|
||||
"border-left: 1px solid #444;"
|
||||
"border-top: 1px solid #444;"
|
||||
"border-bottom: 1px solid #444;");
|
||||
"/*border: 1px solid #555; border-right: none;*/");
|
||||
this->selectedTab_ = tab;
|
||||
if (byUser)
|
||||
{
|
||||
this->lastSelectedByUser_ = tab;
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::selectPage(SettingsPage *page)
|
||||
|
|
|
@ -8,12 +8,19 @@
|
|||
#include <QWidget>
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class SettingsPage;
|
||||
class SettingsDialogTab;
|
||||
class ModerationPage;
|
||||
|
||||
class PageHeader : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
enum class SettingsDialogPreference {
|
||||
NoPreference,
|
||||
Accounts,
|
||||
|
@ -41,8 +48,9 @@ private:
|
|||
void initUi();
|
||||
void addTabs();
|
||||
void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop);
|
||||
void selectTab(SettingsDialogTab *tab);
|
||||
void selectTab(SettingsDialogTab *tab, bool byUser = true);
|
||||
void selectPage(SettingsPage *page);
|
||||
void filterElements(const QString &query);
|
||||
|
||||
void onOkClicked();
|
||||
void onCancelClicked();
|
||||
|
@ -54,9 +62,12 @@ private:
|
|||
QPushButton *okButton{};
|
||||
QPushButton *cancelButton{};
|
||||
ModerationPage *moderationPage{};
|
||||
QLineEdit *search{};
|
||||
} ui_;
|
||||
std::vector<SettingsDialogTab *> tabs_;
|
||||
std::vector<SettingsPage *> pages_;
|
||||
SettingsDialogTab *selectedTab_{};
|
||||
SettingsDialogTab *lastSelectedByUser_{};
|
||||
|
||||
friend class SettingsDialogTab;
|
||||
};
|
||||
|
|
|
@ -24,8 +24,23 @@
|
|||
#define TEXT_FOLLOWERS "Followers: "
|
||||
#define TEXT_VIEWS "Views: "
|
||||
#define TEXT_CREATED "Created: "
|
||||
#define TEXT_USER_ID "ID: "
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
void addCopyableLabel(LayoutCreator<QHBoxLayout> box, Label **assign)
|
||||
{
|
||||
auto label = box.emplace<Label>().assign(assign);
|
||||
auto button = box.emplace<Button>();
|
||||
button->setPixmap(getApp()->resources->buttons.copyDark);
|
||||
button->setScaleIndependantSize(18, 18);
|
||||
button->setDim(Button::Dim::Lots);
|
||||
QObject::connect(button.getElement(), &Button::leftClicked,
|
||||
[label = label.getElement()] {
|
||||
qApp->clipboard()->setText(label->getText());
|
||||
});
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UserInfoPopup::UserInfoPopup()
|
||||
: BaseWindow(nullptr, BaseWindow::Flags(BaseWindow::Frameless |
|
||||
|
@ -50,6 +65,7 @@ UserInfoPopup::UserInfoPopup()
|
|||
auto avatar =
|
||||
head.emplace<Button>(nullptr).assign(&this->ui_.avatarButton);
|
||||
avatar->setScaleIndependantSize(100, 100);
|
||||
avatar->setDim(Button::Dim::None);
|
||||
QObject::connect(avatar.getElement(), &Button::leftClicked, [this] {
|
||||
QDesktopServices::openUrl(
|
||||
QUrl("https://twitch.tv/" + this->userName_.toLower()));
|
||||
|
@ -58,11 +74,19 @@ UserInfoPopup::UserInfoPopup()
|
|||
// items on the right
|
||||
auto vbox = head.emplace<QVBoxLayout>();
|
||||
{
|
||||
auto name = vbox.emplace<Label>().assign(&this->ui_.nameLabel);
|
||||
{
|
||||
auto box = vbox.emplace<QHBoxLayout>()
|
||||
.withoutMargin()
|
||||
.withoutSpacing();
|
||||
addCopyableLabel(box, &this->ui_.nameLabel);
|
||||
this->ui_.nameLabel->setFontStyle(FontStyle::UiMediumBold);
|
||||
box->addStretch(1);
|
||||
addCopyableLabel(box, &this->ui_.userIDLabel);
|
||||
auto palette = QPalette();
|
||||
palette.setColor(QPalette::WindowText, QColor("#aaa"));
|
||||
this->ui_.userIDLabel->setPalette(palette);
|
||||
}
|
||||
|
||||
auto font = name->font();
|
||||
font.setBold(true);
|
||||
name->setFont(font);
|
||||
vbox.emplace<Label>(TEXT_VIEWS).assign(&this->ui_.viewCountLabel);
|
||||
vbox.emplace<Label>(TEXT_FOLLOWERS)
|
||||
.assign(&this->ui_.followerCountLabel);
|
||||
|
@ -199,7 +223,7 @@ UserInfoPopup::UserInfoPopup()
|
|||
});
|
||||
}
|
||||
|
||||
this->setStyleSheet("font-size: 11pt;");
|
||||
// this->setStyleSheet("font-size: 11pt;");
|
||||
|
||||
this->installEvents();
|
||||
}
|
||||
|
@ -208,7 +232,17 @@ void UserInfoPopup::themeChangedEvent()
|
|||
{
|
||||
BaseWindow::themeChangedEvent();
|
||||
|
||||
this->setStyleSheet("background: #333");
|
||||
this->setStyleSheet(
|
||||
"background: #333; font-size: " +
|
||||
QString::number(getFonts()
|
||||
->getFont(FontStyle::UiMediumBold, this->scale())
|
||||
.pixelSize()) +
|
||||
"px;");
|
||||
}
|
||||
|
||||
void UserInfoPopup::scaleChangedEvent(float /*scale*/)
|
||||
{
|
||||
themeChangedEvent();
|
||||
}
|
||||
|
||||
void UserInfoPopup::installEvents()
|
||||
|
@ -348,6 +382,10 @@ void UserInfoPopup::updateUserData()
|
|||
|
||||
this->userId_ = id;
|
||||
|
||||
this->ui_.userIDLabel->setText(TEXT_USER_ID + this->userId_);
|
||||
// don't wait for the request to complete, just put the user id in the card
|
||||
// right away
|
||||
|
||||
QString url("https://api.twitch.tv/kraken/channels/" + id);
|
||||
|
||||
NetworkRequest::twitchRequest(url)
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void themeChangedEvent() override;
|
||||
virtual void scaleChangedEvent(float scale) override;
|
||||
|
||||
private:
|
||||
void installEvents();
|
||||
|
@ -48,6 +49,7 @@ private:
|
|||
Label *viewCountLabel = nullptr;
|
||||
Label *followerCountLabel = nullptr;
|
||||
Label *createdDateLabel = nullptr;
|
||||
Label *userIDLabel = nullptr;
|
||||
|
||||
QCheckBox *follow = nullptr;
|
||||
QCheckBox *ignore = nullptr;
|
||||
|
|
|
@ -133,6 +133,8 @@ ChannelView::ChannelView(BaseWidget *parent)
|
|||
this->clickTimer_ = new QTimer(this);
|
||||
this->clickTimer_->setSingleShot(true);
|
||||
this->clickTimer_->setInterval(500);
|
||||
|
||||
this->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
|
||||
}
|
||||
|
||||
void ChannelView::initializeLayout()
|
||||
|
@ -1526,14 +1528,8 @@ void ChannelView::addContextMenuItems(
|
|||
QString url = hoveredElement->getLink().value;
|
||||
|
||||
// open link
|
||||
bool incognitoByDefault = supportsIncognitoLinks() &&
|
||||
layout->getMessage()->loginName == "hemirt";
|
||||
menu->addAction("Open link", [url, incognitoByDefault] {
|
||||
if (incognitoByDefault)
|
||||
openLinkIncognito(url);
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl(url));
|
||||
});
|
||||
menu->addAction("Open link",
|
||||
[url] { QDesktopServices::openUrl(QUrl(url)); });
|
||||
// open link default
|
||||
if (supportsIncognitoLinks())
|
||||
{
|
||||
|
@ -1699,7 +1695,10 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
|||
|
||||
case Link::Url:
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(link.value));
|
||||
if (getSettings()->openLinksIncognito && supportsIncognitoLinks())
|
||||
openLinkIncognito(link.value);
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl(link.value));
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -10,6 +10,26 @@
|
|||
#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()
|
||||
{
|
||||
|
@ -17,13 +37,24 @@ SearchPopup::SearchPopup()
|
|||
this->resize(400, 600);
|
||||
}
|
||||
|
||||
void SearchPopup::setChannel(ChannelPtr channel)
|
||||
void SearchPopup::setChannel(const ChannelPtr &channel)
|
||||
{
|
||||
this->channelName_ = channel->getName();
|
||||
this->snapshot_ = channel->getMessageSnapshot();
|
||||
this->performSearch();
|
||||
this->search();
|
||||
|
||||
this->setWindowTitle("Searching in " + channel->getName() + "s history");
|
||||
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)
|
||||
|
@ -43,18 +74,20 @@ void SearchPopup::initLayout()
|
|||
{
|
||||
QVBoxLayout *layout1 = new QVBoxLayout(this);
|
||||
layout1->setMargin(0);
|
||||
layout1->setSpacing(0);
|
||||
|
||||
// HBOX
|
||||
{
|
||||
QHBoxLayout *layout2 = new QHBoxLayout(this);
|
||||
layout2->setMargin(6);
|
||||
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->performSearch(); });
|
||||
[this] { this->search(); });
|
||||
}
|
||||
|
||||
// SEARCH BUTTON
|
||||
|
@ -63,7 +96,7 @@ void SearchPopup::initLayout()
|
|||
searchButton->setText("Search");
|
||||
layout2->addWidget(searchButton);
|
||||
QObject::connect(searchButton, &QPushButton::clicked,
|
||||
[this] { this->performSearch(); });
|
||||
[this] { this->search(); });
|
||||
}
|
||||
|
||||
layout1->addLayout(layout2);
|
||||
|
@ -80,25 +113,4 @@ void SearchPopup::initLayout()
|
|||
}
|
||||
}
|
||||
|
||||
void SearchPopup::performSearch()
|
||||
{
|
||||
QString text = searchInput_->text();
|
||||
|
||||
ChannelPtr channel(new Channel(this->channelName_, Channel::Type::None));
|
||||
|
||||
for (size_t i = 0; i < this->snapshot_.size(); i++)
|
||||
{
|
||||
MessagePtr message = this->snapshot_[i];
|
||||
|
||||
if (text.isEmpty() ||
|
||||
message->searchText.indexOf(this->searchInput_->text(), 0,
|
||||
Qt::CaseInsensitive) != -1)
|
||||
{
|
||||
channel->addMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
this->channelView_->setChannel(channel);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "ForwardDecl.hpp"
|
||||
#include "messages/LimitedQueueSnapshot.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
|
@ -9,30 +10,26 @@ class QLineEdit;
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class Channel;
|
||||
class ChannelView;
|
||||
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
|
||||
class SearchPopup : public BaseWindow
|
||||
{
|
||||
public:
|
||||
SearchPopup();
|
||||
|
||||
void setChannel(std::shared_ptr<Channel> channel);
|
||||
virtual void setChannel(const ChannelPtr &channel);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
virtual void updateWindowTitle();
|
||||
|
||||
private:
|
||||
void initLayout();
|
||||
void performSearch();
|
||||
void search();
|
||||
|
||||
LimitedQueueSnapshot<MessagePtr> snapshot_;
|
||||
QLineEdit *searchInput_;
|
||||
ChannelView *channelView_;
|
||||
QString channelName_;
|
||||
QLineEdit *searchInput_{};
|
||||
ChannelView *channelView_{};
|
||||
QString channelName_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -68,6 +68,8 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event)
|
|||
}
|
||||
|
||||
this->dialog_->selectTab(this);
|
||||
|
||||
this->setFocus();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
#include "AdvancedPage.hpp"
|
||||
|
||||
#include "Application.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 <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 {
|
||||
|
||||
AdvancedPage::AdvancedPage()
|
||||
: SettingsPage("Advanced", ":/settings/advanced.svg")
|
||||
{
|
||||
LayoutCreator<AdvancedPage> layoutCreator(this);
|
||||
|
||||
auto tabs = layoutCreator.emplace<QTabWidget>();
|
||||
|
||||
{
|
||||
auto layout = tabs.appendTab(new QVBoxLayout, "Cache");
|
||||
auto folderLabel = layout.emplace<QLabel>();
|
||||
|
||||
folderLabel->setTextFormat(Qt::RichText);
|
||||
folderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
|
||||
Qt::LinksAccessibleByKeyboard |
|
||||
Qt::LinksAccessibleByKeyboard);
|
||||
folderLabel->setOpenExternalLinks(true);
|
||||
|
||||
getSettings()->cachePath.connect([folderLabel](const auto &,
|
||||
auto) mutable {
|
||||
QString newPath = getPaths()->cacheDirectory();
|
||||
|
||||
QString pathShortened = "Cache saved at <a href=\"file:///" +
|
||||
newPath +
|
||||
"\"><span style=\"color: white;\">" +
|
||||
shortenString(newPath, 50) + "</span></a>";
|
||||
|
||||
folderLabel->setText(pathShortened);
|
||||
folderLabel->setToolTip(newPath);
|
||||
});
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
auto selectDir = layout.emplace<QPushButton>("Set custom cache folder");
|
||||
|
||||
QObject::connect(
|
||||
selectDir.getElement(), &QPushButton::clicked, this, [this] {
|
||||
auto dirName = QFileDialog::getExistingDirectory(this);
|
||||
|
||||
getSettings()->cachePath = dirName;
|
||||
});
|
||||
|
||||
auto resetDir =
|
||||
layout.emplace<QPushButton>("Reset custom cache folder");
|
||||
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
|
||||
[]() mutable {
|
||||
getSettings()->cachePath = ""; //
|
||||
});
|
||||
|
||||
// Logs end
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class AdvancedPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
AdvancedPage();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,36 +0,0 @@
|
|||
#include "BrowserExtensionPage.hpp"
|
||||
|
||||
#include "util/LayoutCreator.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
#define CHROME_EXTENSION_LINK \
|
||||
"https://chrome.google.com/webstore/detail/chatterino-native-host/" \
|
||||
"glknmaideaikkmemifbfkhnomoknepka"
|
||||
#define FIREFOX_EXTENSION_LINK \
|
||||
"https://addons.mozilla.org/en-US/firefox/addon/chatterino-native-host/"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
BrowserExtensionPage::BrowserExtensionPage()
|
||||
: SettingsPage("Browser integration", ":/settings/browser.svg")
|
||||
{
|
||||
auto layout =
|
||||
LayoutCreator<BrowserExtensionPage>(this).setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto label = layout.emplace<QLabel>(
|
||||
"The browser extension will replace the default Twitch.tv chat with "
|
||||
"chatterino while chatterino is running.");
|
||||
label->setWordWrap(true);
|
||||
|
||||
auto chrome = layout.emplace<QLabel>("<a href=\"" CHROME_EXTENSION_LINK
|
||||
"\">Download for Google Chrome</a>");
|
||||
chrome->setOpenExternalLinks(true);
|
||||
auto firefox =
|
||||
layout.emplace<QLabel>("<a href=\"" FIREFOX_EXTENSION_LINK
|
||||
"\">Download for Mozilla Firefox</a>");
|
||||
firefox->setOpenExternalLinks(true);
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class BrowserExtensionPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
BrowserExtensionPage();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -38,7 +38,7 @@ CommandPage::CommandPage()
|
|||
auto app = getApp();
|
||||
|
||||
LayoutCreator<CommandPage> layoutCreator(this);
|
||||
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
EditableModelView *view =
|
||||
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
#include "EmotesPage.hpp"
|
||||
|
||||
#include "util/LayoutCreator.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
EmotesPage::EmotesPage()
|
||||
: SettingsPage("Emotes", ":/settings/emote.svg")
|
||||
{
|
||||
// SettingManager &settings = SettingManager::getInstance();
|
||||
// LayoutCreator<EmotesPage> layoutCreator(this);
|
||||
// auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
|
||||
// // clang-format off
|
||||
// layout.append(this->createCheckBox("Enable Twitch emotes",
|
||||
// settings.enableTwitchEmotes));
|
||||
// layout.append(this->createCheckBox("Enable BetterTTV emotes",
|
||||
// settings.enableBttvEmotes));
|
||||
// layout.append(this->createCheckBox("Enable FrankerFaceZ emotes",
|
||||
// settings.enableFfzEmotes)); layout.append(this->createCheckBox("Enable
|
||||
// emojis", settings.enableEmojis));
|
||||
// layout.append(this->createCheckBox("Enable gif animations",
|
||||
// settings.enableGifAnimations));
|
||||
// // clang-format on
|
||||
|
||||
// layout->addStretch(1);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class EmotesPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
EmotesPage();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,103 +0,0 @@
|
|||
#include "FeelPage.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#define PAUSE_HOVERING "When hovering"
|
||||
#define SCROLL_SMOOTH "Smooth scrolling"
|
||||
#define SCROLL_NEWMSG "Smooth scrolling for new messages"
|
||||
|
||||
#define LIMIT_CHATTERS_FOR_SMALLER_STREAMERS \
|
||||
"Only fetch chatters list for viewers under X viewers"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
FeelPage::FeelPage()
|
||||
: SettingsPage("Feel", ":/settings/behave.svg")
|
||||
{
|
||||
LayoutCreator<FeelPage> layoutCreator(this);
|
||||
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
// layout.append(this->createCheckBox("Use a seperate write connection.",
|
||||
// getSettings()->twitchSeperateWriteConnection));
|
||||
layout.append(this->createCheckBox(SCROLL_SMOOTH,
|
||||
getSettings()->enableSmoothScrolling));
|
||||
layout.append(this->createCheckBox(
|
||||
SCROLL_NEWMSG, getSettings()->enableSmoothScrollingNewMessages));
|
||||
|
||||
auto form = layout.emplace<QFormLayout>().withoutMargin();
|
||||
{
|
||||
form->addRow(
|
||||
"", this->createCheckBox(
|
||||
"Show which users joined the channel (up to 1000 chatters)",
|
||||
getSettings()->showJoins));
|
||||
form->addRow(
|
||||
"", this->createCheckBox(
|
||||
"Show which users parted the channel (up to 1000 chatters)",
|
||||
getSettings()->showParts));
|
||||
|
||||
form->addRow("Pause chat:",
|
||||
this->createCheckBox(PAUSE_HOVERING,
|
||||
getSettings()->pauseChatOnHover));
|
||||
|
||||
form->addRow("Mouse scroll speed:", this->createMouseScrollSlider());
|
||||
form->addRow("Links:",
|
||||
this->createCheckBox("Open links only on double click",
|
||||
getSettings()->linksDoubleClickOnly));
|
||||
form->addRow("", this->createCheckBox("Show link info in tooltips",
|
||||
getSettings()->linkInfoTooltip));
|
||||
form->addRow(
|
||||
"", this->createCheckBox("Auto unshort links (requires restart)",
|
||||
getSettings()->unshortLinks));
|
||||
}
|
||||
|
||||
layout->addSpacing(16);
|
||||
|
||||
{
|
||||
auto group = layout.emplace<QGroupBox>("Auto-completion");
|
||||
auto groupLayout = group.setLayoutType<QFormLayout>();
|
||||
groupLayout->addRow(
|
||||
LIMIT_CHATTERS_FOR_SMALLER_STREAMERS,
|
||||
this->createCheckBox(
|
||||
"", getSettings()->onlyFetchChattersForSmallerStreamers));
|
||||
|
||||
groupLayout->addRow(
|
||||
"What viewer count counts as a \"smaller streamer\"",
|
||||
this->createSpinBox(getSettings()->smallStreamerLimit, 10, 50000));
|
||||
}
|
||||
|
||||
{
|
||||
auto group = layout.emplace<QGroupBox>("Misc");
|
||||
auto groupLayout = group.setLayoutType<QVBoxLayout>();
|
||||
|
||||
groupLayout.append(this->createCheckBox("Show whispers inline",
|
||||
getSettings()->inlineWhispers));
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
QSlider *FeelPage::createMouseScrollSlider()
|
||||
{
|
||||
auto slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
float currentValue = getSettings()->mouseScrollMultiplier;
|
||||
int sliderValue = int(((currentValue - 0.1f) / 2.f) * 99.f);
|
||||
slider->setValue(sliderValue);
|
||||
|
||||
QObject::connect(slider, &QSlider::valueChanged, [=](int newValue) {
|
||||
float mul = static_cast<float>(newValue) / 99.f;
|
||||
float newSliderValue = (mul * 2.1f) + 0.1f;
|
||||
getSettings()->mouseScrollMultiplier = newSliderValue;
|
||||
});
|
||||
|
||||
return slider;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QSlider>
|
||||
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class FeelPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
FeelPage();
|
||||
|
||||
private:
|
||||
QSlider *createMouseScrollSlider();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -11,6 +11,7 @@
|
|||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/FuzzyConvert.hpp"
|
||||
#include "util/Helpers.hpp"
|
||||
#include "util/IncognitoBrowser.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
#include "widgets/helper/Line.hpp"
|
||||
|
||||
|
@ -20,36 +21,21 @@
|
|||
#define FIREFOX_EXTENSION_LINK \
|
||||
"https://addons.mozilla.org/en-US/firefox/addon/chatterino-native-host/"
|
||||
|
||||
#define addTitle addTitle
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
QPushButton *makeOpenSettingDirButton()
|
||||
{
|
||||
auto button = new QPushButton("Open settings directory");
|
||||
QObject::connect(button, &QPushButton::clicked, [] {
|
||||
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
|
||||
});
|
||||
return button;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TitleLabel *SettingsLayout::addTitle(const QString &title)
|
||||
{
|
||||
auto label = new TitleLabel(title + ":");
|
||||
|
||||
if (this->count() != 0)
|
||||
this->addSpacing(16);
|
||||
|
||||
if (this->count() == 0)
|
||||
label->setStyleSheet("margin-top: 0");
|
||||
this->addWidget(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
TitleLabel2 *SettingsLayout::addTitle2(const QString &title)
|
||||
{
|
||||
auto label = new TitleLabel2(title + ":");
|
||||
// groups
|
||||
this->groups_.push_back(Group{title, label, {}});
|
||||
|
||||
this->addSpacing(16);
|
||||
|
||||
this->addWidget(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
|
@ -71,6 +57,10 @@ QCheckBox *SettingsLayout::addCheckbox(const QString &text,
|
|||
[&setting, inverse](bool state) { setting = inverse ^ state; });
|
||||
|
||||
this->addWidget(check);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({check, {text}});
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
|
@ -82,11 +72,17 @@ ComboBox *SettingsLayout::addDropdown(const QString &text,
|
|||
combo->setFocusPolicy(Qt::StrongFocus);
|
||||
combo->addItems(list);
|
||||
|
||||
layout->addWidget(new QLabel(text + ":"));
|
||||
auto label = new QLabel(text + ":");
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
layout->addWidget(combo);
|
||||
|
||||
this->addLayout(layout);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({combo, {text}});
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
|
@ -124,6 +120,9 @@ DescriptionLabel *SettingsLayout::addDescription(const QString &text)
|
|||
|
||||
this->addWidget(label);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
|
@ -132,6 +131,64 @@ void SettingsLayout::addSeperator()
|
|||
this->addWidget(new Line(false));
|
||||
}
|
||||
|
||||
bool SettingsLayout::filterElements(const QString &query)
|
||||
{
|
||||
bool any{};
|
||||
|
||||
for (auto &&group : this->groups_)
|
||||
{
|
||||
// if a description in a group matches `query` then show the entire group
|
||||
bool descriptionMatches{};
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
if (auto x = dynamic_cast<DescriptionLabel *>(widget.element); x)
|
||||
{
|
||||
if (x->text().contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
descriptionMatches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if group name matches then all should be visible
|
||||
if (group.name.contains(query, Qt::CaseInsensitive) ||
|
||||
descriptionMatches)
|
||||
{
|
||||
for (auto &&widget : group.widgets)
|
||||
widget.element->show();
|
||||
group.title->show();
|
||||
any = true;
|
||||
}
|
||||
// check if any match
|
||||
else
|
||||
{
|
||||
auto groupAny = false;
|
||||
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
for (auto &&keyword : widget.keywords)
|
||||
{
|
||||
if (keyword.contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
widget.element->show();
|
||||
groupAny = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
widget.element->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.title->setVisible(groupAny);
|
||||
any |= groupAny;
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
GeneralPage::GeneralPage()
|
||||
: SettingsPage("General", ":/settings/about.svg")
|
||||
{
|
||||
|
@ -141,6 +198,7 @@ GeneralPage::GeneralPage()
|
|||
y->addWidget(scroll);
|
||||
auto x = new QHBoxLayout;
|
||||
auto layout = new SettingsLayout;
|
||||
this->settingsLayout_ = layout;
|
||||
x->addLayout(layout, 0);
|
||||
x->addStretch(1);
|
||||
auto z = new QFrame;
|
||||
|
@ -155,6 +213,16 @@ GeneralPage::GeneralPage()
|
|||
this->initExtra();
|
||||
}
|
||||
|
||||
bool GeneralPage::filterElements(const QString &query)
|
||||
{
|
||||
if (this->settingsLayout_)
|
||||
return this->settingsLayout_->filterElements(query) ||
|
||||
this->name_.contains(query, Qt::CaseInsensitive) ||
|
||||
query.isEmpty();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void GeneralPage::initLayout(SettingsLayout &layout)
|
||||
{
|
||||
auto &s = *getSettings();
|
||||
|
@ -220,10 +288,6 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
|||
// layout.addCheckbox("Mark last message you read");
|
||||
// layout.addDropdown("Last read message style", {"Default"});
|
||||
layout.addCheckbox("Show deleted messages", s.hideModerated, true);
|
||||
layout.addCheckbox("Show moderation messages", s.hideModerationActions,
|
||||
true);
|
||||
layout.addCheckbox("Random username color for users who never set a color",
|
||||
s.colorizeNicknames);
|
||||
layout.addDropdown<QString>(
|
||||
"Timestamps", {"Disable", "h:mm", "hh:mm", "h:mm a", "hh:mm a"},
|
||||
s.timestampFormat,
|
||||
|
@ -268,6 +332,11 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
|||
return QString::number(val) + "x";
|
||||
},
|
||||
[](auto args) { return fuzzyToFloat(args.value, 1.f); });
|
||||
|
||||
layout.addDropdown<int>(
|
||||
"Preview on hover", {"Don't show", "Always show", "Hold shift"},
|
||||
s.emotesTooltipPreview, [](int index) { return index; },
|
||||
[](auto args) { return args.index; }, false);
|
||||
layout.addDropdown("Emoji set",
|
||||
{"EmojiOne 2", "EmojiOne 3", "Twitter", "Facebook",
|
||||
"Apple", "Google", "Messenger"},
|
||||
|
@ -284,15 +353,40 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
|||
layout.addCheckbox("Chatterino", getSettings()->showBadgesChatterino);
|
||||
|
||||
layout.addTitle("Chat title");
|
||||
layout.addWidget(new QLabel("In live channels show:"));
|
||||
layout.addDescription("In live channels show:");
|
||||
layout.addCheckbox("Uptime", s.headerUptime);
|
||||
layout.addCheckbox("Viewer count", s.headerViewerCount);
|
||||
layout.addCheckbox("Category", s.headerGame);
|
||||
layout.addCheckbox("Title", s.headerStreamTitle);
|
||||
|
||||
layout.addTitle("Beta");
|
||||
layout.addDescription(
|
||||
"You can receive updates earlier by ticking the box below. Report "
|
||||
"issues <a href='https://chatterino.com/link/issues'>here</a>.");
|
||||
layout.addCheckbox("Receive beta updates", s.betaUpdates);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
layout.addTitle("Browser Integration");
|
||||
layout.addDescription("The browser extension replaces the default "
|
||||
"Twitch.tv chat with chatterino.");
|
||||
|
||||
layout.addDescription(
|
||||
createNamedLink(CHROME_EXTENSION_LINK, "Download for Google Chrome"));
|
||||
layout.addDescription(
|
||||
createNamedLink(FIREFOX_EXTENSION_LINK, "Download for Firefox"));
|
||||
#endif
|
||||
|
||||
layout.addTitle("Miscellaneous");
|
||||
|
||||
//layout.addWidget(makeOpenSettingDirButton());
|
||||
if (supportsIncognitoLinks())
|
||||
{
|
||||
layout.addCheckbox("Open links in incognito/private mode",
|
||||
s.openLinksIncognito);
|
||||
}
|
||||
layout.addCheckbox("Show moderation messages", s.hideModerationActions,
|
||||
true);
|
||||
layout.addCheckbox("Random username color for users who never set a color",
|
||||
s.colorizeNicknames);
|
||||
layout.addCheckbox("Mention users with a comma (User,)",
|
||||
s.mentionUsersWithComma);
|
||||
layout.addCheckbox("Show joined users (< 1000 chatters)", s.showJoins);
|
||||
|
@ -314,11 +408,6 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
|||
s.linksDoubleClickOnly);
|
||||
layout.addCheckbox("Unshorten links", s.unshortLinks);
|
||||
layout.addCheckbox("Show live indicator in tabs", s.showTabLive);
|
||||
layout.addDropdown<int>("Show emote preview in tooltip on hover",
|
||||
{"Don't show", "Always show", "Hold shift"},
|
||||
s.emotesTooltipPreview,
|
||||
[](int index) { return index; },
|
||||
[](auto args) { return args.index; }, false);
|
||||
|
||||
layout.addCheckbox(
|
||||
"Only search for emote autocompletion at the start of emote names",
|
||||
|
@ -330,25 +419,57 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
|||
layout.addCheckbox("Load message history on connect",
|
||||
s.loadTwitchMessageHistoryOnConnect);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
layout.addTitle("Browser Integration");
|
||||
layout.addDescription("The browser extension replaces the default "
|
||||
"Twitch.tv chat with chatterino.");
|
||||
layout.addTitle("Cache");
|
||||
layout.addDescription(
|
||||
"Files that are used often (such as emotes) are saved to disk to "
|
||||
"reduce bandwidth usage and tho speed up loading.");
|
||||
|
||||
layout.addDescription(
|
||||
createNamedLink(CHROME_EXTENSION_LINK, "Download for Google Chrome"));
|
||||
layout.addDescription(
|
||||
createNamedLink(FIREFOX_EXTENSION_LINK, "Download for Firefox"));
|
||||
#endif
|
||||
} // namespace chatterino
|
||||
auto cachePathLabel = layout.addDescription("placeholder :D");
|
||||
getSettings()->cachePath.connect([cachePathLabel](const auto &,
|
||||
auto) mutable {
|
||||
QString newPath = getPaths()->cacheDirectory();
|
||||
|
||||
QString pathShortened = "Cache saved at <a href=\"file:///" + newPath +
|
||||
"\"><span style=\"color: white;\">" +
|
||||
shortenString(newPath, 50) + "</span></a>";
|
||||
cachePathLabel->setText(pathShortened);
|
||||
cachePathLabel->setToolTip(newPath);
|
||||
});
|
||||
|
||||
// Choose and reset buttons
|
||||
{
|
||||
auto box = new QHBoxLayout;
|
||||
|
||||
box->addWidget(layout.makeButton("Choose cache path", [this]() {
|
||||
getSettings()->cachePath = QFileDialog::getExistingDirectory(this);
|
||||
}));
|
||||
box->addWidget(layout.makeButton(
|
||||
"Reset", []() { getSettings()->cachePath = ""; }));
|
||||
box->addStretch(1);
|
||||
|
||||
layout.addLayout(box);
|
||||
}
|
||||
|
||||
layout.addTitle("AppData");
|
||||
layout.addDescription("All local files like settings and cache files are "
|
||||
"store in this directory.");
|
||||
layout.addButton("Open AppData directory", [] {
|
||||
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
|
||||
});
|
||||
|
||||
// invisible element for width
|
||||
auto inv = new BaseWidget(this);
|
||||
inv->setScaleIndependantWidth(500);
|
||||
layout.addWidget(inv);
|
||||
}
|
||||
|
||||
void GeneralPage::initExtra()
|
||||
{
|
||||
/// update cache path
|
||||
if (this->cachePath)
|
||||
if (this->cachePath_)
|
||||
{
|
||||
getSettings()->cachePath.connect(
|
||||
[cachePath = this->cachePath](const auto &, auto) mutable {
|
||||
[cachePath = this->cachePath_](const auto &, auto) mutable {
|
||||
QString newPath = getPaths()->cacheDirectory();
|
||||
|
||||
QString pathShortened = "Current location: <a href=\"file:///" +
|
||||
|
|
|
@ -28,17 +28,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class TitleLabel2 : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TitleLabel2(const QString &text)
|
||||
: QLabel(text)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DescriptionLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -71,7 +60,6 @@ class SettingsLayout : public QVBoxLayout
|
|||
|
||||
public:
|
||||
TitleLabel *addTitle(const QString &text);
|
||||
TitleLabel2 *addTitle2(const QString &text);
|
||||
/// @param inverse Inverses true to false and vice versa
|
||||
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
|
||||
bool inverse = false);
|
||||
|
@ -80,6 +68,26 @@ public:
|
|||
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,
|
||||
|
@ -141,9 +149,23 @@ public:
|
|||
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_;
|
||||
};
|
||||
|
||||
|
@ -154,13 +176,16 @@ class GeneralPage : public SettingsPage
|
|||
public:
|
||||
GeneralPage();
|
||||
|
||||
bool filterElements(const QString &query);
|
||||
|
||||
private:
|
||||
void initLayout(SettingsLayout &layout);
|
||||
void initExtra();
|
||||
|
||||
QString getFont(const DropdownArgs &args) const;
|
||||
|
||||
DescriptionLabel *cachePath{};
|
||||
DescriptionLabel *cachePath_{};
|
||||
SettingsLayout *settingsLayout_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -57,7 +57,7 @@ HighlightingPage::HighlightingPage()
|
|||
|
||||
view->addRegexHelpLink();
|
||||
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
|
||||
"Enable\nregex"});
|
||||
"Enable\nregex", "Case-\nsensitive"});
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::Fixed);
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
||||
|
@ -71,8 +71,8 @@ HighlightingPage::HighlightingPage()
|
|||
});
|
||||
|
||||
view->addButtonPressed.connect([] {
|
||||
getApp()->highlights->phrases.appendItem(
|
||||
HighlightPhrase{"my phrase", true, false, false});
|
||||
getApp()->highlights->phrases.appendItem(HighlightPhrase{
|
||||
"my phrase", true, false, false, false});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,10 @@ HighlightingPage::HighlightingPage()
|
|||
.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(
|
||||
|
@ -103,7 +107,7 @@ HighlightingPage::HighlightingPage()
|
|||
|
||||
view->addButtonPressed.connect([] {
|
||||
getApp()->highlights->highlightedUsers.appendItem(
|
||||
HighlightPhrase{"highlighted user", true, false,
|
||||
HighlightPhrase{"highlighted user", true, false, false,
|
||||
false});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ KeyboardSettingsPage::KeyboardSettingsPage()
|
|||
auto layout =
|
||||
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto form = layout.emplace<QFormLayout>();
|
||||
auto form = layout.emplace<QFormLayout>().withoutMargin();
|
||||
|
||||
form->addRow(new QLabel("Hold Ctrl"), new QLabel("Show resize handles"));
|
||||
form->addRow(new QLabel("Hold Ctrl + Alt"),
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
//#include "LogsPage.hpp"
|
||||
|
||||
//#include "Application.hpp"
|
||||
//#include "singletons/PathManager.hpp"
|
||||
|
||||
//#include <QFormLayout>
|
||||
//#include <QVBoxLayout>
|
||||
|
||||
//#include "util/LayoutCreator.hpp"
|
||||
|
||||
// namespace chatterino {
|
||||
// namespace widgets {
|
||||
// namespace settingspages {
|
||||
|
||||
// inline QString CreateLink(const QString &url, bool file = false)
|
||||
//{
|
||||
// if (file) {
|
||||
// return QString("<a href=\"file:///" + url + "\"><span style=\"color:
|
||||
// white;\">" + url +
|
||||
// "</span></a>");
|
||||
// }
|
||||
|
||||
// return QString("<a href=\"" + url + "\"><span style=\"color: white;\">" +
|
||||
// url +
|
||||
// "</span></a>");
|
||||
//}
|
||||
|
||||
// LogsPage::LogsPage()
|
||||
// : SettingsPage("Logs", "")
|
||||
//{
|
||||
// auto app = getApp();
|
||||
|
||||
// LayoutCreator<LogsPage> layoutCreator(this);
|
||||
// auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
|
||||
// auto logPath = getPaths()->logsFolderPath;
|
||||
|
||||
// auto created = layout.emplace<QLabel>();
|
||||
// created->setText("Logs are saved to " + CreateLink(logPath, true));
|
||||
// created->setTextFormat(Qt::RichText);
|
||||
// created->setTextInteractionFlags(Qt::TextBrowserInteraction |
|
||||
// Qt::LinksAccessibleByKeyboard |
|
||||
// Qt::LinksAccessibleByKeyboard);
|
||||
// created->setOpenExternalLinks(true);
|
||||
// layout.append(this->createCheckBox("Enable logging",
|
||||
// getSettings()->enableLogging));
|
||||
|
||||
// layout->addStretch(1);
|
||||
//}
|
||||
|
||||
//} // namespace settingspages
|
||||
//} // namespace widgets
|
||||
//} // namespace chatterino
|
|
@ -1,17 +0,0 @@
|
|||
//#pragma once
|
||||
|
||||
//#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
// namespace chatterino {
|
||||
// namespace widgets {
|
||||
// namespace settingspages {
|
||||
|
||||
// class LogsPage : public SettingsPage
|
||||
//{
|
||||
// public:
|
||||
// LogsPage();
|
||||
//};
|
||||
|
||||
//} // namespace settingspages
|
||||
//} // namespace widgets
|
||||
//} // namespace chatterino
|
|
@ -1,596 +0,0 @@
|
|||
#include "LookPage.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "util/RemoveScrollAreaBackground.hpp"
|
||||
#include "widgets/helper/ChannelView.hpp"
|
||||
#include "widgets/helper/Line.hpp"
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QFontDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#define THEME_ITEMS "White", "Light", "Dark", "Black", "Custom"
|
||||
|
||||
#define TAB_X "Show tab close button"
|
||||
#define TAB_PREF "Hide preferences button (ctrl+p to show)"
|
||||
#define TAB_USER "Hide user button"
|
||||
|
||||
// clang-format off
|
||||
#define TIMESTAMP_FORMATS "hh:mm a", "h:mm a", "hh:mm:ss a", "h:mm:ss a", "HH:mm", "H:mm", "HH:mm:ss", "H:mm:ss"
|
||||
// clang-format on
|
||||
|
||||
#ifdef USEWINSDK
|
||||
# define WINDOW_TOPMOST "Window always on top"
|
||||
#else
|
||||
# define WINDOW_TOPMOST "Window always on top (requires restart)"
|
||||
#endif
|
||||
#define INPUT_EMPTY "Show input box when empty"
|
||||
#define LAST_MSG "Mark the last message you read"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
LookPage::LookPage()
|
||||
: SettingsPage("Look", ":/settings/theme.svg")
|
||||
{
|
||||
this->initializeUi();
|
||||
}
|
||||
|
||||
void LookPage::initializeUi()
|
||||
{
|
||||
LayoutCreator<LookPage> layoutCreator(this);
|
||||
|
||||
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
|
||||
// settings
|
||||
auto tabs = layout.emplace<QTabWidget>();
|
||||
|
||||
this->addInterfaceTab(tabs.appendTab(new QVBoxLayout, "Interface"));
|
||||
this->addMessageTab(tabs.appendTab(new QVBoxLayout, "Messages"));
|
||||
this->addEmoteTab(tabs.appendTab(new QVBoxLayout, "Emotes"));
|
||||
this->addSplitHeaderTab(tabs.appendTab(new QVBoxLayout, "Split header"));
|
||||
this->addBadgesTab(tabs.appendTab(new QVBoxLayout, "Badges"));
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
// preview
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
auto channelView = layout.emplace<ChannelView>();
|
||||
auto channel = this->createPreviewChannel();
|
||||
channelView->setChannel(channel);
|
||||
channelView->setScaleIndependantHeight(74);
|
||||
}
|
||||
|
||||
void LookPage::addInterfaceTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
// theme
|
||||
{
|
||||
auto *theme =
|
||||
this->createComboBox({THEME_ITEMS}, getApp()->themes->themeName);
|
||||
QDoubleSpinBox *w = new QDoubleSpinBox;
|
||||
|
||||
QObject::connect(theme, &QComboBox::currentTextChanged,
|
||||
[w](const QString &themeName) {
|
||||
if (themeName == "Custom")
|
||||
{
|
||||
w->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
w->hide();
|
||||
}
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
|
||||
auto box = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
box.emplace<QLabel>("Theme: ");
|
||||
box.append(theme);
|
||||
|
||||
{
|
||||
w->setButtonSymbols(QDoubleSpinBox::NoButtons);
|
||||
if (getApp()->themes->themeName.getValue() != "Custom")
|
||||
{
|
||||
w->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
w->show();
|
||||
}
|
||||
w->setRange(-1.0, 1.0);
|
||||
w->setSingleStep(0.05);
|
||||
w->setValue(getSettings()->customThemeMultiplier.getValue());
|
||||
|
||||
QObject::connect(
|
||||
w, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
||||
[](double value) {
|
||||
getSettings()->customThemeMultiplier.setValue(float(value));
|
||||
getApp()->themes->update();
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
box.append(w);
|
||||
}
|
||||
|
||||
box->addStretch(1);
|
||||
}
|
||||
|
||||
layout.append(
|
||||
this->createCheckBox(WINDOW_TOPMOST, getSettings()->windowTopMost));
|
||||
|
||||
// --
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
// tab x
|
||||
layout.append(
|
||||
this->createCheckBox(TAB_X, getSettings()->showTabCloseButton));
|
||||
|
||||
// show buttons
|
||||
#ifndef USEWINSDK
|
||||
layout.append(
|
||||
this->createCheckBox(TAB_PREF, getSettings()->hidePreferencesButton));
|
||||
layout.append(
|
||||
this->createCheckBox(TAB_USER, getSettings()->hideUserButton));
|
||||
#endif
|
||||
|
||||
// empty input
|
||||
layout.append(
|
||||
this->createCheckBox(INPUT_EMPTY, getSettings()->showEmptyInput));
|
||||
layout.append(this->createCheckBox("Show message length while typing",
|
||||
getSettings()->showMessageLength));
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addMessageTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
// font
|
||||
layout.append(this->createFontChanger());
|
||||
|
||||
// --
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
// timestamps
|
||||
{
|
||||
auto box = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
box.append(this->createCheckBox("Show timestamps",
|
||||
getSettings()->showTimestamps));
|
||||
box.append(this->createComboBox({TIMESTAMP_FORMATS},
|
||||
getSettings()->timestampFormat));
|
||||
box->addStretch(1);
|
||||
}
|
||||
|
||||
// --
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
// separate
|
||||
layout.append(this->createCheckBox("Lines between messages",
|
||||
getSettings()->separateMessages));
|
||||
|
||||
// alternate
|
||||
layout.append(this->createCheckBox("Alternate background",
|
||||
getSettings()->alternateMessages));
|
||||
|
||||
layout.append(
|
||||
this->createCheckBox("Compact emotes", getSettings()->compactEmotes));
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
// bold-slider
|
||||
{
|
||||
auto box = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
box.emplace<QLabel>("Username boldness: ");
|
||||
box.append(this->createBoldScaleSlider());
|
||||
}
|
||||
|
||||
// bold usernames
|
||||
layout.append(this->createCheckBox("Bold mentions (@username)",
|
||||
getSettings()->boldUsernames));
|
||||
|
||||
// --
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
// lowercase links
|
||||
layout.append(this->createCheckBox("Lowercase domains",
|
||||
getSettings()->lowercaseDomains));
|
||||
|
||||
// collapsing
|
||||
{
|
||||
auto *combo = new QComboBox(this);
|
||||
combo->addItems({"Never", "2", "3", "4", "5", "6", "7", "8", "9", "10",
|
||||
"11", "12", "13", "14", "15"});
|
||||
|
||||
const auto currentIndex = []() -> int {
|
||||
auto val = getSettings()->collpseMessagesMinLines.getValue();
|
||||
if (val > 0)
|
||||
{
|
||||
--val;
|
||||
}
|
||||
return val;
|
||||
}();
|
||||
combo->setCurrentIndex(currentIndex);
|
||||
|
||||
QObject::connect(
|
||||
combo, &QComboBox::currentTextChanged, [](const QString &str) {
|
||||
getSettings()->collpseMessagesMinLines = str.toInt();
|
||||
});
|
||||
|
||||
auto hbox = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
hbox.emplace<QLabel>("Collapse messages longer than");
|
||||
hbox.append(combo);
|
||||
hbox.emplace<QLabel>("lines");
|
||||
}
|
||||
|
||||
// last read message
|
||||
this->addLastReadMessageIndicatorPatternSelector(layout);
|
||||
|
||||
// --
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addEmoteTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
layout.append(
|
||||
this->createCheckBox("Animations", getSettings()->animateEmotes));
|
||||
layout.append(
|
||||
this->createCheckBox("Animations only when chatterino has focus",
|
||||
getSettings()->animationsWhenFocused));
|
||||
|
||||
auto scaleBox = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
{
|
||||
scaleBox.emplace<QLabel>("Size:");
|
||||
|
||||
auto emoteScale = scaleBox.emplace<QSlider>(Qt::Horizontal);
|
||||
emoteScale->setMinimum(5);
|
||||
emoteScale->setMaximum(50);
|
||||
|
||||
auto scaleLabel = scaleBox.emplace<QLabel>("1.0");
|
||||
scaleLabel->setFixedWidth(100);
|
||||
QObject::connect(emoteScale.getElement(), &QSlider::valueChanged,
|
||||
[scaleLabel](int value) mutable {
|
||||
float f = float(value) / 10.f;
|
||||
scaleLabel->setText(QString::number(f));
|
||||
|
||||
getSettings()->emoteScale.setValue(f);
|
||||
});
|
||||
|
||||
emoteScale->setValue(std::max<int>(
|
||||
5, std::min<int>(
|
||||
50, int(getSettings()->emoteScale.getValue() * 10.f))));
|
||||
|
||||
scaleLabel->setText(
|
||||
QString::number(getSettings()->emoteScale.getValue()));
|
||||
}
|
||||
|
||||
{
|
||||
auto *combo = new QComboBox(this);
|
||||
combo->addItems({"EmojiOne 2", "EmojiOne 3", "Twitter", "Facebook",
|
||||
"Apple", "Google", "Messenger"});
|
||||
|
||||
combo->setCurrentText(getSettings()->emojiSet);
|
||||
|
||||
QObject::connect(combo, &QComboBox::currentTextChanged,
|
||||
[](const QString &str) {
|
||||
getSettings()->emojiSet = str; //
|
||||
});
|
||||
|
||||
auto hbox = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
hbox.emplace<QLabel>("Emoji set:");
|
||||
hbox.append(combo);
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addSplitHeaderTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
layout.append(
|
||||
this->createCheckBox("Show uptime", getSettings()->headerUptime));
|
||||
layout.append(this->createCheckBox("Show viewer count",
|
||||
getSettings()->headerViewerCount));
|
||||
layout.append(this->createCheckBox("Show game", getSettings()->headerGame));
|
||||
layout.append(
|
||||
this->createCheckBox("Show title", getSettings()->headerStreamTitle));
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addBadgesTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
// layout.append(
|
||||
// this->createCheckBox(("Show all badges"), getSettings()->showBadges));
|
||||
auto fastSelection = layout.emplace<QHBoxLayout>();
|
||||
{
|
||||
auto addAll = fastSelection.emplace<QPushButton>("Enable all");
|
||||
QObject::connect(addAll.getElement(), &QPushButton::clicked, this, [] {
|
||||
getSettings()->showBadgesGlobalAuthority = true;
|
||||
getSettings()->showBadgesChannelAuthority = true;
|
||||
getSettings()->showBadgesSubscription = true;
|
||||
getSettings()->showBadgesVanity = true;
|
||||
getSettings()->showBadgesChatterino = true;
|
||||
});
|
||||
auto removeAll = fastSelection.emplace<QPushButton>("Disable all");
|
||||
QObject::connect(removeAll.getElement(), &QPushButton::clicked, this,
|
||||
[] {
|
||||
getSettings()->showBadgesGlobalAuthority = false;
|
||||
getSettings()->showBadgesChannelAuthority = false;
|
||||
getSettings()->showBadgesSubscription = false;
|
||||
getSettings()->showBadgesVanity = false;
|
||||
getSettings()->showBadgesChatterino = false;
|
||||
});
|
||||
}
|
||||
layout.emplace<Line>(false);
|
||||
layout.append(this->createCheckBox(
|
||||
("Show authority badges (staff, admin, turbo, etc)"),
|
||||
getSettings()->showBadgesGlobalAuthority));
|
||||
layout.append(this->createCheckBox(
|
||||
("Show channel badges (broadcaster, moderator, VIP)"),
|
||||
getSettings()->showBadgesChannelAuthority));
|
||||
layout.append(this->createCheckBox(("Show subscriber badges "),
|
||||
getSettings()->showBadgesSubscription));
|
||||
layout.append(
|
||||
this->createCheckBox(("Show vanity badges (prime, bits, subgifter)"),
|
||||
getSettings()->showBadgesVanity));
|
||||
layout.append(this->createCheckBox(("Show chatterino badges"),
|
||||
getSettings()->showBadgesChatterino));
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addLastReadMessageIndicatorPatternSelector(
|
||||
LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
// combo
|
||||
auto *combo = new QComboBox(this);
|
||||
combo->addItems({"Dotted line", "Solid line"});
|
||||
|
||||
const auto currentIndex = []() -> int {
|
||||
switch (getSettings()->lastMessagePattern.getValue())
|
||||
{
|
||||
case Qt::SolidLine:
|
||||
return 1;
|
||||
case Qt::VerPattern:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}();
|
||||
combo->setCurrentIndex(currentIndex);
|
||||
|
||||
QObject::connect(
|
||||
combo,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
[](int index) {
|
||||
getSettings()->lastMessagePattern = [&] {
|
||||
switch (index)
|
||||
{
|
||||
case 1:
|
||||
return Qt::SolidPattern;
|
||||
case 0:
|
||||
default:
|
||||
return Qt::VerPattern;
|
||||
}
|
||||
}();
|
||||
});
|
||||
|
||||
// color picker
|
||||
|
||||
QLabel *colorPreview = new QLabel();
|
||||
|
||||
auto updatePreviewColor = [colorPreview](QColor newColor) {
|
||||
QPixmap pixmap(16, 16);
|
||||
pixmap.fill(QColor(0, 0, 0, 255));
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
QBrush brush(newColor);
|
||||
painter.fillRect(1, 1, pixmap.width() - 2, pixmap.height() - 2, brush);
|
||||
colorPreview->setPixmap(pixmap);
|
||||
};
|
||||
|
||||
auto getCurrentColor = []() {
|
||||
return getSettings()->lastMessageColor != ""
|
||||
? QColor(getSettings()->lastMessageColor.getValue())
|
||||
: getApp()
|
||||
->themes->tabs.selected.backgrounds.regular.color();
|
||||
};
|
||||
|
||||
updatePreviewColor(getCurrentColor());
|
||||
|
||||
QPushButton *button = new QPushButton("Select Color");
|
||||
button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
|
||||
|
||||
QObject::connect(
|
||||
button, &QPushButton::clicked, [updatePreviewColor, getCurrentColor]() {
|
||||
QColor newColor = QColorDialog::getColor(getCurrentColor());
|
||||
if (newColor.isValid())
|
||||
{
|
||||
updatePreviewColor(newColor);
|
||||
getSettings()->lastMessageColor = newColor.name();
|
||||
}
|
||||
});
|
||||
|
||||
QPushButton *resetButton = new QPushButton("Reset Color");
|
||||
resetButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
|
||||
|
||||
QObject::connect(
|
||||
resetButton, &QPushButton::clicked, [updatePreviewColor]() {
|
||||
QColor defaultColor =
|
||||
getApp()->themes->tabs.selected.backgrounds.regular.color();
|
||||
updatePreviewColor(defaultColor);
|
||||
getSettings()->lastMessageColor = "";
|
||||
});
|
||||
|
||||
// layout
|
||||
auto hbox = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||
hbox.append(this->createCheckBox(LAST_MSG,
|
||||
getSettings()->showLastMessageIndicator));
|
||||
hbox.append(combo);
|
||||
hbox.append(colorPreview);
|
||||
hbox.append(button);
|
||||
hbox.append(resetButton);
|
||||
hbox->addStretch(1);
|
||||
}
|
||||
|
||||
ChannelPtr LookPage::createPreviewChannel()
|
||||
{
|
||||
auto channel = ChannelPtr(new Channel("preview", Channel::Type::Misc));
|
||||
|
||||
// clang-format off
|
||||
{
|
||||
MessageBuilder builder;
|
||||
builder.emplace<TimestampElement>(QTime(8, 13, 42));
|
||||
builder.emplace<ImageElement>(Image::fromPixmap(getApp()->resources->twitch.moderator), MessageElementFlag::BadgeChannelAuthority);
|
||||
builder.emplace<ImageElement>(Image::fromPixmap(getApp()->resources->twitch.subscriber, 0.25), MessageElementFlag::BadgeSubscription);
|
||||
builder.emplace<TextElement>("username1:", MessageElementFlag::Username, QColor("#0094FF"), FontStyle::ChatMediumBold);
|
||||
builder.emplace<TextElement>("This is a preview message", MessageElementFlag::Text);
|
||||
builder.emplace<ImageElement>(Image::fromPixmap(getApp()->resources->pajaDank, 0.25), MessageElementFlag::AlwaysShow);
|
||||
builder.emplace<TextElement>("@fourtf", MessageElementFlag::BoldUsername, MessageColor::Text, FontStyle::ChatMediumBold);
|
||||
builder.emplace<TextElement>("@fourtf", MessageElementFlag::NonBoldUsername);
|
||||
channel->addMessage(builder.release());
|
||||
}
|
||||
{
|
||||
MessageBuilder message;
|
||||
message.emplace<TimestampElement>(QTime(8, 15, 21));
|
||||
message.emplace<ImageElement>(Image::fromPixmap(getApp()->resources->twitch.broadcaster), MessageElementFlag::BadgeChannelAuthority);
|
||||
message.emplace<TextElement>("username2:", MessageElementFlag::Username, QColor("#FF6A00"), FontStyle::ChatMediumBold);
|
||||
message.emplace<TextElement>("This is another one", MessageElementFlag::Text);
|
||||
// message.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->ppHop), MessageElementFlag::BttvEmote);
|
||||
message.emplace<TextElement>("www.fourtf.com", MessageElementFlag::LowercaseLink, MessageColor::Link)->setLink(Link(Link::Url, "https://www.fourtf.com"));
|
||||
message.emplace<TextElement>("wWw.FoUrTf.CoM", MessageElementFlag::OriginalLink, MessageColor::Link)->setLink(Link(Link::Url, "https://www.fourtf.com"));
|
||||
channel->addMessage(message.release());
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
QLayout *LookPage::createThemeColorChanger()
|
||||
{
|
||||
auto app = getApp();
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
|
||||
auto &themeHue = app->themes->themeHue;
|
||||
|
||||
// SLIDER
|
||||
QSlider *slider = new QSlider(Qt::Horizontal);
|
||||
layout->addWidget(slider);
|
||||
slider->setValue(
|
||||
int(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 100));
|
||||
|
||||
// BUTTON
|
||||
QPushButton *button = new QPushButton;
|
||||
layout->addWidget(button);
|
||||
button->setFlat(true);
|
||||
button->setFixedWidth(64);
|
||||
|
||||
auto setButtonColor = [button, app](int value) mutable {
|
||||
double newValue = value / 100.0;
|
||||
app->themes->themeHue.setValue(newValue);
|
||||
|
||||
QPalette pal = button->palette();
|
||||
QColor color;
|
||||
color.setHsvF(newValue, 1.0, 1.0, 1.0);
|
||||
pal.setColor(QPalette::Button, color);
|
||||
button->setAutoFillBackground(true);
|
||||
button->setPalette(pal);
|
||||
button->update();
|
||||
};
|
||||
|
||||
// SIGNALS
|
||||
QObject::connect(slider, &QSlider::valueChanged, this, setButtonColor);
|
||||
|
||||
setButtonColor(themeHue * 100);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
QLayout *LookPage::createFontChanger()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->setMargin(0);
|
||||
|
||||
// LABEL
|
||||
QLabel *label = new QLabel();
|
||||
layout->addWidget(label);
|
||||
|
||||
auto updateFontFamilyLabel = [=]() {
|
||||
label->setText("Font (" + app->fonts->chatFontFamily.getValue() + ", " +
|
||||
QString::number(app->fonts->chatFontSize) + "pt)");
|
||||
};
|
||||
|
||||
app->fonts->chatFontFamily.connect(updateFontFamilyLabel,
|
||||
this->managedConnections_);
|
||||
app->fonts->chatFontSize.connect(updateFontFamilyLabel,
|
||||
this->managedConnections_);
|
||||
|
||||
// BUTTON
|
||||
QPushButton *button = new QPushButton("Select");
|
||||
layout->addWidget(button);
|
||||
button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
|
||||
|
||||
QObject::connect(button, &QPushButton::clicked, [=]() {
|
||||
QFontDialog dialog(app->fonts->getFont(FontStyle::ChatMedium, 1.));
|
||||
|
||||
dialog.setWindowFlag(Qt::WindowStaysOnTopHint);
|
||||
|
||||
dialog.connect(&dialog, &QFontDialog::fontSelected,
|
||||
[=](const QFont &font) {
|
||||
app->fonts->chatFontFamily = font.family();
|
||||
app->fonts->chatFontSize = font.pointSize();
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
dialog.exec();
|
||||
});
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
QLayout *LookPage::createBoldScaleSlider()
|
||||
{
|
||||
auto layout = new QHBoxLayout();
|
||||
auto slider = new QSlider(Qt::Horizontal);
|
||||
auto label = new QLabel();
|
||||
|
||||
layout->addWidget(slider);
|
||||
layout->addWidget(label);
|
||||
|
||||
slider->setMinimum(50);
|
||||
slider->setMaximum(100);
|
||||
slider->setValue(getSettings()->boldScale.getValue());
|
||||
|
||||
label->setMinimumWidth(100);
|
||||
|
||||
QObject::connect(slider, &QSlider::valueChanged, [](auto value) {
|
||||
getSettings()->boldScale.setValue(value);
|
||||
});
|
||||
// show value
|
||||
// getSettings()->boldScale.connect(
|
||||
// [label](auto, auto) {
|
||||
// label->setText(QString::number(getSettings()->boldScale.getValue()));
|
||||
// },
|
||||
// this->connections_);
|
||||
|
||||
// QPushButton *button = new QPushButton("Reset");
|
||||
// layout->addWidget(button);
|
||||
// button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
|
||||
|
||||
// QObject::connect(button, &QPushButton::clicked, [=]() {
|
||||
// getSettings()->boldScale.setValue(57);
|
||||
// slider->setValue(57);
|
||||
//});
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
#include <QScrollArea>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class LookPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
LookPage();
|
||||
|
||||
private:
|
||||
void initializeUi();
|
||||
|
||||
void addInterfaceTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addMessageTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addEmoteTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addSplitHeaderTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addBadgesTab(LayoutCreator<QVBoxLayout> layout);
|
||||
|
||||
void addLastReadMessageIndicatorPatternSelector(
|
||||
LayoutCreator<QVBoxLayout> layout);
|
||||
|
||||
QLayout *createThemeColorChanger();
|
||||
QLayout *createFontChanger();
|
||||
QLayout *createBoldScaleSlider();
|
||||
|
||||
ChannelPtr createPreviewChannel();
|
||||
|
||||
std::vector<pajlada::Signals::ScopedConnection> connections_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -83,13 +83,10 @@ ModerationPage::ModerationPage()
|
|||
|
||||
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
|
||||
{
|
||||
logs.append(this->createCheckBox("Enable logging",
|
||||
getSettings()->enableLogging));
|
||||
auto logsPathLabel = logs.emplace<QLabel>();
|
||||
|
||||
// Show how big (size-wise) the logs are
|
||||
auto logsPathSizeLabel = logs.emplace<QLabel>();
|
||||
logsPathSizeLabel->setText(
|
||||
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
|
||||
|
||||
// Logs (copied from LoggingMananger)
|
||||
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
|
||||
auto) mutable {
|
||||
|
@ -109,13 +106,27 @@ ModerationPage::ModerationPage()
|
|||
logsPathLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
|
||||
Qt::LinksAccessibleByKeyboard);
|
||||
logsPathLabel->setOpenExternalLinks(true);
|
||||
logs.append(this->createCheckBox("Enable logging",
|
||||
getSettings()->enableLogging));
|
||||
|
||||
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);
|
||||
auto selectDir = logs.emplace<QPushButton>("Set custom logpath");
|
||||
|
||||
// Setting custom logpath
|
||||
// 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 {
|
||||
|
@ -128,8 +139,9 @@ ModerationPage::ModerationPage()
|
|||
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
|
||||
});
|
||||
|
||||
buttons->addSpacing(16);
|
||||
|
||||
// Reset custom logpath
|
||||
auto resetDir = logs.emplace<QPushButton>("Reset logpath");
|
||||
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
|
||||
[logsPathSizeLabel]() mutable {
|
||||
getSettings()->logPath = "";
|
||||
|
@ -139,8 +151,7 @@ ModerationPage::ModerationPage()
|
|||
[] { return fetchLogDirectorySize(); }));
|
||||
});
|
||||
|
||||
// Logs end
|
||||
}
|
||||
} // logs end
|
||||
|
||||
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
|
||||
{
|
||||
|
|
|
@ -5,15 +5,77 @@
|
|||
|
||||
#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_;
|
||||
|
@ -42,7 +104,7 @@ void SettingsPage::cancel()
|
|||
QCheckBox *SettingsPage::createCheckBox(
|
||||
const QString &text, pajlada::Settings::Setting<bool> &setting)
|
||||
{
|
||||
QCheckBox *checkbox = new QCheckBox(text);
|
||||
QCheckBox *checkbox = new SCheckBox(text);
|
||||
|
||||
// update when setting changes
|
||||
setting.connect(
|
||||
|
@ -64,7 +126,7 @@ QCheckBox *SettingsPage::createCheckBox(
|
|||
QComboBox *SettingsPage::createComboBox(
|
||||
const QStringList &items, pajlada::Settings::Setting<QString> &setting)
|
||||
{
|
||||
QComboBox *combo = new QComboBox();
|
||||
QComboBox *combo = new SComboBox();
|
||||
|
||||
// update setting on toogle
|
||||
combo->addItems(items);
|
||||
|
|
|
@ -8,8 +8,38 @@
|
|||
|
||||
#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
|
||||
|
@ -22,6 +52,8 @@ public:
|
|||
const QString &getName();
|
||||
const QString &getIconResource();
|
||||
|
||||
virtual bool filterElements(const QString &query);
|
||||
|
||||
SettingsDialogTab *tab() const;
|
||||
void setTab(SettingsDialogTab *tab);
|
||||
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#include "SpecialChannelsPage.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "util/LayoutCreator.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
SpecialChannelsPage::SpecialChannelsPage()
|
||||
: SettingsPage("Special channels", "")
|
||||
{
|
||||
LayoutCreator<SpecialChannelsPage> layoutCreator(this);
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto mentions = layout.emplace<QGroupBox>("Mentions channel")
|
||||
.setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
mentions.emplace<QLabel>("Join /mentions to view your mentions.");
|
||||
}
|
||||
|
||||
auto whispers =
|
||||
layout.emplace<QGroupBox>("Whispers").setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
whispers.emplace<QLabel>("Join /whispers to view your mentions.");
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class SpecialChannelsPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
SpecialChannelsPage();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -89,6 +89,7 @@ Split::Split(QWidget *parent)
|
|||
{
|
||||
this->setMouseTracking(true);
|
||||
this->view_->setPausable(true);
|
||||
this->view_->setFocusPolicy(Qt::FocusPolicy::NoFocus);
|
||||
|
||||
this->vbox_->setSpacing(0);
|
||||
this->vbox_->setMargin(1);
|
||||
|
@ -663,7 +664,10 @@ void Split::reloadChannelAndSubscriberEmotes()
|
|||
auto channel = this->getChannel();
|
||||
|
||||
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
||||
twitchChannel->refreshChannelEmotes();
|
||||
{
|
||||
twitchChannel->refreshBTTVChannelEmotes();
|
||||
twitchChannel->refreshFFZChannelEmotes();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iter, typename RandomGenerator>
|
||||
|
|
|
@ -189,7 +189,6 @@ void SplitHeader::initializeLayout()
|
|||
SettingsDialogPreference::
|
||||
ModerationActions);
|
||||
this->split_->setModerationMode(true);
|
||||
w->setDim(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -198,7 +197,7 @@ void SplitHeader::initializeLayout()
|
|||
|
||||
this->split_->setModerationMode(
|
||||
!moderationMode);
|
||||
w->setDim(moderationMode);
|
||||
w->setDim(Button::Dim(moderationMode));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -713,7 +712,10 @@ void SplitHeader::reloadChannelEmotes()
|
|||
auto channel = this->split_->getChannel();
|
||||
|
||||
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
||||
twitchChannel->refreshChannelEmotes();
|
||||
{
|
||||
twitchChannel->refreshFFZChannelEmotes();
|
||||
twitchChannel->refreshBTTVChannelEmotes();
|
||||
}
|
||||
}
|
||||
|
||||
void SplitHeader::reloadSubscriberEmotes()
|
||||
|
|
Loading…
Reference in a new issue