This commit is contained in:
fourtf
2018-01-28 03:39:47 +01:00
76 changed files with 1645 additions and 561 deletions
+109 -13
View File
@@ -13,6 +13,7 @@
#include "widgets/split.hpp"
#include "widgets/tooltipwidget.hpp"
#include <QClipboard>
#include <QDebug>
#include <QDesktopServices>
#include <QGraphicsBlurEffect>
@@ -24,8 +25,7 @@
#include <functional>
#include <memory>
#define LAYOUT_WIDTH \
(this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getDpiMultiplier())
#define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale())
using namespace chatterino::messages;
@@ -96,6 +96,9 @@ ChannelView::ChannelView(BaseWidget *parent)
auto e = new QResizeEvent(this->size(), this->size());
this->resizeEvent(e);
delete e;
singletons::SettingManager::getInstance().showLastMessageIndicator.connect(
[this](auto, auto) { this->update(); }, this->managedConnections);
}
ChannelView::~ChannelView()
@@ -111,6 +114,13 @@ ChannelView::~ChannelView()
this->messageReplacedConnection.disconnect();
}
void ChannelView::themeRefreshEvent()
{
BaseWidget::themeRefreshEvent();
this->layoutMessages();
}
void ChannelView::queueUpdate()
{
if (this->updateTimer.isActive()) {
@@ -164,7 +174,7 @@ void ChannelView::actuallyLayoutMessages()
for (size_t i = start; i < messagesSnapshot.getLength(); ++i) {
auto message = messagesSnapshot[i];
redrawRequired |= message->layout(layoutWidth, this->getDpiMultiplier(), flags);
redrawRequired |= message->layout(layoutWidth, this->getScale(), flags);
y += message->getHeight();
@@ -180,7 +190,7 @@ void ChannelView::actuallyLayoutMessages()
for (int i = (int)messagesSnapshot.getLength() - 1; i >= 0; i--) {
auto *message = messagesSnapshot[i].get();
message->layout(layoutWidth, this->getDpiMultiplier(), flags);
message->layout(layoutWidth, this->getScale(), flags);
h -= message->getHeight();
@@ -281,6 +291,16 @@ bool ChannelView::getEnableScrollingToBottom() const
return this->enableScrollingToBottom;
}
void ChannelView::setOverrideFlags(boost::optional<messages::MessageElement::Flags> value)
{
this->overrideFlags = value;
}
const boost::optional<messages::MessageElement::Flags> &ChannelView::getOverrideFlags() const
{
return this->overrideFlags;
}
messages::LimitedQueueSnapshot<MessageLayoutPtr> ChannelView::getMessagesSnapshot()
{
if (!this->paused) {
@@ -290,7 +310,7 @@ messages::LimitedQueueSnapshot<MessageLayoutPtr> ChannelView::getMessagesSnapsho
return this->snapshot;
}
void ChannelView::setChannel(SharedChannel newChannel)
void ChannelView::setChannel(ChannelPtr newChannel)
{
if (this->channel) {
this->detachChannel();
@@ -328,7 +348,6 @@ void ChannelView::setChannel(SharedChannel newChannel)
newChannel->messagesAddedAtStart.connect([this](std::vector<MessagePtr> &messages) {
std::vector<MessageLayoutPtr> messageRefs;
messageRefs.resize(messages.size());
qDebug() << messages.size();
for (size_t i = 0; i < messages.size(); i++) {
messageRefs.at(i) = MessageLayoutPtr(new MessageLayout(messages.at(i)));
}
@@ -410,6 +429,17 @@ void ChannelView::pause(int msecTimeout)
this->pauseTimeout.start(msecTimeout);
}
void ChannelView::updateLastReadMessage()
{
auto _snapshot = this->getMessagesSnapshot();
if (_snapshot.getLength() > 0) {
this->lastReadMessage = _snapshot[_snapshot.getLength() - 1];
}
this->update();
}
void ChannelView::resizeEvent(QResizeEvent *)
{
this->scrollBar.resize(this->scrollBar.width(), height());
@@ -434,6 +464,10 @@ void ChannelView::setSelection(const SelectionItem &start, const SelectionItem &
messages::MessageElement::Flags ChannelView::getFlags() const
{
if (this->overrideFlags) {
return this->overrideFlags.get();
}
MessageElement::Flags flags = singletons::SettingManager::getInstance().getWordFlags();
Split *split = dynamic_cast<Split *>(this->parentWidget());
@@ -442,6 +476,9 @@ messages::MessageElement::Flags ChannelView::getFlags() const
if (split->getModerationMode()) {
flags = (MessageElement::Flags)(flags | MessageElement::ModeratorTools);
}
if (this->channel == singletons::ChannelManager::getInstance().mentionsChannel) {
flags = (MessageElement::Flags)(flags | MessageElement::ChannelName);
}
}
return flags;
@@ -477,11 +514,17 @@ void ChannelView::drawMessages(QPainter &painter)
(fmod(this->scrollBar.getCurrentValue(), 1)));
messages::MessageLayout *end = nullptr;
bool windowFocused = this->window() == QApplication::activeWindow();
for (size_t i = start; i < messagesSnapshot.getLength(); ++i) {
messages::MessageLayout *layout = messagesSnapshot[i].get();
layout->paint(painter, y, i, this->selection);
bool isLastMessage = false;
if (singletons::SettingManager::getInstance().showLastMessageIndicator) {
isLastMessage = this->lastReadMessage.get() == layout;
}
layout->paint(painter, y, i, this->selection, isLastMessage, windowFocused);
y += layout->getHeight();
@@ -551,8 +594,7 @@ void ChannelView::wheelEvent(QWheelEvent *event)
if (i == 0) {
desired = 0;
} else {
snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(),
this->getFlags());
snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getScale(), this->getFlags());
scrollFactor = 1;
currentScrollLeft = snapshot[i - 1]->getHeight();
}
@@ -574,8 +616,7 @@ void ChannelView::wheelEvent(QWheelEvent *event)
if (i == snapshotLength - 1) {
desired = snapshot.getLength();
} else {
snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(),
this->getFlags());
snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getScale(), this->getFlags());
scrollFactor = 1;
currentScrollLeft = snapshot[i + 1]->getHeight();
@@ -782,12 +823,50 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
}
auto &link = hoverLayoutElement->getLink();
if (event->button() != Qt::LeftButton ||
!singletons::SettingManager::getInstance().linksDoubleClickOnly) {
this->handleLinkClick(event, link, layout.get());
}
this->linkClicked.invoke(link);
}
void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
{
if (singletons::SettingManager::getInstance().linksDoubleClickOnly) {
std::shared_ptr<messages::MessageLayout> layout;
QPoint relativePos;
int messageIndex;
if (!tryGetMessageAt(event->pos(), layout, relativePos, messageIndex)) {
return;
}
// message under cursor is collapsed
if (layout->getFlags() & MessageLayout::Collapsed) {
return;
}
const messages::MessageLayoutElement *hoverLayoutElement =
layout->getElementAt(relativePos);
if (hoverLayoutElement == nullptr) {
return;
}
auto &link = hoverLayoutElement->getLink();
this->handleLinkClick(event, link, layout.get());
}
}
void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link,
messages::MessageLayout *layout)
{
switch (link.getType()) {
case messages::Link::UserInfo: {
auto user = link.getValue();
this->userPopupWidget.setName(user);
this->userPopupWidget.move(event->screenPos().toPoint());
this->userPopupWidget.moveTo(this, event->screenPos().toPoint());
this->userPopupWidget.show();
this->userPopupWidget.setFocus();
@@ -795,7 +874,24 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
break;
}
case messages::Link::Url: {
QDesktopServices::openUrl(QUrl(link.getValue()));
if (event->button() == Qt::RightButton) {
static QMenu *menu = nullptr;
static QString url;
if (menu == nullptr) {
menu = new QMenu;
menu->addAction("Open in browser",
[] { QDesktopServices::openUrl(QUrl(url)); });
menu->addAction("Copy to clipboard",
[] { QApplication::clipboard()->setText(url); });
}
url = link.getValue();
menu->move(QCursor::pos());
menu->show();
} else {
QDesktopServices::openUrl(QUrl(link.getValue()));
}
break;
}
case messages::Link::UserAction: {
+14 -2
View File
@@ -38,9 +38,12 @@ public:
void clearSelection();
void setEnableScrollingToBottom(bool);
bool getEnableScrollingToBottom() const;
void setOverrideFlags(boost::optional<messages::MessageElement::Flags> value);
const boost::optional<messages::MessageElement::Flags> &getOverrideFlags() const;
void pause(int msecTimeout);
void updateLastReadMessage();
void setChannel(SharedChannel channel);
void setChannel(ChannelPtr channel);
messages::LimitedQueueSnapshot<messages::MessageLayoutPtr> getMessagesSnapshot();
void layoutMessages();
@@ -49,8 +52,11 @@ public:
boost::signals2::signal<void(QMouseEvent *)> mouseDown;
boost::signals2::signal<void()> selectionChanged;
pajlada::Signals::NoArgSignal highlightedMessageReceived;
pajlada::Signals::Signal<const messages::Link &> linkClicked;
protected:
virtual void themeRefreshEvent() override;
virtual void resizeEvent(QResizeEvent *) override;
virtual void paintEvent(QPaintEvent *) override;
@@ -62,6 +68,10 @@ protected:
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
void handleLinkClick(QMouseEvent *event, const messages::Link &link,
messages::MessageLayout *layout);
bool tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageLayout> &message,
QPoint &relativePos, int &index);
@@ -72,6 +82,8 @@ private:
bool messageWasAdded = false;
bool paused = false;
QTimer pauseTimeout;
boost::optional<messages::MessageElement::Flags> overrideFlags;
messages::MessageLayoutPtr lastReadMessage;
messages::LimitedQueueSnapshot<messages::MessageLayoutPtr> snapshot;
@@ -82,7 +94,7 @@ private:
void setSelection(const messages::SelectionItem &start, const messages::SelectionItem &end);
messages::MessageElement::Flags getFlags() const;
SharedChannel channel;
ChannelPtr channel;
Scrollbar scrollBar;
RippleEffectLabel *goToBottom;
+78
View File
@@ -0,0 +1,78 @@
#include "label.hpp"
#include "singletons/fontmanager.hpp"
#include <QPainter>
namespace chatterino {
namespace widgets {
Label::Label(BaseWidget *parent)
: BaseWidget(parent)
{
singletons::FontManager::getInstance().fontChanged.connect(
[this]() { this->scaleChangedEvent(this->getScale()); });
}
const QString &Label::getText() const
{
return this->text;
}
void Label::setText(const QString &value)
{
this->text = value;
this->scaleChangedEvent(this->getScale());
}
FontStyle Label::getFontStyle() const
{
return this->fontStyle;
}
void Label::setFontStyle(FontStyle style)
{
this->fontStyle = style;
this->scaleChangedEvent(this->getScale());
}
void Label::scaleChangedEvent(float scale)
{
QFontMetrics metrics =
singletons::FontManager::getInstance().getFontMetrics(this->fontStyle, scale);
this->preferedSize = QSize(metrics.width(this->text), metrics.height());
this->updateGeometry();
}
QSize Label::sizeHint() const
{
return this->preferedSize;
}
QSize Label::minimumSizeHint() const
{
return this->preferedSize;
}
void Label::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setFont(singletons::FontManager::getInstance().getFont(
this->fontStyle, this->getScale() / painter.device()->devicePixelRatioF()));
int width = singletons::FontManager::getInstance()
.getFontMetrics(this->fontStyle, this->getScale())
.width(this->text);
int flags = Qt::TextSingleLine;
if (this->width() < width) {
flags |= Qt::AlignLeft | Qt::AlignVCenter;
} else {
flags |= Qt::AlignCenter;
}
painter.drawText(this->rect(), flags, this->text);
}
} // namespace widgets
} // namespace chatterino
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "singletons/fontmanager.hpp"
#include "widgets/basewidget.hpp"
namespace chatterino {
namespace widgets {
class Label : public BaseWidget
{
public:
Label(BaseWidget *parent);
const QString &getText() const;
void setText(const QString &text);
FontStyle getFontStyle() const;
void setFontStyle(FontStyle style);
protected:
virtual void scaleChangedEvent(float scale) override;
virtual void paintEvent(QPaintEvent *event) override;
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
private:
QSize preferedSize;
QString text;
FontStyle fontStyle = FontStyle::Medium;
};
} // namespace widgets
} // namespace chatterino
+47
View File
@@ -1,12 +1,16 @@
#include "widgets/helper/notebookbutton.hpp"
#include "singletons/thememanager.hpp"
#include "widgets/helper/rippleeffectbutton.hpp"
#include "widgets/notebook.hpp"
#include "widgets/splitcontainer.hpp"
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QRadialGradient>
#define nuuls nullptr
namespace chatterino {
namespace widgets {
@@ -14,6 +18,8 @@ NotebookButton::NotebookButton(BaseWidget *parent)
: RippleEffectButton(parent)
{
setMouseEffectColor(QColor(0, 0, 0));
this->setAcceptDrops(true);
}
void NotebookButton::paintEvent(QPaintEvent *)
@@ -97,5 +103,46 @@ void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
RippleEffectButton::mouseReleaseEvent(event);
}
void NotebookButton::dragEnterEvent(QDragEnterEvent *event)
{
if (!event->mimeData()->hasFormat("chatterino/split"))
return;
event->acceptProposedAction();
auto e = new QMouseEvent(QMouseEvent::MouseButtonPress,
QPointF(this->width() / 2, this->height() / 2), Qt::LeftButton,
Qt::LeftButton, 0);
RippleEffectButton::mousePressEvent(e);
delete e;
}
void NotebookButton::dragLeaveEvent(QDragLeaveEvent *)
{
this->mouseDown = true;
this->update();
auto e = new QMouseEvent(QMouseEvent::MouseButtonRelease,
QPointF(this->width() / 2, this->height() / 2), Qt::LeftButton,
Qt::LeftButton, 0);
RippleEffectButton::mouseReleaseEvent(e);
delete e;
}
void NotebookButton::dropEvent(QDropEvent *event)
{
if (SplitContainer::isDraggingSplit) {
event->acceptProposedAction();
Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget());
if (notebook != nuuls) {
SplitContainer *tab = notebook->addNewPage();
SplitContainer::draggingSplit->setParent(tab);
tab->addToLayout(SplitContainer::draggingSplit);
}
}
}
} // namespace widgets
} // namespace chatterino
+5 -2
View File
@@ -21,8 +21,11 @@ public:
NotebookButton(BaseWidget *parent);
protected:
void paintEvent(QPaintEvent *) override;
void mouseReleaseEvent(QMouseEvent *event) override;
virtual void paintEvent(QPaintEvent *) override;
virtual void mouseReleaseEvent(QMouseEvent *) override;
virtual void dragEnterEvent(QDragEnterEvent *) override;
virtual void dragLeaveEvent(QDragLeaveEvent *) override;
virtual void dropEvent(QDropEvent *) override;
signals:
void clicked();
+17 -15
View File
@@ -25,7 +25,6 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
, useDefaultBehaviour(fS("{}/useDefaultBehaviour", this->settingRoot), true)
, menu(this)
{
this->calcSize();
this->setAcceptDrops(true);
this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
@@ -65,22 +64,25 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
this->menu.addAction(enableHighlightsOnNewMessageAction);
connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) {
QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) {
debug::Log("New value is {}", newValue); //
});
}
void NotebookTab::calcSize()
void NotebookTab::themeRefreshEvent()
{
float scale = getDpiMultiplier();
this->update();
}
void NotebookTab::updateSize()
{
float scale = getScale();
QString qTitle(qS(this->title));
if (singletons::SettingManager::getInstance().hideTabX) {
this->resize(static_cast<int>((fontMetrics().width(qTitle) + 16) * scale),
static_cast<int>(24 * scale));
this->resize((int)((fontMetrics().width(qTitle) + 16) * scale), (int)(24 * scale));
} else {
this->resize(static_cast<int>((fontMetrics().width(qTitle) + 8 + 24) * scale),
static_cast<int>(24 * scale));
this->resize((int)((fontMetrics().width(qTitle) + 8 + 24) * scale), (int)(24 * scale));
}
if (this->parent() != nullptr) {
@@ -97,7 +99,7 @@ void NotebookTab::setTitle(const QString &newTitle)
{
this->title = newTitle.toStdString();
this->calcSize();
this->updateSize();
}
bool NotebookTab::isSelected() const
@@ -134,7 +136,7 @@ QRect NotebookTab::getDesiredRect() const
void NotebookTab::hideTabXChanged(bool)
{
this->calcSize();
this->updateSize();
this->update();
}
@@ -197,7 +199,7 @@ void NotebookTab::paintEvent(QPaintEvent *)
painter.setPen(colors.text);
// set area for text
float scale = this->getDpiMultiplier();
float scale = this->getScale();
int rectW = (settingManager.hideTabX ? 0 : static_cast<int>(16) * scale);
QRect rect(0, 0, this->width() - rectW, this->height());
@@ -291,14 +293,14 @@ void NotebookTab::mouseMoveEvent(QMouseEvent *event)
}
}
if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) {
QPoint relPoint = this->mapToParent(event->pos());
QPoint relPoint = this->mapToParent(event->pos());
if (this->mouseDown && !this->getDesiredRect().contains(relPoint)) {
int index;
SplitContainer *clickedPage = notebook->tabAt(relPoint, index);
SplitContainer *clickedPage = notebook->tabAt(relPoint, index, this->width());
if (clickedPage != nullptr && clickedPage != this->page) {
this->notebook->rearrangePage(clickedPage, index);
this->notebook->rearrangePage(this->page, index);
}
}
}
+11 -9
View File
@@ -25,7 +25,7 @@ class NotebookTab : public BaseWidget
public:
explicit NotebookTab(Notebook *_notebook, const std::string &_uuid);
void calcSize();
void updateSize();
SplitContainer *page;
@@ -42,16 +42,18 @@ public:
void hideTabXChanged(bool);
protected:
void paintEvent(QPaintEvent *) override;
virtual void themeRefreshEvent() override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void enterEvent(QEvent *) override;
void leaveEvent(QEvent *) override;
virtual void paintEvent(QPaintEvent *) override;
void dragEnterEvent(QDragEnterEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override;
void mouseMoveEvent(QMouseEvent *event) override;
virtual void dragEnterEvent(QDragEnterEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
private:
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
@@ -80,7 +82,7 @@ private:
QRect getXRect()
{
float scale = this->getDpiMultiplier();
float scale = this->getScale();
return QRect(this->width() - static_cast<int>(20 * scale), static_cast<int>(4 * scale),
static_cast<int>(16 * scale), static_cast<int>(16 * scale));
}
+3 -1
View File
@@ -44,7 +44,7 @@ void RippleEffectButton::paintEvent(QPaintEvent *)
if (this->pixmap != nullptr) {
QRect rect = this->rect();
int xD = 6 * this->getDpiMultiplier();
int xD = 6 * this->getScale();
rect.moveLeft(xD);
rect.setRight(rect.right() - xD - xD);
@@ -57,6 +57,8 @@ void RippleEffectButton::paintEvent(QPaintEvent *)
void RippleEffectButton::fancyPaint(QPainter &painter)
{
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setRenderHint(QPainter::Antialiasing);
QColor c;
if (this->mouseEffectColor) {
+2 -2
View File
@@ -58,7 +58,7 @@ void SearchPopup::initLayout()
}
}
void SearchPopup::setChannel(SharedChannel channel)
void SearchPopup::setChannel(ChannelPtr channel)
{
this->snapshot = channel->getMessageSnapshot();
this->performSearch();
@@ -70,7 +70,7 @@ void SearchPopup::performSearch()
{
QString text = searchInput->text();
SharedChannel channel(new Channel("search"));
ChannelPtr channel(new Channel("search"));
for (size_t i = 0; i < this->snapshot.getLength(); i++) {
messages::MessagePtr message = this->snapshot[i];
+4 -3
View File
@@ -10,7 +10,8 @@ namespace widgets {
SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, settingspages::SettingsPage *_page,
QString imageFileName)
: dialog(_dialog)
: BaseWidget(_dialog)
, dialog(_dialog)
, page(_page)
{
this->ui.labelText = page->getName();
@@ -47,8 +48,8 @@ void SettingsDialogTab::paintEvent(QPaintEvent *)
this->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
int a = (this->height() - 20) / 2;
QPixmap pixmap = this->ui.icon.pixmap(QSize(20, 20));
int a = (this->height() - (20 * this->getScale())) / 2;
QPixmap pixmap = this->ui.icon.pixmap(QSize(this->height() - a * 2, this->height() - a * 2));
painter.drawPixmap(a, a, pixmap);
+3 -1
View File
@@ -4,6 +4,8 @@
#include <QPaintEvent>
#include <QWidget>
#include "widgets/basewidget.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
@@ -12,7 +14,7 @@ class SettingsPage;
class SettingsDialog;
class SettingsDialogTab : public QWidget
class SettingsDialogTab : public BaseWidget
{
Q_OBJECT
+12 -6
View File
@@ -4,6 +4,7 @@
#include "twitch/twitchchannel.hpp"
#include "util/layoutcreator.hpp"
#include "util/urlfetch.hpp"
#include "widgets/helper/label.hpp"
#include "widgets/split.hpp"
#include "widgets/splitcontainer.hpp"
#include "widgets/tooltipwidget.hpp"
@@ -47,7 +48,9 @@ SplitHeader::SplitHeader(Split *_split)
layout->addStretch(1);
// channel name label
// auto title = layout.emplace<Label>(this).assign(&this->titleLabel);
auto title = layout.emplace<SignalLabel>().assign(&this->titleLabel);
title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
title->setMouseTracking(true);
QObject::connect(this->titleLabel, &SignalLabel::mouseDoubleClick, this,
&SplitHeader::mouseDoubleClickEvent);
@@ -66,7 +69,8 @@ SplitHeader::SplitHeader(Split *_split)
// ---- misc
this->layout()->setMargin(0);
this->refreshTheme();
this->themeRefreshEvent();
this->scaleChangedEvent(this->getScale());
this->updateChannelText();
@@ -93,7 +97,7 @@ void SplitHeader::addDropdownItems(RippleEffectButton *label)
this->dropdownMenu.addSeparator();
#ifdef USEWEBENGINE
this->dropdownMenu.addAction("Start watching", this, [this]{
SharedChannel _channel = this->split->getChannel();
ChannelPtr _channel = this->split->getChannel();
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
if (tc != nullptr) {
@@ -133,13 +137,15 @@ void SplitHeader::initializeChannelSignals()
}
}
void SplitHeader::resizeEvent(QResizeEvent *event)
void SplitHeader::scaleChangedEvent(float scale)
{
int w = 28 * getDpiMultiplier();
int w = 28 * scale;
this->setFixedHeight(w);
this->dropdownButton->setFixedWidth(w);
this->moderationButton->setFixedWidth(w);
// this->titleLabel->setFont(
// singletons::FontManager::getInstance().getFont(FontStyle::Medium, scale));
}
void SplitHeader::updateChannelText()
@@ -180,7 +186,7 @@ void SplitHeader::updateModerationModeIcon()
: resourceManager.moderationmode_disabled->getPixmap());
bool modButtonVisible = false;
SharedChannel channel = this->split->getChannel();
ChannelPtr channel = this->split->getChannel();
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(channel.get());
@@ -242,7 +248,7 @@ void SplitHeader::rightButtonClicked()
{
}
void SplitHeader::refreshTheme()
void SplitHeader::themeRefreshEvent()
{
QPalette palette;
palette.setColor(QPalette::Foreground, this->themeManager.splits.header.text);
+5 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include "widgets/basewidget.hpp"
#include "widgets/helper/label.hpp"
#include "widgets/helper/rippleeffectlabel.hpp"
#include "widgets/helper/signallabel.hpp"
@@ -34,12 +35,14 @@ public:
void updateModerationModeIcon();
protected:
virtual void scaleChangedEvent(float) override;
virtual void themeRefreshEvent() override;
virtual void paintEvent(QPaintEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void leaveEvent(QEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
virtual void resizeEvent(QResizeEvent *event) override;
private:
Split *const split;
@@ -50,6 +53,7 @@ private:
boost::signals2::connection onlineStatusChangedConnection;
RippleEffectButton *dropdownButton;
// Label *titleLabel;
SignalLabel *titleLabel;
RippleEffectButton *moderationButton;
@@ -57,8 +61,6 @@ private:
void rightButtonClicked();
virtual void refreshTheme() override;
void initializeChannelSignals();
QString tooltip;
+111 -75
View File
@@ -4,6 +4,7 @@
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/notebook.hpp"
#include "widgets/split.hpp"
#include "widgets/splitcontainer.hpp"
@@ -17,68 +18,124 @@ namespace widgets {
SplitInput::SplitInput(Split *_chatWidget)
: BaseWidget(_chatWidget)
, chatWidget(_chatWidget)
, emotesLabel(this)
{
this->setLayout(&this->hbox);
this->initLayout();
this->hbox.setMargin(4);
// auto completion
auto completer = new QCompleter(
singletons::CompletionManager::getInstance().createModel(this->chatWidget->channelName));
this->hbox.addLayout(&this->editContainer);
this->hbox.addLayout(&this->vbox);
this->ui.textEdit->setCompleter(completer);
// misc
this->installKeyPressedEvent();
this->themeRefreshEvent();
this->scaleChangedEvent(this->getScale());
}
void SplitInput::initLayout()
{
auto &fontManager = singletons::FontManager::getInstance();
util::LayoutCreator<SplitInput> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QHBoxLayout>().withoutMargin().assign(&this->ui.hbox);
// input
auto textEdit = layout.emplace<ResizingTextEdit>().assign(&this->ui.textEdit);
connect(textEdit.getElement(), &ResizingTextEdit::textChanged, this,
&SplitInput::editTextChanged);
// right box
auto box = layout.emplace<QVBoxLayout>().withoutMargin();
box->setSpacing(0);
{
auto textEditLength = box.emplace<QLabel>().assign(&this->ui.textEditLength);
textEditLength->setAlignment(Qt::AlignRight);
box->addStretch(1);
box.emplace<RippleEffectLabel>().assign(&this->ui.emoteButton);
}
this->ui.emoteButton->getLabel().setTextFormat(Qt::RichText);
// ---- misc
// set edit font
this->ui.textEdit->setFont(
fontManager.getFont(singletons::FontManager::Type::Medium, this->getScale()));
this->textInput.setFont(
fontManager.getFont(singletons::FontManager::Type::Medium, this->getDpiMultiplier()));
this->managedConnections.emplace_back(fontManager.fontChanged.connect([this, &fontManager]() {
this->textInput.setFont(
fontManager.getFont(singletons::FontManager::Type::Medium, this->getDpiMultiplier()));
this->ui.textEdit->setFont(
fontManager.getFont(singletons::FontManager::Type::Medium, this->getScale()));
}));
this->editContainer.addWidget(&this->textInput);
this->editContainer.setMargin(2);
this->emotesLabel.setMinimumHeight(24);
this->vbox.addWidget(&this->textLengthLabel);
this->vbox.addStretch(1);
this->vbox.addWidget(&this->emotesLabel);
this->textLengthLabel.setText("");
this->textLengthLabel.setAlignment(Qt::AlignRight);
this->emotesLabel.getLabel().setTextFormat(Qt::RichText);
this->emotesLabel.getLabel().setText("<img src=':/images/emote.svg' width='12' height='12' "
"/>");
connect(&this->emotesLabel, &RippleEffectLabel::clicked, [this] {
if (this->emotePopup == nullptr) {
this->emotePopup = new EmotePopup(this->themeManager);
// open emote popup
QObject::connect(this->ui.emoteButton, &RippleEffectLabel::clicked, [this] {
if (!this->emotePopup) {
this->emotePopup = std::make_unique<EmotePopup>(this->themeManager);
this->emotePopup->linkClicked.connect([this](const messages::Link &link) {
if (link.getType() == messages::Link::InsertText) {
this->insertText(link.getValue() + " ");
}
});
}
this->emotePopup->resize((int)(300 * this->emotePopup->getDpiMultiplier()),
(int)(500 * this->emotePopup->getDpiMultiplier()));
this->emotePopup->resize((int)(300 * this->emotePopup->getScale()),
(int)(500 * this->emotePopup->getScale()));
this->emotePopup->loadChannel(this->chatWidget->getChannel());
this->emotePopup->show();
});
connect(&textInput, &ResizingTextEdit::textChanged, this, &SplitInput::editTextChanged);
// clear channelview selection when selecting in the input
QObject::connect(this->ui.textEdit, &QTextEdit::copyAvailable, [this](bool available) {
if (available) {
this->chatWidget->view.clearSelection();
}
});
this->refreshTheme();
textLengthLabel.setHidden(!singletons::SettingManager::getInstance().showMessageLength);
// textEditLength visibility
singletons::SettingManager::getInstance().showMessageLength.connect(
[this](const bool &value, auto) { this->ui.textEditLength->setHidden(!value); },
this->managedConnections);
}
auto completer = new QCompleter(
singletons::CompletionManager::getInstance().createModel(this->chatWidget->channelName));
void SplitInput::scaleChangedEvent(float scale)
{
// update the icon size of the emote button
QString text = "<img src=':/images/emote.svg' width='xD' height='xD' />";
text.replace("xD", QString::number((int)12 * scale));
this->textInput.setCompleter(completer);
this->ui.emoteButton->getLabel().setText(text);
this->ui.emoteButton->setFixedHeight((int)18 * scale);
this->textInput.keyPressed.connect([this](QKeyEvent *event) {
// set maximum height
this->setMaximumHeight((int)(150 * this->getScale()));
this->themeRefreshEvent();
}
void SplitInput::themeRefreshEvent()
{
QPalette palette;
palette.setColor(QPalette::Foreground, this->themeManager.splits.input.text);
this->ui.textEditLength->setPalette(palette);
this->ui.textEdit->setStyleSheet(this->themeManager.splits.input.styleSheet);
this->ui.hbox->setMargin((this->themeManager.isLightTheme() ? 4 : 2) * this->getScale());
}
void SplitInput::installKeyPressedEvent()
{
this->ui.textEdit->keyPressed.connect([this](QKeyEvent *event) {
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
auto c = this->chatWidget->getChannel();
if (c == nullptr) {
return;
}
QString message = textInput.toPlainText();
QString message = ui.textEdit->toPlainText();
QString sendMessage =
singletons::CommandManager::getInstance().execCommand(message, c, false);
@@ -89,9 +146,9 @@ SplitInput::SplitInput(Split *_chatWidget)
event->accept();
if (!(event->modifiers() == Qt::ControlModifier)) {
this->textInput.setText(QString());
this->ui.textEdit->setText(QString());
this->prevIndex = 0;
} else if (this->textInput.toPlainText() ==
} else if (this->ui.textEdit->toPlainText() ==
this->prevMsg.at(this->prevMsg.size() - 1)) {
this->prevMsg.removeLast();
}
@@ -110,7 +167,7 @@ SplitInput::SplitInput(Split *_chatWidget)
} else {
if (this->prevMsg.size() && this->prevIndex) {
this->prevIndex--;
this->textInput.setText(this->prevMsg.at(this->prevIndex));
this->ui.textEdit->setText(this->prevMsg.at(this->prevIndex));
}
}
} else if (event->key() == Qt::Key_Down) {
@@ -128,10 +185,10 @@ SplitInput::SplitInput(Split *_chatWidget)
if (this->prevIndex != (this->prevMsg.size() - 1) &&
this->prevIndex != this->prevMsg.size()) {
this->prevIndex++;
this->textInput.setText(this->prevMsg.at(this->prevIndex));
this->ui.textEdit->setText(this->prevMsg.at(this->prevIndex));
} else {
this->prevIndex = this->prevMsg.size();
this->textInput.setText(QString());
this->ui.textEdit->setText(QString());
}
}
} else if (event->key() == Qt::Key_Left) {
@@ -183,49 +240,32 @@ SplitInput::SplitInput(Split *_chatWidget)
}
}
});
singletons::SettingManager::getInstance().showMessageLength.connect(
[this](const bool &value, auto) { this->textLengthLabel.setHidden(!value); },
this->managedConnections);
QObject::connect(&this->textInput, &QTextEdit::copyAvailable, [this](bool available) {
if (available) {
this->chatWidget->view.clearSelection();
}
});
}
void SplitInput::clearSelection()
{
QTextCursor c = this->textInput.textCursor();
QTextCursor c = this->ui.textEdit->textCursor();
c.setPosition(c.position());
c.setPosition(c.position(), QTextCursor::KeepAnchor);
this->textInput.setTextCursor(c);
this->ui.textEdit->setTextCursor(c);
}
QString SplitInput::getInputText() const
{
return this->textInput.toPlainText();
return this->ui.textEdit->toPlainText();
}
void SplitInput::refreshTheme()
void SplitInput::insertText(const QString &text)
{
QPalette palette;
palette.setColor(QPalette::Foreground, this->themeManager.splits.input.text);
this->textLengthLabel.setPalette(palette);
this->textInput.setStyleSheet(this->themeManager.splits.input.styleSheet);
this->hbox.setMargin((this->themeManager.isLightTheme() ? 4 : 2) * this->getDpiMultiplier());
this->ui.textEdit->insertPlainText(text);
}
void SplitInput::editTextChanged()
{
QString text = this->textInput.toPlainText();
// set textLengthLabel value
QString text = this->ui.textEdit->toPlainText();
this->textChanged.invoke(text);
@@ -244,7 +284,7 @@ void SplitInput::editTextChanged()
labelText = QString::number(text.length());
}
this->textLengthLabel.setText(labelText);
this->ui.textEditLength->setText(labelText);
}
void SplitInput::paintEvent(QPaintEvent *)
@@ -255,7 +295,7 @@ void SplitInput::paintEvent(QPaintEvent *)
QPen pen(this->themeManager.splits.input.border);
if (this->themeManager.isLightTheme()) {
pen.setWidth((int)(6 * this->getDpiMultiplier()));
pen.setWidth((int)(6 * this->getScale()));
}
painter.setPen(pen);
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
@@ -264,14 +304,10 @@ void SplitInput::paintEvent(QPaintEvent *)
void SplitInput::resizeEvent(QResizeEvent *)
{
if (this->height() == this->maximumHeight()) {
this->textInput.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
this->ui.textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
} else {
this->textInput.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->ui.textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
this->setMaximumHeight((int)(150 * this->getDpiMultiplier()));
this->refreshTheme();
}
void SplitInput::mousePressEvent(QMouseEvent *)
+22 -8
View File
@@ -27,10 +27,13 @@ public:
void clearSelection();
QString getInputText() const;
void insertText(const QString &text);
pajlada::Signals::Signal<const QString &> textChanged;
protected:
virtual void scaleChangedEvent(float scale) override;
virtual void paintEvent(QPaintEvent *) override;
virtual void resizeEvent(QResizeEvent *) override;
@@ -38,18 +41,29 @@ protected:
private:
Split *const chatWidget;
EmotePopup *emotePopup = nullptr;
std::unique_ptr<EmotePopup> emotePopup;
struct {
ResizingTextEdit *textEdit;
QLabel *textEditLength;
RippleEffectLabel *emoteButton;
QHBoxLayout *hbox;
} ui;
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
QHBoxLayout hbox;
QVBoxLayout vbox;
QHBoxLayout editContainer;
ResizingTextEdit textInput;
QLabel textLengthLabel;
RippleEffectLabel emotesLabel;
// QHBoxLayout hbox;
// QVBoxLayout vbox;
// QHBoxLayout editContainer;
// ResizingTextEdit textInput;
// QLabel textLengthLabel;
// RippleEffectLabel emotesLabel;
QStringList prevMsg;
int prevIndex = 0;
virtual void refreshTheme() override;
void initLayout();
void installKeyPressedEvent();
virtual void themeRefreshEvent() override;
private slots:
void editTextChanged();
+118
View File
@@ -0,0 +1,118 @@
#include "titlebarbutton.hpp"
namespace chatterino {
namespace widgets {
TitleBarButton::TitleBarButton()
: RippleEffectButton(nullptr)
{
}
TitleBarButton::Style TitleBarButton::getButtonStyle() const
{
return this->style;
}
void TitleBarButton::setButtonStyle(Style _style)
{
this->style = _style;
this->update();
}
void TitleBarButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QColor color = "#000";
QColor background = "#fff";
int xD = this->height() / 3;
int centerX = this->width() / 2;
painter.setRenderHint(QPainter::Antialiasing, false);
switch (this->style) {
case Minimize: {
painter.fillRect(centerX - xD / 2, xD * 3 / 2, xD, 1, color);
break;
}
case Maximize: {
painter.setPen(color);
painter.drawRect(centerX - xD / 2, xD, xD - 1, xD - 1);
break;
}
case 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, QColor("#fff"));
painter.drawRect(centerX - xD / 2, xD + xD2, xD3, xD3);
break;
}
case 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 User: {
color = QColor("#333");
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 Settings: {
color = QColor("#333");
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;
}
}
this->fancyPaint(painter);
}
} // namespace widgets
} // namespace chatterino
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "widgets/helper/rippleeffectbutton.hpp"
namespace chatterino {
namespace widgets {
class TitleBarButton : public RippleEffectButton
{
public:
enum Style { Minimize = 1, Maximize = 2, Unmaximize = 4, Close = 8, User = 16, Settings = 32 };
TitleBarButton();
Style getButtonStyle() const;
void setButtonStyle(Style style);
protected:
virtual void paintEvent(QPaintEvent *) override;
private:
Style style;
};
} // namespace widgets
} // namespace chatterino