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