From 64160e60afb9594885881a164af4b91d62ce7720 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Wed, 16 May 2018 03:55:56 +0200 Subject: [PATCH 001/121] Fix right-clicking of emotes There are a few more actions that we might want to add at a later date, but a simple feature set has been implemented Fix #386 --- src/messages/messageelement.hpp | 3 +- src/singletons/emotemanager.cpp | 16 ++++++++-- src/util/emotemap.hpp | 3 ++ src/widgets/helper/channelview.cpp | 50 ++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/messages/messageelement.hpp b/src/messages/messageelement.hpp index 2f281c12..79a78897 100644 --- a/src/messages/messageelement.hpp +++ b/src/messages/messageelement.hpp @@ -169,7 +169,6 @@ public: // b) which size it wants class EmoteElement : public MessageElement { - const util::EmoteData data; std::unique_ptr textElement; public: @@ -177,6 +176,8 @@ public: ~EmoteElement() override = default; void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; + + const util::EmoteData data; }; // contains a text, formated depending on the preferences diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index 3cd841f0..a1a781fb 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -135,8 +135,12 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, link = link.replace("{{id}}", id).replace("{{image}}", "1x"); - auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&code, &link] { - return util::EmoteData(new Image(link, 1, code, code + "
Channel BTTV Emote")); + auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&id, &code, &link] { + util::EmoteData emoteData( + new Image(link, 1, code, code + "
Channel BTTV Emote")); + emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; + + return emoteData; }); this->bttvChannelEmotes.insert(code, emote); @@ -182,9 +186,11 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, QJsonObject urls = emoteObject.value("urls").toObject(); - auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [&code, &urls] { + auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [id, &code, &urls] { util::EmoteData emoteData; FillInFFZEmoteData(urls, code, code + "
Channel FFZ Emote", emoteData); + emoteData.pageLink = + QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); return emoteData; }); @@ -483,6 +489,7 @@ void EmoteManager::loadBTTVEmotes() code + "
Global BTTV Emote"); emoteData.image3x = new Image(GetBTTVEmoteLink(urlTemplate, id, "3x"), 0.25, code, code + "
Global BTTV Emote"); + emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; this->bttvGlobalEmotes.insert(code, emoteData); codes.push_back(code.toStdString()); @@ -510,10 +517,13 @@ void EmoteManager::loadFFZEmotes() QJsonObject object = emote.toObject(); QString code = object.value("name").toString(); + int id = object.value("id").toInt(); QJsonObject urls = object.value("urls").toObject(); util::EmoteData emoteData; FillInFFZEmoteData(urls, code, code + "
Global FFZ Emote", emoteData); + emoteData.pageLink = + QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); this->ffzGlobalEmotes.insert(code, emoteData); codes.push_back(code.toStdString()); diff --git a/src/util/emotemap.hpp b/src/util/emotemap.hpp index 47816909..774a3a84 100644 --- a/src/util/emotemap.hpp +++ b/src/util/emotemap.hpp @@ -23,6 +23,9 @@ struct EmoteData { messages::Image *image1x = nullptr; messages::Image *image2x = nullptr; messages::Image *image3x = nullptr; + + // Link to the emote page i.e. https://www.frankerfacez.com/emoticon/144722-pajaCringe + QString pageLink; }; using EmoteMap = ConcurrentMap; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 95055e6b..a62e0b51 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -883,6 +883,56 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) return; } + const auto &creator = hoverLayoutElement->getCreator(); + auto creatorFlags = creator.getFlags(); + + if ((creatorFlags & (MessageElement::Flags::EmoteImages | MessageElement::Flags::EmojiImage)) != + 0) { + if (event->button() == Qt::RightButton) { + static QMenu *menu = new QMenu; + menu->clear(); + + const auto &emoteElement = static_cast(creator); + + // TODO: We might want to add direct "Open image" variants alongside the Copy actions + + if (emoteElement.data.image1x != nullptr) { + menu->addAction("Copy 1x link", [url = emoteElement.data.image1x->getUrl()] { + QApplication::clipboard()->setText(url); // + }); + } + if (emoteElement.data.image2x != nullptr) { + menu->addAction("Copy 2x link", [url = emoteElement.data.image2x->getUrl()] { + QApplication::clipboard()->setText(url); // + }); + } + if (emoteElement.data.image3x != nullptr) { + menu->addAction("Copy 3x link", [url = emoteElement.data.image3x->getUrl()] { + QApplication::clipboard()->setText(url); // + }); + } + + if ((creatorFlags & MessageElement::Flags::BttvEmote) != 0) { + menu->addSeparator(); + QString emotePageLink = emoteElement.data.pageLink; + menu->addAction("Copy BTTV emote link", [emotePageLink] { + QApplication::clipboard()->setText(emotePageLink); // + }); + } else if ((creatorFlags & MessageElement::Flags::FfzEmote) != 0) { + menu->addSeparator(); + QString emotePageLink = emoteElement.data.pageLink; + menu->addAction("Copy FFZ emote link", [emotePageLink] { + QApplication::clipboard()->setText(emotePageLink); // + }); + } + + menu->move(QCursor::pos()); + menu->show(); + + return; + } + } + auto &link = hoverLayoutElement->getLink(); if (event->button() != Qt::LeftButton || !app->settings->linksDoubleClickOnly) { this->handleLinkClick(event, link, layout.get()); From c1a3764f446ee47ac5c02f6885f5ee88c66cabdd Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 10 May 2018 19:50:31 +0200 Subject: [PATCH 002/121] added basic new layout --- src/singletons/windowmanager.cpp | 22 +- src/widgets/helper/notebookbutton.cpp | 6 +- src/widgets/helper/notebooktab.cpp | 2 +- src/widgets/helper/splitheader.cpp | 14 +- src/widgets/helper/splitinput.cpp | 49 +- src/widgets/helper/splitoverlay.cpp | 12 + src/widgets/helper/splitoverlay.hpp | 3 +- src/widgets/notebook.cpp | 20 +- src/widgets/split.cpp | 98 ++- src/widgets/split.hpp | 31 +- src/widgets/splitcontainer.cpp | 962 +++++++++++++++----------- src/widgets/splitcontainer.hpp | 465 ++++++++++++- 12 files changed, 1101 insertions(+), 583 deletions(-) diff --git a/src/singletons/windowmanager.cpp b/src/singletons/windowmanager.cpp index fa949937..ec077895 100644 --- a/src/singletons/windowmanager.cpp +++ b/src/singletons/windowmanager.cpp @@ -209,7 +209,7 @@ void WindowManager::initialize() QJsonObject split_obj = split_val.toObject(); split->setChannel(this->decodeChannel(split_obj)); - tab->addToLayout(split, std::make_pair(colNr, 10000000)); + // tab->addToLayout(split, std::make_pair(colNr, 10000000)); } colNr++; } @@ -271,20 +271,20 @@ void WindowManager::save() // splits QJsonArray columns_arr; - std::vector> columns = tab->getColumns(); + // std::vector> columns = tab->getColumns(); - for (std::vector &cells : columns) { - QJsonArray cells_arr; + // for (std::vector &cells : columns) { + // QJsonArray cells_arr; - for (widgets::Split *cell : cells) { - QJsonObject cell_obj; + // for (widgets::Split *cell : cells) { + // QJsonObject cell_obj; - this->encodeChannel(cell->getIndirectChannel(), cell_obj); + // this->encodeChannel(cell->getIndirectChannel(), cell_obj); - cells_arr.append(cell_obj); - } - columns_arr.append(cells_arr); - } + // cells_arr.append(cell_obj); + // } + // columns_arr.append(cells_arr); + // } tab_obj.insert("splits", columns_arr); tabs_arr.append(tab_obj); diff --git a/src/widgets/helper/notebookbutton.cpp b/src/widgets/helper/notebookbutton.cpp index 48d97716..a99fcc1e 100644 --- a/src/widgets/helper/notebookbutton.cpp +++ b/src/widgets/helper/notebookbutton.cpp @@ -139,10 +139,10 @@ void NotebookButton::dropEvent(QDropEvent *event) Notebook *notebook = dynamic_cast(this->parentWidget()); if (notebook != nuuls) { - SplitContainer *tab = notebook->addNewPage(); + SplitContainer *page = notebook->addNewPage(); - SplitContainer::draggingSplit->setParent(tab); - tab->addToLayout(SplitContainer::draggingSplit); + SplitContainer::draggingSplit->setParent(page); + page->appendSplit(SplitContainer::draggingSplit); } } } diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 20774bfd..8ff07064 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -429,7 +429,7 @@ NotebookTab::NotebookTab(Notebook *_notebook) QString newTitle = d.getText(); if (newTitle.isEmpty()) { this->useDefaultTitle = true; - this->page->refreshTitle(); + this->page->refreshTabTitle(); } else { this->useDefaultTitle = false; this->setTitle(newTitle); diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 42fef1c0..2b278dea 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -241,13 +241,13 @@ void SplitHeader::mouseMoveEvent(QMouseEvent *event) tooltipWidget->show(); } - if (this->dragging) { - if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || - std::abs(this->dragStart.y() - event->pos().y()) > 12) { - this->split->drag(); - this->dragging = false; - } - } + // if (this->dragging) { + // if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || + // std::abs(this->dragStart.y() - event->pos().y()) > 12) { + // this->split->drag(); + // this->dragging = false; + // } + // } } void SplitHeader::leaveEvent(QEvent *event) diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index 7d595acd..2a086df6 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -163,15 +163,16 @@ void SplitInput::installKeyPressedEvent() return; } if (event->modifiers() == Qt::AltModifier) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + // SplitContainer *page = + // static_cast(this->chatWidget->parentWidget()); - int reqX = page->currentX; - int reqY = page->lastRequestedY[reqX] - 1; + // page->requestFocus(); + // int reqX = page->currentX; + // int reqY = page->lastRequestedY[reqX] - 1; - qDebug() << "Alt+Down to" << reqX << "/" << reqY; + // qDebug() << "Alt+Down to" << reqX << "/" << reqY; - page->requestFocus(reqX, reqY); + // page->requestFocus(reqX, reqY); } else { if (this->prevMsg.size() && this->prevIndex) { if (this->prevIndex == (this->prevMsg.size())) { @@ -191,15 +192,15 @@ void SplitInput::installKeyPressedEvent() return; } if (event->modifiers() == Qt::AltModifier) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + // SplitContainer *page = + // static_cast(this->chatWidget->parentWidget()); - int reqX = page->currentX; - int reqY = page->lastRequestedY[reqX] + 1; + // int reqX = page->currentX; + // int reqY = page->lastRequestedY[reqX] + 1; - qDebug() << "Alt+Down to" << reqX << "/" << reqY; + // qDebug() << "Alt+Down to" << reqX << "/" << reqY; - page->requestFocus(reqX, reqY); + // page->requestFocus(reqX, reqY); } else { if (this->prevIndex != (this->prevMsg.size() - 1) && this->prevIndex != this->prevMsg.size()) { @@ -216,27 +217,27 @@ void SplitInput::installKeyPressedEvent() } } else if (event->key() == Qt::Key_Left) { if (event->modifiers() == Qt::AltModifier) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + // SplitContainer *page = + // static_cast(this->chatWidget->parentWidget()); - int reqX = page->currentX - 1; - int reqY = page->lastRequestedY[reqX]; + // int reqX = page->currentX - 1; + // int reqY = page->lastRequestedY[reqX]; - qDebug() << "Alt+Left to" << reqX << "/" << reqY; + // qDebug() << "Alt+Left to" << reqX << "/" << reqY; - page->requestFocus(reqX, reqY); + // page->requestFocus(reqX, reqY); } } else if (event->key() == Qt::Key_Right) { if (event->modifiers() == Qt::AltModifier) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + // SplitContainer *page = + // static_cast(this->chatWidget->parentWidget()); - int reqX = page->currentX + 1; - int reqY = page->lastRequestedY[reqX]; + // int reqX = page->currentX + 1; + // int reqY = page->lastRequestedY[reqX]; - qDebug() << "Alt+Right to" << reqX << "/" << reqY; + // qDebug() << "Alt+Right to" << reqX << "/" << reqY; - page->requestFocus(reqX, reqY); + // page->requestFocus(reqX, reqY); } } else if (event->key() == Qt::Key_Tab) { if (event->modifiers() == Qt::ControlModifier) { diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index 0943111a..fd6f2324 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -9,6 +9,7 @@ #include "application.hpp" #include "singletons/resourcemanager.hpp" #include "widgets/split.hpp" +#include "widgets/splitcontainer.hpp" namespace chatterino { namespace widgets { @@ -146,6 +147,17 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched, QEvent *even this->parent->split->drag(); } return true; + } else { + SplitContainer *container = this->parent->split->getContainer(); + + if (container != nullptr) { + auto *_split = new Split(container); + container->insertSplit( + _split, + (SplitContainer::Direction)(this->hoveredElement + SplitContainer::Left - + SplitLeft), + this->parent->split); + } } } break; } diff --git a/src/widgets/helper/splitoverlay.hpp b/src/widgets/helper/splitoverlay.hpp index 31b7a4b4..de24dac6 100644 --- a/src/widgets/helper/splitoverlay.hpp +++ b/src/widgets/helper/splitoverlay.hpp @@ -17,7 +17,8 @@ protected: void paintEvent(QPaintEvent *event) override; private: - enum HoveredElement { None, SplitMove, SplitLeft, SplitRight, SplitUp, SplitDown }; + // fourtf: !!! preserve the order of left, up, right and down + enum HoveredElement { None, SplitMove, SplitLeft, SplitUp, SplitRight, SplitDown }; HoveredElement hoveredElement = None; Split *split; diff --git a/src/widgets/notebook.cpp b/src/widgets/notebook.cpp index 06e21b79..8ac53af8 100644 --- a/src/widgets/notebook.cpp +++ b/src/widgets/notebook.cpp @@ -391,13 +391,13 @@ Notebook::Notebook(Window *parent, bool _showButtons) // Window-wide hotkeys // CTRL+T: Create new split in selected notebook page - CreateWindowShortcut(this, "CTRL+T", [this]() { - if (this->selectedPage == nullptr) { - return; - } + // CreateWindowShortcut(this, "CTRL+T", [this]() { + // if (this->selectedPage == nullptr) { + // return; + // } - this->selectedPage->addChat(true); - }); + // this->selectedPage->addChat(true); + // }); } SplitContainer *Notebook::addNewPage(bool select) @@ -420,7 +420,7 @@ SplitContainer *Notebook::addNewPage(bool select) void Notebook::removePage(SplitContainer *page) { - if (page->splitCount() > 0 && closeConfirmDialog.exec() != QMessageBox::Yes) { + if (page->getSplitCount() > 0 && closeConfirmDialog.exec() != QMessageBox::Yes) { return; } @@ -484,7 +484,8 @@ void Notebook::select(SplitContainer *page) if (this->selectedPage != nullptr) { this->selectedPage->setHidden(true); this->selectedPage->getTab()->setSelected(false); - for (auto split : this->selectedPage->getSplits()) { + + for (Split *split : this->selectedPage->getSplits()) { split->updateLastReadMessage(); } } @@ -663,8 +664,7 @@ void Notebook::settingsButtonClicked() void Notebook::usersButtonClicked() { auto app = getApp(); - app->windows->showAccountSelectPopup( - this->mapToGlobal(this->userButton.rect().bottomRight())); + app->windows->showAccountSelectPopup(this->mapToGlobal(this->userButton.rect().bottomRight())); } void Notebook::addPageButtonClicked() diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 441a9a08..4d8681a0 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -135,6 +135,8 @@ Split::Split(QWidget *parent) // this->overlay->hide(); // } }); + + this->setAcceptDrops(true); } Split::~Split() @@ -144,6 +146,16 @@ Split::~Split() this->indirectChannelChangedConnection.disconnect(); } +ChannelView &Split::getChannelView() +{ + return this->view; +} + +SplitContainer *Split::getContainer() +{ + return this->container; +} + bool Split::isInContainer() const { return this->container != nullptr; @@ -185,30 +197,6 @@ void Split::setChannel(IndirectChannel newChannel) this->channelChanged.invoke(); } -void Split::setFlexSizeX(double x) -{ - // this->flexSizeX = x; - // this->parentPage->updateFlexValues(); -} - -double Split::getFlexSizeX() -{ - // return this->flexSizeX; - return 1; -} - -void Split::setFlexSizeY(double y) -{ - // this->flexSizeY = y; - // this->parentPage.updateFlexValues(); -} - -double Split::getFlexSizeY() -{ - // return this->flexSizeY; - return 1; -} - void Split::setModerationMode(bool value) { if (value != this->moderationMode) { @@ -236,7 +224,7 @@ void Split::showChangeChannelPopup(const char *dialogTitle, bool empty, if (dialog->hasSeletedChannel()) { this->setChannel(dialog->getSelectedChannel()); if (this->isInContainer()) { - this->container->refreshTitle(); + this->container->refreshTabTitle(); } } @@ -284,9 +272,9 @@ void Split::mouseMoveEvent(QMouseEvent *event) void Split::mousePressEvent(QMouseEvent *event) { - if (event->buttons() == Qt::LeftButton && event->modifiers() & Qt::AltModifier) { - this->drag(); - } + // if (event->buttons() == Qt::LeftButton && event->modifiers() & Qt::AltModifier) { + // this->drag(); + // } } void Split::keyPressEvent(QKeyEvent *event) @@ -338,19 +326,40 @@ void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers) } } +void Split::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); + this->isDragging = true; + QTimer::singleShot(1, [this] { this->overlay->show(); }); +} + +void Split::dragLeaveEvent(QDragLeaveEvent *event) +{ + this->overlay->hide(); + this->isDragging = false; +} + +void Split::dragMoveEvent(QDragMoveEvent *event) +{ + event->acceptProposedAction(); +} + +void Split::dropEvent(QDropEvent *event) +{ +} + /// Slots void Split::doAddSplit() { if (this->container) { - this->container->addChat(true); + this->container->appendNewSplit(true); } } void Split::doCloseSplit() { if (this->container) { - this->container->removeFromLayout(this); - deleteLater(); + this->container->deleteSplit(this); } } @@ -373,7 +382,7 @@ void Split::doPopup() new Split(static_cast(window.getNotebook().getOrAddSelectedPage())); split->setChannel(this->getIndirectChannel()); - window.getNotebook().getOrAddSelectedPage()->addToLayout(split); + window.getNotebook().getOrAddSelectedPage()->appendSplit(split); window.show(); } @@ -536,26 +545,6 @@ static Iter select_randomly(Iter start, Iter end) return select_randomly(start, end, gen); } -void Split::doIncFlexX() -{ - this->setFlexSizeX(this->getFlexSizeX() * 1.2); -} - -void Split::doDecFlexX() -{ - this->setFlexSizeX(this->getFlexSizeX() * (1 / 1.2)); -} - -void Split::doIncFlexY() -{ - this->setFlexSizeY(this->getFlexSizeY() * 1.2); -} - -void Split::doDecFlexY() -{ - this->setFlexSizeY(this->getFlexSizeY() * (1 / 1.2)); -} - void Split::drag() { auto container = dynamic_cast(this->parentWidget()); @@ -564,7 +553,7 @@ void Split::drag() SplitContainer::isDraggingSplit = true; SplitContainer::draggingSplit = this; - auto originalLocation = container->removeFromLayout(this); + auto originalLocation = container->releaseSplit(this); QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; @@ -576,7 +565,8 @@ void Split::drag() Qt::DropAction dropAction = drag->exec(Qt::MoveAction); if (dropAction == Qt::IgnoreAction) { - container->addToLayout(this, originalLocation); + container->insertSplit(this, + originalLocation); // SplitContainer::dragOriginalPosition); } SplitContainer::isDraggingSplit = false; diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index e184cefc..450623d0 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -39,9 +39,6 @@ class Split : public BaseWidget, pajlada::Signals::SignalHolder Q_OBJECT - static pajlada::Signals::Signal altPressedStatusChanged; - static bool altPressesStatus; - public: explicit Split(SplitContainer *parent); explicit Split(QWidget *parent); @@ -50,20 +47,13 @@ public: pajlada::Signals::NoArgSignal channelChanged; - ChannelView &getChannelView() - { - return this->view; - } + ChannelView &getChannelView(); + SplitContainer *getContainer(); IndirectChannel getIndirectChannel(); ChannelPtr getChannel(); void setChannel(IndirectChannel newChannel); - void setFlexSizeX(double x); - double getFlexSizeX(); - void setFlexSizeY(double y); - double getFlexSizeY(); - void setModerationMode(bool value); bool getModerationMode() const; @@ -79,6 +69,9 @@ public: bool isInContainer() const; + static pajlada::Signals::Signal altPressedStatusChanged; + static bool altPressesStatus; + protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -89,6 +82,11 @@ protected: void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; + private: SplitContainer *container; IndirectChannel channel; @@ -99,12 +97,10 @@ private: SplitInput input; SplitOverlay *overlay; - double flexSizeX = 1; - double flexSizeY = 1; - bool moderationMode = false; bool isMouseOver = false; + bool isDragging = false; pajlada::Signals::Connection channelIDChangedConnection; pajlada::Signals::Connection usermodeChangedConnection; @@ -151,11 +147,6 @@ public slots: // Open viewer list of the channel void doOpenViewerList(); - - void doIncFlexX(); - void doDecFlexX(); - void doIncFlexY(); - void doDecFlexY(); }; } // namespace widgets diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 578e0ad6..0cf7f2c9 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -24,7 +24,7 @@ namespace widgets { bool SplitContainer::isDraggingSplit = false; Split *SplitContainer::draggingSplit = nullptr; -std::pair SplitContainer::dropPosition = std::pair(-1, -1); +// SplitContainer::Position SplitContainer::dragOriginalPosition; SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) : BaseWidget(parent) @@ -33,182 +33,23 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) { this->tab->page = this; - this->setLayout(&this->ui.parentLayout); + // this->setLayout(&this->ui.parentLayout); + + // this->setHidden(true); + // this->setAcceptDrops(true); + + // this->ui.parentLayout.addSpacing(1); + // this->ui.parentLayout.addLayout(&this->ui.hbox); + // this->ui.parentLayout.setMargin(0); + + // this->ui.hbox.setSpacing(1); + // this->ui.hbox.setMargin(0); + + this->refreshTabTitle(); + + this->managedConnect(Split::altPressedStatusChanged, [this](auto) { this->layout(); }); - this->setHidden(true); this->setAcceptDrops(true); - - this->ui.parentLayout.addSpacing(1); - this->ui.parentLayout.addLayout(&this->ui.hbox); - this->ui.parentLayout.setMargin(0); - - this->ui.hbox.setSpacing(1); - this->ui.hbox.setMargin(0); - - this->refreshTitle(); -} - -void SplitContainer::updateFlexValues() -{ - for (int i = 0; i < this->ui.hbox.count(); i++) { - QVBoxLayout *vbox = (QVBoxLayout *)ui.hbox.itemAt(i)->layout(); - - if (vbox->count() != 0) { - ui.hbox.setStretch(i, (int)(1000 * ((Split *)vbox->itemAt(0))->getFlexSizeX())); - } - } -} - -int SplitContainer::splitCount() const -{ - return this->splits.size(); -} - -std::pair SplitContainer::removeFromLayout(Split *widget) -{ - widget->getChannelView().tabHighlightRequested.disconnectAll(); - - // remove reference to chat widget from chatWidgets vector - auto it = std::find(std::begin(this->splits), std::end(this->splits), widget); - if (it != std::end(this->splits)) { - this->splits.erase(it); - - this->refreshTitle(); - } - - Split *neighbouringSplit = nullptr; - - // Position the split was found at - int positionX = -1, positionY = -1; - - bool removed = false; - - QVBoxLayout *layoutToRemove = nullptr; - - // Find widget in box, remove it, return its position - for (int i = 0; i < this->ui.hbox.count(); ++i) { - auto vbox = static_cast(this->ui.hbox.itemAt(i)); - - auto vboxCount = vbox->count(); - - for (int j = 0; j < vboxCount; ++j) { - if (vbox->itemAt(j)->widget() != widget) { - neighbouringSplit = dynamic_cast(vbox->itemAt(j)->widget()); - - if (removed && neighbouringSplit != nullptr) { - // The widget we searched for has been found, and we have a split to switch - // focus to - break; - } - - continue; - } - - removed = true; - positionX = i; - - // Remove split from box - widget->setParent(nullptr); - - if (vbox->count() == 0) { - // The split was the last item remaining in the vbox - // Remove the vbox once all iteration is done - layoutToRemove = vbox; - positionY = -1; - break; - } - - // Don't break here yet, we want to keep iterating this vbox if possible to find the - // closest still-alive neighbour that we can switch focus to - positionY = j; - - --j; - --vboxCount; - } - - if (removed && neighbouringSplit != nullptr) { - // The widget we searched for has been found, and we have a split to switch focus to - break; - } - } - - if (removed) { - if (layoutToRemove != nullptr) { - // The split we removed was the last split in its box. Remove the box - // We delay the removing of the box so we can keep iterating over hbox safely - this->ui.hbox.removeItem(layoutToRemove); - delete layoutToRemove; - } - - if (neighbouringSplit != nullptr) { - // We found a neighbour split we can switch focus to - neighbouringSplit->giveFocus(Qt::MouseFocusReason); - } - } - - return std::make_pair(positionX, positionY); -} - -void SplitContainer::addToLayout(Split *widget, std::pair position) -{ - this->splits.push_back(widget); - widget->getChannelView().tabHighlightRequested.connect( - [this](HighlightState state) { this->tab->setHighlightState(state); }); - - this->refreshTitle(); - - widget->giveFocus(Qt::MouseFocusReason); - - // add vbox at the end - if (position.first < 0 || position.first >= this->ui.hbox.count()) { - auto vbox = new QVBoxLayout(); - vbox->addWidget(widget); - - this->ui.hbox.addLayout(vbox, 1); - - this->refreshCurrentFocusCoordinates(); - return; - } - - // insert vbox - if (position.second == -1) { - auto vbox = new QVBoxLayout(); - vbox->addWidget(widget); - - this->ui.hbox.insertLayout(position.first, vbox, 1); - this->refreshCurrentFocusCoordinates(); - return; - } - - // add to existing vbox - auto vbox = static_cast(this->ui.hbox.itemAt(position.first)); - - vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget); - - this->refreshCurrentFocusCoordinates(); -} - -const std::vector &SplitContainer::getSplits() const -{ - return this->splits; -} - -std::vector> SplitContainer::getColumns() const -{ - std::vector> columns; - - for (int i = 0; i < this->ui.hbox.count(); i++) { - std::vector cells; - - QLayout *vbox = this->ui.hbox.itemAt(i)->layout(); - for (int j = 0; j < vbox->count(); j++) { - cells.push_back(dynamic_cast(vbox->itemAt(j)->widget())); - } - - columns.push_back(cells); - } - - return columns; } NotebookTab *SplitContainer::getTab() const @@ -216,241 +57,126 @@ NotebookTab *SplitContainer::getTab() const return this->tab; } -void SplitContainer::addChat(bool openChannelNameDialog) +void SplitContainer::appendNewSplit(bool openChannelNameDialog) { - Split *w = this->createChatWidget(); - this->addToLayout(w, std::pair(-1, -1)); + Split *split = new Split(this); + this->appendSplit(split); if (openChannelNameDialog) { - w->showChangeChannelPopup("Open channel name", true, [=](bool ok) { + split->showChangeChannelPopup("Open channel name", true, [=](bool ok) { if (!ok) { - this->removeFromLayout(w); - delete w; + this->deleteSplit(split); } }); } } -void SplitContainer::refreshCurrentFocusCoordinates(bool alsoSetLastRequested) +void SplitContainer::appendSplit(Split *split) { - int setX = -1; - int setY = -1; - bool doBreak = false; - for (int x = 0; x < this->ui.hbox.count(); ++x) { - QLayoutItem *item = this->ui.hbox.itemAt(x); - if (item->isEmpty()) { - setX = x; - break; - } - QVBoxLayout *vbox = static_cast(item->layout()); - - for (int y = 0; y < vbox->count(); ++y) { - QLayoutItem *innerItem = vbox->itemAt(y); - - if (innerItem->isEmpty()) { - setX = x; - setY = y; - doBreak = true; - break; - } - - QWidget *w = innerItem->widget(); - if (w) { - Split *chatWidget = static_cast(w); - if (chatWidget->hasFocus()) { - setX = x; - setY = y; - doBreak = true; - break; - } - } - } - - if (doBreak) { - break; - } - } - - if (setX != -1) { - this->currentX = setX; - - if (setY != -1) { - this->currentY = setY; - - if (alsoSetLastRequested) { - this->lastRequestedY[setX] = setY; - } - } - } + this->insertSplit(split, Direction::Right); } -void SplitContainer::requestFocus(int requestedX, int requestedY) +void SplitContainer::insertSplit(Split *split, const Position &position) { - // XXX: Perhaps if we request an Y coordinate out of bounds, we shuold set all previously set - // requestedYs to 0 (if -1 is requested) or that x-coordinates vbox count (if requestedY >= - // currentvbox.count() is requested) - if (requestedX < 0 || requestedX >= this->ui.hbox.count()) { - return; - } - - QLayoutItem *item = this->ui.hbox.itemAt(requestedX); - QWidget *xW = item->widget(); - if (item->isEmpty()) { - qDebug() << "Requested hbox item " << requestedX << "is empty"; - if (xW) { - qDebug() << "but xW is not null"; - // TODO: figure out what to do here - } - return; - } - - QVBoxLayout *vbox = static_cast(item->layout()); - - if (requestedY < 0) { - requestedY = 0; - } else if (requestedY >= vbox->count()) { - requestedY = vbox->count() - 1; - } - - this->lastRequestedY[requestedX] = requestedY; - - QLayoutItem *innerItem = vbox->itemAt(requestedY); - - if (innerItem->isEmpty()) { - qDebug() << "Requested vbox item " << requestedY << "is empty"; - return; - } - - QWidget *w = innerItem->widget(); - if (w) { - Split *chatWidget = static_cast(w); - chatWidget->giveFocus(Qt::OtherFocusReason); - } + this->insertSplit(split, position.direction, position.relativeNode); } -void SplitContainer::enterEvent(QEvent *) +void SplitContainer::insertSplit(Split *split, Direction direction, Split *relativeTo) { - if (this->ui.hbox.count() == 0) { - this->setCursor(QCursor(Qt::PointingHandCursor)); + Node *node = this->baseNode.findNodeContainingSplit(relativeTo); + assert(node != nullptr); + + this->insertSplit(split, direction, node); +} + +void SplitContainer::insertSplit(Split *split, Direction direction, Node *relativeTo) +{ + if (relativeTo == nullptr) { + if (this->baseNode.type == Node::EmptyRoot) { + this->baseNode.setSplit(split); + } else if (this->baseNode.type == Node::_Split) { + this->baseNode.nestSplitIntoCollection(split, direction); + } else { + this->baseNode.insertSplitRelative(split, direction); + } } else { - this->setCursor(QCursor(Qt::ArrowCursor)); + assert(this->baseNode.isOrContainsNode(relativeTo)); + + relativeTo->insertSplitRelative(split, direction); } + + split->setParent(this); + split->show(); + this->splits.push_back(split); + + this->layout(); } -void SplitContainer::leaveEvent(QEvent *) +SplitContainer::Position SplitContainer::releaseSplit(Split *split) { + Node *node = this->baseNode.findNodeContainingSplit(split); + assert(node != nullptr); + + this->splits.erase(std::find(this->splits.begin(), this->splits.end(), split)); + split->setParent(nullptr); + Position position = node->releaseSplit(); + this->layout(); + return position; +} + +SplitContainer::Position SplitContainer::deleteSplit(Split *split) +{ + assert(split != nullptr); + + split->deleteLater(); + return releaseSplit(split); +} + +void SplitContainer::layout() +{ + this->baseNode.geometry = this->rect(); + + std::vector _dropRects; + this->baseNode.layout(Split::altPressesStatus | this->isDragging, _dropRects); + _dropRects.clear(); + this->baseNode.layout(Split::altPressesStatus | this->isDragging, _dropRects); + + this->dropRects = std::move(_dropRects); + this->update(); +} + +/// EVENTS +void SplitContainer::resizeEvent(QResizeEvent *event) +{ + BaseWidget::resizeEvent(event); + + this->layout(); } void SplitContainer::mouseReleaseEvent(QMouseEvent *event) { - if (this->ui.hbox.count() == 0 && event->button() == Qt::LeftButton) { - // "Add Chat" was clicked - this->addChat(true); + if (event->button() == Qt::LeftButton) { + if (this->splits.size() == 0) { + // "Add Chat" was clicked + this->appendNewSplit(true); - this->setCursor(QCursor(Qt::ArrowCursor)); - } -} - -void SplitContainer::dragEnterEvent(QDragEnterEvent *event) -{ - if (!event->mimeData()->hasFormat("chatterino/split")) - return; - - if (!isDraggingSplit) { - return; - } - - this->dropRegions.clear(); - - if (this->ui.hbox.count() == 0) { - this->dropRegions.push_back(DropRegion(rect(), std::pair(-1, -1))); - } else { - for (int i = 0; i < this->ui.hbox.count() + 1; ++i) { - this->dropRegions.push_back( - DropRegion(QRect(((i * 4 - 1) * width() / this->ui.hbox.count()) / 4, 0, - width() / this->ui.hbox.count() / 2 + 1, height() + 1), - std::pair(i, -1))); - } - - for (int i = 0; i < this->ui.hbox.count(); ++i) { - auto vbox = static_cast(this->ui.hbox.itemAt(i)); - - for (int j = 0; j < vbox->count() + 1; ++j) { - this->dropRegions.push_back(DropRegion( - QRect(i * width() / this->ui.hbox.count(), - ((j * 2 - 1) * height() / vbox->count()) / 2, - width() / this->ui.hbox.count() + 1, height() / vbox->count() + 1), - - std::pair(i, j))); + this->setCursor(QCursor(Qt::ArrowCursor)); + } else { + auto it = + std::find_if(this->dropRects.begin(), this->dropRects.end(), + [event](DropRect &rect) { return rect.rect.contains(event->pos()); }); + if (it != this->dropRects.end()) { + this->insertSplit(new Split(this), it->position); } } } - - setPreviewRect(event->pos()); - - event->acceptProposedAction(); -} - -void SplitContainer::dragMoveEvent(QDragMoveEvent *event) -{ - setPreviewRect(event->pos()); -} - -void SplitContainer::setPreviewRect(QPoint mousePos) -{ - for (DropRegion region : this->dropRegions) { - if (region.rect.contains(mousePos)) { - this->dropPreview.setBounds(region.rect); - - if (!this->dropPreview.isVisible()) { - this->dropPreview.setGeometry(this->rect()); - this->dropPreview.show(); - this->dropPreview.raise(); - } - - dropPosition = region.position; - - return; - } - } - - this->dropPreview.hide(); -} - -void SplitContainer::dragLeaveEvent(QDragLeaveEvent *event) -{ - this->dropPreview.hide(); -} - -void SplitContainer::dropEvent(QDropEvent *event) -{ - if (isDraggingSplit) { - event->acceptProposedAction(); - - SplitContainer::draggingSplit->setParent(this); - - addToLayout(SplitContainer::draggingSplit, dropPosition); - } - - this->dropPreview.hide(); -} - -bool SplitContainer::eventFilter(QObject *object, QEvent *event) -{ - if (event->type() == QEvent::FocusIn) { - QFocusEvent *focusEvent = static_cast(event); - - this->refreshCurrentFocusCoordinates((focusEvent->reason() == Qt::MouseFocusReason)); - } - - return false; } void SplitContainer::paintEvent(QPaintEvent *) { QPainter painter(this); - if (this->ui.hbox.count() == 0) { + if (this->splits.size() == 0) { painter.fillRect(rect(), this->themeManager->splits.background); painter.setPen(this->themeManager->splits.header.text); @@ -471,6 +197,13 @@ void SplitContainer::paintEvent(QPaintEvent *) painter.fillRect(rect(), this->themeManager->splits.messageSeperator); } + for (DropRect &dropRect : this->dropRects) { + painter.setPen("#774"); + painter.setBrush(QBrush("#553")); + + painter.drawRect(dropRect.rect.marginsRemoved(QMargins(2, 2, 2, 2))); + } + QBrush accentColor = (QApplication::activeWindow() == this->window() ? this->themeManager->tabs.selected.backgrounds.regular : this->themeManager->tabs.selected.backgrounds.unfocused); @@ -478,69 +211,470 @@ void SplitContainer::paintEvent(QPaintEvent *) painter.fillRect(0, 0, width(), 1, accentColor); } -void SplitContainer::showEvent(QShowEvent *event) +void SplitContainer::dragEnterEvent(QDragEnterEvent *event) { - // Whenever this notebook page is shown, give focus to the last focused chat widget - // If this is the first time this notebook page is shown, it will give focus to the top-left - // chat widget - this->requestFocus(this->currentX, this->currentY); -} + if (!event->mimeData()->hasFormat("chatterino/split")) + return; -static std::pair getWidgetPositionInLayout(QLayout *layout, const Split *chatWidget) -{ - for (int i = 0; i < layout->count(); ++i) { - printf("xD\n"); + if (!SplitContainer::isDraggingSplit) { + return; } - return std::make_pair(-1, -1); + this->isDragging = true; + this->layout(); + + event->acceptProposedAction(); + + // this->dropRegions.clear(); + + // if (this->ui.hbox.count() == 0) { + // this->dropRegions.push_back(DropRegion(rect(), std::pair(-1, -1))); + // } else { + // for (int i = 0; i < this->ui.hbox.count() + 1; ++i) { + // this->dropRegions.push_back( + // DropRegion(QRect(((i * 4 - 1) * width() / this->ui.hbox.count()) / 4, 0, + // width() / this->ui.hbox.count() / 2 + 1, height() + 1), + // std::pair(i, -1))); + // } + + // for (int i = 0; i < this->ui.hbox.count(); ++i) { + // auto vbox = static_cast(this->ui.hbox.itemAt(i)); + + // for (int j = 0; j < vbox->count() + 1; ++j) { + // this->dropRegions.push_back(DropRegion( + // QRect(i * width() / this->ui.hbox.count(), + // ((j * 2 - 1) * height() / vbox->count()) / 2, + // width() / this->ui.hbox.count() + 1, height() / vbox->count() + 1), + + // std::pair(i, j))); + // } + // } + // } + + // setPreviewRect(event->pos()); + + // event->acceptProposedAction(); } -std::pair SplitContainer::getChatPosition(const Split *chatWidget) +void SplitContainer::dragMoveEvent(QDragMoveEvent *event) { - auto layout = this->ui.hbox.layout(); + // setPreviewRect(event->pos()); - if (layout == nullptr) { - return std::make_pair(-1, -1); + for (auto &dropRect : this->dropRects) { + if (dropRect.rect.contains(event->pos())) { + event->acceptProposedAction(); + return; + } + } + event->setAccepted(false); +} + +void SplitContainer::dragLeaveEvent(QDragLeaveEvent *event) +{ + this->isDragging = false; + this->layout(); + + // this->dropPreview.hide(); +} + +void SplitContainer::dropEvent(QDropEvent *event) +{ + // if (isDraggingSplit) { + // event->acceptProposedAction(); + + // SplitContainer::draggingSplit->setParent(this); + + // addToLayout(SplitContainer::draggingSplit, dropPosition); + // } + + // this->dropPreview.hide(); + + for (auto &dropRect : this->dropRects) { + if (dropRect.rect.contains(event->pos())) { + this->insertSplit(SplitContainer::draggingSplit, dropRect.position); + event->acceptProposedAction(); + break; + } } - return getWidgetPositionInLayout(layout, chatWidget); + this->isDragging = false; + this->layout(); } -Split *SplitContainer::createChatWidget() -{ - auto split = new Split(this); +// void SplitContainer::updateFlexValues() +//{ +// for (int i = 0; i < this->ui.hbox.count(); i++) { +// QVBoxLayout *vbox = (QVBoxLayout *)ui.hbox.itemAt(i)->layout(); - return split; -} +// if (vbox->count() != 0) { +// ui.hbox.setStretch(i, (int)(1000 * ((Split *)vbox->itemAt(0))->getFlexSizeX())); +// } +// } +//} -void SplitContainer::refreshTitle() +// int SplitContainer::splitCount() const +//{ +// return this->splits.size(); +//} + +// std::pair SplitContainer::removeFromLayout(Split *widget) +//{ +// widget->getChannelView().tabHighlightRequested.disconnectAll(); + +// // remove reference to chat widget from chatWidgets vector +// auto it = std::find(std::begin(this->splits), std::end(this->splits), widget); +// if (it != std::end(this->splits)) { +// this->splits.erase(it); + +// this->refreshTitle(); +// } + +// Split *neighbouringSplit = nullptr; + +// // Position the split was found at +// int positionX = -1, positionY = -1; + +// bool removed = false; + +// QVBoxLayout *layoutToRemove = nullptr; + +// // Find widget in box, remove it, return its position +// for (int i = 0; i < this->ui.hbox.count(); ++i) { +// auto vbox = static_cast(this->ui.hbox.itemAt(i)); + +// auto vboxCount = vbox->count(); + +// for (int j = 0; j < vboxCount; ++j) { +// if (vbox->itemAt(j)->widget() != widget) { +// neighbouringSplit = dynamic_cast(vbox->itemAt(j)->widget()); + +// if (removed && neighbouringSplit != nullptr) { +// // The widget we searched for has been found, and we have a split to switch +// // focus to +// break; +// } + +// continue; +// } + +// removed = true; +// positionX = i; + +// // Remove split from box +// widget->setParent(nullptr); + +// if (vbox->count() == 0) { +// // The split was the last item remaining in the vbox +// // Remove the vbox once all iteration is done +// layoutToRemove = vbox; +// positionY = -1; +// break; +// } + +// // Don't break here yet, we want to keep iterating this vbox if possible to find the +// // closest still-alive neighbour that we can switch focus to +// positionY = j; + +// --j; +// --vboxCount; +// } + +// if (removed && neighbouringSplit != nullptr) { +// // The widget we searched for has been found, and we have a split to switch focus to +// break; +// } +// } + +// if (removed) { +// if (layoutToRemove != nullptr) { +// // The split we removed was the last split in its box. Remove the box +// // We delay the removing of the box so we can keep iterating over hbox safely +// this->ui.hbox.removeItem(layoutToRemove); +// delete layoutToRemove; +// } + +// if (neighbouringSplit != nullptr) { +// // We found a neighbour split we can switch focus to +// neighbouringSplit->giveFocus(Qt::MouseFocusReason); +// } +// } + +// return std::make_pair(positionX, positionY); +//} + +// void SplitContainer::addToLayout(Split *widget, std::pair position) +//{ +// this->splits.push_back(widget); +// widget->getChannelView().tabHighlightRequested.connect( +// [this](HighlightState state) { this->tab->setHighlightState(state); }); + +// this->refreshTitle(); + +// widget->giveFocus(Qt::MouseFocusReason); + +// // add vbox at the end +// if (position.first < 0 || position.first >= this->ui.hbox.count()) { +// auto vbox = new QVBoxLayout(); +// vbox->addWidget(widget); + +// this->ui.hbox.addLayout(vbox, 1); + +// this->refreshCurrentFocusCoordinates(); +// return; +// } + +// // insert vbox +// if (position.second == -1) { +// auto vbox = new QVBoxLayout(); +// vbox->addWidget(widget); + +// this->ui.hbox.insertLayout(position.first, vbox, 1); +// this->refreshCurrentFocusCoordinates(); +// return; +// } + +// // add to existing vbox +// auto vbox = static_cast(this->ui.hbox.itemAt(position.first)); + +// vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget); + +// this->refreshCurrentFocusCoordinates(); +//} + +// const std::vector &SplitContainer::getSplits() const +//{ +// return this->splits; +//} + +// std::vector> SplitContainer::getColumns() const +//{ +// std::vector> columns; + +// for (int i = 0; i < this->ui.hbox.count(); i++) { +// std::vector cells; + +// QLayout *vbox = this->ui.hbox.itemAt(i)->layout(); +// for (int j = 0; j < vbox->count(); j++) { +// cells.push_back(dynamic_cast(vbox->itemAt(j)->widget())); +// } + +// columns.push_back(cells); +// } + +// return columns; +//} + +// void SplitContainer::refreshCurrentFocusCoordinates(bool alsoSetLastRequested) +//{ +// int setX = -1; +// int setY = -1; +// bool doBreak = false; +// for (int x = 0; x < this->ui.hbox.count(); ++x) { +// QLayoutItem *item = this->ui.hbox.itemAt(x); +// if (item->isEmpty()) { +// setX = x; +// break; +// } +// QVBoxLayout *vbox = static_cast(item->layout()); + +// for (int y = 0; y < vbox->count(); ++y) { +// QLayoutItem *innerItem = vbox->itemAt(y); + +// if (innerItem->isEmpty()) { +// setX = x; +// setY = y; +// doBreak = true; +// break; +// } + +// QWidget *w = innerItem->widget(); +// if (w) { +// Split *chatWidget = static_cast(w); +// if (chatWidget->hasFocus()) { +// setX = x; +// setY = y; +// doBreak = true; +// break; +// } +// } +// } + +// if (doBreak) { +// break; +// } +// } + +// if (setX != -1) { +// this->currentX = setX; + +// if (setY != -1) { +// this->currentY = setY; + +// if (alsoSetLastRequested) { +// this->lastRequestedY[setX] = setY; +// } +// } +// } +//} + +// void SplitContainer::requestFocus(int requestedX, int requestedY) +//{ +// // XXX: Perhaps if we request an Y coordinate out of bounds, we shuold set all previously set +// // requestedYs to 0 (if -1 is requested) or that x-coordinates vbox count (if requestedY >= +// // currentvbox.count() is requested) +// if (requestedX < 0 || requestedX >= this->ui.hbox.count()) { +// return; +// } + +// QLayoutItem *item = this->ui.hbox.itemAt(requestedX); +// QWidget *xW = item->widget(); +// if (item->isEmpty()) { +// qDebug() << "Requested hbox item " << requestedX << "is empty"; +// if (xW) { +// qDebug() << "but xW is not null"; +// // TODO: figure out what to do here +// } +// return; +// } + +// QVBoxLayout *vbox = static_cast(item->layout()); + +// if (requestedY < 0) { +// requestedY = 0; +// } else if (requestedY >= vbox->count()) { +// requestedY = vbox->count() - 1; +// } + +// this->lastRequestedY[requestedX] = requestedY; + +// QLayoutItem *innerItem = vbox->itemAt(requestedY); + +// if (innerItem->isEmpty()) { +// qDebug() << "Requested vbox item " << requestedY << "is empty"; +// return; +// } + +// QWidget *w = innerItem->widget(); +// if (w) { +// Split *chatWidget = static_cast(w); +// chatWidget->giveFocus(Qt::OtherFocusReason); +// } +//} + +// void SplitContainer::enterEvent(QEvent *) +//{ +// if (this->ui.hbox.count() == 0) { +// this->setCursor(QCursor(Qt::PointingHandCursor)); +// } else { +// this->setCursor(QCursor(Qt::ArrowCursor)); +// } +//} + +// void SplitContainer::leaveEvent(QEvent *) +//{ +//} + +// void SplitContainer::setPreviewRect(QPoint mousePos) +//{ +// for (DropRegion region : this->dropRegions) { +// if (region.rect.contains(mousePos)) { +// this->dropPreview.setBounds(region.rect); + +// if (!this->dropPreview.isVisible()) { +// this->dropPreview.setGeometry(this->rect()); +// this->dropPreview.show(); +// this->dropPreview.raise(); +// } + +// dropPosition = region.position; + +// return; +// } +// } + +// this->dropPreview.hide(); +//} + +// bool SplitContainer::eventFilter(QObject *object, QEvent *event) +//{ +// if (event->type() == QEvent::FocusIn) { +// QFocusEvent *focusEvent = static_cast(event); + +// this->refreshCurrentFocusCoordinates((focusEvent->reason() == Qt::MouseFocusReason)); +// } + +// return false; +//} + +// void SplitContainer::showEvent(QShowEvent *event) +//{ +// // Whenever this notebook page is shown, give focus to the last focused chat widget +// // If this is the first time this notebook page is shown, it will give focus to the +// top-left +// // chat widget +// this->requestFocus(this->currentX, this->currentY); +//} + +// static std::pair getWidgetPositionInLayout(QLayout *layout, const Split +// *chatWidget) +//{ +// for (int i = 0; i < layout->count(); ++i) { +// printf("xD\n"); +// } + +// return std::make_pair(-1, -1); +//} + +// std::pair SplitContainer::getChatPosition(const Split *chatWidget) +//{ +// auto layout = this->ui.hbox.layout(); + +// if (layout == nullptr) { +// return std::make_pair(-1, -1); +// } + +// return getWidgetPositionInLayout(layout, chatWidget); +//} + +// Split *SplitContainer::createChatWidget() +//{ +// auto split = new Split(this); + +// return split; +//} + +void SplitContainer::refreshTabTitle() { + assert(this->tab != nullptr); + if (!this->tab->useDefaultTitle) { return; } - QString newTitle = ""; - bool first = true; + this->tab->setTitle("default title"); - for (const auto &chatWidget : this->splits) { - auto channelName = chatWidget->getChannel()->name; - if (channelName.isEmpty()) { - continue; - } + // QString newTitle = ""; + // bool first = true; - if (!first) { - newTitle += ", "; - } - newTitle += channelName; + // for (const auto &chatWidget : this->splits) { + // auto channelName = chatWidget->getChannel()->name; + // if (channelName.isEmpty()) { + // continue; + // } - first = false; - } + // if (!first) { + // newTitle += ", "; + // } + // newTitle += channelName; - if (newTitle.isEmpty()) { - newTitle = "empty"; - } + // first = false; + // } - this->tab->setTitle(newTitle); + // if (newTitle.isEmpty()) { + // newTitle = "empty"; + // } + + // this->tab->setTitle(newTitle); } } // namespace widgets diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index e2cef4d8..1ed3253e 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -12,47 +12,433 @@ #include #include +#include +#include +#include + +#include + namespace chatterino { namespace widgets { -class SplitContainer : public BaseWidget +class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder { Q_OBJECT public: + // fourtf: !!! preserve the order of left, up, right and down + enum Direction { Left, Above, Right, Below }; + + struct Node; + + struct Position { + private: + Position() = default; + Position(Node *_relativeNode, Direction _direcion) + : relativeNode(_relativeNode) + , direction(_direcion) + { + } + + Node *relativeNode; + Direction direction; + + friend struct Node; + friend class SplitContainer; + }; + + struct DropRect { + QRect rect; + Position position; + + DropRect(const QRect &_rect, const Position &_position) + : rect(_rect) + , position(_position) + { + } + }; + + struct Node final { + enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer }; + + Type getType() + { + return this->type; + } + Split *getSplit() + { + return this->split; + } + const std::vector> &getChildren() + { + return this->children; + } + + private: + Type type; + Split *split; + Node *parent; + QRectF geometry; + std::vector> children; + + Node() + : type(Type::EmptyRoot) + , split(nullptr) + , parent(nullptr) + { + } + + Node(Split *_split, Node *_parent) + : type(Type::_Split) + , split(_split) + , parent(_parent) + { + } + + bool isOrContainsNode(Node *_node) + { + if (this == _node) { + return true; + } + + return std::any_of( + this->children.begin(), this->children.end(), + [_node](std::unique_ptr &n) { return n->isOrContainsNode(_node); }); + } + + Node *findNodeContainingSplit(Split *_split) + { + if (this->type == Type::_Split && this->split == _split) { + return this; + } + + for (std::unique_ptr &node : this->children) { + Node *a = node->findNodeContainingSplit(_split); + + if (a != nullptr) { + return a; + } + } + return nullptr; + } + + void insertSplitRelative(Split *_split, Direction _direction) + { + if (this->parent == nullptr) { + switch (this->type) { + case Node::EmptyRoot: { + this->setSplit(_split); + } break; + case Node::_Split: { + this->nestSplitIntoCollection(_split, _direction); + } break; + case Node::HorizontalContainer: { + if (toContainerType(_direction) == Node::HorizontalContainer) { + assert(false); + } else { + this->nestSplitIntoCollection(_split, _direction); + } + } break; + case Node::VerticalContainer: { + if (toContainerType(_direction) == Node::VerticalContainer) { + assert(false); + } else { + this->nestSplitIntoCollection(_split, _direction); + } + } break; + } + return; + } + + // parent != nullptr + if (parent->type == toContainerType(_direction)) { + // hell yeah we'll just insert it next to outselves + this->_insertNextToThis(_split, _direction); + } else { + this->nestSplitIntoCollection(_split, _direction); + } + } + + void nestSplitIntoCollection(Split *_split, Direction _direction) + { + // we'll need to nest outselves + // move all our data into a new node + Node *clone = new Node(); + clone->type = this->type; + clone->children = std::move(this->children); + for (std::unique_ptr &node : clone->children) { + node->parent = clone; + } + clone->split = this->split; + clone->parent = this; + + // add the node to our children and change our type + this->children.push_back(std::unique_ptr(clone)); + this->type = toContainerType(_direction); + this->split = nullptr; + + clone->_insertNextToThis(_split, _direction); + } + + void _insertNextToThis(Split *_split, Direction _direction) + { + auto &siblings = this->parent->children; + + qreal size = + // std::accumulate(this->parent->children.begin(), + // this->parent->children.end(), 0, + // [_direction](qreal val, Node *node) { + // if (toContainerType(_direction) == + // Type::VerticalContainer) { + // return val + node->geometry.height(); + // } else { + // return val + node->geometry.width(); + // } + // }); + + this->parent->geometry.width() / siblings.size(); + + if (siblings.size() == 1) { + this->geometry = QRect(0, 0, size, size); + } + + auto it = std::find_if(siblings.begin(), siblings.end(), + [this](auto &node) { return this == node.get(); }); + + assert(it != siblings.end()); + if (_direction == Direction::Right || _direction == Direction::Below) { + it++; + } + + Node *node = new Node(_split, this->parent); + node->geometry = QRectF(0, 0, size, size); + siblings.insert(it, std::unique_ptr(node)); + } + + void setSplit(Split *_split) + { + assert(this->split == nullptr); + assert(this->children.size() == 0); + + this->split = _split; + this->type = Type::_Split; + } + + Position releaseSplit() + { + assert(this->type == Type::_Split); + + if (parent == nullptr) { + this->type = Type::EmptyRoot; + this->split = nullptr; + + Position pos; + pos.relativeNode = nullptr; + pos.direction = Direction::Right; + return pos; + } else { + auto &siblings = this->parent->children; + + auto it = std::find_if(begin(siblings), end(siblings), + [this](auto &node) { return this == node.get(); }); + assert(it != siblings.end()); + + Position position; + if (siblings.size() == 2) { + // delete this and move split to parent + position.relativeNode = this->parent; + if (this->parent->type == Type::VerticalContainer) { + position.direction = + siblings.begin() == it ? Direction::Above : Direction::Below; + } else { + position.direction = + siblings.begin() == it ? Direction::Left : Direction::Right; + } + + Node *_parent = this->parent; + siblings.erase(it); + std::unique_ptr &sibling = siblings.front(); + _parent->type = sibling->type; + _parent->split = sibling->split; + std::vector> nodes = std::move(sibling->children); + for (auto &node : nodes) { + node->parent = _parent; + } + _parent->children = std::move(nodes); + } else { + if (this == siblings.back().get()) { + position.direction = this->parent->type == Type::VerticalContainer + ? Direction::Below + : Direction::Right; + siblings.erase(it); + position.relativeNode = siblings.back().get(); + } else { + position.relativeNode = (it + 1)->get(); + position.direction = this->parent->type == Type::VerticalContainer + ? Direction::Above + : Direction::Left; + siblings.erase(it); + } + } + + return position; + } + } + + void layout(bool addSpacing, std::vector &dropRects) + { + switch (this->type) { + case Node::_Split: { + QRect rect = this->geometry.toRect(); + this->split->setGeometry(rect.marginsRemoved(QMargins(1, 1, 1, 1))); + } break; + case Node::VerticalContainer: { + qreal totalHeight = + std::accumulate(this->children.begin(), this->children.end(), 0, + [](qreal val, std::unique_ptr &node) { + return val + node->geometry.height(); + }); + + qreal childX = this->geometry.left(); + qreal childWidth = this->geometry.width(); + + if (addSpacing) { + qreal offset = this->geometry.width() * 0.1; + dropRects.emplace_back( + QRect(childX, this->geometry.top(), offset, this->geometry.height()), + Position(this, Direction::Left)); + dropRects.emplace_back( + QRect(childX + this->geometry.width() - offset, this->geometry.top(), + offset, this->geometry.height()), + Position(this, Direction::Right)); + + childX += offset; + childWidth -= offset * 2; + } + + qreal scaleFactor = this->geometry.height() / totalHeight; + + qreal y = this->geometry.top(); + for (std::unique_ptr &child : this->children) { + child->geometry = + QRectF(childX, y, childWidth, child->geometry.height() * scaleFactor); + + child->layout(addSpacing, dropRects); + y += child->geometry.height(); + } + } break; + case Node::HorizontalContainer: { + qreal totalWidth = + std::accumulate(this->children.begin(), this->children.end(), 0, + [](qreal val, std::unique_ptr &node) { + return val + node->geometry.width(); + }); + + qreal childY = this->geometry.top(); + qreal childHeight = this->geometry.height(); + + if (addSpacing) { + qreal offset = this->geometry.height() * 0.1; + dropRects.emplace_back( + QRect(this->geometry.left(), childY, this->geometry.width(), offset), + Position(this, Direction::Above)); + dropRects.emplace_back( + QRect(this->geometry.left(), childY + this->geometry.height() - offset, + this->geometry.width(), offset), + Position(this, Direction::Below)); + + childY += this->geometry.height() * 0.1; + childHeight -= this->geometry.height() * 0.2; + } + + qreal scaleFactor = this->geometry.width() / totalWidth; + + qreal x = this->geometry.left(); + for (std::unique_ptr &child : this->children) { + child->geometry = + QRectF(x, childY, child->geometry.width() * scaleFactor, childHeight); + + child->layout(addSpacing, dropRects); + x += child->geometry.width(); + } + } break; + }; + } + + static Type toContainerType(Direction _dir) + { + return _dir == Direction::Left || _dir == Direction::Right ? Type::HorizontalContainer + : Type::VerticalContainer; + } + + friend class SplitContainer; + }; + SplitContainer(Notebook *parent, NotebookTab *_tab); - std::pair removeFromLayout(Split *widget); - void addToLayout(Split *widget, std::pair position = std::pair(-1, -1)); + void appendNewSplit(bool openChannelNameDialog); + void appendSplit(Split *split); + void insertSplit(Split *split, const Position &position); + void insertSplit(Split *split, Direction direction, Split *relativeTo); + void insertSplit(Split *split, Direction direction, Node *relativeTo = nullptr); + Position releaseSplit(Split *split); + Position deleteSplit(Split *split); + + int getSplitCount() + { + return 0; + } + + const std::vector getSplits() const + { + return this->splits; + } + + void refreshTabTitle(); - const std::vector &getSplits() const; - std::vector> getColumns() const; NotebookTab *getTab() const; + const Node *getBaseNode() + { + return &this->baseNode; + } - void addChat(bool openChannelNameDialog = false); + // std::pair removeFromLayout(Split *widget); + // void addToLayout(Split *widget, std::pair position = std::pair(-1, + // -1)); + + // const std::vector &getSplits() const; + // std::vector> getColumns() const; + + // void addChat(bool openChannelNameDialog = false); + + // static std::pair dropPosition; + + // int currentX = 0; + // int currentY = 0; + // std::map lastRequestedY; + + // void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false); + // void requestFocus(int x, int y); + + // void updateFlexValues(); + // int splitCount() const; + + // void loadSplits(); + + // void save(); static bool isDraggingSplit; static Split *draggingSplit; - static std::pair dropPosition; - - int currentX = 0; - int currentY = 0; - std::map lastRequestedY; - - void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false); - void requestFocus(int x, int y); - - void updateFlexValues(); - int splitCount() const; + // static Position dragOriginalPosition; protected: - bool eventFilter(QObject *object, QEvent *event) override; + // bool eventFilter(QObject *object, QEvent *event) override; void paintEvent(QPaintEvent *event) override; - void showEvent(QShowEvent *event) override; + // void showEvent(QShowEvent *event) override; - void enterEvent(QEvent *event) override; - void leaveEvent(QEvent *event) override; + // void enterEvent(QEvent *event) override; + // void leaveEvent(QEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; @@ -60,6 +446,8 @@ protected: void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + private: struct DropRegion { QRect rect; @@ -72,31 +460,32 @@ private: } }; - NotebookTab *tab; - - struct { - QVBoxLayout parentLayout; - - QHBoxLayout hbox; - } ui; - - std::vector splits; + std::vector dropRects; std::vector dropRegions; - NotebookPageDropPreview dropPreview; - void setPreviewRect(QPoint mousePos); + void layout(); - std::pair getChatPosition(const Split *chatWidget); + Node baseNode; - Split *createChatWidget(); + NotebookTab *tab; + std::vector splits; -public: - void refreshTitle(); + bool isDragging = false; - void loadSplits(); + // struct { + // QVBoxLayout parentLayout; - void save(); + // QHBoxLayout hbox; + // } ui; + + // std::vector splits; + + // void setPreviewRect(QPoint mousePos); + + // std::pair getChatPosition(const Split *chatWidget); + + // Split *createChatWidget(); }; } // namespace widgets From 27cd953c8caf3e35e1de8783d578a6c87b9b702d Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 10 May 2018 23:58:07 +0200 Subject: [PATCH 003/121] improved splits --- src/widgets/helper/dropoverlay.cpp | 13 + src/widgets/helper/dropoverlay.hpp | 11 + src/widgets/helper/splitnode.cpp | 6 + src/widgets/helper/splitnode.hpp | 11 + src/widgets/helper/splitoverlay.cpp | 22 +- src/widgets/helper/splitoverlay.hpp | 9 + src/widgets/split.cpp | 57 ++-- src/widgets/split.hpp | 6 +- src/widgets/splitcontainer.cpp | 415 ++++++++-------------------- src/widgets/splitcontainer.hpp | 149 +++++++--- 10 files changed, 336 insertions(+), 363 deletions(-) create mode 100644 src/widgets/helper/dropoverlay.cpp create mode 100644 src/widgets/helper/dropoverlay.hpp create mode 100644 src/widgets/helper/splitnode.cpp create mode 100644 src/widgets/helper/splitnode.hpp diff --git a/src/widgets/helper/dropoverlay.cpp b/src/widgets/helper/dropoverlay.cpp new file mode 100644 index 00000000..f98160a5 --- /dev/null +++ b/src/widgets/helper/dropoverlay.cpp @@ -0,0 +1,13 @@ +//#include "dropoverlay.hpp" + +// namespace chatterino { +// namespace widgets { +// namespace helper { + +// DropOverlay::DropOverlay() +//{ +//} + +//} // namespace helper +//} // namespace widgets +//} // namespace chatterino diff --git a/src/widgets/helper/dropoverlay.hpp b/src/widgets/helper/dropoverlay.hpp new file mode 100644 index 00000000..7ae89104 --- /dev/null +++ b/src/widgets/helper/dropoverlay.hpp @@ -0,0 +1,11 @@ +//#pragma once + +//#include "widgets/helper/splitnode.hpp" + +// namespace chatterino { +// namespace widgets { +// namespace helper { + +//} // namespace helper +//} // namespace widgets +//} // namespace chatterino diff --git a/src/widgets/helper/splitnode.cpp b/src/widgets/helper/splitnode.cpp new file mode 100644 index 00000000..fac9a673 --- /dev/null +++ b/src/widgets/helper/splitnode.cpp @@ -0,0 +1,6 @@ +#include "splitnode.hpp" + +SplitNode::SplitNode() +{ + +} diff --git a/src/widgets/helper/splitnode.hpp b/src/widgets/helper/splitnode.hpp new file mode 100644 index 00000000..dc57b098 --- /dev/null +++ b/src/widgets/helper/splitnode.hpp @@ -0,0 +1,11 @@ +#ifndef SPLITNODE_HPP +#define SPLITNODE_HPP + + +class SplitNode +{ +public: + SplitNode(); +}; + +#endif // SPLITNODE_HPP \ No newline at end of file diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index fd6f2324..a43ac956 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -19,6 +19,7 @@ SplitOverlay::SplitOverlay(Split *parent) , split(parent) { QGridLayout *layout = new QGridLayout(this); + this->_layout = layout; layout->setMargin(1); layout->setSpacing(1); @@ -28,10 +29,11 @@ SplitOverlay::SplitOverlay(Split *parent) layout->setColumnStretch(3, 1); QPushButton *move = new QPushButton(getApp()->resources->split.move, QString()); - QPushButton *left = new QPushButton(getApp()->resources->split.left, QString()); - QPushButton *right = new QPushButton(getApp()->resources->split.right, QString()); - QPushButton *up = new QPushButton(getApp()->resources->split.up, QString()); - QPushButton *down = new QPushButton(getApp()->resources->split.down, QString()); + QPushButton *left = this->_left = new QPushButton(getApp()->resources->split.left, QString()); + QPushButton *right = this->_right = + new QPushButton(getApp()->resources->split.right, QString()); + QPushButton *up = this->_up = new QPushButton(getApp()->resources->split.up, QString()); + QPushButton *down = this->_down = new QPushButton(getApp()->resources->split.down, QString()); move->setGraphicsEffect(new QGraphicsOpacityEffect(this)); left->setGraphicsEffect(new QGraphicsOpacityEffect(this)); @@ -108,6 +110,18 @@ void SplitOverlay::paintEvent(QPaintEvent *event) } } +void SplitOverlay::resizeEvent(QResizeEvent *event) +{ + float scale = this->getScale(); + bool wideEnough = event->size().width() > 150 * scale; + bool highEnough = event->size().height() > 150 * scale; + + this->_left->setVisible(wideEnough); + this->_right->setVisible(wideEnough); + this->_up->setVisible(highEnough); + this->_down->setVisible(highEnough); +} + SplitOverlay::ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent, HoveredElement _element) : QObject(_parent) , parent(_parent) diff --git a/src/widgets/helper/splitoverlay.hpp b/src/widgets/helper/splitoverlay.hpp index de24dac6..4877835c 100644 --- a/src/widgets/helper/splitoverlay.hpp +++ b/src/widgets/helper/splitoverlay.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "pajlada/signals/signalholder.hpp" #include "widgets/basewidget.hpp" @@ -15,12 +18,18 @@ public: protected: void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; private: // fourtf: !!! preserve the order of left, up, right and down enum HoveredElement { None, SplitMove, SplitLeft, SplitUp, SplitRight, SplitDown }; HoveredElement hoveredElement = None; Split *split; + QGridLayout *_layout; + QPushButton *_left; + QPushButton *_up; + QPushButton *_right; + QPushButton *_down; class ButtonEventFilter : public QObject { diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 4d8681a0..1d1b9a5e 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -39,8 +39,8 @@ using namespace chatterino::messages; namespace chatterino { namespace widgets { -pajlada::Signals::Signal Split::altPressedStatusChanged; -bool Split::altPressesStatus = false; +pajlada::Signals::Signal Split::modifierStatusChanged; +Qt::KeyboardModifiers Split::modifierStatus = Qt::NoModifier; Split::Split(SplitContainer *parent) : Split((QWidget *)parent) @@ -128,15 +128,14 @@ Split::Split(QWidget *parent) this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - this->managedConnect(altPressedStatusChanged, [this](bool status) { - // if (status && this->isMouseOver) { - // this->overlay->show(); - // } else { - // this->overlay->hide(); - // } + this->managedConnect(modifierStatusChanged, [this](Qt::KeyboardModifiers status) { + if ((status == Qt::AltModifier || status == (Qt::AltModifier | Qt::ControlModifier)) && + this->isMouseOver) { + this->overlay->show(); + } else { + this->overlay->hide(); + } }); - - this->setAcceptDrops(true); } Split::~Split() @@ -161,6 +160,11 @@ bool Split::isInContainer() const return this->container != nullptr; } +void Split::setContainer(SplitContainer *_container) +{ + this->container = _container; +} + IndirectChannel Split::getIndirectChannel() { return this->channel; @@ -299,7 +303,11 @@ void Split::resizeEvent(QResizeEvent *event) void Split::enterEvent(QEvent *event) { this->isMouseOver = true; - if (altPressesStatus) { + + auto a = modifierStatus; + + if (modifierStatus == Qt::AltModifier || + modifierStatus == (Qt::AltModifier | Qt::ControlModifier)) { this->overlay->show(); } } @@ -312,18 +320,23 @@ void Split::leaveEvent(QEvent *event) void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers) { - if (modifiers == Qt::AltModifier) { - if (!altPressesStatus) { - altPressesStatus = true; - altPressedStatusChanged.invoke(true); - } - } else { - if (altPressesStatus) { - altPressesStatus = false; - altPressedStatusChanged.invoke(false); - } - this->setCursor(Qt::ArrowCursor); + if (modifierStatus != modifiers) { + modifierStatus = modifiers; + modifierStatusChanged.invoke(modifiers); } + + // if (modifiers == Qt::AltModifier) { + // if (!modifierStatus) { + // modifierStatus = true; + // modifierStatusChanged.invoke(true); + // } + // } else { + // if (modifierStatus) { + // modifierStatus = false; + // modifierStatusChanged.invoke(false); + // } + // this->setCursor(Qt::ArrowCursor); + // } } void Split::dragEnterEvent(QDragEnterEvent *event) diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 450623d0..565844b3 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -69,8 +69,10 @@ public: bool isInContainer() const; - static pajlada::Signals::Signal altPressedStatusChanged; - static bool altPressesStatus; + void setContainer(SplitContainer *container); + + static pajlada::Signals::Signal modifierStatusChanged; + static Qt::KeyboardModifiers modifierStatus; protected: void paintEvent(QPaintEvent *event) override; diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 0cf7f2c9..53395e45 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -1,4 +1,5 @@ #include "widgets/splitcontainer.hpp" +#include "application.hpp" #include "common.hpp" #include "singletons/thememanager.hpp" #include "util/helpers.hpp" @@ -30,25 +31,29 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) : BaseWidget(parent) , tab(_tab) , dropPreview(this) + , mouseOverPoint(-10000, -10000) + , overlay(this) { this->tab->page = this; - // this->setLayout(&this->ui.parentLayout); - - // this->setHidden(true); - // this->setAcceptDrops(true); - - // this->ui.parentLayout.addSpacing(1); - // this->ui.parentLayout.addLayout(&this->ui.hbox); - // this->ui.parentLayout.setMargin(0); - - // this->ui.hbox.setSpacing(1); - // this->ui.hbox.setMargin(0); - this->refreshTabTitle(); - this->managedConnect(Split::altPressedStatusChanged, [this](auto) { this->layout(); }); + this->managedConnect(Split::modifierStatusChanged, [this](auto) { + // fourtf: maybe optimize + this->layout(); + }); + this->setCursor(Qt::PointingHandCursor); + this->setAcceptDrops(true); + + this->managedConnect(this->overlay.dragEnded, [this]() { + this->isDragging = false; + this->layout(); + }); + + this->overlay.hide(); + + this->setMouseTracking(true); this->setAcceptDrops(true); } @@ -91,6 +96,8 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Split *relat void SplitContainer::insertSplit(Split *split, Direction direction, Node *relativeTo) { + split->setContainer(this); + if (relativeTo == nullptr) { if (this->baseNode.type == Node::EmptyRoot) { this->baseNode.setSplit(split); @@ -107,8 +114,11 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Node *relati split->setParent(this); split->show(); + split->giveFocus(Qt::MouseFocusReason); this->splits.push_back(split); + // this->setAcceptDrops(false); + this->layout(); } @@ -121,6 +131,14 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) split->setParent(nullptr); Position position = node->releaseSplit(); this->layout(); + if (splits.size() != 0) { + this->splits.front()->giveFocus(Qt::MouseFocusReason); + } + + // if (this->splits.empty()) { + // this->setAcceptDrops(true); + // } + return position; } @@ -137,11 +155,41 @@ void SplitContainer::layout() this->baseNode.geometry = this->rect(); std::vector _dropRects; - this->baseNode.layout(Split::altPressesStatus | this->isDragging, _dropRects); + this->baseNode.layout( + Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging, + this->getScale(), _dropRects); _dropRects.clear(); - this->baseNode.layout(Split::altPressesStatus | this->isDragging, _dropRects); + this->baseNode.layout( + Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging, + this->getScale(), _dropRects); - this->dropRects = std::move(_dropRects); + this->dropRects = _dropRects; + + for (Split *split : this->splits) { + const QRect &g = split->geometry(); + + Node *node = this->baseNode.findNodeContainingSplit(split); + + _dropRects.push_back(DropRect(QRect(g.left(), g.top(), g.width() / 4, g.height()), + Position(node, Direction::Left))); + _dropRects.push_back( + DropRect(QRect(g.left() + g.width() / 4 * 3, g.top(), g.width() / 4, g.height()), + Position(node, Direction::Right))); + + _dropRects.push_back(DropRect(QRect(g.left(), g.top(), g.width(), g.height() / 2), + Position(node, Direction::Above))); + _dropRects.push_back( + DropRect(QRect(g.left(), g.top() + g.height() / 2, g.width(), g.height() / 2), + Position(node, Direction::Below))); + } + + if (this->splits.empty()) { + QRect g = this->rect(); + _dropRects.push_back(DropRect(QRect(g.left(), g.top(), g.width(), g.height()), + Position(nullptr, Direction::Below))); + } + + this->overlay.setRects(std::move(_dropRects)); this->update(); } @@ -160,7 +208,7 @@ void SplitContainer::mouseReleaseEvent(QMouseEvent *event) // "Add Chat" was clicked this->appendNewSplit(true); - this->setCursor(QCursor(Qt::ArrowCursor)); + // this->setCursor(QCursor(Qt::ArrowCursor)); } else { auto it = std::find_if(this->dropRects.begin(), this->dropRects.end(), @@ -198,8 +246,16 @@ void SplitContainer::paintEvent(QPaintEvent *) } for (DropRect &dropRect : this->dropRects) { - painter.setPen("#774"); - painter.setBrush(QBrush("#553")); + QColor border = getApp()->themes->splits.dropPreviewBorder; + QColor background = getApp()->themes->splits.dropPreview; + + if (!dropRect.rect.contains(this->mouseOverPoint)) { + // border.setAlphaF(0.1); + background.setAlphaF(0.1); + } + + painter.setPen(border); + painter.setBrush(background); painter.drawRect(dropRect.rect.marginsRemoved(QMargins(2, 2, 2, 2))); } @@ -213,310 +269,57 @@ void SplitContainer::paintEvent(QPaintEvent *) void SplitContainer::dragEnterEvent(QDragEnterEvent *event) { - if (!event->mimeData()->hasFormat("chatterino/split")) - return; + // if (!event->mimeData()->hasFormat("chatterino/split")) + // return; - if (!SplitContainer::isDraggingSplit) { - return; - } + // if (!SplitContainer::isDraggingSplit) { + // return; + // } this->isDragging = true; this->layout(); - event->acceptProposedAction(); - - // this->dropRegions.clear(); - - // if (this->ui.hbox.count() == 0) { - // this->dropRegions.push_back(DropRegion(rect(), std::pair(-1, -1))); - // } else { - // for (int i = 0; i < this->ui.hbox.count() + 1; ++i) { - // this->dropRegions.push_back( - // DropRegion(QRect(((i * 4 - 1) * width() / this->ui.hbox.count()) / 4, 0, - // width() / this->ui.hbox.count() / 2 + 1, height() + 1), - // std::pair(i, -1))); - // } - - // for (int i = 0; i < this->ui.hbox.count(); ++i) { - // auto vbox = static_cast(this->ui.hbox.itemAt(i)); - - // for (int j = 0; j < vbox->count() + 1; ++j) { - // this->dropRegions.push_back(DropRegion( - // QRect(i * width() / this->ui.hbox.count(), - // ((j * 2 - 1) * height() / vbox->count()) / 2, - // width() / this->ui.hbox.count() + 1, height() / vbox->count() + 1), - - // std::pair(i, j))); - // } - // } + // if (this->splits.empty()) { + // event->acceptProposedAction(); // } - // setPreviewRect(event->pos()); - - // event->acceptProposedAction(); + this->overlay.setGeometry(this->rect()); + this->overlay.show(); + this->overlay.raise(); } void SplitContainer::dragMoveEvent(QDragMoveEvent *event) { - // setPreviewRect(event->pos()); + // if (this->splits.empty()) { + // event->acceptProposedAction(); + // } - for (auto &dropRect : this->dropRects) { - if (dropRect.rect.contains(event->pos())) { - event->acceptProposedAction(); - return; - } - } - event->setAccepted(false); + // for (auto &dropRect : this->dropRects) { + // if (dropRect.rect.contains(event->pos())) { + // event->acceptProposedAction(); + // return; + // } + // } + // event->setAccepted(false); } void SplitContainer::dragLeaveEvent(QDragLeaveEvent *event) { - this->isDragging = false; - this->layout(); - - // this->dropPreview.hide(); + // this->isDragging = false; + // this->layout(); } void SplitContainer::dropEvent(QDropEvent *event) { - // if (isDraggingSplit) { + // if (this->splits.empty()) { + // this->insertSplit(SplitContainer::draggingSplit, Direction::Above); // event->acceptProposedAction(); - - // SplitContainer::draggingSplit->setParent(this); - - // addToLayout(SplitContainer::draggingSplit, dropPosition); // } - // this->dropPreview.hide(); - - for (auto &dropRect : this->dropRects) { - if (dropRect.rect.contains(event->pos())) { - this->insertSplit(SplitContainer::draggingSplit, dropRect.position); - event->acceptProposedAction(); - break; - } - } - - this->isDragging = false; - this->layout(); + // this->isDragging = false; + // this->layout(); } -// void SplitContainer::updateFlexValues() -//{ -// for (int i = 0; i < this->ui.hbox.count(); i++) { -// QVBoxLayout *vbox = (QVBoxLayout *)ui.hbox.itemAt(i)->layout(); - -// if (vbox->count() != 0) { -// ui.hbox.setStretch(i, (int)(1000 * ((Split *)vbox->itemAt(0))->getFlexSizeX())); -// } -// } -//} - -// int SplitContainer::splitCount() const -//{ -// return this->splits.size(); -//} - -// std::pair SplitContainer::removeFromLayout(Split *widget) -//{ -// widget->getChannelView().tabHighlightRequested.disconnectAll(); - -// // remove reference to chat widget from chatWidgets vector -// auto it = std::find(std::begin(this->splits), std::end(this->splits), widget); -// if (it != std::end(this->splits)) { -// this->splits.erase(it); - -// this->refreshTitle(); -// } - -// Split *neighbouringSplit = nullptr; - -// // Position the split was found at -// int positionX = -1, positionY = -1; - -// bool removed = false; - -// QVBoxLayout *layoutToRemove = nullptr; - -// // Find widget in box, remove it, return its position -// for (int i = 0; i < this->ui.hbox.count(); ++i) { -// auto vbox = static_cast(this->ui.hbox.itemAt(i)); - -// auto vboxCount = vbox->count(); - -// for (int j = 0; j < vboxCount; ++j) { -// if (vbox->itemAt(j)->widget() != widget) { -// neighbouringSplit = dynamic_cast(vbox->itemAt(j)->widget()); - -// if (removed && neighbouringSplit != nullptr) { -// // The widget we searched for has been found, and we have a split to switch -// // focus to -// break; -// } - -// continue; -// } - -// removed = true; -// positionX = i; - -// // Remove split from box -// widget->setParent(nullptr); - -// if (vbox->count() == 0) { -// // The split was the last item remaining in the vbox -// // Remove the vbox once all iteration is done -// layoutToRemove = vbox; -// positionY = -1; -// break; -// } - -// // Don't break here yet, we want to keep iterating this vbox if possible to find the -// // closest still-alive neighbour that we can switch focus to -// positionY = j; - -// --j; -// --vboxCount; -// } - -// if (removed && neighbouringSplit != nullptr) { -// // The widget we searched for has been found, and we have a split to switch focus to -// break; -// } -// } - -// if (removed) { -// if (layoutToRemove != nullptr) { -// // The split we removed was the last split in its box. Remove the box -// // We delay the removing of the box so we can keep iterating over hbox safely -// this->ui.hbox.removeItem(layoutToRemove); -// delete layoutToRemove; -// } - -// if (neighbouringSplit != nullptr) { -// // We found a neighbour split we can switch focus to -// neighbouringSplit->giveFocus(Qt::MouseFocusReason); -// } -// } - -// return std::make_pair(positionX, positionY); -//} - -// void SplitContainer::addToLayout(Split *widget, std::pair position) -//{ -// this->splits.push_back(widget); -// widget->getChannelView().tabHighlightRequested.connect( -// [this](HighlightState state) { this->tab->setHighlightState(state); }); - -// this->refreshTitle(); - -// widget->giveFocus(Qt::MouseFocusReason); - -// // add vbox at the end -// if (position.first < 0 || position.first >= this->ui.hbox.count()) { -// auto vbox = new QVBoxLayout(); -// vbox->addWidget(widget); - -// this->ui.hbox.addLayout(vbox, 1); - -// this->refreshCurrentFocusCoordinates(); -// return; -// } - -// // insert vbox -// if (position.second == -1) { -// auto vbox = new QVBoxLayout(); -// vbox->addWidget(widget); - -// this->ui.hbox.insertLayout(position.first, vbox, 1); -// this->refreshCurrentFocusCoordinates(); -// return; -// } - -// // add to existing vbox -// auto vbox = static_cast(this->ui.hbox.itemAt(position.first)); - -// vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget); - -// this->refreshCurrentFocusCoordinates(); -//} - -// const std::vector &SplitContainer::getSplits() const -//{ -// return this->splits; -//} - -// std::vector> SplitContainer::getColumns() const -//{ -// std::vector> columns; - -// for (int i = 0; i < this->ui.hbox.count(); i++) { -// std::vector cells; - -// QLayout *vbox = this->ui.hbox.itemAt(i)->layout(); -// for (int j = 0; j < vbox->count(); j++) { -// cells.push_back(dynamic_cast(vbox->itemAt(j)->widget())); -// } - -// columns.push_back(cells); -// } - -// return columns; -//} - -// void SplitContainer::refreshCurrentFocusCoordinates(bool alsoSetLastRequested) -//{ -// int setX = -1; -// int setY = -1; -// bool doBreak = false; -// for (int x = 0; x < this->ui.hbox.count(); ++x) { -// QLayoutItem *item = this->ui.hbox.itemAt(x); -// if (item->isEmpty()) { -// setX = x; -// break; -// } -// QVBoxLayout *vbox = static_cast(item->layout()); - -// for (int y = 0; y < vbox->count(); ++y) { -// QLayoutItem *innerItem = vbox->itemAt(y); - -// if (innerItem->isEmpty()) { -// setX = x; -// setY = y; -// doBreak = true; -// break; -// } - -// QWidget *w = innerItem->widget(); -// if (w) { -// Split *chatWidget = static_cast(w); -// if (chatWidget->hasFocus()) { -// setX = x; -// setY = y; -// doBreak = true; -// break; -// } -// } -// } - -// if (doBreak) { -// break; -// } -// } - -// if (setX != -1) { -// this->currentX = setX; - -// if (setY != -1) { -// this->currentY = setY; - -// if (alsoSetLastRequested) { -// this->lastRequestedY[setX] = setY; -// } -// } -// } -//} - // void SplitContainer::requestFocus(int requestedX, int requestedY) //{ // // XXX: Perhaps if we request an Y coordinate out of bounds, we shuold set all previously set @@ -561,7 +364,7 @@ void SplitContainer::dropEvent(QDropEvent *event) // } //} -// void SplitContainer::enterEvent(QEvent *) +// void SplitContainer::enterEvent(QEvent *event) //{ // if (this->ui.hbox.count() == 0) { // this->setCursor(QCursor(Qt::PointingHandCursor)); @@ -570,9 +373,17 @@ void SplitContainer::dropEvent(QDropEvent *event) // } //} -// void SplitContainer::leaveEvent(QEvent *) -//{ -//} +void SplitContainer::mouseMoveEvent(QMouseEvent *event) +{ + this->mouseOverPoint = event->pos(); + this->update(); +} + +void SplitContainer::leaveEvent(QEvent *event) +{ + this->mouseOverPoint = QPoint(-1000, -10000); + this->update(); +} // void SplitContainer::setPreviewRect(QPoint mousePos) //{ diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index 1ed3253e..1fb3043a 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -16,8 +16,13 @@ #include #include +#include #include +// remove +#include "application.hpp" +#include "singletons/thememanager.hpp" + namespace chatterino { namespace widgets { @@ -285,7 +290,7 @@ public: } } - void layout(bool addSpacing, std::vector &dropRects) + void layout(bool addSpacing, float _scale, std::vector &dropRects) { switch (this->type) { case Node::_Split: { @@ -303,7 +308,8 @@ public: qreal childWidth = this->geometry.width(); if (addSpacing) { - qreal offset = this->geometry.width() * 0.1; + qreal offset = std::min(this->geometry.width() * 0.1, _scale * 24); + dropRects.emplace_back( QRect(childX, this->geometry.top(), offset, this->geometry.height()), Position(this, Direction::Left)); @@ -323,7 +329,7 @@ public: child->geometry = QRectF(childX, y, childWidth, child->geometry.height() * scaleFactor); - child->layout(addSpacing, dropRects); + child->layout(addSpacing, _scale, dropRects); y += child->geometry.height(); } } break; @@ -338,7 +344,7 @@ public: qreal childHeight = this->geometry.height(); if (addSpacing) { - qreal offset = this->geometry.height() * 0.1; + qreal offset = std::min(this->geometry.height() * 0.1, _scale * 24); dropRects.emplace_back( QRect(this->geometry.left(), childY, this->geometry.width(), offset), Position(this, Direction::Above)); @@ -347,8 +353,8 @@ public: this->geometry.width(), offset), Position(this, Direction::Below)); - childY += this->geometry.height() * 0.1; - childHeight -= this->geometry.height() * 0.2; + childY += offset; + childHeight -= offset * 2; } qreal scaleFactor = this->geometry.width() / totalWidth; @@ -358,7 +364,7 @@ public: child->geometry = QRectF(x, childY, child->geometry.width() * scaleFactor, childHeight); - child->layout(addSpacing, dropRects); + child->layout(addSpacing, _scale, dropRects); x += child->geometry.width(); } } break; @@ -374,6 +380,105 @@ public: friend class SplitContainer; }; + class DropOverlay : public QWidget + { + public: + DropOverlay(SplitContainer *_parent = nullptr) + : QWidget(_parent) + , parent(_parent) + , mouseOverPoint(-10000, -10000) + { + this->setMouseTracking(true); + this->setAcceptDrops(true); + } + + void setRects(std::vector _rects) + { + this->rects = std::move(_rects); + } + + pajlada::Signals::NoArgSignal dragEnded; + + protected: + void paintEvent(QPaintEvent *event) override + { + QPainter painter(this); + + // painter.fillRect(this->rect(), QColor("#334")); + + bool foundMover = false; + + for (DropRect &rect : this->rects) { + if (!foundMover && rect.rect.contains(this->mouseOverPoint)) { + painter.setBrush(getApp()->themes->splits.dropPreview); + painter.setPen(getApp()->themes->splits.dropPreviewBorder); + foundMover = true; + } else { + painter.setBrush(QColor(0, 0, 0, 0)); + painter.setPen(QColor(0, 0, 0, 0)); + // painter.setPen(getApp()->themes->splits.dropPreviewBorder); + } + + painter.drawRect(rect.rect); + } + } + + void mouseMoveEvent(QMouseEvent *event) + { + this->mouseOverPoint = event->pos(); + } + + void leaveEvent(QEvent *event) + { + this->mouseOverPoint = QPoint(-10000, -10000); + } + + void dragEnterEvent(QDragEnterEvent *event) + { + event->acceptProposedAction(); + } + + void dragMoveEvent(QDragMoveEvent *event) + { + event->acceptProposedAction(); + + this->mouseOverPoint = event->pos(); + this->update(); + } + + void dragLeaveEvent(QDragLeaveEvent *event) + { + this->mouseOverPoint = QPoint(-10000, -10000); + this->close(); + this->dragEnded.invoke(); + } + + void dropEvent(QDropEvent *event) + { + Position *position = nullptr; + for (DropRect &rect : this->rects) { + if (rect.rect.contains(this->mouseOverPoint)) { + position = &rect.position; + break; + } + } + + if (position != nullptr) { + this->parent->insertSplit(SplitContainer::draggingSplit, *position); + event->acceptProposedAction(); + } + + this->mouseOverPoint = QPoint(-10000, -10000); + this->close(); + this->dragEnded.invoke(); + } + + private: + std::vector rects; + QPoint mouseOverPoint; + SplitContainer *parent; + }; + SplitContainer(Notebook *parent, NotebookTab *_tab); void appendNewSplit(bool openChannelNameDialog); @@ -402,31 +507,6 @@ public: return &this->baseNode; } - // std::pair removeFromLayout(Split *widget); - // void addToLayout(Split *widget, std::pair position = std::pair(-1, - // -1)); - - // const std::vector &getSplits() const; - // std::vector> getColumns() const; - - // void addChat(bool openChannelNameDialog = false); - - // static std::pair dropPosition; - - // int currentX = 0; - // int currentY = 0; - // std::map lastRequestedY; - - // void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false); - // void requestFocus(int x, int y); - - // void updateFlexValues(); - // int splitCount() const; - - // void loadSplits(); - - // void save(); - static bool isDraggingSplit; static Split *draggingSplit; // static Position dragOriginalPosition; @@ -438,7 +518,8 @@ protected: // void showEvent(QShowEvent *event) override; // void enterEvent(QEvent *event) override; - // void leaveEvent(QEvent *event) override; + void leaveEvent(QEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; @@ -463,6 +544,8 @@ private: std::vector dropRects; std::vector dropRegions; NotebookPageDropPreview dropPreview; + DropOverlay overlay; + QPoint mouseOverPoint; void layout(); From 28fb877020382430eb2d7e61676e02edaaa0c3e8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 11 May 2018 13:55:10 +0200 Subject: [PATCH 004/121] updated splitheader --- chatterino.pro | 8 ++++++-- src/widgets/helper/splitheader.cpp | 14 +++++++------- src/widgets/helper/splitoverlay.cpp | 6 +++--- src/widgets/splitcontainer.hpp | 19 ++++--------------- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index 980bb2c5..5317d757 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -197,7 +197,9 @@ SOURCES += \ src/controllers/accounts/accountcontroller.cpp \ src/controllers/accounts/accountmodel.cpp \ src/controllers/accounts/account.cpp \ - src/widgets/helper/splitoverlay.cpp + src/widgets/helper/splitoverlay.cpp \ + src/widgets/helper/dropoverlay.cpp \ + src/widgets/helper/splitnode.cpp HEADERS += \ src/precompiled_header.hpp \ @@ -343,7 +345,9 @@ HEADERS += \ src/controllers/accounts/accountmodel.hpp \ src/controllers/accounts/account.hpp \ src/util/sharedptrelementless.hpp \ - src/widgets/helper/splitoverlay.hpp + src/widgets/helper/splitoverlay.hpp \ + src/widgets/helper/dropoverlay.hpp \ + src/widgets/helper/splitnode.hpp RESOURCES += \ resources/resources.qrc diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 2b278dea..42fef1c0 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -241,13 +241,13 @@ void SplitHeader::mouseMoveEvent(QMouseEvent *event) tooltipWidget->show(); } - // if (this->dragging) { - // if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || - // std::abs(this->dragStart.y() - event->pos().y()) > 12) { - // this->split->drag(); - // this->dragging = false; - // } - // } + if (this->dragging) { + if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || + std::abs(this->dragStart.y() - event->pos().y()) > 12) { + this->split->drag(); + this->dragging = false; + } + } } void SplitHeader::leaveEvent(QEvent *event) diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index a43ac956..27ac05a0 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -112,9 +112,9 @@ void SplitOverlay::paintEvent(QPaintEvent *event) void SplitOverlay::resizeEvent(QResizeEvent *event) { - float scale = this->getScale(); - bool wideEnough = event->size().width() > 150 * scale; - bool highEnough = event->size().height() > 150 * scale; + float _scale = this->getScale(); + bool wideEnough = event->size().width() > 150 * _scale; + bool highEnough = event->size().height() > 150 * _scale; this->_left->setVisible(wideEnough); this->_right->setVisible(wideEnough); diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index 1fb3043a..8feb137a 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -189,22 +189,11 @@ public: { auto &siblings = this->parent->children; - qreal size = - // std::accumulate(this->parent->children.begin(), - // this->parent->children.end(), 0, - // [_direction](qreal val, Node *node) { - // if (toContainerType(_direction) == - // Type::VerticalContainer) { - // return val + node->geometry.height(); - // } else { - // return val + node->geometry.width(); - // } - // }); - - this->parent->geometry.width() / siblings.size(); + qreal width = this->parent->geometry.width() / siblings.size(); + qreal height = this->parent->geometry.height() / siblings.size(); if (siblings.size() == 1) { - this->geometry = QRect(0, 0, size, size); + this->geometry = QRect(0, 0, width, height); } auto it = std::find_if(siblings.begin(), siblings.end(), @@ -216,7 +205,7 @@ public: } Node *node = new Node(_split, this->parent); - node->geometry = QRectF(0, 0, size, size); + node->geometry = QRectF(0, 0, width, height); siblings.insert(it, std::unique_ptr(node)); } From 5b26cdaa0777562a0b3c663a203528eca56bd5df Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 16 May 2018 14:55:45 +0200 Subject: [PATCH 005/121] added split resizing and splitting --- src/singletons/windowmanager.cpp | 73 ++- src/singletons/windowmanager.hpp | 12 +- src/widgets/basewidget.cpp | 1 - src/widgets/helper/splitheader.cpp | 58 +- src/widgets/helper/splitheader.hpp | 3 + src/widgets/splitcontainer.cpp | 852 ++++++++++++++++++++++------- src/widgets/splitcontainer.hpp | 480 +++------------- src/widgets/tooltipwidget.cpp | 5 + src/widgets/tooltipwidget.hpp | 1 + 9 files changed, 856 insertions(+), 629 deletions(-) diff --git a/src/singletons/windowmanager.cpp b/src/singletons/windowmanager.cpp index ec077895..a7716aae 100644 --- a/src/singletons/windowmanager.cpp +++ b/src/singletons/windowmanager.cpp @@ -10,6 +10,7 @@ #include "widgets/accountswitchpopupwidget.hpp" #include "widgets/settingsdialog.hpp" +#include #include #include @@ -20,6 +21,9 @@ namespace chatterino { namespace singletons { +using SplitNode = widgets::SplitContainer::Node; +using SplitDirection = widgets::SplitContainer::Direction; + void WindowManager::showSettingsDialog() { QTimer::singleShot(80, [] { widgets::SettingsDialog::showDialog(); }); @@ -201,15 +205,24 @@ void WindowManager::initialize() } // load splits + QJsonObject splitRoot = tab_obj.value("splits2").toObject(); + + if (!splitRoot.isEmpty()) { + tab->decodeFromJson(splitRoot); + + continue; + } + + // fallback load splits (old) int colNr = 0; for (QJsonValue column_val : tab_obj.value("splits").toArray()) { for (QJsonValue split_val : column_val.toArray()) { widgets::Split *split = new widgets::Split(tab); QJsonObject split_obj = split_val.toObject(); - split->setChannel(this->decodeChannel(split_obj)); + split->setChannel(decodeChannel(split_obj)); - // tab->addToLayout(split, std::make_pair(colNr, 10000000)); + tab->appendSplit(split); } colNr++; } @@ -270,23 +283,11 @@ void WindowManager::save() } // splits - QJsonArray columns_arr; - // std::vector> columns = tab->getColumns(); + QJsonObject splits; - // for (std::vector &cells : columns) { - // QJsonArray cells_arr; + this->encodeNodeRecusively(tab->getBaseNode(), splits); - // for (widgets::Split *cell : cells) { - // QJsonObject cell_obj; - - // this->encodeChannel(cell->getIndirectChannel(), cell_obj); - - // cells_arr.append(cell_obj); - // } - // columns_arr.append(cells_arr); - // } - - tab_obj.insert("splits", columns_arr); + tab_obj.insert("splits2", splits); tabs_arr.append(tab_obj); } @@ -302,10 +303,46 @@ void WindowManager::save() QString settingsPath = app->paths->settingsFolderPath + SETTINGS_FILENAME; QFile file(settingsPath); file.open(QIODevice::WriteOnly | QIODevice::Truncate); - file.write(document.toJson()); + + QJsonDocument::JsonFormat format = +#ifdef _DEBUG + QJsonDocument::JsonFormat::Compact +#else + (QJsonDocument::JsonFormat)0 +#endif + ; + + file.write(document.toJson(format)); file.flush(); } +void WindowManager::encodeNodeRecusively(SplitNode *node, QJsonObject &obj) +{ + switch (node->getType()) { + case SplitNode::_Split: { + obj.insert("type", "split"); + QJsonObject split; + encodeChannel(node->getSplit()->getIndirectChannel(), split); + obj.insert("data", split); + obj.insert("flexh", node->getHorizontalFlex()); + obj.insert("flexv", node->getVerticalFlex()); + } break; + case SplitNode::HorizontalContainer: + case SplitNode::VerticalContainer: { + obj.insert("type", node->getType() == SplitNode::HorizontalContainer ? "horizontal" + : "vertical"); + + QJsonArray items_arr; + for (const std::unique_ptr &n : node->getChildren()) { + QJsonObject subObj; + this->encodeNodeRecusively(n.get(), subObj); + items_arr.append(subObj); + } + obj.insert("items", items_arr); + } break; + } +} + void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj) { util::assertInGuiThread(); diff --git a/src/singletons/windowmanager.hpp b/src/singletons/windowmanager.hpp index 814f9231..5ff7b2b4 100644 --- a/src/singletons/windowmanager.hpp +++ b/src/singletons/windowmanager.hpp @@ -3,6 +3,9 @@ #include "widgets/window.hpp" namespace chatterino { +namespace widgets { +class SplitContainer::Node; +} namespace singletons { class WindowManager @@ -42,8 +45,13 @@ private: widgets::Window *mainWindow = nullptr; widgets::Window *selectedWindow = nullptr; - void encodeChannel(IndirectChannel channel, QJsonObject &obj); - IndirectChannel decodeChannel(const QJsonObject &obj); + void encodeNodeRecusively(widgets::SplitContainer::Node *node, QJsonObject &obj); + void decodeNodeRecusively(widgets::SplitContainer *container, + widgets::SplitContainer::Node *node, QJsonObject &obj, bool vertical); + +public: + static void encodeChannel(IndirectChannel channel, QJsonObject &obj); + static IndirectChannel decodeChannel(const QJsonObject &obj); }; } // namespace singletons diff --git a/src/widgets/basewidget.cpp b/src/widgets/basewidget.cpp index 4dcc1a6d..5a1c5b0c 100644 --- a/src/widgets/basewidget.cpp +++ b/src/widgets/basewidget.cpp @@ -27,7 +27,6 @@ BaseWidget::~BaseWidget() float BaseWidget::getScale() const { - // return 1.f; BaseWidget *baseWidget = dynamic_cast(this->window()); if (baseWidget == nullptr) { diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 42fef1c0..845f037b 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -227,9 +227,47 @@ void SplitHeader::paintEvent(QPaintEvent *) void SplitHeader::mousePressEvent(QMouseEvent *event) { - this->dragging = true; + if (event->button() == Qt::LeftButton) { + this->dragging = true; - this->dragStart = event->pos(); + this->dragStart = event->pos(); + } + + this->doubleClicked = false; +} + +void SplitHeader::mouseReleaseEvent(QMouseEvent *event) +{ + if (this->dragging && event->button() == Qt::LeftButton) { + QPoint pos = event->globalPos(); + + if (!showingHelpTooltip) { + this->showingHelpTooltip = true; + + QTimer::singleShot(400, this, [this, pos] { + if (this->doubleClicked) { + this->doubleClicked = false; + this->showingHelpTooltip = false; + return; + } + + TooltipWidget *widget = new TooltipWidget(); + + widget->setText("Double click or press to change the channel.\nClick and " + "drag to move the split."); + widget->setAttribute(Qt::WA_DeleteOnClose); + widget->move(pos); + widget->show(); + + QTimer::singleShot(3000, widget, [this, widget] { + widget->close(); + this->showingHelpTooltip = false; + }); + }); + } + } + + this->dragging = false; } void SplitHeader::mouseMoveEvent(QMouseEvent *event) @@ -242,27 +280,27 @@ void SplitHeader::mouseMoveEvent(QMouseEvent *event) } if (this->dragging) { - if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || - std::abs(this->dragStart.y() - event->pos().y()) > 12) { + if (std::abs(this->dragStart.x() - event->pos().x()) > (int)(12 * this->getScale()) || + std::abs(this->dragStart.y() - event->pos().y()) > (int)(12 * this->getScale())) { this->split->drag(); this->dragging = false; } } } -void SplitHeader::leaveEvent(QEvent *event) -{ - TooltipWidget::getInstance()->hide(); - BaseWidget::leaveEvent(event); -} - void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { this->split->doChangeChannel(); } + this->doubleClicked = true; } +void SplitHeader::leaveEvent(QEvent *event) +{ + TooltipWidget::getInstance()->hide(); + BaseWidget::leaveEvent(event); +} void SplitHeader::rightButtonClicked() { } diff --git a/src/widgets/helper/splitheader.hpp b/src/widgets/helper/splitheader.hpp index 3bfa5541..167d5e7f 100644 --- a/src/widgets/helper/splitheader.hpp +++ b/src/widgets/helper/splitheader.hpp @@ -41,6 +41,7 @@ protected: virtual void paintEvent(QPaintEvent *) override; virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void leaveEvent(QEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override; @@ -50,6 +51,8 @@ private: QPoint dragStart; bool dragging = false; + bool doubleClicked = false; + bool showingHelpTooltip = false; pajlada::Signals::Connection onlineStatusChangedConnection; diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 53395e45..dd6b517e 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -2,6 +2,7 @@ #include "application.hpp" #include "common.hpp" #include "singletons/thememanager.hpp" +#include "singletons/windowmanager.hpp" #include "util/helpers.hpp" #include "util/layoutcreator.hpp" #include "widgets/helper/notebooktab.hpp" @@ -11,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,7 +28,6 @@ namespace widgets { bool SplitContainer::isDraggingSplit = false; Split *SplitContainer::draggingSplit = nullptr; -// SplitContainer::Position SplitContainer::dragOriginalPosition; SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) : BaseWidget(parent) @@ -38,9 +40,19 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) this->refreshTabTitle(); - this->managedConnect(Split::modifierStatusChanged, [this](auto) { - // fourtf: maybe optimize + this->managedConnect(Split::modifierStatusChanged, [this](auto modifiers) { this->layout(); + + if (modifiers == Qt::AltModifier) { + for (std::unique_ptr &handle : this->resizeHandles) { + handle->show(); + handle->raise(); + } + } else { + for (std::unique_ptr &handle : this->resizeHandles) { + handle->hide(); + } + } }); this->setCursor(Qt::PointingHandCursor); @@ -83,7 +95,7 @@ void SplitContainer::appendSplit(Split *split) void SplitContainer::insertSplit(Split *split, const Position &position) { - this->insertSplit(split, position.direction, position.relativeNode); + this->insertSplit(split, position.direction, (Node *)position.relativeNode); } void SplitContainer::insertSplit(Split *split, Direction direction, Split *relativeTo) @@ -117,7 +129,7 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Node *relati split->giveFocus(Qt::MouseFocusReason); this->splits.push_back(split); - // this->setAcceptDrops(false); + this->refreshTabTitle(); this->layout(); } @@ -135,9 +147,7 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) this->splits.front()->giveFocus(Qt::MouseFocusReason); } - // if (this->splits.empty()) { - // this->setAcceptDrops(true); - // } + this->refreshTabTitle(); return position; } @@ -155,13 +165,10 @@ void SplitContainer::layout() this->baseNode.geometry = this->rect(); std::vector _dropRects; + std::vector _resizeRects; this->baseNode.layout( Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging, - this->getScale(), _dropRects); - _dropRects.clear(); - this->baseNode.layout( - Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging, - this->getScale(), _dropRects); + this->getScale(), _dropRects, _resizeRects); this->dropRects = _dropRects; @@ -190,10 +197,37 @@ void SplitContainer::layout() } this->overlay.setRects(std::move(_dropRects)); + + // handle resizeHandles + if (this->resizeHandles.size() < _resizeRects.size()) { + while (this->resizeHandles.size() < _resizeRects.size()) { + this->resizeHandles.push_back(std::make_unique(this)); + } + } else if (this->resizeHandles.size() > _resizeRects.size()) { + this->resizeHandles.resize(_resizeRects.size()); + } + + { + int i = 0; + for (ResizeRect &resizeRect : _resizeRects) { + ResizeHandle *handle = this->resizeHandles[i].get(); + handle->setGeometry(resizeRect.rect); + handle->setVertical(resizeRect.vertical); + handle->node = resizeRect.node; + + if (Split::modifierStatus == Qt::AltModifier) { + handle->show(); + handle->raise(); + } + + i++; + } + } + + // redraw this->update(); } -/// EVENTS void SplitContainer::resizeEvent(QResizeEvent *event) { BaseWidget::resizeEvent(event); @@ -229,14 +263,14 @@ void SplitContainer::paintEvent(QPaintEvent *) painter.setPen(this->themeManager->splits.header.text); - QString text = "Click to add a "; + QString text = "Click to add a split"; Notebook *notebook = dynamic_cast(this->parentWidget()); if (notebook != nullptr) { if (notebook->tabCount() > 1) { - text += "\n\ntip: you can drag a while holding "; - text += "\nor add another one by pressing "; + text += "\n\nTip: After adding a split you can hold to move it or split it " + "further."; } } @@ -269,110 +303,21 @@ void SplitContainer::paintEvent(QPaintEvent *) void SplitContainer::dragEnterEvent(QDragEnterEvent *event) { - // if (!event->mimeData()->hasFormat("chatterino/split")) - // return; + if (!event->mimeData()->hasFormat("chatterino/split")) + return; - // if (!SplitContainer::isDraggingSplit) { - // return; - // } + if (!SplitContainer::isDraggingSplit) { + return; + } this->isDragging = true; this->layout(); - // if (this->splits.empty()) { - // event->acceptProposedAction(); - // } - this->overlay.setGeometry(this->rect()); this->overlay.show(); this->overlay.raise(); } -void SplitContainer::dragMoveEvent(QDragMoveEvent *event) -{ - // if (this->splits.empty()) { - // event->acceptProposedAction(); - // } - - // for (auto &dropRect : this->dropRects) { - // if (dropRect.rect.contains(event->pos())) { - // event->acceptProposedAction(); - // return; - // } - // } - // event->setAccepted(false); -} - -void SplitContainer::dragLeaveEvent(QDragLeaveEvent *event) -{ - // this->isDragging = false; - // this->layout(); -} - -void SplitContainer::dropEvent(QDropEvent *event) -{ - // if (this->splits.empty()) { - // this->insertSplit(SplitContainer::draggingSplit, Direction::Above); - // event->acceptProposedAction(); - // } - - // this->isDragging = false; - // this->layout(); -} - -// void SplitContainer::requestFocus(int requestedX, int requestedY) -//{ -// // XXX: Perhaps if we request an Y coordinate out of bounds, we shuold set all previously set -// // requestedYs to 0 (if -1 is requested) or that x-coordinates vbox count (if requestedY >= -// // currentvbox.count() is requested) -// if (requestedX < 0 || requestedX >= this->ui.hbox.count()) { -// return; -// } - -// QLayoutItem *item = this->ui.hbox.itemAt(requestedX); -// QWidget *xW = item->widget(); -// if (item->isEmpty()) { -// qDebug() << "Requested hbox item " << requestedX << "is empty"; -// if (xW) { -// qDebug() << "but xW is not null"; -// // TODO: figure out what to do here -// } -// return; -// } - -// QVBoxLayout *vbox = static_cast(item->layout()); - -// if (requestedY < 0) { -// requestedY = 0; -// } else if (requestedY >= vbox->count()) { -// requestedY = vbox->count() - 1; -// } - -// this->lastRequestedY[requestedX] = requestedY; - -// QLayoutItem *innerItem = vbox->itemAt(requestedY); - -// if (innerItem->isEmpty()) { -// qDebug() << "Requested vbox item " << requestedY << "is empty"; -// return; -// } - -// QWidget *w = innerItem->widget(); -// if (w) { -// Split *chatWidget = static_cast(w); -// chatWidget->giveFocus(Qt::OtherFocusReason); -// } -//} - -// void SplitContainer::enterEvent(QEvent *event) -//{ -// if (this->ui.hbox.count() == 0) { -// this->setCursor(QCursor(Qt::PointingHandCursor)); -// } else { -// this->setCursor(QCursor(Qt::ArrowCursor)); -// } -//} - void SplitContainer::mouseMoveEvent(QMouseEvent *event) { this->mouseOverPoint = event->pos(); @@ -385,75 +330,6 @@ void SplitContainer::leaveEvent(QEvent *event) this->update(); } -// void SplitContainer::setPreviewRect(QPoint mousePos) -//{ -// for (DropRegion region : this->dropRegions) { -// if (region.rect.contains(mousePos)) { -// this->dropPreview.setBounds(region.rect); - -// if (!this->dropPreview.isVisible()) { -// this->dropPreview.setGeometry(this->rect()); -// this->dropPreview.show(); -// this->dropPreview.raise(); -// } - -// dropPosition = region.position; - -// return; -// } -// } - -// this->dropPreview.hide(); -//} - -// bool SplitContainer::eventFilter(QObject *object, QEvent *event) -//{ -// if (event->type() == QEvent::FocusIn) { -// QFocusEvent *focusEvent = static_cast(event); - -// this->refreshCurrentFocusCoordinates((focusEvent->reason() == Qt::MouseFocusReason)); -// } - -// return false; -//} - -// void SplitContainer::showEvent(QShowEvent *event) -//{ -// // Whenever this notebook page is shown, give focus to the last focused chat widget -// // If this is the first time this notebook page is shown, it will give focus to the -// top-left -// // chat widget -// this->requestFocus(this->currentX, this->currentY); -//} - -// static std::pair getWidgetPositionInLayout(QLayout *layout, const Split -// *chatWidget) -//{ -// for (int i = 0; i < layout->count(); ++i) { -// printf("xD\n"); -// } - -// return std::make_pair(-1, -1); -//} - -// std::pair SplitContainer::getChatPosition(const Split *chatWidget) -//{ -// auto layout = this->ui.hbox.layout(); - -// if (layout == nullptr) { -// return std::make_pair(-1, -1); -// } - -// return getWidgetPositionInLayout(layout, chatWidget); -//} - -// Split *SplitContainer::createChatWidget() -//{ -// auto split = new Split(this); - -// return split; -//} - void SplitContainer::refreshTabTitle() { assert(this->tab != nullptr); @@ -462,30 +338,610 @@ void SplitContainer::refreshTabTitle() return; } - this->tab->setTitle("default title"); + QString newTitle = ""; + bool first = true; - // QString newTitle = ""; - // bool first = true; + for (const auto &chatWidget : this->splits) { + auto channelName = chatWidget->getChannel()->name; + if (channelName.isEmpty()) { + continue; + } - // for (const auto &chatWidget : this->splits) { - // auto channelName = chatWidget->getChannel()->name; - // if (channelName.isEmpty()) { - // continue; - // } + if (!first) { + newTitle += ", "; + } + newTitle += channelName; - // if (!first) { - // newTitle += ", "; - // } - // newTitle += channelName; + first = false; + } - // first = false; - // } + if (newTitle.isEmpty()) { + newTitle = "empty"; + } - // if (newTitle.isEmpty()) { - // newTitle = "empty"; - // } + this->tab->setTitle(newTitle); +} - // this->tab->setTitle(newTitle); +void SplitContainer::decodeFromJson(QJsonObject &obj) +{ + assert(this->baseNode.type == Node::EmptyRoot); + + this->decodeNodeRecusively(obj, &this->baseNode); +} + +void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node) +{ + QString type = obj.value("type").toString(); + + if (type == "split") { + auto *split = new Split(this); + split->setChannel(singletons::WindowManager::decodeChannel(obj.value("data").toObject())); + + this->appendSplit(split); + } else if (type == "horizontal" || type == "vertical") { + bool vertical = type == "vertical"; + + Direction direction = vertical ? Direction::Below : Direction::Right; + + node->type = vertical ? Node::VerticalContainer : Node::HorizontalContainer; + + for (QJsonValue _val : obj.value("items").toArray()) { + auto _obj = _val.toObject(); + + auto _type = _obj.value("type"); + if (_type == "split") { + auto *split = new Split(this); + split->setChannel( + singletons::WindowManager::decodeChannel(_obj.value("data").toObject())); + + this->insertSplit(split, direction, node); + + this->baseNode.findNodeContainingSplit(split)->flexH = + _obj.value("flexh").toDouble(1.0); + this->baseNode.findNodeContainingSplit(split)->flexV = + _obj.value("flexv").toDouble(1.0); + } else { + Node *_node = new Node(); + _node->parent = node; + node->children.emplace_back(_node); + this->decodeNodeRecusively(_obj, _node); + } + } + + for (int i = 0; i < 2; i++) { + if (node->getChildren().size() < 2) { + auto *split = new Split(this); + split->setChannel( + singletons::WindowManager::decodeChannel(obj.value("data").toObject())); + + this->insertSplit(split, direction, node); + } + } + } +} + +// +// Node +// + +SplitContainer::Node::Type SplitContainer::Node::getType() +{ + return this->type; +} +Split *SplitContainer::Node::getSplit() +{ + return this->split; +} + +SplitContainer::Node *SplitContainer::Node::getParent() +{ + return this->parent; +} + +qreal SplitContainer::Node::getHorizontalFlex() +{ + return this->flexH; +} + +qreal SplitContainer::Node::getVerticalFlex() +{ + return this->flexV; +} + +const std::vector> &SplitContainer::Node::getChildren() +{ + return this->children; +} + +SplitContainer::Node::Node() + : type(SplitContainer::Node::Type::EmptyRoot) + , split(nullptr) + , parent(nullptr) +{ +} + +SplitContainer::Node::Node(Split *_split, Node *_parent) + : type(Type::_Split) + , split(_split) + , parent(_parent) +{ +} + +bool SplitContainer::Node::isOrContainsNode(SplitContainer::Node *_node) +{ + if (this == _node) { + return true; + } + + return std::any_of(this->children.begin(), this->children.end(), + [_node](std::unique_ptr &n) { return n->isOrContainsNode(_node); }); +} + +SplitContainer::Node *SplitContainer::Node::findNodeContainingSplit(Split *_split) +{ + if (this->type == Type::_Split && this->split == _split) { + return this; + } + + for (std::unique_ptr &node : this->children) { + Node *a = node->findNodeContainingSplit(_split); + + if (a != nullptr) { + return a; + } + } + return nullptr; +} + +void SplitContainer::Node::insertSplitRelative(Split *_split, Direction _direction) +{ + if (this->parent == nullptr) { + switch (this->type) { + case Node::EmptyRoot: { + this->setSplit(_split); + } break; + case Node::_Split: { + this->nestSplitIntoCollection(_split, _direction); + } break; + case Node::HorizontalContainer: { + this->nestSplitIntoCollection(_split, _direction); + } break; + case Node::VerticalContainer: { + this->nestSplitIntoCollection(_split, _direction); + } break; + } + return; + } + + // parent != nullptr + if (parent->type == toContainerType(_direction)) { + // hell yeah we'll just insert it next to outselves + this->_insertNextToThis(_split, _direction); + } else { + this->nestSplitIntoCollection(_split, _direction); + } +} + +void SplitContainer::Node::nestSplitIntoCollection(Split *_split, Direction _direction) +{ + if (toContainerType(_direction) == this->type) { + this->children.emplace_back(new Node(_split, this)); + } else { + // we'll need to nest outselves + // move all our data into a new node + Node *clone = new Node(); + clone->type = this->type; + clone->children = std::move(this->children); + for (std::unique_ptr &node : clone->children) { + node->parent = clone; + } + clone->split = this->split; + clone->parent = this; + + // add the node to our children and change our type + this->children.push_back(std::unique_ptr(clone)); + this->type = toContainerType(_direction); + this->split = nullptr; + + clone->_insertNextToThis(_split, _direction); + } +} + +void SplitContainer::Node::_insertNextToThis(Split *_split, Direction _direction) +{ + auto &siblings = this->parent->children; + + qreal width = this->parent->geometry.width() / siblings.size(); + qreal height = this->parent->geometry.height() / siblings.size(); + + if (siblings.size() == 1) { + this->geometry = QRect(0, 0, width, height); + } + + auto it = std::find_if(siblings.begin(), siblings.end(), + [this](auto &node) { return this == node.get(); }); + + assert(it != siblings.end()); + if (_direction == Direction::Right || _direction == Direction::Below) { + it++; + } + + Node *node = new Node(_split, this->parent); + node->geometry = QRectF(0, 0, width, height); + siblings.insert(it, std::unique_ptr(node)); +} + +void SplitContainer::Node::setSplit(Split *_split) +{ + assert(this->split == nullptr); + assert(this->children.size() == 0); + + this->split = _split; + this->type = Type::_Split; +} + +SplitContainer::Position SplitContainer::Node::releaseSplit() +{ + assert(this->type == Type::_Split); + + if (parent == nullptr) { + this->type = Type::EmptyRoot; + this->split = nullptr; + + Position pos; + pos.relativeNode = nullptr; + pos.direction = Direction::Right; + return pos; + } else { + auto &siblings = this->parent->children; + + auto it = std::find_if(begin(siblings), end(siblings), + [this](auto &node) { return this == node.get(); }); + assert(it != siblings.end()); + + Position position; + if (siblings.size() == 2) { + // delete this and move split to parent + position.relativeNode = this->parent; + if (this->parent->type == Type::VerticalContainer) { + position.direction = siblings.begin() == it ? Direction::Above : Direction::Below; + } else { + position.direction = siblings.begin() == it ? Direction::Left : Direction::Right; + } + + Node *_parent = this->parent; + siblings.erase(it); + std::unique_ptr &sibling = siblings.front(); + _parent->type = sibling->type; + _parent->split = sibling->split; + std::vector> nodes = std::move(sibling->children); + for (auto &node : nodes) { + node->parent = _parent; + } + _parent->children = std::move(nodes); + } else { + if (this == siblings.back().get()) { + position.direction = this->parent->type == Type::VerticalContainer + ? Direction::Below + : Direction::Right; + siblings.erase(it); + position.relativeNode = siblings.back().get(); + } else { + position.relativeNode = (it + 1)->get(); + position.direction = this->parent->type == Type::VerticalContainer + ? Direction::Above + : Direction::Left; + siblings.erase(it); + } + } + + return position; + } +} + +qreal SplitContainer::Node::getFlex(bool isVertical) +{ + return isVertical ? this->flexV : this->flexH; +} + +qreal SplitContainer::Node::getSize(bool isVertical) +{ + return isVertical ? this->geometry.height() : this->geometry.width(); +} + +qreal SplitContainer::Node::getChildrensTotalFlex(bool isVertical) +{ + return std::accumulate( + this->children.begin(), this->children.end(), (qreal)0, + [=](qreal val, std::unique_ptr &node) { return val + node->getFlex(isVertical); }); +} + +void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vector &dropRects, + std::vector &resizeRects) +{ + for (std::unique_ptr &node : this->children) { + if (node->flexH <= 0) + node->flexH = 0; + if (node->flexV <= 0) + node->flexV = 0; + } + + switch (this->type) { + case Node::_Split: { + QRect rect = this->geometry.toRect(); + this->split->setGeometry(rect.marginsRemoved(QMargins(1, 1, 1, 1))); + } break; + case Node::VerticalContainer: + case Node::HorizontalContainer: { + bool isVertical = this->type == Node::VerticalContainer; + + // vars + qreal minSize = 48 * _scale; + + qreal totalFlex = this->getChildrensTotalFlex(isVertical); + qreal totalSize = std::accumulate( + this->children.begin(), this->children.end(), (qreal)0, + [=](int val, std::unique_ptr &node) { + return val + std::max(this->getSize(isVertical) / totalFlex * + node->getFlex(isVertical), + minSize); + }); + + qreal sizeMultiplier = this->getSize(isVertical) / totalSize; + QRectF childRect = this->geometry; + + // add spacing if reqested + if (addSpacing) { + qreal offset = std::min(this->getSize(!isVertical) * 0.1, _scale * 24); + + // droprect left / above + dropRects.emplace_back( + QRect(this->geometry.left(), this->geometry.top(), + isVertical ? offset : this->geometry.width(), + isVertical ? this->geometry.height() : offset), + Position(this, isVertical ? Direction::Left : Direction::Above)); + + // droprect right / below + if (isVertical) { + dropRects.emplace_back( + QRect(this->geometry.right() - offset, this->geometry.top(), offset, + this->geometry.height()), + Position(this, Direction::Right)); + } else { + dropRects.emplace_back( + QRect(this->geometry.left(), this->geometry.bottom() - offset, + this->geometry.width(), offset), + Position(this, Direction::Below)); + } + + // shrink childRect + if (isVertical) { + childRect.setLeft(childRect.left() + offset); + childRect.setRight(childRect.right() - offset); + } else { + childRect.setTop(childRect.top() + offset); + childRect.setBottom(childRect.bottom() - offset); + } + } + + // iterate children + qreal pos = isVertical ? childRect.top() : childRect.left(); + for (std::unique_ptr &child : this->children) { + // set rect + QRectF rect = childRect; + if (isVertical) { + rect.setTop(pos); + rect.setHeight( + std::max(this->geometry.height() / totalFlex * child->flexV, + minSize) * + sizeMultiplier); + } else { + rect.setLeft(pos); + rect.setWidth(std::max(this->geometry.width() / totalFlex * child->flexH, + minSize) * + sizeMultiplier); + } + + child->geometry = rect; + child->layout(addSpacing, _scale, dropRects, resizeRects); + + pos += child->getSize(isVertical); + + // add resize rect + if (child != this->children.front()) { + QRect r = isVertical ? QRect(this->geometry.left(), child->geometry.top() - 4, + this->geometry.width(), 8) + : QRect(child->geometry.left() - 4, this->geometry.top(), + 8, this->geometry.height()); + resizeRects.push_back(ResizeRect(r, child.get(), isVertical)); + } + + // normalize flex + if (isVertical) { + child->flexV = child->flexV / totalFlex * this->children.size(); + child->flexH = 1; + } else { + child->flexH = child->flexH / totalFlex * this->children.size(); + child->flexV = 1; + } + } + } break; + }; +} + +SplitContainer::Node::Type SplitContainer::Node::toContainerType(Direction _dir) +{ + return _dir == Direction::Left || _dir == Direction::Right ? Type::HorizontalContainer + : Type::VerticalContainer; +} + +// +// DropOverlay +// + +SplitContainer::DropOverlay::DropOverlay(SplitContainer *_parent) + : QWidget(_parent) + , parent(_parent) + , mouseOverPoint(-10000, -10000) +{ + this->setMouseTracking(true); + this->setAcceptDrops(true); +} + +void SplitContainer::DropOverlay::setRects(std::vector _rects) +{ + this->rects = std::move(_rects); +} + +// pajlada::Signals::NoArgSignal dragEnded; + +void SplitContainer::DropOverlay::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // painter.fillRect(this->rect(), QColor("#334")); + + bool foundMover = false; + + for (DropRect &rect : this->rects) { + if (!foundMover && rect.rect.contains(this->mouseOverPoint)) { + painter.setBrush(getApp()->themes->splits.dropPreview); + painter.setPen(getApp()->themes->splits.dropPreviewBorder); + foundMover = true; + } else { + painter.setBrush(QColor(0, 0, 0, 0)); + painter.setPen(QColor(0, 0, 0, 0)); + // painter.setPen(getApp()->themes->splits.dropPreviewBorder); + } + + painter.drawRect(rect.rect); + } +} + +void SplitContainer::DropOverlay::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void SplitContainer::DropOverlay::dragMoveEvent(QDragMoveEvent *event) +{ + event->acceptProposedAction(); + + this->mouseOverPoint = event->pos(); + this->update(); +} + +void SplitContainer::DropOverlay::dragLeaveEvent(QDragLeaveEvent *event) +{ + this->mouseOverPoint = QPoint(-10000, -10000); + this->close(); + this->dragEnded.invoke(); +} + +void SplitContainer::DropOverlay::dropEvent(QDropEvent *event) +{ + Position *position = nullptr; + for (DropRect &rect : this->rects) { + if (rect.rect.contains(this->mouseOverPoint)) { + position = &rect.position; + break; + } + } + + if (position != nullptr) { + this->parent->insertSplit(SplitContainer::draggingSplit, *position); + event->acceptProposedAction(); + } + + this->mouseOverPoint = QPoint(-10000, -10000); + this->close(); + this->dragEnded.invoke(); +} + +// +// ResizeHandle +// + +void SplitContainer::ResizeHandle::setVertical(bool isVertical) +{ + this->setCursor(isVertical ? Qt::SplitVCursor : Qt::SplitHCursor); + this->vertical = isVertical; +} + +SplitContainer::ResizeHandle::ResizeHandle(SplitContainer *_parent) + : QWidget(_parent) + , parent(_parent) +{ + this->setMouseTracking(true); +} + +void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + painter.fillRect(this->rect(), "#999"); +} + +void SplitContainer::ResizeHandle::mousePressEvent(QMouseEvent *event) +{ + this->isMouseDown = true; +} + +void SplitContainer::ResizeHandle::mouseReleaseEvent(QMouseEvent *event) +{ + this->isMouseDown = false; +} + +void SplitContainer::ResizeHandle::mouseMoveEvent(QMouseEvent *event) +{ + if (!this->isMouseDown) { + return; + } + + assert(node != nullptr); + assert(node->parent != nullptr); + + auto &siblings = node->parent->getChildren(); + auto it = + std::find_if(siblings.begin(), siblings.end(), + [this](const std::unique_ptr &n) { return n.get() == this->node; }); + + assert(it != siblings.end()); + Node *before = siblings[it - siblings.begin() - 1].get(); + + QPoint topLeft = this->parent->mapToGlobal(before->geometry.topLeft().toPoint()); + QPoint bottomRight = this->parent->mapToGlobal(this->node->geometry.bottomRight().toPoint()); + + int globalX = topLeft.x() > event->globalX() + ? topLeft.x() + : (bottomRight.x() < event->globalX() ? bottomRight.x() : event->globalX()); + int globalY = topLeft.y() > event->globalY() + ? topLeft.y() + : (bottomRight.y() < event->globalY() ? bottomRight.y() : event->globalY()); + + QPoint mousePoint(globalX, globalY); + + if (this->vertical) { + qreal totalFlexV = this->node->flexV + before->flexV; + before->flexV = + totalFlexV * (mousePoint.y() - topLeft.y()) / (bottomRight.y() - topLeft.y()); + this->node->flexV = totalFlexV - before->flexV; + + this->parent->layout(); + + // move handle + this->move(this->x(), (int)before->geometry.bottom() - 4); + } else { + qreal totalFlexH = this->node->flexH + before->flexH; + before->flexH = + totalFlexH * (mousePoint.x() - topLeft.x()) / (bottomRight.x() - topLeft.x()); + this->node->flexH = totalFlexH - before->flexH; + + this->parent->layout(); + + // move handle + this->move((int)before->geometry.right() - 4, this->y()); + } } } // namespace widgets diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index 8feb137a..de203833 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -19,23 +19,26 @@ #include #include -// remove -#include "application.hpp" -#include "singletons/thememanager.hpp" +class QJsonObject; namespace chatterino { namespace widgets { +// +// Note: This class is a spaghetti container. There is a lot of spaghetti code inside but it doesn't +// expose any of it publicly. +// + class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder { Q_OBJECT + struct Node; + public: // fourtf: !!! preserve the order of left, up, right and down enum Direction { Left, Above, Right, Below }; - struct Node; - struct Position { private: Position() = default; @@ -52,6 +55,7 @@ public: friend class SplitContainer; }; +private: struct DropRect { QRect rect; Position position; @@ -63,404 +67,76 @@ public: } }; + struct ResizeRect { + QRect rect; + Node *node; + bool vertical; + + ResizeRect(const QRect &_rect, Node *_node, bool _vertical) + : rect(_rect) + , node(_node) + , vertical(_vertical) + { + } + }; + +public: struct Node final { enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer }; - Type getType() - { - return this->type; - } - Split *getSplit() - { - return this->split; - } - const std::vector> &getChildren() - { - return this->children; - } + Type getType(); + Split *getSplit(); + Node *getParent(); + qreal getHorizontalFlex(); + qreal getVerticalFlex(); + const std::vector> &getChildren(); private: Type type; Split *split; Node *parent; QRectF geometry; + qreal flexH = 1; + qreal flexV = 1; std::vector> children; - Node() - : type(Type::EmptyRoot) - , split(nullptr) - , parent(nullptr) - { - } + Node(); + Node(Split *_split, Node *_parent); - Node(Split *_split, Node *_parent) - : type(Type::_Split) - , split(_split) - , parent(_parent) - { - } + bool isOrContainsNode(Node *_node); + Node *findNodeContainingSplit(Split *_split); + void insertSplitRelative(Split *_split, Direction _direction); + void nestSplitIntoCollection(Split *_split, Direction _direction); + void _insertNextToThis(Split *_split, Direction _direction); + void setSplit(Split *_split); + Position releaseSplit(); + qreal getFlex(bool isVertical); + qreal getSize(bool isVertical); + qreal getChildrensTotalFlex(bool isVertical); + void layout(bool addSpacing, float _scale, std::vector &dropRects, + std::vector &resizeRects); - bool isOrContainsNode(Node *_node) - { - if (this == _node) { - return true; - } - - return std::any_of( - this->children.begin(), this->children.end(), - [_node](std::unique_ptr &n) { return n->isOrContainsNode(_node); }); - } - - Node *findNodeContainingSplit(Split *_split) - { - if (this->type == Type::_Split && this->split == _split) { - return this; - } - - for (std::unique_ptr &node : this->children) { - Node *a = node->findNodeContainingSplit(_split); - - if (a != nullptr) { - return a; - } - } - return nullptr; - } - - void insertSplitRelative(Split *_split, Direction _direction) - { - if (this->parent == nullptr) { - switch (this->type) { - case Node::EmptyRoot: { - this->setSplit(_split); - } break; - case Node::_Split: { - this->nestSplitIntoCollection(_split, _direction); - } break; - case Node::HorizontalContainer: { - if (toContainerType(_direction) == Node::HorizontalContainer) { - assert(false); - } else { - this->nestSplitIntoCollection(_split, _direction); - } - } break; - case Node::VerticalContainer: { - if (toContainerType(_direction) == Node::VerticalContainer) { - assert(false); - } else { - this->nestSplitIntoCollection(_split, _direction); - } - } break; - } - return; - } - - // parent != nullptr - if (parent->type == toContainerType(_direction)) { - // hell yeah we'll just insert it next to outselves - this->_insertNextToThis(_split, _direction); - } else { - this->nestSplitIntoCollection(_split, _direction); - } - } - - void nestSplitIntoCollection(Split *_split, Direction _direction) - { - // we'll need to nest outselves - // move all our data into a new node - Node *clone = new Node(); - clone->type = this->type; - clone->children = std::move(this->children); - for (std::unique_ptr &node : clone->children) { - node->parent = clone; - } - clone->split = this->split; - clone->parent = this; - - // add the node to our children and change our type - this->children.push_back(std::unique_ptr(clone)); - this->type = toContainerType(_direction); - this->split = nullptr; - - clone->_insertNextToThis(_split, _direction); - } - - void _insertNextToThis(Split *_split, Direction _direction) - { - auto &siblings = this->parent->children; - - qreal width = this->parent->geometry.width() / siblings.size(); - qreal height = this->parent->geometry.height() / siblings.size(); - - if (siblings.size() == 1) { - this->geometry = QRect(0, 0, width, height); - } - - auto it = std::find_if(siblings.begin(), siblings.end(), - [this](auto &node) { return this == node.get(); }); - - assert(it != siblings.end()); - if (_direction == Direction::Right || _direction == Direction::Below) { - it++; - } - - Node *node = new Node(_split, this->parent); - node->geometry = QRectF(0, 0, width, height); - siblings.insert(it, std::unique_ptr(node)); - } - - void setSplit(Split *_split) - { - assert(this->split == nullptr); - assert(this->children.size() == 0); - - this->split = _split; - this->type = Type::_Split; - } - - Position releaseSplit() - { - assert(this->type == Type::_Split); - - if (parent == nullptr) { - this->type = Type::EmptyRoot; - this->split = nullptr; - - Position pos; - pos.relativeNode = nullptr; - pos.direction = Direction::Right; - return pos; - } else { - auto &siblings = this->parent->children; - - auto it = std::find_if(begin(siblings), end(siblings), - [this](auto &node) { return this == node.get(); }); - assert(it != siblings.end()); - - Position position; - if (siblings.size() == 2) { - // delete this and move split to parent - position.relativeNode = this->parent; - if (this->parent->type == Type::VerticalContainer) { - position.direction = - siblings.begin() == it ? Direction::Above : Direction::Below; - } else { - position.direction = - siblings.begin() == it ? Direction::Left : Direction::Right; - } - - Node *_parent = this->parent; - siblings.erase(it); - std::unique_ptr &sibling = siblings.front(); - _parent->type = sibling->type; - _parent->split = sibling->split; - std::vector> nodes = std::move(sibling->children); - for (auto &node : nodes) { - node->parent = _parent; - } - _parent->children = std::move(nodes); - } else { - if (this == siblings.back().get()) { - position.direction = this->parent->type == Type::VerticalContainer - ? Direction::Below - : Direction::Right; - siblings.erase(it); - position.relativeNode = siblings.back().get(); - } else { - position.relativeNode = (it + 1)->get(); - position.direction = this->parent->type == Type::VerticalContainer - ? Direction::Above - : Direction::Left; - siblings.erase(it); - } - } - - return position; - } - } - - void layout(bool addSpacing, float _scale, std::vector &dropRects) - { - switch (this->type) { - case Node::_Split: { - QRect rect = this->geometry.toRect(); - this->split->setGeometry(rect.marginsRemoved(QMargins(1, 1, 1, 1))); - } break; - case Node::VerticalContainer: { - qreal totalHeight = - std::accumulate(this->children.begin(), this->children.end(), 0, - [](qreal val, std::unique_ptr &node) { - return val + node->geometry.height(); - }); - - qreal childX = this->geometry.left(); - qreal childWidth = this->geometry.width(); - - if (addSpacing) { - qreal offset = std::min(this->geometry.width() * 0.1, _scale * 24); - - dropRects.emplace_back( - QRect(childX, this->geometry.top(), offset, this->geometry.height()), - Position(this, Direction::Left)); - dropRects.emplace_back( - QRect(childX + this->geometry.width() - offset, this->geometry.top(), - offset, this->geometry.height()), - Position(this, Direction::Right)); - - childX += offset; - childWidth -= offset * 2; - } - - qreal scaleFactor = this->geometry.height() / totalHeight; - - qreal y = this->geometry.top(); - for (std::unique_ptr &child : this->children) { - child->geometry = - QRectF(childX, y, childWidth, child->geometry.height() * scaleFactor); - - child->layout(addSpacing, _scale, dropRects); - y += child->geometry.height(); - } - } break; - case Node::HorizontalContainer: { - qreal totalWidth = - std::accumulate(this->children.begin(), this->children.end(), 0, - [](qreal val, std::unique_ptr &node) { - return val + node->geometry.width(); - }); - - qreal childY = this->geometry.top(); - qreal childHeight = this->geometry.height(); - - if (addSpacing) { - qreal offset = std::min(this->geometry.height() * 0.1, _scale * 24); - dropRects.emplace_back( - QRect(this->geometry.left(), childY, this->geometry.width(), offset), - Position(this, Direction::Above)); - dropRects.emplace_back( - QRect(this->geometry.left(), childY + this->geometry.height() - offset, - this->geometry.width(), offset), - Position(this, Direction::Below)); - - childY += offset; - childHeight -= offset * 2; - } - - qreal scaleFactor = this->geometry.width() / totalWidth; - - qreal x = this->geometry.left(); - for (std::unique_ptr &child : this->children) { - child->geometry = - QRectF(x, childY, child->geometry.width() * scaleFactor, childHeight); - - child->layout(addSpacing, _scale, dropRects); - x += child->geometry.width(); - } - } break; - }; - } - - static Type toContainerType(Direction _dir) - { - return _dir == Direction::Left || _dir == Direction::Right ? Type::HorizontalContainer - : Type::VerticalContainer; - } + static Type toContainerType(Direction _dir); friend class SplitContainer; }; +private: class DropOverlay : public QWidget { public: - DropOverlay(SplitContainer *_parent = nullptr) - : QWidget(_parent) - , parent(_parent) - , mouseOverPoint(-10000, -10000) - { - this->setMouseTracking(true); - this->setAcceptDrops(true); - } + DropOverlay(SplitContainer *_parent = nullptr); - void setRects(std::vector _rects) - { - this->rects = std::move(_rects); - } + void setRects(std::vector _rects); pajlada::Signals::NoArgSignal dragEnded; protected: - void paintEvent(QPaintEvent *event) override - { - QPainter painter(this); - - // painter.fillRect(this->rect(), QColor("#334")); - - bool foundMover = false; - - for (DropRect &rect : this->rects) { - if (!foundMover && rect.rect.contains(this->mouseOverPoint)) { - painter.setBrush(getApp()->themes->splits.dropPreview); - painter.setPen(getApp()->themes->splits.dropPreviewBorder); - foundMover = true; - } else { - painter.setBrush(QColor(0, 0, 0, 0)); - painter.setPen(QColor(0, 0, 0, 0)); - // painter.setPen(getApp()->themes->splits.dropPreviewBorder); - } - - painter.drawRect(rect.rect); - } - } - - void mouseMoveEvent(QMouseEvent *event) - { - this->mouseOverPoint = event->pos(); - } - - void leaveEvent(QEvent *event) - { - this->mouseOverPoint = QPoint(-10000, -10000); - } - - void dragEnterEvent(QDragEnterEvent *event) - { - event->acceptProposedAction(); - } - - void dragMoveEvent(QDragMoveEvent *event) - { - event->acceptProposedAction(); - - this->mouseOverPoint = event->pos(); - this->update(); - } - - void dragLeaveEvent(QDragLeaveEvent *event) - { - this->mouseOverPoint = QPoint(-10000, -10000); - this->close(); - this->dragEnded.invoke(); - } - - void dropEvent(QDropEvent *event) - { - Position *position = nullptr; - for (DropRect &rect : this->rects) { - if (rect.rect.contains(this->mouseOverPoint)) { - position = &rect.position; - break; - } - } - - if (position != nullptr) { - this->parent->insertSplit(SplitContainer::draggingSplit, *position); - event->acceptProposedAction(); - } - - this->mouseOverPoint = QPoint(-10000, -10000); - this->close(); - this->dragEnded.invoke(); - } + void paintEvent(QPaintEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); private: std::vector rects; @@ -468,6 +144,27 @@ public: SplitContainer *parent; }; + class ResizeHandle : public QWidget + { + public: + SplitContainer *parent; + Node *node; + + void setVertical(bool isVertical); + ResizeHandle(SplitContainer *_parent = nullptr); + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + + friend class SplitContainer; + + private: + bool vertical; + bool isMouseDown = false; + }; + +public: SplitContainer(Notebook *parent, NotebookTab *_tab); void appendNewSplit(bool openChannelNameDialog); @@ -478,6 +175,8 @@ public: Position releaseSplit(Split *split); Position deleteSplit(Split *split); + void decodeFromJson(QJsonObject &obj); + int getSplitCount() { return 0; @@ -491,30 +190,22 @@ public: void refreshTabTitle(); NotebookTab *getTab() const; - const Node *getBaseNode() + Node *getBaseNode() { return &this->baseNode; } static bool isDraggingSplit; static Split *draggingSplit; - // static Position dragOriginalPosition; protected: - // bool eventFilter(QObject *object, QEvent *event) override; void paintEvent(QPaintEvent *event) override; - // void showEvent(QShowEvent *event) override; - - // void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; void resizeEvent(QResizeEvent *event) override; @@ -534,6 +225,7 @@ private: std::vector dropRegions; NotebookPageDropPreview dropPreview; DropOverlay overlay; + std::vector> resizeHandles; QPoint mouseOverPoint; void layout(); @@ -545,19 +237,7 @@ private: bool isDragging = false; - // struct { - // QVBoxLayout parentLayout; - - // QHBoxLayout hbox; - // } ui; - - // std::vector splits; - - // void setPreviewRect(QPoint mousePos); - - // std::pair getChatPosition(const Split *chatWidget); - - // Split *createChatWidget(); + void decodeNodeRecusively(QJsonObject &obj, Node *node); }; } // namespace widgets diff --git a/src/widgets/tooltipwidget.cpp b/src/widgets/tooltipwidget.cpp index 9637e828..26026b6b 100644 --- a/src/widgets/tooltipwidget.cpp +++ b/src/widgets/tooltipwidget.cpp @@ -43,6 +43,11 @@ TooltipWidget::~TooltipWidget() this->fontChangedConnection.disconnect(); } +void TooltipWidget::themeRefreshEvent() +{ + this->setStyleSheet("color: #fff; background: #000"); +} + void TooltipWidget::scaleChangedEvent(float) { this->updateFont(); diff --git a/src/widgets/tooltipwidget.hpp b/src/widgets/tooltipwidget.hpp index ff3a7bf0..90259e7b 100644 --- a/src/widgets/tooltipwidget.hpp +++ b/src/widgets/tooltipwidget.hpp @@ -31,6 +31,7 @@ public: protected: void changeEvent(QEvent *) override; void leaveEvent(QEvent *) override; + void themeRefreshEvent() override; void scaleChangedEvent(float) override; private: From 937fffc34bc3f8aab9c908cf7dcf8b168909573a Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 12 May 2018 20:34:13 +0200 Subject: [PATCH 006/121] Implement /ignore and /unignore commands Simplify authorized network requests for Twitch V5 api add onShow virtual function to settings pages if they need to be refreshed when shown Actually ignoring messages from ignored users is still not implemented Working on #247 --- src/providers/twitch/twitchaccount.hpp | 1 - src/util/networkrequest.hpp | 49 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/providers/twitch/twitchaccount.hpp b/src/providers/twitch/twitchaccount.hpp index 649fd1f8..86f14153 100644 --- a/src/providers/twitch/twitchaccount.hpp +++ b/src/providers/twitch/twitchaccount.hpp @@ -55,7 +55,6 @@ public: bool isAnon() const; void loadIgnores(); - void ignore(const QString &targetName, std::function onFinished); void ignoreByID(const QString &targetUserID, const QString &targetName, diff --git a/src/util/networkrequest.hpp b/src/util/networkrequest.hpp index 6131528c..d90c63a0 100644 --- a/src/util/networkrequest.hpp +++ b/src/util/networkrequest.hpp @@ -151,15 +151,26 @@ public: } void setRawHeader(const char *headerName, const char *value) +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df +======= { this->data.request.setRawHeader(headerName, value); } void setRawHeader(const char *headerName, const QByteArray &value) +>>>>>>> Implement /ignore and /unignore commands { this->data.request.setRawHeader(headerName, value); } +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df + void setRawHeader(const char *headerName, const QByteArray &value) + { + this->data.request.setRawHeader(headerName, value); + } + +======= +>>>>>>> Implement /ignore and /unignore commands void setRawHeader(const char *headerName, const QString &value) { this->data.request.setRawHeader(headerName, value.toUtf8()); @@ -302,16 +313,28 @@ public: void execute() { switch (this->data.requestType) { +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df case GetRequest: { this->executeGet(); } break; case PutRequest: { +======= + case GET: { + this->executeGet(); + } break; + + case PUT: { +>>>>>>> Implement /ignore and /unignore commands debug::Log("Call PUT request!"); this->executePut(); } break; +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df case DeleteRequest: { +======= + case DELETE: { +>>>>>>> Implement /ignore and /unignore commands debug::Log("Call DELETE request!"); this->executeDelete(); } break; @@ -369,6 +392,7 @@ private: worker->moveToThread(&NetworkManager::workerThread); if (this->data.caller != nullptr) { +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller, [data = this->data](auto reply) mutable { auto &dat = data; @@ -377,6 +401,12 @@ private: if (data.onError) { data.onError(reply->error()); } +======= + QObject::connect(worker, &NetworkWorker::doneUrl, + this->data.caller, [data = this->data](auto reply) mutable { + if (reply->error() != QNetworkReply::NetworkError::NoError) { + // TODO: We might want to call an onError callback here +>>>>>>> Implement /ignore and /unignore commands return; } @@ -395,6 +425,7 @@ private: } QObject::connect(&requester, &NetworkRequester::requestUrl, worker, +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df [ timer, data = std::move(this->data), worker ]() { QNetworkReply *reply; switch (data.requestType) { @@ -407,6 +438,20 @@ private: } break; case DeleteRequest: { +======= + [ timer, data = std::move(this->data), worker ]() { + QNetworkReply *reply; + switch (data.requestType) { + case GET: { + reply = NetworkManager::NaM.get(data.request); + } break; + + case PUT: { + reply = NetworkManager::NaM.put(data.request, data.payload); + } break; + + case DELETE: { +>>>>>>> Implement /ignore and /unignore commands reply = NetworkManager::NaM.deleteResource(data.request); } break; } @@ -431,7 +476,11 @@ private: } QObject::connect(reply, &QNetworkReply::finished, worker, +<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df [ data = std::move(data), worker, reply ]() mutable { +======= + [ data = std::move(data), worker, reply ]() mutable { +>>>>>>> Implement /ignore and /unignore commands if (data.caller == nullptr) { QByteArray bytes = reply->readAll(); data.writeToCache(bytes); From 88e97325f85ff9a697fc8f222631647219c70ebf Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 12 May 2018 19:50:22 +0200 Subject: [PATCH 007/121] Fix windows-specific compilation error --- src/providers/twitch/twitchaccount.cpp | 2 +- src/util/networkrequest.hpp | 145 +++++++++---------------- 2 files changed, 50 insertions(+), 97 deletions(-) diff --git a/src/providers/twitch/twitchaccount.cpp b/src/providers/twitch/twitchaccount.cpp index c089a7c2..1b6032c7 100644 --- a/src/providers/twitch/twitchaccount.cpp +++ b/src/providers/twitch/twitchaccount.cpp @@ -176,7 +176,7 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe }); req.execute(); -} +} // namespace twitch void TwitchAccount::unignore(const QString &targetName, std::function onFinished) diff --git a/src/util/networkrequest.hpp b/src/util/networkrequest.hpp index d90c63a0..133f1139 100644 --- a/src/util/networkrequest.hpp +++ b/src/util/networkrequest.hpp @@ -151,26 +151,15 @@ public: } void setRawHeader(const char *headerName, const char *value) -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df -======= { this->data.request.setRawHeader(headerName, value); } void setRawHeader(const char *headerName, const QByteArray &value) ->>>>>>> Implement /ignore and /unignore commands { this->data.request.setRawHeader(headerName, value); } -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df - void setRawHeader(const char *headerName, const QByteArray &value) - { - this->data.request.setRawHeader(headerName, value); - } - -======= ->>>>>>> Implement /ignore and /unignore commands void setRawHeader(const char *headerName, const QString &value) { this->data.request.setRawHeader(headerName, value.toUtf8()); @@ -313,28 +302,17 @@ public: void execute() { switch (this->data.requestType) { -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df case GetRequest: { + debug::Log("Call GET request!"); this->executeGet(); } break; case PutRequest: { -======= - case GET: { - this->executeGet(); - } break; - - case PUT: { ->>>>>>> Implement /ignore and /unignore commands debug::Log("Call PUT request!"); this->executePut(); } break; -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df case DeleteRequest: { -======= - case DELETE: { ->>>>>>> Implement /ignore and /unignore commands debug::Log("Call DELETE request!"); this->executeDelete(); } break; @@ -343,7 +321,7 @@ public: debug::Log("Unhandled request type {}", (int)this->data.requestType); } break; } - } + } // namespace util private: void useCache() @@ -392,7 +370,6 @@ private: worker->moveToThread(&NetworkManager::workerThread); if (this->data.caller != nullptr) { -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller, [data = this->data](auto reply) mutable { auto &dat = data; @@ -401,12 +378,6 @@ private: if (data.onError) { data.onError(reply->error()); } -======= - QObject::connect(worker, &NetworkWorker::doneUrl, - this->data.caller, [data = this->data](auto reply) mutable { - if (reply->error() != QNetworkReply::NetworkError::NoError) { - // TODO: We might want to call an onError callback here ->>>>>>> Implement /ignore and /unignore commands return; } @@ -418,85 +389,67 @@ private: reply->deleteLater(); }); - } + } // namespace chatterino if (timer != nullptr) { timer->start(this->data.timeoutMS); } - QObject::connect(&requester, &NetworkRequester::requestUrl, worker, -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df - [ timer, data = std::move(this->data), worker ]() { - QNetworkReply *reply; - switch (data.requestType) { - case GetRequest: { - reply = NetworkManager::NaM.get(data.request); - } break; + QObject::connect( + &requester, &NetworkRequester::requestUrl, worker, + [ timer, data = std::move(this->data), worker ]() { + QNetworkReply *reply; + switch (data.requestType) { + case GetRequest: { + reply = NetworkManager::NaM.get(data.request); + } break; - case PutRequest: { - reply = NetworkManager::NaM.put(data.request, data.payload); - } break; + case PutRequest: { + reply = NetworkManager::NaM.put(data.request, data.payload); + } break; - case DeleteRequest: { -======= - [ timer, data = std::move(this->data), worker ]() { - QNetworkReply *reply; - switch (data.requestType) { - case GET: { - reply = NetworkManager::NaM.get(data.request); - } break; + case DeleteRequest: { + reply = NetworkManager::NaM.deleteResource(data.request); + } break; + } // namespace chatterino - case PUT: { - reply = NetworkManager::NaM.put(data.request, data.payload); - } break; + if (reply == nullptr) { + debug::Log("Unhandled request type {}", (int)data.requestType); + return; + } - case DELETE: { ->>>>>>> Implement /ignore and /unignore commands - reply = NetworkManager::NaM.deleteResource(data.request); - } break; - } + if (timer != nullptr) { + QObject::connect(timer, &QTimer::timeout, worker, [reply, timer, data]() { + debug::Log("Aborted!"); + reply->abort(); + timer->deleteLater(); + data.onError(-2); + }); + } - if (reply == nullptr) { - debug::Log("Unhandled request type {}", (int)data.requestType); - return; - } + if (data.onReplyCreated) { + data.onReplyCreated(reply); + } - if (timer != nullptr) { - QObject::connect(timer, &QTimer::timeout, worker, - [reply, timer, data]() { - debug::Log("Aborted!"); - reply->abort(); - timer->deleteLater(); - data.onError(-2); - }); - } + QObject::connect(reply, &QNetworkReply::finished, worker, + [ data = std::move(data), worker, reply ]() mutable { + if (data.caller == nullptr) { + QByteArray bytes = reply->readAll(); + data.writeToCache(bytes); + data.onSuccess(parseJSONFromData2(bytes)); - if (data.onReplyCreated) { - data.onReplyCreated(reply); - } + reply->deleteLater(); + } else { + emit worker->doneUrl(reply); + } - QObject::connect(reply, &QNetworkReply::finished, worker, -<<<<<<< 5b26cdaa0777562a0b3c663a203528eca56bd5df - [ data = std::move(data), worker, reply ]() mutable { -======= - [ data = std::move(data), worker, reply ]() mutable { ->>>>>>> Implement /ignore and /unignore commands - if (data.caller == nullptr) { - QByteArray bytes = reply->readAll(); - data.writeToCache(bytes); - data.onSuccess(parseJSONFromData2(bytes)); - - reply->deleteLater(); - } else { - emit worker->doneUrl(reply); - } - - delete worker; - }); - }); + delete worker; + }); + } // namespace util + ); emit requester.requestUrl(); - } + } // namespace chatterino void executeGet() { @@ -514,7 +467,7 @@ private: { this->doRequest(); } -}; +}; // namespace util } // namespace util } // namespace chatterino From 61dac49f6d67c684d8bdadaf9bfed8a7b71b0a3b Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 12 May 2018 23:27:34 +0200 Subject: [PATCH 008/121] Implement basic and stupid ignore functionality We currently do not respect mod status --- src/providers/twitch/twitchmessagebuilder.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 172cc325..4ec686b1 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -76,6 +76,17 @@ bool TwitchMessageBuilder::isIgnored() const } } + if (this->tags.contains("user-id")) { + auto sourceUserID = this->tags.value("user-id").toString(); + + for (const auto &user : app->accounts->Twitch.getCurrent()->getIgnores()) { + if (sourceUserID == user.id) { + debug::Log("Blocking message because it's from blocked user {}", user.name); + return true; + } + } + } + return false; } From 26262f4ce45000826d6da2803eadcdd569d96a4a Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 12 May 2018 23:34:22 +0200 Subject: [PATCH 009/121] Also respect "enable twitch ignored users" setting --- src/providers/twitch/twitchmessagebuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 4ec686b1..760979dc 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -76,7 +76,7 @@ bool TwitchMessageBuilder::isIgnored() const } } - if (this->tags.contains("user-id")) { + if (app->settings->enableTwitchIgnoredUsers && this->tags.contains("user-id")) { auto sourceUserID = this->tags.value("user-id").toString(); for (const auto &user : app->accounts->Twitch.getCurrent()->getIgnores()) { From c2c3b67f6b6682a7c367b2c2262833ee290d7aa6 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 13 May 2018 17:53:24 +0200 Subject: [PATCH 010/121] Added result code to ignore/unignore calls Add ignore/unignore calls that take the user ID to avoid double api calls Fully implement the account popup ignore/unignore feature Fix #247 --- src/providers/twitch/twitchaccount.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/twitchaccount.cpp b/src/providers/twitch/twitchaccount.cpp index 1b6032c7..c089a7c2 100644 --- a/src/providers/twitch/twitchaccount.cpp +++ b/src/providers/twitch/twitchaccount.cpp @@ -176,7 +176,7 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe }); req.execute(); -} // namespace twitch +} void TwitchAccount::unignore(const QString &targetName, std::function onFinished) From 7b192897dac8c542f40f4d1069b7b0a071552960 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 16 May 2018 15:42:45 +0200 Subject: [PATCH 011/121] minor fixes --- src/util/networkrequest.hpp | 97 ++++++++++++++--------------- src/widgets/helper/splitoverlay.cpp | 2 +- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/util/networkrequest.hpp b/src/util/networkrequest.hpp index 133f1139..d0c6fef5 100644 --- a/src/util/networkrequest.hpp +++ b/src/util/networkrequest.hpp @@ -321,7 +321,7 @@ public: debug::Log("Unhandled request type {}", (int)this->data.requestType); } break; } - } // namespace util + } private: void useCache() @@ -372,8 +372,6 @@ private: if (this->data.caller != nullptr) { QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller, [data = this->data](auto reply) mutable { - auto &dat = data; - if (reply->error() != QNetworkReply::NetworkError::NoError) { if (data.onError) { data.onError(reply->error()); @@ -389,67 +387,66 @@ private: reply->deleteLater(); }); - } // namespace chatterino + } if (timer != nullptr) { timer->start(this->data.timeoutMS); } - QObject::connect( - &requester, &NetworkRequester::requestUrl, worker, - [ timer, data = std::move(this->data), worker ]() { - QNetworkReply *reply; - switch (data.requestType) { - case GetRequest: { - reply = NetworkManager::NaM.get(data.request); - } break; + QObject::connect(&requester, &NetworkRequester::requestUrl, worker, + [ timer, data = std::move(this->data), worker ]() { + QNetworkReply *reply = nullptr; + switch (data.requestType) { + case GetRequest: { + reply = NetworkManager::NaM.get(data.request); + } break; - case PutRequest: { - reply = NetworkManager::NaM.put(data.request, data.payload); - } break; + case PutRequest: { + reply = NetworkManager::NaM.put(data.request, data.payload); + } break; - case DeleteRequest: { - reply = NetworkManager::NaM.deleteResource(data.request); - } break; - } // namespace chatterino + case DeleteRequest: { + reply = NetworkManager::NaM.deleteResource(data.request); + } break; + } - if (reply == nullptr) { - debug::Log("Unhandled request type {}", (int)data.requestType); - return; - } + if (reply == nullptr) { + debug::Log("Unhandled request type {}", (int)data.requestType); + return; + } - if (timer != nullptr) { - QObject::connect(timer, &QTimer::timeout, worker, [reply, timer, data]() { - debug::Log("Aborted!"); - reply->abort(); - timer->deleteLater(); - data.onError(-2); - }); - } + if (timer != nullptr) { + QObject::connect(timer, &QTimer::timeout, worker, + [reply, timer, data]() { + debug::Log("Aborted!"); + reply->abort(); + timer->deleteLater(); + data.onError(-2); + }); + } - if (data.onReplyCreated) { - data.onReplyCreated(reply); - } + if (data.onReplyCreated) { + data.onReplyCreated(reply); + } - QObject::connect(reply, &QNetworkReply::finished, worker, - [ data = std::move(data), worker, reply ]() mutable { - if (data.caller == nullptr) { - QByteArray bytes = reply->readAll(); - data.writeToCache(bytes); - data.onSuccess(parseJSONFromData2(bytes)); + QObject::connect(reply, &QNetworkReply::finished, worker, + [ data = std::move(data), worker, reply ]() mutable { + if (data.caller == nullptr) { + QByteArray bytes = reply->readAll(); + data.writeToCache(bytes); + data.onSuccess(parseJSONFromData2(bytes)); - reply->deleteLater(); - } else { - emit worker->doneUrl(reply); - } + reply->deleteLater(); + } else { + emit worker->doneUrl(reply); + } - delete worker; - }); - } // namespace util - ); + delete worker; + }); + }); emit requester.requestUrl(); - } // namespace chatterino + } void executeGet() { @@ -467,7 +464,7 @@ private: { this->doRequest(); } -}; // namespace util +}; } // namespace util } // namespace chatterino diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index 27ac05a0..cded6211 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -137,7 +137,7 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched, QEvent *even dynamic_cast(((QWidget *)watched)->graphicsEffect()); if (effect != nullptr) { - effect->setOpacity(1); + effect->setOpacity(0.99); } this->parent->hoveredElement = this->hoveredElement; From 8ee0f85a2b8fde82e21d126f2b51a0a3359cda46 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 16 May 2018 17:47:58 +0200 Subject: [PATCH 012/121] weekly "fixed building on linux" --- chatterino.pro | 4 ++++ src/widgets/accountpopup.cpp | 8 ++++---- src/widgets/splitcontainer.hpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index 5317d757..eff6c715 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -45,6 +45,10 @@ exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { DEFINES += "USEWEBENGINE" } +linux { + LIBS += -lrt +} + win32 { LIBS += -luser32 } diff --git a/src/widgets/accountpopup.cpp b/src/widgets/accountpopup.cpp index 560aa9d3..76263f2e 100644 --- a/src/widgets/accountpopup.cpp +++ b/src/widgets/accountpopup.cpp @@ -121,11 +121,11 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel) switch (result) { case IgnoreResult_Success: { this->relationship.setIgnoring(true); - emit refreshButtons(); + emit this->refreshButtons(); } break; case IgnoreResult_AlreadyIgnored: { this->relationship.setIgnoring(true); - emit refreshButtons(); + emit this->refreshButtons(); } break; case IgnoreResult_Failed: { } break; @@ -137,7 +137,7 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel) switch (result) { case UnignoreResult_Success: { this->relationship.setIgnoring(false); - emit refreshButtons(); + emit this->refreshButtons(); } break; case UnignoreResult_Failed: { } break; @@ -230,7 +230,7 @@ void AccountPopupWidget::getUserData() currentUser->checkFollow(this->popupWidgetUser.userID, [=](auto result) { this->relationship.setFollowing(result == FollowResult_Following); - emit refreshButtons(); + emit this->refreshButtons(); }); bool isIgnoring = false; diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index de203833..c366850b 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -33,9 +33,9 @@ class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder { Q_OBJECT +public: struct Node; -public: // fourtf: !!! preserve the order of left, up, right and down enum Direction { Left, Above, Right, Below }; From d24e1f8314e19ad7596b39a442a9fd2f9bb5bd6a Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 12:16:13 +0200 Subject: [PATCH 013/121] fixed selections moving when new messages come in while selecting --- src/messages/selection.hpp | 5 ++ src/providers/twitch/twitchchannel.cpp | 7 +- src/widgets/helper/channelview.cpp | 115 +++++++++++++++++++------ src/widgets/helper/channelview.hpp | 9 +- src/widgets/helper/splitheader.cpp | 2 +- src/widgets/helper/splitoverlay.cpp | 2 +- src/widgets/scrollbar.cpp | 4 +- 7 files changed, 112 insertions(+), 32 deletions(-) diff --git a/src/messages/selection.hpp b/src/messages/selection.hpp index 62a3ba6a..805f48a8 100644 --- a/src/messages/selection.hpp +++ b/src/messages/selection.hpp @@ -42,6 +42,11 @@ struct SelectionItem { { return this->messageIndex == b.messageIndex && this->charIndex == b.charIndex; } + + bool operator!=(const SelectionItem &b) const + { + return this->operator==(b); + } }; struct Selection { diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index 4acff566..49d29730 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -45,7 +45,8 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection this->refreshLiveStatus(); // }); - this->managedConnect(app->accounts->Twitch.currentUserChanged, [this]() { this->setMod(false); }); + this->managedConnect(app->accounts->Twitch.currentUserChanged, + [this]() { this->setMod(false); }); auto refreshPubSubState = [=]() { if (!this->hasModRights()) { @@ -102,6 +103,10 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection this->chattersListTimer = new QTimer; QObject::connect(this->chattersListTimer, &QTimer::timeout, doRefreshChatters); this->chattersListTimer->start(5 * 60 * 1000); + + for (int i = 0; i < 1000; i++) { + this->addMessage(messages::Message::createSystemMessage("asdf")); + } } TwitchChannel::~TwitchChannel() diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index a62e0b51..f30ab232 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -28,6 +28,8 @@ #include #define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale()) +#define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3 +#define CHAT_HOVER_PAUSE_DURATION 300 using namespace chatterino::messages; using namespace chatterino::providers::twitch; @@ -42,9 +44,6 @@ ChannelView::ChannelView(BaseWidget *parent) { auto app = getApp(); -#ifndef Q_OS_MAC -// this->setAttribute(Qt::WA_OpaquePaintEvent); -#endif this->setMouseTracking(true); this->managedConnections.emplace_back(app->settings->wordFlagsChanged.connect([=] { @@ -56,7 +55,8 @@ ChannelView::ChannelView(BaseWidget *parent) // Whenever the scrollbar value has been changed, re-render the ChatWidgetView this->actuallyLayoutMessages(true); this->goToBottom->setVisible(this->enableScrollingToBottom && this->scrollBar.isVisible() && - !this->scrollBar.isAtBottom()); + !this->scrollBar.isAtBottom() && + !this->scrollToBottomAfterTemporaryPause); this->queueUpdate(); }); @@ -97,12 +97,21 @@ ChannelView::ChannelView(BaseWidget *parent) // }); this->pauseTimeout.setSingleShot(true); + QObject::connect(&this->pauseTimeout, &QTimer::timeout, [this] { + + this->pausedTemporarily = false; + if (!this->isPaused() && this->scrollToBottomAfterTemporaryPause) { + this->scrollBar.scrollToBottom(); + } + + this->scrollToBottomAfterTemporaryPause = false; + }); // auto e = new QResizeEvent(this->size(), this->size()); // this->resizeEvent(e); // delete e; - this->scrollBar.resize(this->scrollBar.width(), this->height() + 1); + // this->scrollBar.resize(this->scrollBar.width(), this->height() + 1); app->settings->showLastMessageIndicator.connect( [this](auto, auto) { @@ -120,6 +129,11 @@ ChannelView::ChannelView(BaseWidget *parent) this->layoutQueued = false; } }); + + QTimer::singleShot(1, this, [this] { + this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0, + this->scrollBar.width(), this->height()); + }); } ChannelView::~ChannelView() @@ -336,10 +350,11 @@ const boost::optional &ChannelView::getOverride messages::LimitedQueueSnapshot ChannelView::getMessagesSnapshot() { - if (!this->paused) { - this->snapshot = this->messages.getSnapshot(); - } + // if (!this->isPaused()) { + this->snapshot = this->messages.getSnapshot(); + // } + // return this->snapshot; return this->snapshot; } @@ -363,14 +378,18 @@ void ChannelView::setChannel(ChannelPtr newChannel) } this->lastMessageHasAlternateBackground = !this->lastMessageHasAlternateBackground; + if (this->isPaused()) { + this->messagesAddedSinceSelectionPause++; + } + if (this->messages.pushBack(MessageLayoutPtr(messageRef), deleted)) { - if (!this->paused) { - if (this->scrollBar.isAtBottom()) { - this->scrollBar.scrollToBottom(); - } else { - this->scrollBar.offset(-1); - } + // if (!this->isPaused()) { + if (this->scrollBar.isAtBottom()) { + this->scrollBar.scrollToBottom(); + } else { + this->scrollBar.offset(-1); } + // } } if (!(message->flags & Message::DoNotTriggerNotification)) { @@ -395,7 +414,7 @@ void ChannelView::setChannel(ChannelPtr newChannel) messageRefs.at(i) = MessageLayoutPtr(new MessageLayout(messages.at(i))); } - if (!this->paused) { + if (!this->isPaused()) { if (this->messages.pushFront(messageRefs).size() > 0) { if (this->scrollBar.isAtBottom()) { this->scrollBar.scrollToBottom(); @@ -474,7 +493,15 @@ void ChannelView::detachChannel() void ChannelView::pause(int msecTimeout) { - this->paused = true; + if (!this->pauseTimeout.isActive()) { + this->scrollToBottomAfterTemporaryPause = this->scrollBar.isAtBottom(); + } + + this->beginPause(); + + // this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.01); + + this->pausedTemporarily = true; this->pauseTimeout.start(msecTimeout); } @@ -492,8 +519,8 @@ void ChannelView::updateLastReadMessage() void ChannelView::resizeEvent(QResizeEvent *) { - this->scrollBar.resize(this->scrollBar.width(), this->height()); - this->scrollBar.move(this->width() - this->scrollBar.width(), 0); + this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0, this->scrollBar.width(), + this->height()); this->goToBottom->setGeometry(0, this->height() - 32, this->width(), 32); @@ -507,6 +534,15 @@ void ChannelView::resizeEvent(QResizeEvent *) void ChannelView::setSelection(const SelectionItem &start, const SelectionItem &end) { // selections + if (!this->selecting && start != end) { + this->messagesAddedSinceSelectionPause = + (this->scrollToBottomAfterTemporaryPause || this->scrollBar.isAtBottom()) ? 0 : 100; + + this->beginPause(); + this->selecting = true; + this->pausedBySelection = true; + } + this->selection = Selection(start, end); this->selectionChanged.invoke(); @@ -536,6 +572,23 @@ messages::MessageElement::Flags ChannelView::getFlags() const return flags; } +bool ChannelView::isPaused() +{ + return this->pausedTemporarily || this->pausedBySelection; +} + +void ChannelView::beginPause() +{ + if (this->scrollBar.isAtBottom()) { + this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.001); + this->layoutMessages(); + } +} + +void ChannelView::endPause() +{ +} + void ChannelView::paintEvent(QPaintEvent * /*event*/) { // BENCH(timer); @@ -691,7 +744,7 @@ void ChannelView::enterEvent(QEvent *) void ChannelView::leaveEvent(QEvent *) { - this->paused = false; + this->pausedTemporarily = false; } void ChannelView::mouseMoveEvent(QMouseEvent *event) @@ -706,7 +759,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) auto app = getApp(); if (app->settings->pauseChatHover.getValue()) { - this->pause(300); + this->pause(CHAT_HOVER_PAUSE_DURATION); } auto tooltipWidget = TooltipWidget::getInstance(); @@ -722,8 +775,8 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } // is selecting - if (this->selecting) { - this->pause(500); + if (this->isMouseDown) { + this->pause(300); int index = layout->getSelectionIndex(relativePos); this->setSelection(this->selection.start, SelectionItem(messageIndex, index)); @@ -804,7 +857,6 @@ void ChannelView::mousePressEvent(QMouseEvent *event) SelectionItem selectionItem(lastMessageIndex, lastCharacterIndex); this->setSelection(selectionItem, selectionItem); - this->selecting = true; return; } @@ -818,7 +870,6 @@ void ChannelView::mousePressEvent(QMouseEvent *event) auto selectionItem = SelectionItem(messageIndex, index); this->setSelection(selectionItem, selectionItem); - this->selecting = true; this->repaint(); } @@ -841,11 +892,23 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) auto app = getApp(); if (this->selecting) { - this->paused = false; + if (this->messagesAddedSinceSelectionPause <= SELECTION_RESUME_SCROLLING_MSG_THRESHOLD) { + // don't scroll + this->scrollBar.scrollToBottom(false); + // this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - + // this->messagesAddedSinceSelectionPause); + } + + this->scrollToBottomAfterTemporaryPause = false; + this->pausedBySelection = false; + this->selecting = false; + this->pauseTimeout.stop(); + this->pausedTemporarily = false; + + this->layoutMessages(); } this->isMouseDown = false; - this->selecting = false; float distance = util::distanceBetweenPoints(this->lastPressPosition, event->screenPos()); diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index 47262632..a3c9b663 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -86,7 +86,10 @@ private: bool updateQueued = false; bool messageWasAdded = false; bool lastMessageHasAlternateBackground = false; - bool paused = false; + bool pausedTemporarily = false; + bool pausedBySelection = false; + bool scrollToBottomAfterTemporaryPause = false; + int messagesAddedSinceSelectionPause = 0; QTimer pauseTimeout; boost::optional overrideFlags; messages::MessageLayoutPtr lastReadMessage; @@ -99,6 +102,10 @@ private: void drawMessages(QPainter &painter); void setSelection(const messages::SelectionItem &start, const messages::SelectionItem &end); messages::MessageElement::Flags getFlags() const; + bool isPaused(); + + void beginPause(); + void endPause(); ChannelPtr channel; diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 845f037b..7264f81a 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -253,7 +253,7 @@ void SplitHeader::mouseReleaseEvent(QMouseEvent *event) TooltipWidget *widget = new TooltipWidget(); - widget->setText("Double click or press to change the channel.\nClick and " + widget->setText("Double click or press to change the channel.\nClick and " "drag to move the split."); widget->setAttribute(Qt::WA_DeleteOnClose); widget->move(pos); diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index cded6211..1c78a9de 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -86,7 +86,7 @@ SplitOverlay::SplitOverlay(Split *parent) void SplitOverlay::paintEvent(QPaintEvent *event) { QPainter painter(this); - painter.fillRect(this->rect(), QColor(0, 0, 0, 90)); + painter.fillRect(this->rect(), QColor(0, 0, 0, 150)); QRect rect; switch (this->hoveredElement) { diff --git a/src/widgets/scrollbar.cpp b/src/widgets/scrollbar.cpp index 01df6321..039d9a20 100644 --- a/src/widgets/scrollbar.cpp +++ b/src/widgets/scrollbar.cpp @@ -109,14 +109,14 @@ void Scrollbar::setDesiredValue(qreal value, bool animated) // } this->currentValueAnimation.setEndValue(value); this->smoothScrollingOffset = 0; - this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.01; + this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001; this->currentValueAnimation.start(); } else { if (this->currentValueAnimation.state() != QPropertyAnimation::Running) { this->smoothScrollingOffset = 0; this->desiredValue = value; this->currentValueAnimation.stop(); - this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.01; + this->atBottom = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001; setCurrentValue(value); } } From 05261ef67cceebfc24c051d05173a0478e252912 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 12:17:18 +0200 Subject: [PATCH 014/121] commented out development code --- src/providers/twitch/twitchchannel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index 49d29730..51da1299 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -104,9 +104,9 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection QObject::connect(this->chattersListTimer, &QTimer::timeout, doRefreshChatters); this->chattersListTimer->start(5 * 60 * 1000); - for (int i = 0; i < 1000; i++) { - this->addMessage(messages::Message::createSystemMessage("asdf")); - } + // for (int i = 0; i < 1000; i++) { + // this->addMessage(messages::Message::createSystemMessage("asdf")); + // } } TwitchChannel::~TwitchChannel() From 89ca71aec429440161421cf345728e00d85df0af Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 12:29:14 +0200 Subject: [PATCH 015/121] commented out the unused logspage --- src/widgets/settingspages/logspage.cpp | 73 +++++++++++++------------- src/widgets/settingspages/logspage.hpp | 26 ++++----- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/widgets/settingspages/logspage.cpp b/src/widgets/settingspages/logspage.cpp index 04c5ebf7..b7455fe6 100644 --- a/src/widgets/settingspages/logspage.cpp +++ b/src/widgets/settingspages/logspage.cpp @@ -1,48 +1,49 @@ -#include "logspage.hpp" +//#include "logspage.hpp" -#include "application.hpp" -#include "singletons/pathmanager.hpp" +//#include "application.hpp" +//#include "singletons/pathmanager.hpp" -#include -#include +//#include +//#include -#include "util/layoutcreator.hpp" +//#include "util/layoutcreator.hpp" -namespace chatterino { -namespace widgets { -namespace settingspages { +// namespace chatterino { +// namespace widgets { +// namespace settingspages { -inline QString CreateLink(const QString &url, bool file = false) -{ - if (file) { - return QString("" + url + - ""); - } +// inline QString CreateLink(const QString &url, bool file = false) +//{ +// if (file) { +// return QString("" + url + +// ""); +// } - return QString("" + url + ""); -} +// return QString("" + url + +// ""); +//} -LogsPage::LogsPage() - : SettingsPage("Logs", "") -{ - auto app = getApp(); +// LogsPage::LogsPage() +// : SettingsPage("Logs", "") +//{ +// auto app = getApp(); - util::LayoutCreator layoutCreator(this); - auto layout = layoutCreator.emplace().withoutMargin(); +// util::LayoutCreator layoutCreator(this); +// auto layout = layoutCreator.emplace().withoutMargin(); - auto logPath = app->paths->logsFolderPath; +// auto logPath = app->paths->logsFolderPath; - auto created = layout.emplace(); - created->setText("Logs are saved to " + CreateLink(logPath, true)); - created->setTextFormat(Qt::RichText); - created->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByKeyboard); - created->setOpenExternalLinks(true); - layout.append(this->createCheckBox("Enable logging", app->settings->enableLogging)); +// auto created = layout.emplace(); +// created->setText("Logs are saved to " + CreateLink(logPath, true)); +// created->setTextFormat(Qt::RichText); +// created->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard | +// Qt::LinksAccessibleByKeyboard); +// created->setOpenExternalLinks(true); +// layout.append(this->createCheckBox("Enable logging", app->settings->enableLogging)); - layout->addStretch(1); -} +// layout->addStretch(1); +//} -} // namespace settingspages -} // namespace widgets -} // namespace chatterino +//} // namespace settingspages +//} // namespace widgets +//} // namespace chatterino diff --git a/src/widgets/settingspages/logspage.hpp b/src/widgets/settingspages/logspage.hpp index 01af3537..5103571b 100644 --- a/src/widgets/settingspages/logspage.hpp +++ b/src/widgets/settingspages/logspage.hpp @@ -1,17 +1,17 @@ -#pragma once +//#pragma once -#include "widgets/settingspages/settingspage.hpp" +//#include "widgets/settingspages/settingspage.hpp" -namespace chatterino { -namespace widgets { -namespace settingspages { +// namespace chatterino { +// namespace widgets { +// namespace settingspages { -class LogsPage : public SettingsPage -{ -public: - LogsPage(); -}; +// class LogsPage : public SettingsPage +//{ +// public: +// LogsPage(); +//}; -} // namespace settingspages -} // namespace widgets -} // namespace chatterino +//} // namespace settingspages +//} // namespace widgets +//} // namespace chatterino From 4d3437e6d1ab92e4c8fd5af94595d534b6fa7732 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 13:43:01 +0200 Subject: [PATCH 016/121] fixed timeouts for non moderators --- src/application.cpp | 3 +- src/channel.cpp | 118 +++++++++++++-------- src/channel.hpp | 1 + src/messages/message.cpp | 1 - src/messages/message.hpp | 3 +- src/providers/twitch/ircmessagehandler.cpp | 100 +++++++---------- 6 files changed, 112 insertions(+), 114 deletions(-) diff --git a/src/application.cpp b/src/application.cpp index 53746ff2..5d1bb688 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -182,8 +182,9 @@ void Application::initialize() } auto msg = messages::Message::createTimeoutMessage(action); + msg->flags |= messages::Message::PubSub; - util::postToThread([chan, msg] { chan->addMessage(msg); }); + util::postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); }); }); this->twitch.pubsub->sig.moderation.userUnbanned.connect([&](const auto &action) { diff --git a/src/channel.cpp b/src/channel.cpp index 2b718797..2e340665 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -60,57 +60,14 @@ void Channel::addMessage(MessagePtr message) auto app = getApp(); MessagePtr deleted; - bool isTimeout = (message->flags & Message::Timeout) != 0; - - if (!isTimeout) { - const QString &username = message->loginName; - if (!username.isEmpty()) { - // TODO: Add recent chatters display name. This should maybe be a setting - this->addRecentChatter(message); - } + const QString &username = message->loginName; + if (!username.isEmpty()) { + // TODO: Add recent chatters display name. This should maybe be a setting + this->addRecentChatter(message); } app->logging->addMessage(this->name, message); - if (isTimeout) { - LimitedQueueSnapshot snapshot = this->getMessageSnapshot(); - bool addMessage = true; - int snapshotLength = snapshot.getLength(); - - int end = std::max(0, snapshotLength - 20); - - for (int i = snapshotLength - 1; i >= end; --i) { - auto &s = snapshot[i]; - if (s->flags.HasFlag(Message::Untimeout) && s->timeoutUser == message->timeoutUser) { - break; - } - - if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) { - assert(message->banAction != nullptr); - MessagePtr replacement( - Message::createTimeoutMessage(*(message->banAction), s->count + 1)); - this->replaceMessage(s, replacement); - addMessage = false; - } - } - - // disable the messages from the user - for (int i = 0; i < snapshotLength; i++) { - auto &s = snapshot[i]; - if ((s->flags & (Message::Timeout | Message::Untimeout)) == 0 && - s->loginName == message->timeoutUser) { - s->flags.EnableFlag(Message::Disabled); - } - } - - // XXX: Might need the following line - // WindowManager::getInstance().repaintVisibleChatWidgets(this); - - if (!addMessage) { - return; - } - } - if (this->messages.pushBack(message, deleted)) { this->messageRemovedFromStart.invoke(deleted); } @@ -118,6 +75,73 @@ void Channel::addMessage(MessagePtr message) this->messageAppended.invoke(message); } +void Channel::addOrReplaceTimeout(messages::MessagePtr message) +{ + LimitedQueueSnapshot snapshot = this->getMessageSnapshot(); + int snapshotLength = snapshot.getLength(); + + int end = std::max(0, snapshotLength - 20); + + bool addMessage = true; + + QTime minimumTime = QTime::currentTime().addSecs(-5); + + for (int i = snapshotLength - 1; i >= end; --i) { + auto &s = snapshot[i]; + + qDebug() << s->parseTime << minimumTime; + + if (s->parseTime < minimumTime) { + break; + } + + if (s->flags.HasFlag(Message::Untimeout) && s->timeoutUser == message->timeoutUser) { + break; + } + + if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) { + if (message->flags.HasFlag(Message::PubSub) && !s->flags.HasFlag(Message::PubSub)) { + this->replaceMessage(s, message); + addMessage = false; + break; + } + if (!message->flags.HasFlag(Message::PubSub) && s->flags.HasFlag(Message::PubSub)) { + addMessage = false; + break; + } + + int count = s->count + 1; + + messages::MessagePtr replacement(Message::createSystemMessage( + message->searchText + QString("(") + QString::number(count) + " times)")); + + replacement->timeoutUser = message->timeoutUser; + replacement->count = count; + replacement->flags = message->flags; + + this->replaceMessage(s, replacement); + + return; + } + } + + // disable the messages from the user + for (int i = 0; i < snapshotLength; i++) { + auto &s = snapshot[i]; + if ((s->flags & (Message::Timeout | Message::Untimeout)) == 0 && + s->loginName == message->timeoutUser) { + s->flags.EnableFlag(Message::Disabled); + } + } + + if (addMessage) { + this->addMessage(message); + } + + // XXX: Might need the following line + // WindowManager::getInstance().repaintVisibleChatWidgets(this); +} + void Channel::addMessagesAtStart(std::vector &_messages) { std::vector addedMessages = this->messages.pushFront(_messages); diff --git a/src/channel.hpp b/src/channel.hpp index 6a2433f6..91d2ad4f 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -48,6 +48,7 @@ public: void addMessage(messages::MessagePtr message); void addMessagesAtStart(std::vector &messages); + void addOrReplaceTimeout(messages::MessagePtr message); void replaceMessage(messages::MessagePtr message, messages::MessagePtr replacement); virtual void addRecentChatter(const std::shared_ptr &message); diff --git a/src/messages/message.cpp b/src/messages/message.cpp index 38ea0837..4b0e9f3c 100644 --- a/src/messages/message.cpp +++ b/src/messages/message.cpp @@ -89,7 +89,6 @@ MessagePtr Message::createTimeoutMessage(const providers::twitch::BanAction &act msg->timeoutUser = action.target.name; msg->count = count; - msg->banAction.reset(new providers::twitch::BanAction(action)); QString text; diff --git a/src/messages/message.hpp b/src/messages/message.hpp index d3f81367..5465ac96 100644 --- a/src/messages/message.hpp +++ b/src/messages/message.hpp @@ -18,6 +18,7 @@ namespace messages { struct Message { Message() + : parseTime(QTime::currentTime()) { util::DebugCount::increase("messages"); } @@ -39,6 +40,7 @@ struct Message { Collapsed = (1 << 7), DisconnectedMessage = (1 << 8), Untimeout = (1 << 9), + PubSub = (1 << 10), }; util::FlagsEnum flags; @@ -50,7 +52,6 @@ struct Message { QString localizedName; QString timeoutUser; - std::unique_ptr banAction; uint32_t count = 1; // Messages should not be added after the message is done initializing. diff --git a/src/providers/twitch/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp index 7111a0f5..3d199e97 100644 --- a/src/providers/twitch/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -60,80 +60,52 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) { - return; - // // check parameter count - // if (message->parameters().length() < 1) { - // return; - // } + // check parameter count + if (message->parameters().length() < 1) { + return; + } - // QString chanName; - // if (!TrimChannelName(message->parameter(0), chanName)) { - // return; - // } + QString chanName; + if (!TrimChannelName(message->parameter(0), chanName)) { + return; + } - // auto app = getApp(); + auto app = getApp(); - // // get channel - // auto chan = app->twitch.server->getChannelOrEmpty(chanName); + // get channel + auto chan = app->twitch.server->getChannelOrEmpty(chanName); - // if (chan->isEmpty()) { - // debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found", - // chanName); - // return; - // } + if (chan->isEmpty()) { + debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found", + chanName); + return; + } - // // check if the chat has been cleared by a moderator - // if (message->parameters().length() == 1) { - // chan->addMessage(Message::createSystemMessage("Chat has been cleared by a - // moderator.")); + // check if the chat has been cleared by a moderator + if (message->parameters().length() == 1) { + chan->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator.")); - // return; - // } + return; + } - // // get username, duration and message of the timed out user - // QString username = message->parameter(1); - // QString durationInSeconds, reason; - // QVariant v = message->tag("ban-duration"); - // if (v.isValid()) { - // durationInSeconds = v.toString(); - // } + // get username, duration and message of the timed out user + QString username = message->parameter(1); + QString durationInSeconds, reason; + QVariant v = message->tag("ban-duration"); + if (v.isValid()) { + durationInSeconds = v.toString(); + } - // v = message->tag("ban-reason"); - // if (v.isValid()) { - // reason = v.toString(); - // } + v = message->tag("ban-reason"); + if (v.isValid()) { + reason = v.toString(); + } - // // add the notice that the user has been timed out - // LimitedQueueSnapshot snapshot = chan->getMessageSnapshot(); - // bool addMessage = true; - // int snapshotLength = snapshot.getLength(); + auto timeoutMsg = Message::createTimeoutMessage(username, durationInSeconds, reason, false); + chan->addOrReplaceTimeout(timeoutMsg); - // for (int i = std::max(0, snapshotLength - 20); i < snapshotLength; i++) { - // auto &s = snapshot[i]; - // if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == username) { - // MessagePtr replacement( - // Message::createTimeoutMessage(username, durationInSeconds, reason, true)); - // chan->replaceMessage(s, replacement); - // addMessage = false; - // break; - // } - // } - - // if (addMessage) { - // chan->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason, - // false)); - // } - - // // disable the messages from the user - // for (int i = 0; i < snapshotLength; i++) { - // auto &s = snapshot[i]; - // if (!(s->flags & Message::Timeout) && s->loginName == username) { - // s->flags.EnableFlag(Message::Disabled); - // } - // } - - // // refresh all - // app->windows->repaintVisibleChatWidgets(chan.get()); + // refresh all + app->windows->repaintVisibleChatWidgets(chan.get()); } void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message) From c27a4eec333a4130324756d60c94ac5a7163c72a Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 14:47:23 +0200 Subject: [PATCH 017/121] fixed the splitoverlay not disappearing when the window is not selected --- src/widgets/helper/splitoverlay.cpp | 9 +++++++++ src/widgets/helper/splitoverlay.hpp | 1 + 2 files changed, 10 insertions(+) diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index 1c78a9de..664177aa 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -81,6 +81,8 @@ SplitOverlay::SplitOverlay(Split *parent) up->setIconSize(size); down->setIconSize(size); }); + + this->setMouseTracking(true); } void SplitOverlay::paintEvent(QPaintEvent *event) @@ -122,6 +124,13 @@ void SplitOverlay::resizeEvent(QResizeEvent *event) this->_down->setVisible(highEnough); } +void SplitOverlay::mouseMoveEvent(QMouseEvent *event) +{ + if ((event->modifiers() & Qt::AltModifier) == 0) { + this->hide(); + } +} + SplitOverlay::ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent, HoveredElement _element) : QObject(_parent) , parent(_parent) diff --git a/src/widgets/helper/splitoverlay.hpp b/src/widgets/helper/splitoverlay.hpp index 4877835c..f7e8c16d 100644 --- a/src/widgets/helper/splitoverlay.hpp +++ b/src/widgets/helper/splitoverlay.hpp @@ -19,6 +19,7 @@ public: protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; private: // fourtf: !!! preserve the order of left, up, right and down From 795758f618180ff95c1f803b49fa24f2c8241e58 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 16:39:38 +0200 Subject: [PATCH 018/121] fixed the split overlay not appearing proprly --- src/providers/twitch/twitchchannel.cpp | 2 + src/widgets/helper/splitoverlay.cpp | 10 +++-- src/widgets/helper/splitoverlay.hpp | 1 + src/widgets/split.cpp | 58 ++++++-------------------- src/widgets/split.hpp | 6 --- 5 files changed, 22 insertions(+), 55 deletions(-) diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index 51da1299..2b7300d2 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -191,6 +191,8 @@ bool TwitchChannel::isBroadcaster() { auto app = getApp(); + qDebug() << "ASD" << (this->name == app->accounts->Twitch.getCurrent()->getUserName()); + return this->name == app->accounts->Twitch.getCurrent()->getUserName(); } diff --git a/src/widgets/helper/splitoverlay.cpp b/src/widgets/helper/splitoverlay.cpp index 664177aa..ccde94cd 100644 --- a/src/widgets/helper/splitoverlay.cpp +++ b/src/widgets/helper/splitoverlay.cpp @@ -126,9 +126,13 @@ void SplitOverlay::resizeEvent(QResizeEvent *event) void SplitOverlay::mouseMoveEvent(QMouseEvent *event) { - if ((event->modifiers() & Qt::AltModifier) == 0) { - this->hide(); - } + BaseWidget::mouseMoveEvent(event); + + // qDebug() << QGuiApplication::queryKeyboardModifiers(); + + // if ((QGuiApplication::queryKeyboardModifiers() & Qt::AltModifier) == Qt::AltModifier) { + // this->hide(); + // } } SplitOverlay::ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent, HoveredElement _element) diff --git a/src/widgets/helper/splitoverlay.hpp b/src/widgets/helper/splitoverlay.hpp index f7e8c16d..9dc06799 100644 --- a/src/widgets/helper/splitoverlay.hpp +++ b/src/widgets/helper/splitoverlay.hpp @@ -17,6 +17,7 @@ public: explicit SplitOverlay(Split *parent = nullptr); protected: + // bool event(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 1d1b9a5e..2f30d4e7 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -129,10 +129,15 @@ Split::Split(QWidget *parent) this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); this->managedConnect(modifierStatusChanged, [this](Qt::KeyboardModifiers status) { + + qDebug() << "xD" << status; + if ((status == Qt::AltModifier || status == (Qt::AltModifier | Qt::ControlModifier)) && this->isMouseOver) { + qDebug() << "show"; this->overlay->show(); } else { + qDebug() << "hide"; this->overlay->hide(); } }); @@ -271,26 +276,19 @@ void Split::paintEvent(QPaintEvent *) void Split::mouseMoveEvent(QMouseEvent *event) { - this->handleModifiers(event, event->modifiers()); -} - -void Split::mousePressEvent(QMouseEvent *event) -{ - // if (event->buttons() == Qt::LeftButton && event->modifiers() & Qt::AltModifier) { - // this->drag(); - // } + this->handleModifiers(event, QGuiApplication::queryKeyboardModifiers()); } void Split::keyPressEvent(QKeyEvent *event) { this->view.unsetCursor(); - this->handleModifiers(event, event->modifiers()); + this->handleModifiers(event, QGuiApplication::queryKeyboardModifiers()); } void Split::keyReleaseEvent(QKeyEvent *event) { this->view.unsetCursor(); - this->handleModifiers(event, event->modifiers()); + this->handleModifiers(event, QGuiApplication::queryKeyboardModifiers()); } void Split::resizeEvent(QResizeEvent *event) @@ -304,7 +302,7 @@ void Split::enterEvent(QEvent *event) { this->isMouseOver = true; - auto a = modifierStatus; + this->handleModifiers(event, QGuiApplication::queryKeyboardModifiers()); if (modifierStatus == Qt::AltModifier || modifierStatus == (Qt::AltModifier | Qt::ControlModifier)) { @@ -315,7 +313,10 @@ void Split::enterEvent(QEvent *event) void Split::leaveEvent(QEvent *event) { this->isMouseOver = false; + this->overlay->hide(); + + this->handleModifiers(event, QGuiApplication::queryKeyboardModifiers()); } void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers) @@ -324,41 +325,6 @@ void Split::handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers) modifierStatus = modifiers; modifierStatusChanged.invoke(modifiers); } - - // if (modifiers == Qt::AltModifier) { - // if (!modifierStatus) { - // modifierStatus = true; - // modifierStatusChanged.invoke(true); - // } - // } else { - // if (modifierStatus) { - // modifierStatus = false; - // modifierStatusChanged.invoke(false); - // } - // this->setCursor(Qt::ArrowCursor); - // } -} - -void Split::dragEnterEvent(QDragEnterEvent *event) -{ - event->acceptProposedAction(); - this->isDragging = true; - QTimer::singleShot(1, [this] { this->overlay->show(); }); -} - -void Split::dragLeaveEvent(QDragLeaveEvent *event) -{ - this->overlay->hide(); - this->isDragging = false; -} - -void Split::dragMoveEvent(QDragMoveEvent *event) -{ - event->acceptProposedAction(); -} - -void Split::dropEvent(QDropEvent *event) -{ } /// Slots diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 565844b3..45a09d88 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -77,18 +77,12 @@ public: protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void resizeEvent(QResizeEvent *event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dropEvent(QDropEvent *event) override; - private: SplitContainer *container; IndirectChannel channel; From f6d02fffc99ec203e1b90733dd9237fdb451f6c0 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 17:27:20 +0200 Subject: [PATCH 019/121] rewrote the pausing chat on hover functionality --- src/widgets/helper/channelview.cpp | 75 ++++++++++++------------------ src/widgets/helper/channelview.hpp | 7 +-- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index f30ab232..07e7044f 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -54,9 +54,12 @@ ChannelView::ChannelView(BaseWidget *parent) this->scrollBar.getCurrentValueChanged().connect([this] { // Whenever the scrollbar value has been changed, re-render the ChatWidgetView this->actuallyLayoutMessages(true); - this->goToBottom->setVisible(this->enableScrollingToBottom && this->scrollBar.isVisible() && - !this->scrollBar.isAtBottom() && - !this->scrollToBottomAfterTemporaryPause); + + if (!this->isPaused()) { + this->goToBottom->setVisible(this->enableScrollingToBottom && + this->scrollBar.isVisible() && + !this->scrollBar.isAtBottom()); + } this->queueUpdate(); }); @@ -98,21 +101,10 @@ ChannelView::ChannelView(BaseWidget *parent) this->pauseTimeout.setSingleShot(true); QObject::connect(&this->pauseTimeout, &QTimer::timeout, [this] { - this->pausedTemporarily = false; - if (!this->isPaused() && this->scrollToBottomAfterTemporaryPause) { - this->scrollBar.scrollToBottom(); - } - - this->scrollToBottomAfterTemporaryPause = false; + this->layoutMessages(); }); - // auto e = new QResizeEvent(this->size(), this->size()); - // this->resizeEvent(e); - // delete e; - - // this->scrollBar.resize(this->scrollBar.width(), this->height() + 1); - app->settings->showLastMessageIndicator.connect( [this](auto, auto) { this->update(); // @@ -134,7 +126,7 @@ ChannelView::ChannelView(BaseWidget *parent) this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0, this->scrollBar.width(), this->height()); }); -} +} // namespace widgets ChannelView::~ChannelView() { @@ -260,8 +252,11 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) // Perhaps also if the user scrolled with the scrollwheel in this ChatWidget in the last 0.2 // seconds or something if (this->enableScrollingToBottom && this->showingLatestMessages && showScrollbar) { - this->scrollBar.scrollToBottom(this->messageWasAdded && - app->settings->enableSmoothScrollingNewMessages.getValue()); + if (!this->isPaused()) { + this->scrollBar.scrollToBottom( + this->messageWasAdded && + app->settings->enableSmoothScrollingNewMessages.getValue()); + } this->messageWasAdded = false; } @@ -493,14 +488,6 @@ void ChannelView::detachChannel() void ChannelView::pause(int msecTimeout) { - if (!this->pauseTimeout.isActive()) { - this->scrollToBottomAfterTemporaryPause = this->scrollBar.isAtBottom(); - } - - this->beginPause(); - - // this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.01); - this->pausedTemporarily = true; this->pauseTimeout.start(msecTimeout); @@ -535,10 +522,8 @@ void ChannelView::setSelection(const SelectionItem &start, const SelectionItem & { // selections if (!this->selecting && start != end) { - this->messagesAddedSinceSelectionPause = - (this->scrollToBottomAfterTemporaryPause || this->scrollBar.isAtBottom()) ? 0 : 100; + this->messagesAddedSinceSelectionPause = 0; - this->beginPause(); this->selecting = true; this->pausedBySelection = true; } @@ -577,17 +562,17 @@ bool ChannelView::isPaused() return this->pausedTemporarily || this->pausedBySelection; } -void ChannelView::beginPause() -{ - if (this->scrollBar.isAtBottom()) { - this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.001); - this->layoutMessages(); - } -} +// void ChannelView::beginPause() +//{ +// if (this->scrollBar.isAtBottom()) { +// this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - 0.001); +// this->layoutMessages(); +// } +//} -void ChannelView::endPause() -{ -} +// void ChannelView::endPause() +//{ +//} void ChannelView::paintEvent(QPaintEvent * /*event*/) { @@ -675,6 +660,9 @@ void ChannelView::drawMessages(QPainter &painter) void ChannelView::wheelEvent(QWheelEvent *event) { + this->pausedBySelection = false; + this->pausedTemporarily = false; + if (this->scrollBar.isVisible()) { auto app = getApp(); @@ -745,6 +733,7 @@ void ChannelView::enterEvent(QEvent *) void ChannelView::leaveEvent(QEvent *) { this->pausedTemporarily = false; + this->layoutMessages(); } void ChannelView::mouseMoveEvent(QMouseEvent *event) @@ -892,14 +881,10 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) auto app = getApp(); if (this->selecting) { - if (this->messagesAddedSinceSelectionPause <= SELECTION_RESUME_SCROLLING_MSG_THRESHOLD) { - // don't scroll - this->scrollBar.scrollToBottom(false); - // this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue() - - // this->messagesAddedSinceSelectionPause); + if (this->messagesAddedSinceSelectionPause > SELECTION_RESUME_SCROLLING_MSG_THRESHOLD) { + this->showingLatestMessages = false; } - this->scrollToBottomAfterTemporaryPause = false; this->pausedBySelection = false; this->selecting = false; this->pauseTimeout.stop(); diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index a3c9b663..98f028d9 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -86,10 +86,11 @@ private: bool updateQueued = false; bool messageWasAdded = false; bool lastMessageHasAlternateBackground = false; + bool pausedTemporarily = false; bool pausedBySelection = false; - bool scrollToBottomAfterTemporaryPause = false; int messagesAddedSinceSelectionPause = 0; + QTimer pauseTimeout; boost::optional overrideFlags; messages::MessageLayoutPtr lastReadMessage; @@ -104,8 +105,8 @@ private: messages::MessageElement::Flags getFlags() const; bool isPaused(); - void beginPause(); - void endPause(); + // void beginPause(); + // void endPause(); ChannelPtr channel; From 0f7c25d09887192f16f1ef4022c3457d828ab156 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 17 May 2018 17:47:17 +0200 Subject: [PATCH 020/121] removed webengine support so the ci can build again --- chatterino.pro | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index eff6c715..2461429b 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -39,11 +39,11 @@ include(dependencies/openssl.pri) include(dependencies/boost.pri) # Optional feature: QtWebEngine -exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { - message(Using QWebEngine) - QT += webenginewidgets - DEFINES += "USEWEBENGINE" -} +#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { +# message(Using QWebEngine) +# QT += webenginewidgets +# DEFINES += "USEWEBENGINE" +#} linux { LIBS += -lrt From bf560d37bd3f13f23a3108b976fdd88c828afe5d Mon Sep 17 00:00:00 2001 From: Vilgot Fredenberg Date: Mon, 21 May 2018 17:37:15 +0200 Subject: [PATCH 021/121] Update README.md (#413) Add new library requirements for Ubuntu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21a39a34..08c94852 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ You can also add `-o2` to optimize the final binary size but increase compilatio #### Ubuntu 18.04 *most likely works the same for other Debian-like distros* -1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev` +1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev` 2. Install rapidjson to `/usr/local/` like this: From the Chatterino2 root folder: `sudo cp -r lib/rapidjson/include/rapidjson /usr/local/include`. If you want to install it to another place, you have to make sure it's in the chatterino.pro include path 3. open `chatterino.pro` with QT Creator and build From dafbda6a4a8cff655774faac78e0d08b799be839 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 04:22:17 +0200 Subject: [PATCH 022/121] asdf --- src/messages/layouts/messagelayoutelement.cpp | 4 +- src/messages/messageelement.cpp | 4 +- src/messages/messageelement.hpp | 2 +- src/providers/twitch/twitchmessagebuilder.cpp | 8 +- src/singletons/fontmanager.cpp | 174 ++++--- src/singletons/fontmanager.hpp | 144 ++---- src/singletons/thememanager.cpp | 60 ++- src/singletons/thememanager.hpp | 11 +- src/singletons/windowmanager.cpp | 22 +- src/widgets/helper/label.hpp | 2 +- src/widgets/helper/notebookbutton.cpp | 6 +- src/widgets/helper/notebooktab.cpp | 486 +++--------------- src/widgets/helper/notebooktab.hpp | 77 +-- src/widgets/helper/splitinput.cpp | 33 +- src/widgets/notebook.cpp | 340 ++---------- src/widgets/notebook.hpp | 58 +-- src/widgets/settingspages/appearancepage.cpp | 14 +- src/widgets/splitcontainer.cpp | 37 +- src/widgets/splitcontainer.hpp | 8 +- src/widgets/tooltipwidget.cpp | 2 +- src/widgets/window.cpp | 9 +- src/widgets/window.hpp | 6 +- 22 files changed, 404 insertions(+), 1103 deletions(-) diff --git a/src/messages/layouts/messagelayoutelement.cpp b/src/messages/layouts/messagelayoutelement.cpp index d2c0d2cc..788e23ee 100644 --- a/src/messages/layouts/messagelayoutelement.cpp +++ b/src/messages/layouts/messagelayoutelement.cpp @@ -188,7 +188,7 @@ int TextLayoutElement::getMouseOverIndex(const QPoint &abs) auto app = getApp(); - QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, this->scale); + QFontMetrics metrics = app->fonts->getFontMetrics(this->style, this->scale); int x = this->getRect().left(); @@ -209,7 +209,7 @@ int TextLayoutElement::getXFromIndex(int index) { auto app = getApp(); - QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, this->scale); + QFontMetrics metrics = app->fonts->getFontMetrics(this->style, this->scale); if (index <= 0) { return this->getRect().left(); diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index dc288ed3..4aa90c52 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -139,7 +139,7 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme auto app = getApp(); if (_flags & this->getFlags()) { - QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, container.getScale()); + QFontMetrics metrics = app->fonts->getFontMetrics(this->style, container.getScale()); for (Word &word : this->words) { auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) { @@ -242,7 +242,7 @@ TextElement *TimestampElement::formatTime(const QTime &time) QString format = locale.toString(time, getApp()->settings->timestampFormat); - return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::Medium); + return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::ChatMedium); } // TWITCH MODERATION diff --git a/src/messages/messageelement.hpp b/src/messages/messageelement.hpp index 79a78897..fc2be592 100644 --- a/src/messages/messageelement.hpp +++ b/src/messages/messageelement.hpp @@ -158,7 +158,7 @@ class TextElement : public MessageElement public: TextElement(const QString &text, MessageElement::Flags flags, const MessageColor &color = MessageColor::Text, - FontStyle style = FontStyle::Medium); + FontStyle style = FontStyle::ChatMedium); ~TextElement() override = default; void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 760979dc..2c628273 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -361,14 +361,14 @@ void TwitchMessageBuilder::appendUsername() } else if (this->args.isReceivedWhisper) { // Sender username this->emplace(usernameText, MessageElement::Text, this->usernameColor, - FontStyle::MediumBold) + FontStyle::ChatMediumBold) ->setLink({Link::UserInfo, this->userName}); auto currentUser = app->accounts->Twitch.getCurrent(); // Separator this->emplace("->", MessageElement::Text, - app->themes->messages.textColors.system, FontStyle::Medium); + app->themes->messages.textColors.system, FontStyle::ChatMedium); QColor selfColor = currentUser->color; if (!selfColor.isValid()) { @@ -377,14 +377,14 @@ void TwitchMessageBuilder::appendUsername() // Your own username this->emplace(currentUser->getUserName() + ":", MessageElement::Text, - selfColor, FontStyle::MediumBold); + selfColor, FontStyle::ChatMediumBold); } else { if (!this->action) { usernameText += ":"; } this->emplace(usernameText, MessageElement::Text, this->usernameColor, - FontStyle::MediumBold) + FontStyle::ChatMediumBold) ->setLink({Link::UserInfo, this->userName}); } } diff --git a/src/singletons/fontmanager.cpp b/src/singletons/fontmanager.cpp index 59a5e874..b25bc8b2 100644 --- a/src/singletons/fontmanager.cpp +++ b/src/singletons/fontmanager.cpp @@ -3,6 +3,8 @@ #include #include +#include "util/assertinguithread.hpp" + #ifdef Q_OS_WIN32 #define DEFAULT_FONT_FAMILY "Segoe UI" #define DEFAULT_FONT_SIZE 10 @@ -20,86 +22,110 @@ namespace chatterino { namespace singletons { FontManager::FontManager() - : currentFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY) - , currentFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE) -// , currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue()) + : chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY) + , chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE) { qDebug() << "init FontManager"; - this->currentFontFamily.connect([this](const std::string &newValue, auto) { + this->chatFontFamily.connect([this](const std::string &newValue, auto) { + util::assertInGuiThread(); + this->incGeneration(); - // this->currentFont.setFamily(newValue.c_str()); - this->currentFontByScale.clear(); - this->fontChanged.invoke(); - }); - - this->currentFontSize.connect([this](const int &newValue, auto) { - this->incGeneration(); - // this->currentFont.setSize(newValue); - this->currentFontByScale.clear(); - this->fontChanged.invoke(); - }); -} - -QFont &FontManager::getFont(FontManager::Type type, float scale) -{ - // return this->currentFont.getFont(type); - return this->getCurrentFont(scale).getFont(type); -} - -QFontMetrics &FontManager::getFontMetrics(FontManager::Type type, float scale) -{ - // return this->currentFont.getFontMetrics(type); - return this->getCurrentFont(scale).getFontMetrics(type); -} - -FontManager::FontData &FontManager::Font::getFontData(FontManager::Type type) -{ - switch (type) { - case Tiny: - return this->tiny; - case Small: - return this->small; - case MediumSmall: - return this->mediumSmall; - case Medium: - return this->medium; - case MediumBold: - return this->mediumBold; - case MediumItalic: - return this->mediumItalic; - case Large: - return this->large; - case VeryLarge: - return this->veryLarge; - default: - qDebug() << "Unknown font type:" << type << ", defaulting to medium"; - return this->medium; - } -} - -QFont &FontManager::Font::getFont(Type type) -{ - return this->getFontData(type).font; -} - -QFontMetrics &FontManager::Font::getFontMetrics(Type type) -{ - return this->getFontData(type).metrics; -} - -FontManager::Font &FontManager::getCurrentFont(float scale) -{ - for (auto it = this->currentFontByScale.begin(); it != this->currentFontByScale.end(); it++) { - if (it->first == scale) { - return it->second; + for (auto &map : this->fontsByType) { + map.clear(); } - } - this->currentFontByScale.push_back( - std::make_pair(scale, Font(this->currentFontFamily.getValue().c_str(), - this->currentFontSize.getValue() * scale))); + this->fontChanged.invoke(); + }); - return this->currentFontByScale.back().second; + this->chatFontSize.connect([this](const int &newValue, auto) { + util::assertInGuiThread(); + + this->incGeneration(); + for (auto &map : this->fontsByType) { + map.clear(); + } + this->fontChanged.invoke(); + }); + + this->fontsByType.resize((size_t)EndType); +} + +QFont FontManager::getFont(FontManager::Type type, float scale) +{ + return this->getOrCreateFontData(type, scale).font; +} + +QFontMetrics FontManager::getFontMetrics(FontManager::Type type, float scale) +{ + return this->getOrCreateFontData(type, scale).metrics; +} + +int FontManager::getGeneration() const +{ + return this->generation; +} + +void FontManager::incGeneration() +{ + this->generation++; +} + +FontManager::FontData &FontManager::getOrCreateFontData(Type type, float scale) +{ + util::assertInGuiThread(); + + assert(type >= 0 && type < EndType); + + auto &map = this->fontsByType[(size_t)type]; + + // find element + auto it = map.find(scale); + if (it != map.end()) { + // return if found + + qDebug() << it->second.font; + return it->second; + } + + // emplace new element + auto result = map.emplace(scale, this->createFontData(type, scale)); + assert(result.second); + + return result.first->second; +} + +FontManager::FontData FontManager::createFontData(Type type, float scale) +{ + // check if it's a chat (scale the setting) + if (type >= ChatStart && type <= ChatEnd) { + static std::unordered_map sizeScale{ + {ChatSmall, {0.6f, false, QFont::Normal}}, + {ChatMediumSmall, {0.8f, false, QFont::Normal}}, + {ChatMedium, {1, false, QFont::Normal}}, + {ChatMediumBold, {1, false, QFont::Medium}}, + {ChatMediumItalic, {1, true, QFont::Normal}}, + {ChatLarge, {1.2f, false, QFont::Normal}}, + {ChatVeryLarge, {1.4f, false, QFont::Normal}}, + }; + + auto data = sizeScale[type]; + return FontData(QFont(QString::fromStdString(this->chatFontFamily.getValue()), + this->chatFontSize.getValue() * data.scale * scale, data.weight, + data.italic)); + } + + // normal Ui font (use pt size) + { + static std::unordered_map defaultSize{ + {Tiny, {8, "Monospace", false, QFont::Normal}}, + {UiMedium, {12, DEFAULT_FONT_FAMILY, false, QFont::Normal}}, + {UiTabs, {9, "Segoe UI", false, QFont::Normal}}, + }; + + UiFontData &data = defaultSize[type]; + QFont font(data.name, data.size * scale, data.weight, data.italic); + return FontData(font); + } } } // namespace singletons diff --git a/src/singletons/fontmanager.hpp b/src/singletons/fontmanager.hpp index 8f7d4eaa..1a6ddf81 100644 --- a/src/singletons/fontmanager.hpp +++ b/src/singletons/fontmanager.hpp @@ -3,136 +3,82 @@ #include #include #include +#include +#include #include #include +#include namespace chatterino { namespace singletons { -class FontManager +class FontManager : boost::noncopyable { public: FontManager(); - FontManager(const FontManager &) = delete; - FontManager(FontManager &&) = delete; - ~FontManager() = delete; - + // font data gets set in createFontData(...) enum Type : uint8_t { Tiny, - Small, - MediumSmall, - Medium, - MediumBold, - MediumItalic, - Large, - VeryLarge, + ChatSmall, + ChatMediumSmall, + ChatMedium, + ChatMediumBold, + ChatMediumItalic, + ChatLarge, + ChatVeryLarge, + + UiMedium, + UiTabs, + + // don't remove this value + EndType, + + // make sure to update these values accordingly! + ChatStart = ChatSmall, + ChatEnd = ChatVeryLarge, }; - QFont &getFont(Type type, float scale); - QFontMetrics &getFontMetrics(Type type, float scale); + QFont getFont(Type type, float scale); + QFontMetrics getFontMetrics(Type type, float scale); - int getGeneration() const - { - return this->generation; - } + int getGeneration() const; + void incGeneration(); - void incGeneration() - { - this->generation++; - } - - pajlada::Settings::Setting currentFontFamily; - pajlada::Settings::Setting currentFontSize; + pajlada::Settings::Setting chatFontFamily; + pajlada::Settings::Setting chatFontSize; pajlada::Signals::NoArgSignal fontChanged; private: struct FontData { - FontData(QFont &&_font) + FontData(const QFont &_font) : font(_font) - , metrics(this->font) + , metrics(_font) { } - QFont font; - QFontMetrics metrics; + const QFont font; + const QFontMetrics metrics; }; - struct Font { - Font() = delete; - - Font(const char *fontFamilyName, int mediumSize) - : tiny(QFont("Monospace", 8)) - , small(QFont(fontFamilyName, mediumSize - 4)) - , mediumSmall(QFont(fontFamilyName, mediumSize - 2)) - , medium(QFont(fontFamilyName, mediumSize)) - , mediumBold(QFont(fontFamilyName, mediumSize, QFont::DemiBold)) - , mediumItalic(QFont(fontFamilyName, mediumSize, -1, true)) - , large(QFont(fontFamilyName, mediumSize)) - , veryLarge(QFont(fontFamilyName, mediumSize)) - { - tiny.font.setStyleHint(QFont::TypeWriter); - } - - void setFamily(const char *newFamily) - { - this->small.font.setFamily(newFamily); - this->mediumSmall.font.setFamily(newFamily); - this->medium.font.setFamily(newFamily); - this->mediumBold.font.setFamily(newFamily); - this->mediumItalic.font.setFamily(newFamily); - this->large.font.setFamily(newFamily); - this->veryLarge.font.setFamily(newFamily); - - this->updateMetrics(); - } - - void setSize(int newMediumSize) - { - this->small.font.setPointSize(newMediumSize - 4); - this->mediumSmall.font.setPointSize(newMediumSize - 2); - this->medium.font.setPointSize(newMediumSize); - this->mediumBold.font.setPointSize(newMediumSize); - this->mediumItalic.font.setPointSize(newMediumSize); - this->large.font.setPointSize(newMediumSize + 2); - this->veryLarge.font.setPointSize(newMediumSize + 4); - - this->updateMetrics(); - } - - void updateMetrics() - { - this->small.metrics = QFontMetrics(this->small.font); - this->mediumSmall.metrics = QFontMetrics(this->mediumSmall.font); - this->medium.metrics = QFontMetrics(this->medium.font); - this->mediumBold.metrics = QFontMetrics(this->mediumBold.font); - this->mediumItalic.metrics = QFontMetrics(this->mediumItalic.font); - this->large.metrics = QFontMetrics(this->large.font); - this->veryLarge.metrics = QFontMetrics(this->veryLarge.font); - } - - FontData &getFontData(Type type); - - QFont &getFont(Type type); - QFontMetrics &getFontMetrics(Type type); - - FontData tiny; - FontData small; - FontData mediumSmall; - FontData medium; - FontData mediumBold; - FontData mediumItalic; - FontData large; - FontData veryLarge; + struct ChatFontData { + float scale; + bool italic; + QFont::Weight weight; }; - Font &getCurrentFont(float scale); + struct UiFontData { + float size; + const char *name; + bool italic; + QFont::Weight weight; + }; - // Future plans: - // Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font" + FontData &getOrCreateFontData(Type type, float scale); + FontData createFontData(Type type, float scale); - std::list> currentFontByScale; + std::vector> fontsByType; int generation = 0; }; diff --git a/src/singletons/thememanager.cpp b/src/singletons/thememanager.cpp index 8296dbef..c2a81860 100644 --- a/src/singletons/thememanager.cpp +++ b/src/singletons/thememanager.cpp @@ -52,7 +52,6 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier) isLight = multiplier > 0; bool lightWin = isLight; - QColor none(0, 0, 0, 0); QColor themeColor = QColor::fromHslF(hue, 0.43, 0.5); QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5); @@ -69,7 +68,7 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier) #ifdef Q_OS_LINUX this->window.background = lightWin ? "#fff" : QColor(61, 60, 56); #else - this->window.background = lightWin ? "#fff" : "#444"; + this->window.background = lightWin ? "#fff" : "#111"; #endif QColor fg = this->window.text = lightWin ? "#000" : "#eee"; @@ -89,27 +88,48 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier) /// TABS if (lightWin) { - this->tabs.regular = {fg, {bg, QColor("#ccc"), bg}}; + this->tabs.regular = {QColor("#444"), + {QColor("#fff"), QColor("#fff"), QColor("#fff")}, + {QColor("#fff"), QColor("#fff"), QColor("#fff")}}; this->tabs.newMessage = { - fg, - {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), - QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), - QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}}; - this->tabs.highlighted = {fg, {QColor("#ccc"), QColor("#ccc"), QColor("#bbb")}}; - this->tabs.selected = {QColor("#fff"), - {QColor("#777"), QColor("#777"), QColor("#888")}}; - } else { - this->tabs.regular = {fg, {bg, QColor("#555"), bg}}; - this->tabs.newMessage = { - fg, - {QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern), - QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern), - QBrush(blendColors(themeColorNoSat, "#666", 0.7), Qt::FDiagPattern)}}; - this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"), QColor("#666")}}; + fg, {bg, QColor("#ccc"), bg}, {QColor("#aaa"), QColor("#aaa"), QColor("#aaa")}}; + this->tabs.highlighted = {fg, + {bg, QColor("#ccc"), bg}, + {QColor("#b60505"), QColor("#b60505"), QColor("#b60505")}}; this->tabs.selected = {QColor("#000"), - {QColor("#999"), QColor("#999"), QColor("#888")}}; + {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; + } else { + this->tabs.regular = {QColor("#aaa"), + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#444"), QColor("#444"), QColor("#444")}}; + this->tabs.newMessage = {fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#888"), QColor("#888"), QColor("#888")}}; + this->tabs.highlighted = {fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#ee6166"), QColor("#ee6166"), QColor("#ee6166")}}; + + this->tabs.selected = {QColor("#fff"), + {QColor("#555555"), QColor("#555555"), QColor("#555555")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; } + // this->tabs.newMessage = { + // fg, + // {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), + // QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), + // QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}}; + + // this->tabs.newMessage = { + // fg, + // {QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern), + // QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern), + // QBrush(blendColors(themeColorNoSat, "#666", 0.7), + // Qt::FDiagPattern)}}; + // this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"), + // QColor("#666")}}; + this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color(); } @@ -163,7 +183,7 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier) this->messages.selection = isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64); this->updated.invoke(); -} +} // namespace singletons QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qreal ratio) { diff --git a/src/singletons/thememanager.hpp b/src/singletons/thememanager.hpp index 443f0482..5165600a 100644 --- a/src/singletons/thememanager.hpp +++ b/src/singletons/thememanager.hpp @@ -25,11 +25,16 @@ public: struct TabColors { QColor text; - struct Backgrounds { + struct { QBrush regular; QBrush hover; QBrush unfocused; } backgrounds; + struct { + QColor regular; + QColor hover; + QColor unfocused; + } line; }; /// WINDOW @@ -43,9 +48,9 @@ public: /// TABS struct { TabColors regular; - TabColors selected; - TabColors highlighted; TabColors newMessage; + TabColors highlighted; + TabColors selected; QColor border; QColor bottomLine; } tabs; diff --git a/src/singletons/windowmanager.cpp b/src/singletons/windowmanager.cpp index a7716aae..3cbfa9f5 100644 --- a/src/singletons/windowmanager.cpp +++ b/src/singletons/windowmanager.cpp @@ -188,27 +188,27 @@ void WindowManager::initialize() // load tabs QJsonArray tabs = window_obj.value("tabs").toArray(); for (QJsonValue tab_val : tabs) { - widgets::SplitContainer *tab = window.getNotebook().addNewPage(); + widgets::SplitContainer *page = window.getNotebook().addPage(false); QJsonObject tab_obj = tab_val.toObject(); // set custom title QJsonValue title_val = tab_obj.value("title"); if (title_val.isString()) { - tab->getTab()->setTitle(title_val.toString()); - tab->getTab()->useDefaultTitle = false; + page->getTab()->setTitle(title_val.toString()); + page->getTab()->useDefaultTitle = false; } // selected if (tab_obj.value("selected").toBool(false)) { - window.getNotebook().select(tab); + window.getNotebook().select(page); } // load splits QJsonObject splitRoot = tab_obj.value("splits2").toObject(); if (!splitRoot.isEmpty()) { - tab->decodeFromJson(splitRoot); + page->decodeFromJson(splitRoot); continue; } @@ -217,12 +217,12 @@ void WindowManager::initialize() int colNr = 0; for (QJsonValue column_val : tab_obj.value("splits").toArray()) { for (QJsonValue split_val : column_val.toArray()) { - widgets::Split *split = new widgets::Split(tab); + widgets::Split *split = new widgets::Split(page); QJsonObject split_obj = split_val.toObject(); split->setChannel(decodeChannel(split_obj)); - tab->appendSplit(split); + page->appendSplit(split); } colNr++; } @@ -231,7 +231,7 @@ void WindowManager::initialize() if (mainWindow == nullptr) { mainWindow = &createWindow(widgets::Window::Main); - mainWindow->getNotebook().addNewPage(true); + mainWindow->getNotebook().addPage(true); } this->initialized = true; @@ -268,9 +268,11 @@ void WindowManager::save() // window tabs QJsonArray tabs_arr; - for (int tab_i = 0; tab_i < window->getNotebook().tabCount(); tab_i++) { + for (int tab_i = 0; tab_i < window->getNotebook().getPageCount(); tab_i++) { QJsonObject tab_obj; - widgets::SplitContainer *tab = window->getNotebook().tabAt(tab_i); + widgets::SplitContainer *tab = + dynamic_cast(window->getNotebook().getPageAt(tab_i)); + assert(tab != nullptr); // custom tab title if (!tab->getTab()->useDefaultTitle) { diff --git a/src/widgets/helper/label.hpp b/src/widgets/helper/label.hpp index 5afa0e00..3af4eadf 100644 --- a/src/widgets/helper/label.hpp +++ b/src/widgets/helper/label.hpp @@ -27,7 +27,7 @@ protected: private: QSize preferedSize; QString text; - FontStyle fontStyle = FontStyle::Medium; + FontStyle fontStyle = FontStyle::ChatMedium; }; } // namespace widgets diff --git a/src/widgets/helper/notebookbutton.cpp b/src/widgets/helper/notebookbutton.cpp index a99fcc1e..87783919 100644 --- a/src/widgets/helper/notebookbutton.cpp +++ b/src/widgets/helper/notebookbutton.cpp @@ -136,10 +136,12 @@ void NotebookButton::dropEvent(QDropEvent *event) if (SplitContainer::isDraggingSplit) { event->acceptProposedAction(); - Notebook *notebook = dynamic_cast(this->parentWidget()); + Notebook2 *notebook = dynamic_cast(this->parentWidget()); if (notebook != nuuls) { - SplitContainer *page = notebook->addNewPage(); + SplitContainer *page = new SplitContainer(notebook); + auto *tab = notebook->addPage(page); + page->setTab(tab); SplitContainer::draggingSplit->setParent(page); page->appendSplit(SplitContainer::draggingSplit); diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 8ff07064..34894bf1 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -61,17 +61,18 @@ NotebookTab2::NotebookTab2(Notebook2 *_notebook) } }); - QAction *enableHighlightsOnNewMessageAction = - new QAction("Enable highlights on new message", &this->menu); - enableHighlightsOnNewMessageAction->setCheckable(true); + // QAction *enableHighlightsOnNewMessageAction = + // new QAction("Enable highlights on new message", &this->menu); + // enableHighlightsOnNewMessageAction->setCheckable(true); this->menu.addAction("Close", [=]() { this->notebook->removePage(this->page); }); - this->menu.addAction(enableHighlightsOnNewMessageAction); + // this->menu.addAction(enableHighlightsOnNewMessageAction); - QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) { - debug::Log("New value is {}", newValue); // - }); + // QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool + // newValue) { + // debug::Log("New value is {}", newValue); // + // }); } void NotebookTab2::themeRefreshEvent() @@ -85,15 +86,20 @@ void NotebookTab2::updateSize() float scale = getScale(); int width; - QFontMetrics metrics(this->font()); + QFontMetrics metrics = getApp()->fonts->getFontMetrics(FontStyle::UiTabs, this->getScale()); - if (!app->settings->showTabCloseButton) { - width = (int)((metrics.width(this->title) + 16 /*+ 16*/) * scale); + if (this->hasXButton()) { + width = (int)((metrics.width(this->title) + 32) * scale); } else { - width = (int)((metrics.width(this->title) + 8 + 24 /*+ 16*/) * scale); + width = (int)((metrics.width(this->title) + 16) * scale); } - this->resize(std::min((int)(150 * scale), width), (int)(24 * scale)); + width = std::min((int)(150 * scale), width); + + if (this->width() != width) { + this->resize(width, (int)(NOTEBOOK_TAB_HEIGHT * scale)); + this->notebook->performLayout(); + } // if (this->parent() != nullptr) { // (static_cast(this->parent()))->performLayout(true); @@ -182,7 +188,9 @@ void NotebookTab2::paintEvent(QPaintEvent *) QPainter painter(this); float scale = this->getScale(); - int height = (int)(scale * 24); + painter.setFont(getApp()->fonts->getFont(FontStyle::UiTabs, scale)); + + int height = (int)(scale * NOTEBOOK_TAB_HEIGHT); // int fullHeight = (int)(scale * 48); // select the right tab colors @@ -206,48 +214,28 @@ void NotebookTab2::paintEvent(QPaintEvent *) : (windowFocused ? colors.backgrounds.regular : colors.backgrounds.unfocused); - if (true) { - painter.fillRect(rect(), this->mouseOver ? regular.backgrounds.hover - : (windowFocused ? regular.backgrounds.regular - : regular.backgrounds.unfocused)); + painter.fillRect(rect(), this->mouseOver ? regular.backgrounds.hover + : (windowFocused ? regular.backgrounds.regular + : regular.backgrounds.unfocused)); - // fill the tab background - painter.fillRect(rect(), tabBackground); + // fill the tab background + painter.fillRect(rect(), tabBackground); - // draw border - // painter.setPen(QPen("#ccc")); - // QPainterPath path(QPointF(0, height)); - // path.lineTo(0, 0); - // path.lineTo(this->width() - 1, 0); - // path.lineTo(this->width() - 1, this->height() - 1); - // path.lineTo(0, this->height() - 1); - // painter.drawPath(path); - } else { - // QPainterPath path(QPointF(0, height)); - // path.lineTo(8 * scale, 0); - // path.lineTo(this->width() - 8 * scale, 0); - // path.lineTo(this->width(), height); - // painter.fillPath(path, this->mouseOver ? regular.backgrounds.hover - // : (windowFocused ? - // regular.backgrounds.regular - // : - // regular.backgrounds.unfocused)); + // draw border + // painter.setPen(QPen("#fff")); + // QPainterPath path(QPointF(0, height)); + // path.lineTo(0, 0); + // path.lineTo(this->width() - 1, 0); + // path.lineTo(this->width() - 1, this->height() - 1); + // path.lineTo(0, this->height() - 1); + // painter.drawPath(path); - // // fill the tab background - // painter.fillPath(path, tabBackground); - // painter.setPen(QColor("#FFF")); - // painter.setRenderHint(QPainter::Antialiasing); - // painter.drawPath(path); - // // painter.setBrush(QColor("#000")); - - // QLinearGradient gradient(0, height, 0, fullHeight); - // gradient.setColorAt(0, tabBackground.color()); - // gradient.setColorAt(1, "#fff"); - - // QBrush brush(gradient); - // painter.fillRect(0, height, this->width(), fullHeight - height, - // brush); - } + // top line + painter.fillRect(QRectF(0, (this->selected ? 0.f : 1.f) * scale, this->width(), + (this->selected ? 2.f : 1.f) * scale), + this->mouseOver + ? colors.line.hover + : (windowFocused ? colors.line.regular : colors.line.unfocused)); // set the pen color painter.setPen(colors.text); @@ -260,7 +248,11 @@ void NotebookTab2::paintEvent(QPaintEvent *) if (true) { // legacy // painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter)); int offset = (int)(scale * 8); - QRect textRect(offset, 0, this->width() - offset - offset, height); + QRect textRect(offset, this->selected ? 0 : 1, this->width() - offset - offset, height); + + if (this->shouldDrawXButton()) { + textRect.setRight(textRect.right() - this->height() / 2); + } QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); option.setWrapMode(QTextOption::NoWrap); @@ -274,9 +266,11 @@ void NotebookTab2::paintEvent(QPaintEvent *) } // draw close x - if (!app->settings->showTabCloseButton && (mouseOver || selected)) { + if (this->shouldDrawXButton()) { QRect xRect = this->getXRect(); if (!xRect.isNull()) { + painter.setBrush(QColor("#fff")); + if (mouseOverX) { painter.fillRect(xRect, QColor(0, 0, 0, 64)); @@ -291,6 +285,21 @@ void NotebookTab2::paintEvent(QPaintEvent *) painter.drawLine(xRect.topRight() + QPoint(-a, a), xRect.bottomLeft() + QPoint(a, -a)); } } + + // draw line at bottom + if (!this->selected) { + painter.fillRect(0, this->height() - 1, this->width(), 1, app->themes->window.background); + } +} + +bool NotebookTab2::hasXButton() +{ + return getApp()->settings->showTabCloseButton && this->notebook->getAllowUserTabManagement(); +} + +bool NotebookTab2::shouldDrawXButton() +{ + return this->hasXButton() && (mouseOver || selected); } void NotebookTab2::mousePressEvent(QMouseEvent *event) @@ -320,8 +329,7 @@ void NotebookTab2::mouseReleaseEvent(QMouseEvent *event) this->notebook->removePage(this->page); } } else { - if (getApp()->settings->showTabCloseButton && this->mouseDownX && - this->getXRect().contains(event->pos())) { + if (this->hasXButton() && this->mouseDownX && this->getXRect().contains(event->pos())) { this->mouseDownX = false; this->notebook->removePage(this->page); @@ -377,7 +385,7 @@ void NotebookTab2::mouseMoveEvent(QMouseEvent *event) int index; QWidget *clickedPage = notebook->tabAt(relPoint, index, this->width()); - assert(clickedPage); + // assert(clickedPage); if (clickedPage != nullptr && clickedPage != this->page) { this->notebook->rearrangePage(this->page, index); @@ -387,370 +395,14 @@ void NotebookTab2::mouseMoveEvent(QMouseEvent *event) QRect NotebookTab2::getXRect() { - if (this->notebook->getAllowUserTabManagement()) { - return QRect(); - } + // if (!this->notebook->getAllowUserTabManagement()) { + // return QRect(); + // } float s = this->getScale(); - return QRect(this->width() - static_cast(20 * s), static_cast(4 * s), + return QRect(this->width() - static_cast(20 * s), static_cast(6 * s), static_cast(16 * s), static_cast(16 * s)); } -// 2 -NotebookTab::NotebookTab(Notebook *_notebook) - : BaseWidget(_notebook) - , positionChangedAnimation(this, "pos") - , notebook(_notebook) - , menu(this) -{ - auto app = getApp(); - - this->setAcceptDrops(true); - - this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); - - app->settings->showTabCloseButton.connect(boost::bind(&NotebookTab::hideTabXChanged, this, _1), - this->managedConnections); - - this->setMouseTracking(true); - - this->menu.addAction("Rename", [this]() { - TextInputDialog d(this); - - d.setWindowTitle("Change tab title (Leave empty for default behaviour)"); - if (this->useDefaultTitle) { - d.setText(""); - } else { - d.setText(this->getTitle()); - d.highlightText(); - } - - if (d.exec() == QDialog::Accepted) { - QString newTitle = d.getText(); - if (newTitle.isEmpty()) { - this->useDefaultTitle = true; - this->page->refreshTabTitle(); - } else { - this->useDefaultTitle = false; - this->setTitle(newTitle); - } - } - }); - - QAction *enableHighlightsOnNewMessageAction = - new QAction("Enable highlights on new message", &this->menu); - enableHighlightsOnNewMessageAction->setCheckable(true); - - this->menu.addAction("Close", [=]() { this->notebook->removePage(this->page); }); - - this->menu.addAction(enableHighlightsOnNewMessageAction); - - QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [](bool newValue) { - debug::Log("New value is {}", newValue); // - }); -} - -void NotebookTab::themeRefreshEvent() -{ - this->update(); -} - -void NotebookTab::updateSize() -{ - auto app = getApp(); - - float scale = getScale(); - - int width; - - if (!app->settings->showTabCloseButton) { - width = (int)((fontMetrics().width(this->title) + 16 /*+ 16*/) * scale); - } else { - width = (int)((fontMetrics().width(this->title) + 8 + 24 /*+ 16*/) * scale); - } - - this->resize(std::min((int)(150 * scale), width), (int)(24 * scale)); - - if (this->parent() != nullptr) { - (static_cast(this->parent()))->performLayout(true); - } -} - -const QString &NotebookTab::getTitle() const -{ - return this->title; -} - -void NotebookTab::setTitle(const QString &newTitle) -{ - if (this->title != newTitle) { - this->title = newTitle; - this->updateSize(); - this->update(); - } -} - -bool NotebookTab::isSelected() const -{ - return this->selected; -} - -void NotebookTab::setSelected(bool value) -{ - this->selected = value; - - this->highlightState = HighlightState::None; - - this->update(); -} - -void NotebookTab::setHighlightState(HighlightState newHighlightStyle) -{ - if (this->isSelected()) { - return; - } - - if (this->highlightState != HighlightState::Highlighted) { - this->highlightState = newHighlightStyle; - - this->update(); - } -} - -QRect NotebookTab::getDesiredRect() const -{ - return QRect(positionAnimationDesiredPoint, size()); -} - -void NotebookTab::hideTabXChanged(bool) -{ - this->updateSize(); - this->update(); -} - -void NotebookTab::moveAnimated(QPoint pos, bool animated) -{ - this->positionAnimationDesiredPoint = pos; - - QWidget *w = this->window(); - - if ((w != nullptr && !w->isVisible()) || !animated || !positionChangedAnimationRunning) { - this->move(pos); - - this->positionChangedAnimationRunning = true; - return; - } - - if (this->positionChangedAnimation.endValue() == pos) { - return; - } - - this->positionChangedAnimation.stop(); - this->positionChangedAnimation.setDuration(75); - this->positionChangedAnimation.setStartValue(this->pos()); - this->positionChangedAnimation.setEndValue(pos); - this->positionChangedAnimation.start(); -} - -void NotebookTab::paintEvent(QPaintEvent *) -{ - auto app = getApp(); - QPainter painter(this); - float scale = this->getScale(); - - int height = (int)(scale * 24); - // int fullHeight = (int)(scale * 48); - - // select the right tab colors - singletons::ThemeManager::TabColors colors; - singletons::ThemeManager::TabColors regular = this->themeManager->tabs.regular; - - if (this->selected) { - colors = this->themeManager->tabs.selected; - } else if (this->highlightState == HighlightState::Highlighted) { - colors = this->themeManager->tabs.highlighted; - } else if (this->highlightState == HighlightState::NewMessage) { - colors = this->themeManager->tabs.newMessage; - } else { - colors = this->themeManager->tabs.regular; - } - - bool windowFocused = this->window() == QApplication::activeWindow(); - // || SettingsDialog::getHandle() == QApplication::activeWindow(); - - QBrush tabBackground = this->mouseOver ? colors.backgrounds.hover - : (windowFocused ? colors.backgrounds.regular - : colors.backgrounds.unfocused); - - if (true) { - painter.fillRect(rect(), this->mouseOver ? regular.backgrounds.hover - : (windowFocused ? regular.backgrounds.regular - : regular.backgrounds.unfocused)); - - // fill the tab background - painter.fillRect(rect(), tabBackground); - - // draw border - // painter.setPen(QPen("#ccc")); - // QPainterPath path(QPointF(0, height)); - // path.lineTo(0, 0); - // path.lineTo(this->width() - 1, 0); - // path.lineTo(this->width() - 1, this->height() - 1); - // path.lineTo(0, this->height() - 1); - // painter.drawPath(path); - } else { - // QPainterPath path(QPointF(0, height)); - // path.lineTo(8 * scale, 0); - // path.lineTo(this->width() - 8 * scale, 0); - // path.lineTo(this->width(), height); - // painter.fillPath(path, this->mouseOver ? regular.backgrounds.hover - // : (windowFocused ? - // regular.backgrounds.regular - // : - // regular.backgrounds.unfocused)); - - // // fill the tab background - // painter.fillPath(path, tabBackground); - // painter.setPen(QColor("#FFF")); - // painter.setRenderHint(QPainter::Antialiasing); - // painter.drawPath(path); - // // painter.setBrush(QColor("#000")); - - // QLinearGradient gradient(0, height, 0, fullHeight); - // gradient.setColorAt(0, tabBackground.color()); - // gradient.setColorAt(1, "#fff"); - - // QBrush brush(gradient); - // painter.fillRect(0, height, this->width(), fullHeight - height, - // brush); - } - - // set the pen color - painter.setPen(colors.text); - - // set area for text - int rectW = (!app->settings->showTabCloseButton ? 0 : static_cast(16) * scale); - QRect rect(0, 0, this->width() - rectW, height); - - // draw text - if (true) { // legacy - // painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter)); - int offset = (int)(scale * 8); - QRect textRect(offset, 0, this->width() - offset - offset, height); - - QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); - option.setWrapMode(QTextOption::NoWrap); - painter.drawText(textRect, this->getTitle(), option); - } else { - // QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); - // option.setWrapMode(QTextOption::NoWrap); - // int offset = (int)(scale * 16); - // QRect textRect(offset, 0, this->width() - offset - offset, height); - // painter.drawText(textRect, this->getTitle(), option); - } - - // draw close x - if (app->settings->showTabCloseButton && (mouseOver || selected)) { - QRect xRect = this->getXRect(); - if (mouseOverX) { - painter.fillRect(xRect, QColor(0, 0, 0, 64)); - - if (mouseDownX) { - painter.fillRect(xRect, QColor(0, 0, 0, 64)); - } - } - - int a = static_cast(scale * 4); - - painter.drawLine(xRect.topLeft() + QPoint(a, a), xRect.bottomRight() + QPoint(-a, -a)); - painter.drawLine(xRect.topRight() + QPoint(-a, a), xRect.bottomLeft() + QPoint(a, -a)); - } -} // namespace widgets - -void NotebookTab::mousePressEvent(QMouseEvent *event) -{ - this->mouseDown = true; - this->mouseDownX = this->getXRect().contains(event->pos()); - - this->update(); - - this->notebook->select(page); - - switch (event->button()) { - case Qt::RightButton: { - this->menu.popup(event->globalPos()); - } break; - } -} - -void NotebookTab::mouseReleaseEvent(QMouseEvent *event) -{ - auto app = getApp(); - - this->mouseDown = false; - - if (event->button() == Qt::MiddleButton) { - if (this->rect().contains(event->pos())) { - this->notebook->removePage(this->page); - } - } else { - if (app->settings->showTabCloseButton && this->mouseDownX && - this->getXRect().contains(event->pos())) { - this->mouseDownX = false; - - this->notebook->removePage(this->page); - } else { - this->update(); - } - } -} - -void NotebookTab::enterEvent(QEvent *) -{ - this->mouseOver = true; - - this->update(); -} - -void NotebookTab::leaveEvent(QEvent *) -{ - this->mouseOverX = false; - this->mouseOver = false; - - this->update(); -} - -void NotebookTab::dragEnterEvent(QDragEnterEvent *) -{ - this->notebook->select(this->page); -} - -void NotebookTab::mouseMoveEvent(QMouseEvent *event) -{ - auto app = getApp(); - - if (app->settings->showTabCloseButton) { - bool overX = this->getXRect().contains(event->pos()); - - if (overX != this->mouseOverX) { - // Over X state has been changed (we either left or entered it; - this->mouseOverX = overX; - - this->update(); - } - } - - QPoint relPoint = this->mapToParent(event->pos()); - - if (this->mouseDown && !this->getDesiredRect().contains(relPoint)) { - int index; - SplitContainer *clickedPage = notebook->tabAt(relPoint, index, this->width()); - - if (clickedPage != nullptr && clickedPage != this->page) { - this->notebook->rearrangePage(this->page, index); - } - } -} - } // namespace widgets } // namespace chatterino diff --git a/src/widgets/helper/notebooktab.hpp b/src/widgets/helper/notebooktab.hpp index 628b835b..e65c758f 100644 --- a/src/widgets/helper/notebooktab.hpp +++ b/src/widgets/helper/notebooktab.hpp @@ -11,7 +11,9 @@ namespace chatterino { namespace widgets { -class Notebook; +#define NOTEBOOK_TAB_HEIGHT 28 + +// class Notebook; class Notebook2; class SplitContainer; @@ -73,6 +75,9 @@ private: bool mouseOverX = false; bool mouseDownX = false; + bool hasXButton(); + bool shouldDrawXButton(); + HighlightState highlightState = HighlightState::None; QMenu menu; @@ -80,75 +85,5 @@ private: QRect getXRect(); }; -class NotebookTab : public BaseWidget -{ - Q_OBJECT - -public: - explicit NotebookTab(Notebook *_notebook); - - void updateSize(); - - SplitContainer *page; - - const QString &getTitle() const; - void setTitle(const QString &newTitle); - bool isSelected() const; - void setSelected(bool value); - - void setHighlightState(HighlightState style); - - void moveAnimated(QPoint pos, bool animated = true); - - QRect getDesiredRect() const; - void hideTabXChanged(bool); - -protected: - virtual void themeRefreshEvent() override; - - virtual void paintEvent(QPaintEvent *) override; - - virtual void mousePressEvent(QMouseEvent *event) override; - virtual void mouseReleaseEvent(QMouseEvent *event) override; - virtual void enterEvent(QEvent *) override; - virtual void leaveEvent(QEvent *) override; - - virtual void dragEnterEvent(QDragEnterEvent *event) override; - - virtual void mouseMoveEvent(QMouseEvent *event) override; - -private: - std::vector managedConnections; - - QPropertyAnimation positionChangedAnimation; - bool positionChangedAnimationRunning = false; - QPoint positionAnimationDesiredPoint; - - Notebook *notebook; - - QString title; - -public: - bool useDefaultTitle = true; - -private: - bool selected = false; - bool mouseOver = false; - bool mouseDown = false; - bool mouseOverX = false; - bool mouseDownX = false; - - HighlightState highlightState = HighlightState::None; - - QMenu menu; - - QRect getXRect() - { - float s = this->getScale(); - return QRect(this->width() - static_cast(20 * s), static_cast(4 * s), - static_cast(16 * s), static_cast(16 * s)); - } -}; - } // namespace widgets } // namespace chatterino diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index 2a086df6..facd70b6 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -65,11 +65,11 @@ void SplitInput::initLayout() // set edit font this->ui.textEdit->setFont( - app->fonts->getFont(singletons::FontManager::Type::Medium, this->getScale())); + app->fonts->getFont(singletons::FontManager::Type::ChatMedium, this->getScale())); this->managedConnections.emplace_back(app->fonts->fontChanged.connect([=]() { this->ui.textEdit->setFont( - app->fonts->getFont(singletons::FontManager::Type::Medium, this->getScale())); + app->fonts->getFont(singletons::FontManager::Type::ChatMedium, this->getScale())); })); // open emote popup @@ -244,18 +244,18 @@ void SplitInput::installKeyPressedEvent() SplitContainer *page = static_cast(this->chatWidget->parentWidget()); - Notebook *notebook = static_cast(page->parentWidget()); + Notebook2 *notebook = static_cast(page->parentWidget()); - notebook->nextTab(); + notebook->selectNextTab(); } } else if (event->key() == Qt::Key_Backtab) { if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { SplitContainer *page = static_cast(this->chatWidget->parentWidget()); - Notebook *notebook = static_cast(page->parentWidget()); + Notebook2 *notebook = static_cast(page->parentWidget()); - notebook->previousTab(); + notebook->selectPreviousTab(); } } else if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) { if (this->chatWidget->view.hasSelection()) { @@ -316,14 +316,23 @@ void SplitInput::paintEvent(QPaintEvent *) { QPainter painter(this); - painter.fillRect(this->rect(), this->themeManager->splits.input.background); - - QPen pen(this->themeManager->splits.input.border); if (this->themeManager->isLightTheme()) { - pen.setWidth((int)(6 * this->getScale())); + int s = (int)(3 * this->getScale()); + QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s)); + + painter.fillRect(rect, this->themeManager->splits.input.background); + + painter.setPen(QColor("#ccc")); + painter.drawRect(rect); + } else { + int s = (int)(1 * this->getScale()); + QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s)); + + painter.fillRect(rect, this->themeManager->splits.input.background); + + painter.setPen(QColor("#333")); + painter.drawRect(rect); } - painter.setPen(pen); - painter.drawRect(0, 0, this->width() - 1, this->height() - 1); } void SplitInput::resizeEvent(QResizeEvent *) diff --git a/src/widgets/notebook.cpp b/src/widgets/notebook.cpp index 8ac53af8..1acae1a7 100644 --- a/src/widgets/notebook.cpp +++ b/src/widgets/notebook.cpp @@ -89,6 +89,8 @@ void Notebook2::removePage(QWidget *page) break; } } + + this->performLayout(); } void Notebook2::removeCurrentPage() @@ -179,6 +181,11 @@ int Notebook2::getPageCount() const return this->items.count(); } +QWidget *Notebook2::getPageAt(int index) const +{ + return this->items[index].page; +} + int Notebook2::getSelectedIndex() const { return this->indexOf(this->selectedPage); @@ -242,11 +249,11 @@ void Notebook2::setShowAddButton(bool value) void Notebook2::scaleChangedEvent(float scale) { - // float h = 24 * this->getScale(); + float h = NOTEBOOK_TAB_HEIGHT * this->getScale(); // this->settingsButton.setFixedSize(h, h); // this->userButton.setFixedSize(h, h); - // this->addButton.setFixedSize(h, h); + this->addButton.setFixedSize(h, h); for (auto &i : this->items) { i.tab->updateSize(); @@ -290,10 +297,12 @@ void Notebook2::performLayout(bool animated) // x += (int)(scale * 2); // } - int tabHeight = static_cast(24 * scale); + int tabHeight = static_cast(NOTEBOOK_TAB_HEIGHT * scale); bool first = true; for (auto i = this->items.begin(); i != this->items.end(); i++) { + // int yOffset = i->tab->isSelected() ? 0 : 1; + if (!first && (i == this->items.end() && this->showAddButton ? tabHeight : 0) + x + i->tab->width() > width()) // @@ -321,7 +330,7 @@ void Notebook2::performLayout(bool animated) this->update(); } - y += (int)(1 * scale); + y += (int)(3 * scale); for (auto &i : this->items) { i.tab->raise(); @@ -343,10 +352,15 @@ void Notebook2::paintEvent(QPaintEvent *event) BaseWidget::paintEvent(event); QPainter painter(this); - painter.fillRect(0, this->lineY, this->width(), (int)(1 * this->getScale()), + painter.fillRect(0, this->lineY, this->width(), (int)(3 * this->getScale()), this->themeManager->tabs.bottomLine); } +NotebookButton *Notebook2::getAddButton() +{ + return &this->addButton; +} + NotebookTab2 *Notebook2::getTabFromPage(QWidget *page) { for (auto &it : this->items) { @@ -358,318 +372,28 @@ NotebookTab2 *Notebook2::getTabFromPage(QWidget *page) return nullptr; } -// Notebook2::OLD NOTEBOOK -Notebook::Notebook(Window *parent, bool _showButtons) - : BaseWidget(parent) - , parentWindow(parent) - , addButton(this) - , settingsButton(this) - , userButton(this) - , showButtons(_showButtons) - , closeConfirmDialog(this) +SplitNotebook::SplitNotebook(QWidget *parent) + : Notebook2(parent) { - auto app = getApp(); - - this->connect(&this->settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked())); - this->connect(&this->userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked())); - this->connect(&this->addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked())); - - this->settingsButton.icon = NotebookButton::IconSettings; - - this->userButton.move(24, 0); - this->userButton.icon = NotebookButton::IconUser; - - app->settings->hidePreferencesButton.connectSimple([this](auto) { this->performLayout(); }); - app->settings->hideUserButton.connectSimple([this](auto) { this->performLayout(); }); - - closeConfirmDialog.setText("Are you sure you want to close this tab?"); - closeConfirmDialog.setIcon(QMessageBox::Icon::Question); - closeConfirmDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - closeConfirmDialog.setDefaultButton(QMessageBox::Yes); - - this->scaleChangedEvent(this->getScale()); - - // Window-wide hotkeys - // CTRL+T: Create new split in selected notebook page - // CreateWindowShortcut(this, "CTRL+T", [this]() { - // if (this->selectedPage == nullptr) { - // return; - // } - - // this->selectedPage->addChat(true); - // }); + this->connect(this->getAddButton(), &NotebookButton::clicked, + [this]() { QTimer::singleShot(80, this, [this] { this->addPage(true); }); }); } -SplitContainer *Notebook::addNewPage(bool select) +SplitContainer *SplitNotebook::addPage(bool select) { - auto tab = new NotebookTab(this); - auto page = new SplitContainer(this, tab); - + SplitContainer *container = new SplitContainer(this); + auto *tab = Notebook2::addPage(container, QString(), select); + container->setTab(tab); + tab->setParent(this); tab->show(); - - if (select || this->pages.count() == 0) { - this->select(page); - } - - this->pages.append(page); - - this->performLayout(); - - return page; + return container; } -void Notebook::removePage(SplitContainer *page) +SplitContainer *SplitNotebook::getOrAddSelectedPage() { - if (page->getSplitCount() > 0 && closeConfirmDialog.exec() != QMessageBox::Yes) { - return; - } + auto *selectedPage = this->getSelectedPage(); - int index = this->pages.indexOf(page); - - if (this->pages.size() == 1) { - select(nullptr); - } else if (index == this->pages.count() - 1) { - select(this->pages[index - 1]); - } else { - select(this->pages[index + 1]); - } - - page->getTab()->deleteLater(); - page->deleteLater(); - - this->pages.removeOne(page); - - if (this->pages.empty()) { - this->addNewPage(); - } - - this->performLayout(); -} - -void Notebook::removeCurrentPage() -{ - if (this->selectedPage == nullptr) { - return; - } - - this->removePage(this->selectedPage); -} - -SplitContainer *Notebook::getOrAddSelectedPage() -{ - if (selectedPage == nullptr) { - this->addNewPage(true); - } - - return selectedPage; -} - -SplitContainer *Notebook::getSelectedPage() -{ - return selectedPage; -} - -void Notebook::select(SplitContainer *page) -{ - if (page == this->selectedPage) { - return; - } - - if (page != nullptr) { - page->setHidden(false); - page->getTab()->setSelected(true); - page->getTab()->raise(); - } - - if (this->selectedPage != nullptr) { - this->selectedPage->setHidden(true); - this->selectedPage->getTab()->setSelected(false); - - for (Split *split : this->selectedPage->getSplits()) { - split->updateLastReadMessage(); - } - } - - this->selectedPage = page; - - this->performLayout(); -} - -void Notebook::selectIndex(int index) -{ - if (index < 0 || index >= this->pages.size()) { - return; - } - - this->select(this->pages.at(index)); -} - -int Notebook::tabCount() -{ - return this->pages.size(); -} - -SplitContainer *Notebook::tabAt(QPoint point, int &index, int maxWidth) -{ - int i = 0; - - for (auto *page : this->pages) { - QRect rect = page->getTab()->getDesiredRect(); - rect.setHeight((int)(this->getScale() * 24)); - - rect.setWidth(std::min(maxWidth, rect.width())); - - if (rect.contains(point)) { - index = i; - return page; - } - - i++; - } - - index = -1; - return nullptr; -} - -SplitContainer *Notebook::tabAt(int index) -{ - return this->pages[index]; -} - -void Notebook::rearrangePage(SplitContainer *page, int index) -{ - this->pages.move(this->pages.indexOf(page), index); - - this->performLayout(); -} - -void Notebook::nextTab() -{ - if (this->pages.size() <= 1) { - return; - } - - int index = (this->pages.indexOf(this->selectedPage) + 1) % this->pages.size(); - - this->select(this->pages[index]); -} - -void Notebook::previousTab() -{ - if (this->pages.size() <= 1) { - return; - } - - int index = (this->pages.indexOf(this->selectedPage) - 1); - - if (index < 0) { - index += this->pages.size(); - } - - this->select(this->pages[index]); -} - -void Notebook::performLayout(bool animated) -{ - auto app = getApp(); - - int x = 0, y = 0; - float scale = this->getScale(); - bool customFrame = this->parentWindow->hasCustomWindowFrame(); - - if (!this->showButtons || app->settings->hidePreferencesButton || customFrame) { - this->settingsButton.hide(); - } else { - this->settingsButton.show(); - x += settingsButton.width(); - } - if (!this->showButtons || app->settings->hideUserButton || customFrame) { - this->userButton.hide(); - } else { - this->userButton.move(x, 0); - this->userButton.show(); - x += userButton.width(); - } - - if (customFrame || !this->showButtons || - (app->settings->hideUserButton && app->settings->hidePreferencesButton)) { - x += (int)(scale * 2); - } - - int tabHeight = static_cast(24 * scale); - bool first = true; - - for (auto &i : this->pages) { - if (!first && - (i == this->pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) { - y += i->getTab()->height(); - // y += 20; - i->getTab()->moveAnimated(QPoint(0, y), animated); - x = i->getTab()->width(); - } else { - i->getTab()->moveAnimated(QPoint(x, y), animated); - x += i->getTab()->width(); - } - - // x -= (int)(8 * scale); - x += 1; - - first = false; - } - - // x += (int)(8 * scale); - // x += 1; - - this->addButton.move(x, y); - - y -= 1; - - for (auto &i : this->pages) { - i->getTab()->raise(); - } - - this->addButton.raise(); - - if (this->selectedPage != nullptr) { - this->selectedPage->move(0, y + tabHeight); - this->selectedPage->resize(width(), height() - y - tabHeight); - this->selectedPage->raise(); - } -} - -void Notebook::resizeEvent(QResizeEvent *) -{ - this->performLayout(false); -} - -void Notebook::scaleChangedEvent(float) -{ - float h = 24 * this->getScale(); - - this->settingsButton.setFixedSize(h, h); - this->userButton.setFixedSize(h, h); - this->addButton.setFixedSize(h, h); - - for (auto &i : this->pages) { - i->getTab()->updateSize(); - } -} - -void Notebook::settingsButtonClicked() -{ - auto app = getApp(); - app->windows->showSettingsDialog(); -} - -void Notebook::usersButtonClicked() -{ - auto app = getApp(); - app->windows->showAccountSelectPopup(this->mapToGlobal(this->userButton.rect().bottomRight())); -} - -void Notebook::addPageButtonClicked() -{ - QTimer::singleShot(80, [this] { this->addNewPage(true); }); + return selectedPage != nullptr ? (SplitContainer *)selectedPage : this->addPage(); } } // namespace widgets diff --git a/src/widgets/notebook.hpp b/src/widgets/notebook.hpp index d82f26a5..dae526b4 100644 --- a/src/widgets/notebook.hpp +++ b/src/widgets/notebook.hpp @@ -32,6 +32,7 @@ public: void selectPreviousTab(); int getPageCount() const; + QWidget *getPageAt(int index) const; int getSelectedIndex() const; QWidget *getSelectedPage() const; @@ -44,11 +45,15 @@ public: bool getShowAddButton() const; void setShowAddButton(bool value); + void performLayout(bool animate = true); + protected: virtual void scaleChangedEvent(float scale) override; virtual void resizeEvent(QResizeEvent *) override; virtual void paintEvent(QPaintEvent *) override; + NotebookButton *getAddButton(); + private: struct Item { NotebookTab2 *tab; @@ -64,63 +69,16 @@ private: bool showAddButton = false; int lineY = 20; - void performLayout(bool animate = true); - NotebookTab2 *getTabFromPage(QWidget *page); }; -class Notebook : public BaseWidget +class SplitNotebook : public Notebook2 { - Q_OBJECT - public: - explicit Notebook(Window *parent, bool _showButtons); - - SplitContainer *addNewPage(bool select = false); - - void removePage(SplitContainer *page); - void removeCurrentPage(); - void select(SplitContainer *page); - void selectIndex(int index); + SplitNotebook(QWidget *parent); + SplitContainer *addPage(bool select = false); SplitContainer *getOrAddSelectedPage(); - SplitContainer *getSelectedPage(); - - void performLayout(bool animate = true); - - int tabCount(); - SplitContainer *tabAt(QPoint point, int &index, int maxWidth = 2000000000); - SplitContainer *tabAt(int index); - void rearrangePage(SplitContainer *page, int index); - - void nextTab(); - void previousTab(); - -protected: - void scaleChangedEvent(float scale); - void resizeEvent(QResizeEvent *); - - void settingsButtonMouseReleased(QMouseEvent *event); - -public slots: - void settingsButtonClicked(); - void usersButtonClicked(); - void addPageButtonClicked(); - -private: - Window *parentWindow; - - QList pages; - - NotebookButton addButton; - NotebookButton settingsButton; - NotebookButton userButton; - - SplitContainer *selectedPage = nullptr; - - bool showButtons; - - QMessageBox closeConfirmDialog; }; } // namespace widgets diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp index 6139e28d..afabcc42 100644 --- a/src/widgets/settingspages/appearancepage.cpp +++ b/src/widgets/settingspages/appearancepage.cpp @@ -165,12 +165,12 @@ QLayout *AppearancePage::createFontChanger() layout->addWidget(label); auto updateFontFamilyLabel = [=](auto) { - label->setText(QString::fromStdString(app->fonts->currentFontFamily.getValue()) + ", " + - QString::number(app->fonts->currentFontSize) + "pt"); + label->setText(QString::fromStdString(app->fonts->chatFontFamily.getValue()) + ", " + + QString::number(app->fonts->chatFontSize) + "pt"); }; - app->fonts->currentFontFamily.connectSimple(updateFontFamilyLabel, this->managedConnections); - app->fonts->currentFontSize.connectSimple(updateFontFamilyLabel, this->managedConnections); + app->fonts->chatFontFamily.connectSimple(updateFontFamilyLabel, this->managedConnections); + app->fonts->chatFontSize.connectSimple(updateFontFamilyLabel, this->managedConnections); // BUTTON QPushButton *button = new QPushButton("Select"); @@ -178,11 +178,11 @@ QLayout *AppearancePage::createFontChanger() button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed); QObject::connect(button, &QPushButton::clicked, [=]() { - QFontDialog dialog(app->fonts->getFont(singletons::FontManager::Medium, 1.)); + QFontDialog dialog(app->fonts->getFont(singletons::FontManager::ChatMedium, 1.)); dialog.connect(&dialog, &QFontDialog::fontSelected, [=](const QFont &font) { - app->fonts->currentFontFamily = font.family().toStdString(); - app->fonts->currentFontSize = font.pointSize(); + app->fonts->chatFontFamily = font.family().toStdString(); + app->fonts->chatFontSize = font.pointSize(); }); dialog.show(); diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index dd6b517e..dbc21e8d 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -29,15 +29,13 @@ namespace widgets { bool SplitContainer::isDraggingSplit = false; Split *SplitContainer::draggingSplit = nullptr; -SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) +SplitContainer::SplitContainer(Notebook2 *parent) : BaseWidget(parent) - , tab(_tab) + , tab(nullptr) , dropPreview(this) , mouseOverPoint(-10000, -10000) , overlay(this) { - this->tab->page = this; - this->refreshTabTitle(); this->managedConnect(Split::modifierStatusChanged, [this](auto modifiers) { @@ -69,11 +67,20 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) this->setAcceptDrops(true); } -NotebookTab *SplitContainer::getTab() const +NotebookTab2 *SplitContainer::getTab() const { return this->tab; } +void SplitContainer::setTab(NotebookTab2 *_tab) +{ + this->tab = _tab; + + this->tab->page = this; + + this->refreshTabTitle(); +} + void SplitContainer::appendNewSplit(bool openChannelNameDialog) { Split *split = new Split(this); @@ -131,6 +138,12 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Node *relati this->refreshTabTitle(); + split->getChannelView().tabHighlightRequested.connect([this](HighlightState state) { + if (this->tab != nullptr) { + this->tab->setHighlightState(state); + } + }); + this->layout(); } @@ -149,6 +162,8 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) this->refreshTabTitle(); + split->getChannelView().tabHighlightRequested.disconnectAll(); + return position; } @@ -265,10 +280,10 @@ void SplitContainer::paintEvent(QPaintEvent *) QString text = "Click to add a split"; - Notebook *notebook = dynamic_cast(this->parentWidget()); + Notebook2 *notebook = dynamic_cast(this->parentWidget()); if (notebook != nullptr) { - if (notebook->tabCount() > 1) { + if (notebook->getPageCount() > 1) { text += "\n\nTip: After adding a split you can hold to move it or split it " "further."; } @@ -276,7 +291,7 @@ void SplitContainer::paintEvent(QPaintEvent *) painter.drawText(rect(), text, QTextOption(Qt::AlignCenter)); } else { - painter.fillRect(rect(), this->themeManager->splits.messageSeperator); + painter.fillRect(rect(), QColor("#555")); } for (DropRect &dropRect : this->dropRects) { @@ -326,13 +341,15 @@ void SplitContainer::mouseMoveEvent(QMouseEvent *event) void SplitContainer::leaveEvent(QEvent *event) { - this->mouseOverPoint = QPoint(-1000, -10000); + this->mouseOverPoint = QPoint(-10000, -10000); this->update(); } void SplitContainer::refreshTabTitle() { - assert(this->tab != nullptr); + if (this->tab == nullptr) { + return; + } if (!this->tab->useDefaultTitle) { return; diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index c366850b..b36fda19 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -165,7 +165,7 @@ private: }; public: - SplitContainer(Notebook *parent, NotebookTab *_tab); + SplitContainer(Notebook2 *parent); void appendNewSplit(bool openChannelNameDialog); void appendSplit(Split *split); @@ -189,12 +189,14 @@ public: void refreshTabTitle(); - NotebookTab *getTab() const; + NotebookTab2 *getTab() const; Node *getBaseNode() { return &this->baseNode; } + void setTab(NotebookTab2 *tab); + static bool isDraggingSplit; static Split *draggingSplit; @@ -232,7 +234,7 @@ private: Node baseNode; - NotebookTab *tab; + NotebookTab2 *tab; std::vector splits; bool isDragging = false; diff --git a/src/widgets/tooltipwidget.cpp b/src/widgets/tooltipwidget.cpp index 26026b6b..6baa8052 100644 --- a/src/widgets/tooltipwidget.cpp +++ b/src/widgets/tooltipwidget.cpp @@ -58,7 +58,7 @@ void TooltipWidget::updateFont() auto app = getApp(); this->setFont( - app->fonts->getFont(singletons::FontManager::Type::MediumSmall, this->getScale())); + app->fonts->getFont(singletons::FontManager::Type::ChatMediumSmall, this->getScale())); } void TooltipWidget::setText(QString text) diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index ef667c50..b159257d 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -27,7 +27,7 @@ Window::Window(WindowType _type) : BaseWindow(nullptr, true) , type(_type) , dpi(this->getScale()) - , notebook(this, !this->hasCustomWindowFrame()) + , notebook(this) { auto app = getApp(); @@ -81,7 +81,7 @@ Window::Window(WindowType _type) CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); }); // CTRL+SHIFT+T: New tab - CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(true); }); + CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addPage(true); }); // CTRL+SHIFT+W: Close current tab CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); }); @@ -106,6 +106,9 @@ Window::Window(WindowType _type) // }); this->setWindowTitle("Chatterino 2 Development Build"); + + this->notebook.setAllowUserTabManagement(true); + this->notebook.setShowAddButton(true); } Window::WindowType Window::getType() @@ -128,7 +131,7 @@ void Window::repaintVisibleChatWidgets(Channel *channel) } } -Notebook &Window::getNotebook() +SplitNotebook &Window::getNotebook() { return this->notebook; } diff --git a/src/widgets/window.hpp b/src/widgets/window.hpp index 5fa8eee1..02a774e0 100644 --- a/src/widgets/window.hpp +++ b/src/widgets/window.hpp @@ -29,7 +29,7 @@ public: void repaintVisibleChatWidgets(Channel *channel = nullptr); - Notebook &getNotebook(); + SplitNotebook &getNotebook(); void refreshWindowTitle(const QString &username); @@ -47,9 +47,9 @@ private: void loadGeometry(); - Notebook notebook; + SplitNotebook notebook; - friend class Notebook; + friend class Notebook2; public: void save(); From 8b3fb691a73ad334d51a9d1d47fff99682057dda Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 11:59:37 +0200 Subject: [PATCH 023/121] renamed Notebook2 to Notebook --- src/widgets/emotepopup.cpp | 2 +- src/widgets/helper/notebookbutton.cpp | 2 +- src/widgets/helper/notebooktab.cpp | 44 ++++++++++---------- src/widgets/helper/notebooktab.hpp | 8 ++-- src/widgets/helper/splitinput.cpp | 4 +- src/widgets/notebook.cpp | 60 +++++++++++++-------------- src/widgets/notebook.hpp | 12 +++--- src/widgets/selectchanneldialog.cpp | 4 +- src/widgets/selectchanneldialog.hpp | 2 +- src/widgets/splitcontainer.cpp | 8 ++-- src/widgets/splitcontainer.hpp | 8 ++-- src/widgets/window.hpp | 2 +- 12 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/widgets/emotepopup.cpp b/src/widgets/emotepopup.cpp index 85496578..1bcd7503 100644 --- a/src/widgets/emotepopup.cpp +++ b/src/widgets/emotepopup.cpp @@ -32,7 +32,7 @@ EmotePopup::EmotePopup() auto *layout = new QVBoxLayout(this); this->getLayoutContainer()->setLayout(layout); - Notebook2 *notebook = new Notebook2(this); + Notebook *notebook = new Notebook(this); layout->addWidget(notebook); layout->setMargin(0); diff --git a/src/widgets/helper/notebookbutton.cpp b/src/widgets/helper/notebookbutton.cpp index 87783919..174e1a99 100644 --- a/src/widgets/helper/notebookbutton.cpp +++ b/src/widgets/helper/notebookbutton.cpp @@ -136,7 +136,7 @@ void NotebookButton::dropEvent(QDropEvent *event) if (SplitContainer::isDraggingSplit) { event->acceptProposedAction(); - Notebook2 *notebook = dynamic_cast(this->parentWidget()); + Notebook *notebook = dynamic_cast(this->parentWidget()); if (notebook != nuuls) { SplitContainer *page = new SplitContainer(notebook); diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 34894bf1..51cf3bcd 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -19,7 +19,7 @@ namespace chatterino { namespace widgets { -NotebookTab2::NotebookTab2(Notebook2 *_notebook) +NotebookTab::NotebookTab(Notebook *_notebook) : BaseWidget(_notebook) , positionChangedAnimation(this, "pos") , notebook(_notebook) @@ -31,7 +31,7 @@ NotebookTab2::NotebookTab2(Notebook2 *_notebook) this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); - app->settings->showTabCloseButton.connect(boost::bind(&NotebookTab2::hideTabXChanged, this, _1), + app->settings->showTabCloseButton.connect(boost::bind(&NotebookTab::hideTabXChanged, this, _1), this->managedConnections); this->setMouseTracking(true); @@ -75,12 +75,12 @@ NotebookTab2::NotebookTab2(Notebook2 *_notebook) // }); } -void NotebookTab2::themeRefreshEvent() +void NotebookTab::themeRefreshEvent() { this->update(); } -void NotebookTab2::updateSize() +void NotebookTab::updateSize() { auto app = getApp(); float scale = getScale(); @@ -106,12 +106,12 @@ void NotebookTab2::updateSize() // } } -const QString &NotebookTab2::getTitle() const +const QString &NotebookTab::getTitle() const { return this->title; } -void NotebookTab2::setTitle(const QString &newTitle) +void NotebookTab::setTitle(const QString &newTitle) { if (this->title != newTitle) { this->title = newTitle; @@ -120,12 +120,12 @@ void NotebookTab2::setTitle(const QString &newTitle) } } -bool NotebookTab2::isSelected() const +bool NotebookTab::isSelected() const { return this->selected; } -void NotebookTab2::setSelected(bool value) +void NotebookTab::setSelected(bool value) { this->selected = value; @@ -134,7 +134,7 @@ void NotebookTab2::setSelected(bool value) this->update(); } -void NotebookTab2::setHighlightState(HighlightState newHighlightStyle) +void NotebookTab::setHighlightState(HighlightState newHighlightStyle) { if (this->isSelected()) { return; @@ -147,18 +147,18 @@ void NotebookTab2::setHighlightState(HighlightState newHighlightStyle) } } -QRect NotebookTab2::getDesiredRect() const +QRect NotebookTab::getDesiredRect() const { return QRect(positionAnimationDesiredPoint, size()); } -void NotebookTab2::hideTabXChanged(bool) +void NotebookTab::hideTabXChanged(bool) { this->updateSize(); this->update(); } -void NotebookTab2::moveAnimated(QPoint pos, bool animated) +void NotebookTab::moveAnimated(QPoint pos, bool animated) { this->positionAnimationDesiredPoint = pos; @@ -182,7 +182,7 @@ void NotebookTab2::moveAnimated(QPoint pos, bool animated) this->positionChangedAnimation.start(); } -void NotebookTab2::paintEvent(QPaintEvent *) +void NotebookTab::paintEvent(QPaintEvent *) { auto app = getApp(); QPainter painter(this); @@ -292,17 +292,17 @@ void NotebookTab2::paintEvent(QPaintEvent *) } } -bool NotebookTab2::hasXButton() +bool NotebookTab::hasXButton() { return getApp()->settings->showTabCloseButton && this->notebook->getAllowUserTabManagement(); } -bool NotebookTab2::shouldDrawXButton() +bool NotebookTab::shouldDrawXButton() { return this->hasXButton() && (mouseOver || selected); } -void NotebookTab2::mousePressEvent(QMouseEvent *event) +void NotebookTab::mousePressEvent(QMouseEvent *event) { this->mouseDown = true; this->mouseDownX = this->getXRect().contains(event->pos()); @@ -320,7 +320,7 @@ void NotebookTab2::mousePressEvent(QMouseEvent *event) } } -void NotebookTab2::mouseReleaseEvent(QMouseEvent *event) +void NotebookTab::mouseReleaseEvent(QMouseEvent *event) { this->mouseDown = false; @@ -339,14 +339,14 @@ void NotebookTab2::mouseReleaseEvent(QMouseEvent *event) } } -void NotebookTab2::enterEvent(QEvent *) +void NotebookTab::enterEvent(QEvent *) { this->mouseOver = true; this->update(); } -void NotebookTab2::leaveEvent(QEvent *) +void NotebookTab::leaveEvent(QEvent *) { this->mouseOverX = false; this->mouseOver = false; @@ -354,14 +354,14 @@ void NotebookTab2::leaveEvent(QEvent *) this->update(); } -void NotebookTab2::dragEnterEvent(QDragEnterEvent *) +void NotebookTab::dragEnterEvent(QDragEnterEvent *) { if (this->notebook->getAllowUserTabManagement()) { this->notebook->select(this->page); } } -void NotebookTab2::mouseMoveEvent(QMouseEvent *event) +void NotebookTab::mouseMoveEvent(QMouseEvent *event) { auto app = getApp(); @@ -393,7 +393,7 @@ void NotebookTab2::mouseMoveEvent(QMouseEvent *event) } } -QRect NotebookTab2::getXRect() +QRect NotebookTab::getXRect() { // if (!this->notebook->getAllowUserTabManagement()) { // return QRect(); diff --git a/src/widgets/helper/notebooktab.hpp b/src/widgets/helper/notebooktab.hpp index e65c758f..a1d16e9f 100644 --- a/src/widgets/helper/notebooktab.hpp +++ b/src/widgets/helper/notebooktab.hpp @@ -14,15 +14,15 @@ namespace widgets { #define NOTEBOOK_TAB_HEIGHT 28 // class Notebook; -class Notebook2; +class Notebook; class SplitContainer; -class NotebookTab2 : public BaseWidget +class NotebookTab : public BaseWidget { Q_OBJECT public: - explicit NotebookTab2(Notebook2 *_notebook); + explicit NotebookTab(Notebook *_notebook); void updateSize(); @@ -61,7 +61,7 @@ private: bool positionChangedAnimationRunning = false; QPoint positionAnimationDesiredPoint; - Notebook2 *notebook; + Notebook *notebook; QString title; diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index facd70b6..f08ddfc0 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -244,7 +244,7 @@ void SplitInput::installKeyPressedEvent() SplitContainer *page = static_cast(this->chatWidget->parentWidget()); - Notebook2 *notebook = static_cast(page->parentWidget()); + Notebook *notebook = static_cast(page->parentWidget()); notebook->selectNextTab(); } @@ -253,7 +253,7 @@ void SplitInput::installKeyPressedEvent() SplitContainer *page = static_cast(this->chatWidget->parentWidget()); - Notebook2 *notebook = static_cast(page->parentWidget()); + Notebook *notebook = static_cast(page->parentWidget()); notebook->selectPreviousTab(); } diff --git a/src/widgets/notebook.cpp b/src/widgets/notebook.cpp index 1acae1a7..b52abe62 100644 --- a/src/widgets/notebook.cpp +++ b/src/widgets/notebook.cpp @@ -25,7 +25,7 @@ namespace chatterino { namespace widgets { -Notebook2::Notebook2(QWidget *parent) +Notebook::Notebook(QWidget *parent) : BaseWidget(parent) , addButton(this) { @@ -38,9 +38,9 @@ Notebook2::Notebook2(QWidget *parent) QObject::connect(shortcut_prev, &QShortcut::activated, [this] { this->selectPreviousTab(); }); } -NotebookTab2 *Notebook2::addPage(QWidget *page, QString title, bool select) +NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select) { - auto *tab = new NotebookTab2(this); + auto *tab = new NotebookTab(this); tab->page = page; if (!title.isEmpty()) { @@ -66,7 +66,7 @@ NotebookTab2 *Notebook2::addPage(QWidget *page, QString title, bool select) return tab; } -void Notebook2::removePage(QWidget *page) +void Notebook::removePage(QWidget *page) { for (int i = 0; i < this->items.count(); i++) { if (this->items[i].page == page) { @@ -93,14 +93,14 @@ void Notebook2::removePage(QWidget *page) this->performLayout(); } -void Notebook2::removeCurrentPage() +void Notebook::removeCurrentPage() { if (this->selectedPage != nullptr) { this->removePage(this->selectedPage); } } -int Notebook2::indexOf(QWidget *page) const +int Notebook::indexOf(QWidget *page) const { for (int i = 0; i < this->items.count(); i++) { if (this->items[i].page == page) { @@ -111,7 +111,7 @@ int Notebook2::indexOf(QWidget *page) const return -1; } -void Notebook2::select(QWidget *page) +void Notebook::select(QWidget *page) { if (page == this->selectedPage) { return; @@ -120,7 +120,7 @@ void Notebook2::select(QWidget *page) if (page != nullptr) { page->setHidden(false); - NotebookTab2 *tab = this->getTabFromPage(page); + NotebookTab *tab = this->getTabFromPage(page); tab->setSelected(true); tab->raise(); } @@ -128,7 +128,7 @@ void Notebook2::select(QWidget *page) if (this->selectedPage != nullptr) { this->selectedPage->setHidden(true); - NotebookTab2 *tab = this->getTabFromPage(selectedPage); + NotebookTab *tab = this->getTabFromPage(selectedPage); tab->setSelected(false); // for (auto split : this->selectedPage->getSplits()) { @@ -141,7 +141,7 @@ void Notebook2::select(QWidget *page) this->performLayout(); } -void Notebook2::selectIndex(int index) +void Notebook::selectIndex(int index) { if (index < 0 || this->items.count() <= index) { return; @@ -150,7 +150,7 @@ void Notebook2::selectIndex(int index) this->select(this->items[index].page); } -void Notebook2::selectNextTab() +void Notebook::selectNextTab() { if (this->items.size() <= 1) { return; @@ -161,7 +161,7 @@ void Notebook2::selectNextTab() this->select(this->items[index].page); } -void Notebook2::selectPreviousTab() +void Notebook::selectPreviousTab() { if (this->items.size() <= 1) { return; @@ -176,27 +176,27 @@ void Notebook2::selectPreviousTab() this->select(this->items[index].page); } -int Notebook2::getPageCount() const +int Notebook::getPageCount() const { return this->items.count(); } -QWidget *Notebook2::getPageAt(int index) const +QWidget *Notebook::getPageAt(int index) const { return this->items[index].page; } -int Notebook2::getSelectedIndex() const +int Notebook::getSelectedIndex() const { return this->indexOf(this->selectedPage); } -QWidget *Notebook2::getSelectedPage() const +QWidget *Notebook::getSelectedPage() const { return this->selectedPage; } -QWidget *Notebook2::tabAt(QPoint point, int &index, int maxWidth) +QWidget *Notebook::tabAt(QPoint point, int &index, int maxWidth) { int i = 0; @@ -218,36 +218,36 @@ QWidget *Notebook2::tabAt(QPoint point, int &index, int maxWidth) return nullptr; } -void Notebook2::rearrangePage(QWidget *page, int index) +void Notebook::rearrangePage(QWidget *page, int index) { this->items.move(this->indexOf(page), index); this->performLayout(); } -bool Notebook2::getAllowUserTabManagement() const +bool Notebook::getAllowUserTabManagement() const { return this->allowUserTabManagement; } -void Notebook2::setAllowUserTabManagement(bool value) +void Notebook::setAllowUserTabManagement(bool value) { this->allowUserTabManagement = value; } -bool Notebook2::getShowAddButton() const +bool Notebook::getShowAddButton() const { return this->showAddButton; } -void Notebook2::setShowAddButton(bool value) +void Notebook::setShowAddButton(bool value) { this->showAddButton = value; this->addButton.setHidden(!value); } -void Notebook2::scaleChangedEvent(float scale) +void Notebook::scaleChangedEvent(float scale) { float h = NOTEBOOK_TAB_HEIGHT * this->getScale(); @@ -260,12 +260,12 @@ void Notebook2::scaleChangedEvent(float scale) } } -void Notebook2::resizeEvent(QResizeEvent *) +void Notebook::resizeEvent(QResizeEvent *) { this->performLayout(); } -void Notebook2::performLayout(bool animated) +void Notebook::performLayout(bool animated) { auto app = getApp(); @@ -347,7 +347,7 @@ void Notebook2::performLayout(bool animated) } } -void Notebook2::paintEvent(QPaintEvent *event) +void Notebook::paintEvent(QPaintEvent *event) { BaseWidget::paintEvent(event); @@ -356,12 +356,12 @@ void Notebook2::paintEvent(QPaintEvent *event) this->themeManager->tabs.bottomLine); } -NotebookButton *Notebook2::getAddButton() +NotebookButton *Notebook::getAddButton() { return &this->addButton; } -NotebookTab2 *Notebook2::getTabFromPage(QWidget *page) +NotebookTab *Notebook::getTabFromPage(QWidget *page) { for (auto &it : this->items) { if (it.page == page) { @@ -373,7 +373,7 @@ NotebookTab2 *Notebook2::getTabFromPage(QWidget *page) } SplitNotebook::SplitNotebook(QWidget *parent) - : Notebook2(parent) + : Notebook(parent) { this->connect(this->getAddButton(), &NotebookButton::clicked, [this]() { QTimer::singleShot(80, this, [this] { this->addPage(true); }); }); @@ -382,7 +382,7 @@ SplitNotebook::SplitNotebook(QWidget *parent) SplitContainer *SplitNotebook::addPage(bool select) { SplitContainer *container = new SplitContainer(this); - auto *tab = Notebook2::addPage(container, QString(), select); + auto *tab = Notebook::addPage(container, QString(), select); container->setTab(tab); tab->setParent(this); tab->show(); diff --git a/src/widgets/notebook.hpp b/src/widgets/notebook.hpp index dae526b4..e9b2e72b 100644 --- a/src/widgets/notebook.hpp +++ b/src/widgets/notebook.hpp @@ -14,14 +14,14 @@ namespace widgets { class Window; -class Notebook2 : public BaseWidget +class Notebook : public BaseWidget { Q_OBJECT public: - explicit Notebook2(QWidget *parent); + explicit Notebook(QWidget *parent); - NotebookTab2 *addPage(QWidget *page, QString title = QString(), bool select = false); + NotebookTab *addPage(QWidget *page, QString title = QString(), bool select = false); void removePage(QWidget *page); void removeCurrentPage(); @@ -56,7 +56,7 @@ protected: private: struct Item { - NotebookTab2 *tab; + NotebookTab *tab; QWidget *page; }; @@ -69,10 +69,10 @@ private: bool showAddButton = false; int lineY = 20; - NotebookTab2 *getTabFromPage(QWidget *page); + NotebookTab *getTabFromPage(QWidget *page); }; -class SplitNotebook : public Notebook2 +class SplitNotebook : public Notebook { public: SplitNotebook(QWidget *parent); diff --git a/src/widgets/selectchanneldialog.cpp b/src/widgets/selectchanneldialog.cpp index 1a0b8a1f..d444f4a8 100644 --- a/src/widgets/selectchanneldialog.cpp +++ b/src/widgets/selectchanneldialog.cpp @@ -26,7 +26,7 @@ SelectChannelDialog::SelectChannelDialog() util::LayoutCreator layoutWidget(this->getLayoutContainer()); auto layout = layoutWidget.setLayoutType().withoutMargin(); - auto notebook = layout.emplace(this).assign(&this->ui.notebook); + auto notebook = layout.emplace(this).assign(&this->ui.notebook); // twitch { @@ -99,7 +99,7 @@ SelectChannelDialog::SelectChannelDialog() QWidget::setTabOrder(*watching_btn, *channel_btn); // tab - NotebookTab2 *tab = notebook->addPage(obj.getElement()); + NotebookTab *tab = notebook->addPage(obj.getElement()); tab->setTitle("Twitch"); } diff --git a/src/widgets/selectchanneldialog.hpp b/src/widgets/selectchanneldialog.hpp index a13576e3..48fc0b0e 100644 --- a/src/widgets/selectchanneldialog.hpp +++ b/src/widgets/selectchanneldialog.hpp @@ -38,7 +38,7 @@ private: }; struct { - Notebook2 *notebook; + Notebook *notebook; struct { QRadioButton *channel; QLineEdit *channelName; diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index dbc21e8d..106e9242 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -29,7 +29,7 @@ namespace widgets { bool SplitContainer::isDraggingSplit = false; Split *SplitContainer::draggingSplit = nullptr; -SplitContainer::SplitContainer(Notebook2 *parent) +SplitContainer::SplitContainer(Notebook *parent) : BaseWidget(parent) , tab(nullptr) , dropPreview(this) @@ -67,12 +67,12 @@ SplitContainer::SplitContainer(Notebook2 *parent) this->setAcceptDrops(true); } -NotebookTab2 *SplitContainer::getTab() const +NotebookTab *SplitContainer::getTab() const { return this->tab; } -void SplitContainer::setTab(NotebookTab2 *_tab) +void SplitContainer::setTab(NotebookTab *_tab) { this->tab = _tab; @@ -280,7 +280,7 @@ void SplitContainer::paintEvent(QPaintEvent *) QString text = "Click to add a split"; - Notebook2 *notebook = dynamic_cast(this->parentWidget()); + Notebook *notebook = dynamic_cast(this->parentWidget()); if (notebook != nullptr) { if (notebook->getPageCount() > 1) { diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index b36fda19..ebbb3122 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -165,7 +165,7 @@ private: }; public: - SplitContainer(Notebook2 *parent); + SplitContainer(Notebook *parent); void appendNewSplit(bool openChannelNameDialog); void appendSplit(Split *split); @@ -189,13 +189,13 @@ public: void refreshTabTitle(); - NotebookTab2 *getTab() const; + NotebookTab *getTab() const; Node *getBaseNode() { return &this->baseNode; } - void setTab(NotebookTab2 *tab); + void setTab(NotebookTab *tab); static bool isDraggingSplit; static Split *draggingSplit; @@ -234,7 +234,7 @@ private: Node baseNode; - NotebookTab2 *tab; + NotebookTab *tab; std::vector splits; bool isDragging = false; diff --git a/src/widgets/window.hpp b/src/widgets/window.hpp index 02a774e0..fe3d68b3 100644 --- a/src/widgets/window.hpp +++ b/src/widgets/window.hpp @@ -49,7 +49,7 @@ private: SplitNotebook notebook; - friend class Notebook2; + friend class Notebook; public: void save(); From 67e580059c4ec5aae5de2e25651ebea4e5f54193 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 12:24:18 +0200 Subject: [PATCH 024/121] show settings and user button when it's not available in the window frame --- src/widgets/notebook.cpp | 45 +++++++++++++++++++++++++++++++++++++--- src/widgets/notebook.hpp | 4 +++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/widgets/notebook.cpp b/src/widgets/notebook.cpp index b52abe62..818a885a 100644 --- a/src/widgets/notebook.cpp +++ b/src/widgets/notebook.cpp @@ -251,8 +251,6 @@ void Notebook::scaleChangedEvent(float scale) { float h = NOTEBOOK_TAB_HEIGHT * this->getScale(); - // this->settingsButton.setFixedSize(h, h); - // this->userButton.setFixedSize(h, h); this->addButton.setFixedSize(h, h); for (auto &i : this->items) { @@ -297,6 +295,14 @@ void Notebook::performLayout(bool animated) // x += (int)(scale * 2); // } + float h = NOTEBOOK_TAB_HEIGHT * this->getScale(); + + for (auto *btn : this->customButtons) { + btn->setFixedSize(h, h); + btn->move(x, 0); + x += h; + } + int tabHeight = static_cast(NOTEBOOK_TAB_HEIGHT * scale); bool first = true; @@ -361,6 +367,16 @@ NotebookButton *Notebook::getAddButton() return &this->addButton; } +NotebookButton *Notebook::addCustomButton() +{ + NotebookButton *btn = new NotebookButton(this); + + this->customButtons.push_back(btn); + + this->performLayout(); + return btn; +} + NotebookTab *Notebook::getTabFromPage(QWidget *page) { for (auto &it : this->items) { @@ -372,11 +388,34 @@ NotebookTab *Notebook::getTabFromPage(QWidget *page) return nullptr; } -SplitNotebook::SplitNotebook(QWidget *parent) +SplitNotebook::SplitNotebook(Window *parent) : Notebook(parent) { this->connect(this->getAddButton(), &NotebookButton::clicked, [this]() { QTimer::singleShot(80, this, [this] { this->addPage(true); }); }); + + bool customFrame = parent->hasCustomWindowFrame(); + + if (!customFrame) { + auto *settingsBtn = this->addCustomButton(); + auto *userBtn = this->addCustomButton(); + + // app->settings->hidePreferencesButton.connectSimple( + // [this](bool hide) { this->performLayout(); }); + // app->settings->hideUserButton.connectSimple([this](auto) { this->performLayout(); + // }); + + settingsBtn->icon = NotebookButton::IconSettings; + userBtn->icon = NotebookButton::IconUser; + + QObject::connect(settingsBtn, &NotebookButton::clicked, + [this] { getApp()->windows->showSettingsDialog(); }); + + QObject::connect(userBtn, &NotebookButton::clicked, [this, userBtn] { + getApp()->windows->showAccountSelectPopup( + this->mapToGlobal(userBtn->rect().bottomRight())); + }); + } } SplitContainer *SplitNotebook::addPage(bool select) diff --git a/src/widgets/notebook.hpp b/src/widgets/notebook.hpp index e9b2e72b..e578e17a 100644 --- a/src/widgets/notebook.hpp +++ b/src/widgets/notebook.hpp @@ -53,6 +53,7 @@ protected: virtual void paintEvent(QPaintEvent *) override; NotebookButton *getAddButton(); + NotebookButton *addCustomButton(); private: struct Item { @@ -64,6 +65,7 @@ private: QWidget *selectedPage = nullptr; NotebookButton addButton; + std::vector customButtons; bool allowUserTabManagement = false; bool showAddButton = false; @@ -75,7 +77,7 @@ private: class SplitNotebook : public Notebook { public: - SplitNotebook(QWidget *parent); + SplitNotebook(Window *parent); SplitContainer *addPage(bool select = false); SplitContainer *getOrAddSelectedPage(); From eb25e863e3146cf83d4f7b8930b9c14e7f69a760 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 12:31:03 +0200 Subject: [PATCH 025/121] allowing hotswapping the top-most setting on windows --- src/widgets/basewindow.cpp | 7 +++++++ src/widgets/settingspages/behaviourpage.cpp | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index 65408b5d..7682c06b 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -115,9 +115,16 @@ void BaseWindow::init() } #endif +#ifdef USEWINSDK + app->settings->windowTopMost.connect([this](bool topMost, auto) { + ::SetWindowPos((HWND)this->winId(), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); + }); +#else if (app->settings->windowTopMost.getValue()) { this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint); } +#endif } void BaseWindow::setStayInScreenRect(bool value) diff --git a/src/widgets/settingspages/behaviourpage.cpp b/src/widgets/settingspages/behaviourpage.cpp index e251d9ad..68ea46ef 100644 --- a/src/widgets/settingspages/behaviourpage.cpp +++ b/src/widgets/settingspages/behaviourpage.cpp @@ -8,7 +8,11 @@ #include #include +#ifdef USEWINSDK +#define WINDOW_TOPMOST "Window always on top" +#else #define WINDOW_TOPMOST "Window always on top (requires restart)" +#endif #define INPUT_EMPTY "Hide input box when empty" #define PAUSE_HOVERING "When hovering" From a0fb6630e7f4744501c77b3797c88e9571984180 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 12:35:10 +0200 Subject: [PATCH 026/121] Fixes #406 --- src/widgets/helper/notebooktab.cpp | 8 +++++++- src/widgets/splitcontainer.cpp | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 51cf3bcd..6fcb6747 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -354,8 +354,14 @@ void NotebookTab::leaveEvent(QEvent *) this->update(); } -void NotebookTab::dragEnterEvent(QDragEnterEvent *) +void NotebookTab::dragEnterEvent(QDragEnterEvent *event) { + if (!event->mimeData()->hasFormat("chatterino/split")) + return; + + if (!SplitContainer::isDraggingSplit) + return; + if (this->notebook->getAllowUserTabManagement()) { this->notebook->select(this->page); } diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 106e9242..ec5dd8f6 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -321,9 +321,8 @@ void SplitContainer::dragEnterEvent(QDragEnterEvent *event) if (!event->mimeData()->hasFormat("chatterino/split")) return; - if (!SplitContainer::isDraggingSplit) { + if (!SplitContainer::isDraggingSplit) return; - } this->isDragging = true; this->layout(); From 75627bc03769b5ea25d96478246f3c70e55bb474 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 12:44:01 +0200 Subject: [PATCH 027/121] disabled theme color option --- src/main.cpp | 4 ++++ src/widgets/settingspages/appearancepage.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 3c372094..1493d5da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,10 @@ int main(int argc, char *argv[]) // TODO: can be any argument if (args.size() > 0 && args[0].startsWith("chrome-extension://")) { + chatterino::Application::instantiate(argc, argv); + auto app = chatterino::getApp(); + app->construct(); + chatterino::Application::runNativeMessagingHost(); return 0; } diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp index afabcc42..383af4df 100644 --- a/src/widgets/settingspages/appearancepage.cpp +++ b/src/widgets/settingspages/appearancepage.cpp @@ -52,7 +52,7 @@ AppearancePage::AppearancePage() // clang-format off form->addRow("Theme:", this->createComboBox({THEME_ITEMS}, app->themes->themeName)); - form->addRow("Theme color:", this->createThemeColorChanger()); + // form->addRow("Theme color:", this->createThemeColorChanger()); form->addRow("Font:", this->createFontChanger()); form->addRow("Tabs:", this->createCheckBox(TAB_X, app->settings->showTabCloseButton)); From 65846fe1c712c767c185ded93ab0a4dc70f3337c Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 13:31:55 +0200 Subject: [PATCH 028/121] scaling emotes and badges --- src/messages/image.cpp | 6 +++-- .../layouts/messagelayoutcontainer.cpp | 10 +++++++- .../layouts/messagelayoutcontainer.hpp | 1 + src/messages/messageelement.cpp | 4 ++-- src/singletons/settingsmanager.cpp | 2 ++ src/widgets/settingspages/appearancepage.cpp | 23 +++++++++++++++++++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/messages/image.cpp b/src/messages/image.cpp index aec60a57..ce92e815 100644 --- a/src/messages/image.cpp +++ b/src/messages/image.cpp @@ -240,7 +240,8 @@ int Image::getWidth() const int Image::getScaledWidth() const { - return static_cast(this->getWidth() * this->scale); + return static_cast(this->getWidth() * this->scale * + getApp()->settings->emoteScale.getValue()); } int Image::getHeight() const @@ -253,7 +254,8 @@ int Image::getHeight() const int Image::getScaledHeight() const { - return static_cast(this->getHeight() * this->scale); + return static_cast(this->getHeight() * this->scale * + getApp()->settings->emoteScale.getValue()); } } // namespace messages diff --git a/src/messages/layouts/messagelayoutcontainer.cpp b/src/messages/layouts/messagelayoutcontainer.cpp index dbb1ee9c..e5e3b474 100644 --- a/src/messages/layouts/messagelayoutcontainer.cpp +++ b/src/messages/layouts/messagelayoutcontainer.cpp @@ -36,6 +36,9 @@ void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFla this->width = _width; this->scale = _scale; this->flags = _flags; + auto mediumFontMetrics = getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, _scale); + this->textLineHeight = mediumFontMetrics.height(); + this->spaceWidth = mediumFontMetrics.width(' '); } void MessageLayoutContainer::clear() @@ -129,6 +132,11 @@ void MessageLayoutContainer::breakLine() yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale; } + // if (element->getCreator().getFlags() & MessageElement::Badges) { + if (element->getRect().height() < this->textLineHeight) { + yExtra -= (this->textLineHeight - element->getRect().height()) / 2; + } + element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left, element->getRect().y() + this->lineHeight + yExtra)); } @@ -151,7 +159,7 @@ void MessageLayoutContainer::breakLine() this->height = this->currentY + (this->margin.bottom * this->scale); this->lineHeight = 0; this->line++; -} +} // namespace layouts bool MessageLayoutContainer::atStartOfLine() { diff --git a/src/messages/layouts/messagelayoutcontainer.hpp b/src/messages/layouts/messagelayoutcontainer.hpp index 6400b789..b55c1427 100644 --- a/src/messages/layouts/messagelayoutcontainer.hpp +++ b/src/messages/layouts/messagelayoutcontainer.hpp @@ -100,6 +100,7 @@ private: size_t lineStart = 0; int lineHeight = 0; int spaceWidth = 4; + int textLineHeight = 0; std::vector> elements; std::vector lines; diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index 4aa90c52..d53a6a2e 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -70,8 +70,8 @@ ImageElement::ImageElement(Image *_image, MessageElement::Flags flags) void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags) { if (_flags & this->getFlags()) { - QSize size(this->image->getWidth() * this->image->getScale() * container.getScale(), - this->image->getHeight() * this->image->getScale() * container.getScale()); + QSize size(this->image->getScaledWidth() * this->image->getScale() * container.getScale(), + this->image->getScaledHeight() * this->image->getScale() * container.getScale()); container.addElement( (new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink())); diff --git a/src/singletons/settingsmanager.cpp b/src/singletons/settingsmanager.cpp index 2516df09..7c3cbee3 100644 --- a/src/singletons/settingsmanager.cpp +++ b/src/singletons/settingsmanager.cpp @@ -41,6 +41,8 @@ void SettingManager::initialize() auto app = getApp(); app->windows->layoutVisibleChatWidgets(); }); + + this->emoteScale.connect([](auto, auto) { getApp()->windows->layoutVisibleChatWidgets(); }); } MessageElement::Flags SettingManager::getWordFlags() diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp index 383af4df..0209f031 100644 --- a/src/widgets/settingspages/appearancepage.cpp +++ b/src/widgets/settingspages/appearancepage.cpp @@ -110,6 +110,29 @@ AppearancePage::AppearancePage() emotes.append(this->createCheckBox("Enable emojis", app->settings->enableEmojis)); emotes.append( this->createCheckBox("Enable animations", app->settings->enableGifAnimations)); + + auto scaleBox = emotes.emplace(); + { + scaleBox.emplace("Emote scale:"); + + auto emoteScale = scaleBox.emplace(Qt::Horizontal); + emoteScale->setMinimum(5); + emoteScale->setMaximum(50); + + auto scaleLabel = scaleBox.emplace("1.0"); + scaleLabel->setFixedWidth(100); + QObject::connect(*emoteScale, &QSlider::valueChanged, [scaleLabel](int value) mutable { + float f = (float)value / 10.f; + scaleLabel->setText(QString::number(f)); + + getApp()->settings->emoteScale.setValue(f); + }); + + emoteScale->setValue(std::max( + 5, std::min(50, (int)(app->settings->emoteScale.getValue() * 10.f)))); + + scaleLabel->setText(QString::number(app->settings->emoteScale.getValue())); + } } layout->addStretch(1); From 935cd1bab07728dc63e5341072a0742fe3082c98 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 13:47:22 +0200 Subject: [PATCH 029/121] Fixes #409 hovering smilies shows the regex emote code --- src/singletons/emotemanager.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index a1a781fb..191c5c2f 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -541,6 +541,19 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa QString _emoteName = emoteName; _emoteName.replace("<", "<"); + static QMap emoteNameReplacements{ + {"[oO](_|\\.)[oO]", "o_O"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, + {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, + {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + {"R-?\\)", "R-)"}, + }; + + auto it = emoteNameReplacements.find(_emoteName); + if (it != emoteNameReplacements.end()) { + _emoteName = it.value(); + } + return _twitchEmoteFromCache.getOrAdd(id, [&emoteName, &_emoteName, &id] { util::EmoteData newEmoteData; newEmoteData.image1x = new Image(GetTwitchEmoteLink(id, "1.0"), 1, emoteName, From 0475ea0b6f46d0e83138bfbaf4ee688f088b4cc2 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 13:54:42 +0200 Subject: [PATCH 030/121] fixed text copying in the emote popup --- src/widgets/emotepopup.cpp | 1 + src/widgets/helper/channelview.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/widgets/emotepopup.cpp b/src/widgets/emotepopup.cpp index 1bcd7503..004cadce 100644 --- a/src/widgets/emotepopup.cpp +++ b/src/widgets/emotepopup.cpp @@ -7,6 +7,7 @@ #include "widgets/notebook.hpp" #include +#include #include using namespace chatterino::providers::twitch; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 07e7044f..b693d968 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -126,7 +126,11 @@ ChannelView::ChannelView(BaseWidget *parent) this->scrollBar.setGeometry(this->width() - this->scrollBar.width(), 0, this->scrollBar.width(), this->height()); }); -} // namespace widgets + + QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+C"), this); + QObject::connect(shortcut, &QShortcut::activated, + [this] { QGuiApplication::clipboard()->setText(this->getSelectedText()); }); +} ChannelView::~ChannelView() { From 8173b3d60db10a624740ee9d4ca3fc9190f6c335 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 17:24:07 +0200 Subject: [PATCH 031/121] improved the window broder on windows 8 --- src/widgets/basewindow.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index 7682c06b..a1cd1042 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -302,10 +302,18 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r NCCALCSIZE_PARAMS *ncp = (reinterpret_cast(msg->lParam)); ncp->lppos->flags |= SWP_NOREDRAW; RECT *clientRect = &ncp->rgrc[0]; - clientRect->left += cx; - clientRect->top += 0; - clientRect->right -= cx; - clientRect->bottom -= cy; + + if (IsWindows10OrGreater()) { + clientRect->left += cx; + clientRect->top += 0; + clientRect->right -= cx; + clientRect->bottom -= cy; + } else { + clientRect->left += 1; + clientRect->top += 0; + clientRect->right -= 1; + clientRect->bottom -= 1; + } } *result = 0; From aefdb60ada15e9dafa1603dfdba47ed9ffd28eee Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 19:42:42 +0200 Subject: [PATCH 032/121] enabled dark fusion theme --- src/main.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 1493d5da..6872b6f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef USEWINSDK #include "util/nativeeventhelper.hpp" @@ -51,6 +52,35 @@ int runGui(int argc, char *argv[]) // QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true); QApplication a(argc, argv); + QApplication::setStyle(QStyleFactory::create("Fusion")); + + // borrowed from + // https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows + QPalette darkPalette = a.palette(); + + darkPalette.setColor(QPalette::Window, QColor(33, 33, 33)); + darkPalette.setColor(QPalette::WindowText, Qt::white); + darkPalette.setColor(QPalette::Text, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Base, QColor(90, 90, 90)); + darkPalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66)); + darkPalette.setColor(QPalette::ToolTipBase, Qt::white); + darkPalette.setColor(QPalette::ToolTipText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); + darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); + darkPalette.setColor(QPalette::Button, QColor(70, 70, 70)); + darkPalette.setColor(QPalette::ButtonText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::BrightText, Qt::red); + darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); + darkPalette.setColor(QPalette::HighlightedText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); + + qApp->setPalette(darkPalette); + // Install native event handler for hidpi on windows #ifdef USEWINSDK a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter); From 4cb666b75aad9335200d30e7bc3ef0396f9fc998 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 19:46:08 +0200 Subject: [PATCH 033/121] added ctrl+t again --- src/widgets/window.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index b159257d..a3fa1477 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -66,9 +66,13 @@ Window::Window(WindowType _type) layout->setMargin(0); /// Initialize program-wide hotkeys - // CTRL+P: Open Settings Dialog + // CTRL+P: Open settings dialog CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); }); + // CTRL+T: Create new split + CreateWindowShortcut(this, "CTRL+T", + [this] { this->notebook.getOrAddSelectedPage()->appendNewSplit(true); }); + // CTRL+Number: Switch to n'th tab CreateWindowShortcut(this, "CTRL+1", [this] { this->notebook.selectIndex(0); }); CreateWindowShortcut(this, "CTRL+2", [this] { this->notebook.selectIndex(1); }); From fb750d6d85796b2f64f4093d315307a0cf9dadee Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 20:02:12 +0200 Subject: [PATCH 034/121] added confirmation dialog when closing a tab --- src/widgets/helper/notebooktab.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 6fcb6747..621a37fc 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -324,15 +324,25 @@ void NotebookTab::mouseReleaseEvent(QMouseEvent *event) { this->mouseDown = false; + auto removeThisPage = [this] { + auto reply = QMessageBox::question(this, "Remove this tab", + "Are you sure that you want to remove this tab?", + QMessageBox::Yes | QMessageBox::Cancel); + + if (reply == QMessageBox::Yes) { + this->notebook->removePage(this->page); + } + }; + if (event->button() == Qt::MiddleButton) { if (this->rect().contains(event->pos())) { - this->notebook->removePage(this->page); + removeThisPage(); } } else { if (this->hasXButton() && this->mouseDownX && this->getXRect().contains(event->pos())) { this->mouseDownX = false; - this->notebook->removePage(this->page); + removeThisPage(); } else { this->update(); } From 96103de1ea4c4c4df1f8f7402d80c240c6b1b31f Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 20:13:55 +0200 Subject: [PATCH 035/121] fixed tiny badges --- src/messages/image.cpp | 4 ++-- src/messages/messageelement.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/messages/image.cpp b/src/messages/image.cpp index ce92e815..732d56cd 100644 --- a/src/messages/image.cpp +++ b/src/messages/image.cpp @@ -240,7 +240,7 @@ int Image::getWidth() const int Image::getScaledWidth() const { - return static_cast(this->getWidth() * this->scale * + return static_cast((float)this->getWidth() * this->scale * getApp()->settings->emoteScale.getValue()); } @@ -254,7 +254,7 @@ int Image::getHeight() const int Image::getScaledHeight() const { - return static_cast(this->getHeight() * this->scale * + return static_cast((float)this->getHeight() * this->scale * getApp()->settings->emoteScale.getValue()); } diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index d53a6a2e..c84f4566 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -70,8 +70,8 @@ ImageElement::ImageElement(Image *_image, MessageElement::Flags flags) void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags) { if (_flags & this->getFlags()) { - QSize size(this->image->getScaledWidth() * this->image->getScale() * container.getScale(), - this->image->getScaledHeight() * this->image->getScale() * container.getScale()); + QSize size(this->image->getScaledWidth() * container.getScale(), + this->image->getScaledHeight() * container.getScale()); container.addElement( (new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink())); From 63e88938efeeaf3909fc10413864fcb45cd64a69 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 20:22:41 +0200 Subject: [PATCH 036/121] fixed channelview not scrolling down when emotes get loaded --- src/widgets/helper/channelview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index b693d968..79c7a3ef 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -258,7 +258,7 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) if (this->enableScrollingToBottom && this->showingLatestMessages && showScrollbar) { if (!this->isPaused()) { this->scrollBar.scrollToBottom( - this->messageWasAdded && + // this->messageWasAdded && app->settings->enableSmoothScrollingNewMessages.getValue()); } this->messageWasAdded = false; From 0d76f6f39f56dd6503170238cf1686da4142ff7d Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 20:34:37 +0200 Subject: [PATCH 037/121] added "Copy message" context menu item ofr messages --- src/messages/layouts/messagelayout.hpp | 2 +- src/widgets/helper/channelview.cpp | 39 +++++++++++++++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/messages/layouts/messagelayout.hpp b/src/messages/layouts/messagelayout.hpp index 76878c07..b6f045a8 100644 --- a/src/messages/layouts/messagelayout.hpp +++ b/src/messages/layouts/messagelayout.hpp @@ -50,7 +50,7 @@ public: const MessageLayoutElement *getElementAt(QPoint point); int getLastCharacterIndex() const; int getSelectionIndex(QPoint position); - void addSelectionText(QString &str, int from, int to); + void addSelectionText(QString &str, int from = 0, int to = INT_MAX); // Misc bool isDisabled() const; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 79c7a3ef..b4644498 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -938,12 +938,13 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) const auto &creator = hoverLayoutElement->getCreator(); auto creatorFlags = creator.getFlags(); - if ((creatorFlags & (MessageElement::Flags::EmoteImages | MessageElement::Flags::EmojiImage)) != - 0) { - if (event->button() == Qt::RightButton) { - static QMenu *menu = new QMenu; - menu->clear(); + if (event->button() == Qt::RightButton) { + static QMenu *menu = new QMenu; + menu->clear(); + // Emote actions + if ((creatorFlags & + (MessageElement::Flags::EmoteImages | MessageElement::Flags::EmojiImage)) != 0) { const auto &emoteElement = static_cast(creator); // TODO: We might want to add direct "Open image" variants alongside the Copy actions @@ -977,12 +978,30 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) QApplication::clipboard()->setText(emotePageLink); // }); } - - menu->move(QCursor::pos()); - menu->show(); - - return; } + + // add seperator + if (!menu->actions().empty()) + menu->addSeparator(); + + // Message actions + menu->addAction("Copy message", [layout] { + QString copyString; + layout->addSelectionText(copyString); + + QGuiApplication::clipboard()->setText(copyString); + }); + // menu->addAction("Quote message", [layout] { + // QString copyString; + // layout->addSelectionText(copyString); + + // // insert into input + // }); + + menu->move(QCursor::pos()); + menu->show(); + + return; } auto &link = hoverLayoutElement->getLink(); From a74c19d1f3e7cae7eaff57ba2aa02ad1190ab6fe Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 21:16:34 +0200 Subject: [PATCH 038/121] fixed rightclicking links --- src/widgets/helper/channelview.cpp | 34 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index b4644498..67393db8 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -984,6 +984,17 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) if (!menu->actions().empty()) menu->addSeparator(); + // Link copy + if (hoverLayoutElement->getLink().type == Link::Url) { + QString url = hoverLayoutElement->getLink().value; + + menu->addAction("Open link in browser", + [url] { QDesktopServices::openUrl(QUrl(url)); }); + menu->addAction("Copy link", [url] { QApplication::clipboard()->setText(url); }); + + menu->addSeparator(); + } + // Message actions menu->addAction("Copy message", [layout] { QString copyString; @@ -1054,6 +1065,10 @@ void ChannelView::hideEvent(QHideEvent *) void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link, messages::MessageLayout *layout) { + if (event->button() != Qt::LeftButton) { + return; + } + switch (link.type) { case messages::Link::UserInfo: { auto user = link.value; @@ -1066,24 +1081,7 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link break; } case messages::Link::Url: { - 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.value; - menu->move(QCursor::pos()); - menu->show(); - } else { - QDesktopServices::openUrl(QUrl(link.value)); - } + QDesktopServices::openUrl(QUrl(link.value)); break; } case messages::Link::UserAction: { From 48e94a1169bc7b3f96a2b558929c9c11f3f6b09a Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 23 May 2018 22:27:29 +0200 Subject: [PATCH 039/121] added code for a notification system --- chatterino.pro | 6 +- .../highlights/highlightcontroller.cpp | 10 ++ .../highlights/highlightcontroller.hpp | 3 + src/providers/twitch/twitchserver.cpp | 10 +- src/widgets/basewidget.hpp | 2 +- src/widgets/basewindow.cpp | 126 ++++++++++-------- src/widgets/basewindow.hpp | 11 +- src/widgets/emotepopup.cpp | 2 +- src/widgets/notificationpopup.cpp | 50 +++++++ src/widgets/notificationpopup.hpp | 27 ++++ src/widgets/selectchanneldialog.cpp | 2 +- src/widgets/window.cpp | 2 +- 12 files changed, 182 insertions(+), 69 deletions(-) create mode 100644 src/widgets/notificationpopup.cpp create mode 100644 src/widgets/notificationpopup.hpp diff --git a/chatterino.pro b/chatterino.pro index 2461429b..8a5651d2 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -203,7 +203,8 @@ SOURCES += \ src/controllers/accounts/account.cpp \ src/widgets/helper/splitoverlay.cpp \ src/widgets/helper/dropoverlay.cpp \ - src/widgets/helper/splitnode.cpp + src/widgets/helper/splitnode.cpp \ + src/widgets/notificationpopup.cpp HEADERS += \ src/precompiled_header.hpp \ @@ -351,7 +352,8 @@ HEADERS += \ src/util/sharedptrelementless.hpp \ src/widgets/helper/splitoverlay.hpp \ src/widgets/helper/dropoverlay.hpp \ - src/widgets/helper/splitnode.hpp + src/widgets/helper/splitnode.hpp \ + src/widgets/notificationpopup.hpp RESOURCES += \ resources/resources.qrc diff --git a/src/controllers/highlights/highlightcontroller.cpp b/src/controllers/highlights/highlightcontroller.cpp index cab3cbbe..8c6b789d 100644 --- a/src/controllers/highlights/highlightcontroller.cpp +++ b/src/controllers/highlights/highlightcontroller.cpp @@ -2,6 +2,7 @@ #include "application.hpp" #include "controllers/highlights/highlightmodel.hpp" +#include "widgets/notificationpopup.hpp" namespace chatterino { namespace controllers { @@ -34,6 +35,15 @@ HighlightModel *HighlightController::createModel(QObject *parent) return model; } +void HighlightController::addHighlight(const messages::MessagePtr &msg) +{ + // static widgets::NotificationPopup popup; + + // popup.updatePosition(); + // popup.addMessage(msg); + // popup.show(); +} + } // namespace highlights } // namespace controllers } // namespace chatterino diff --git a/src/controllers/highlights/highlightcontroller.hpp b/src/controllers/highlights/highlightcontroller.hpp index 98ad843d..e6bed334 100644 --- a/src/controllers/highlights/highlightcontroller.hpp +++ b/src/controllers/highlights/highlightcontroller.hpp @@ -1,6 +1,7 @@ #pragma once #include "controllers/highlights/highlightphrase.hpp" +#include "messages/message.hpp" #include "singletons/settingsmanager.hpp" #include "util/signalvector2.hpp" @@ -21,6 +22,8 @@ public: HighlightModel *createModel(QObject *parent); + void addHighlight(const messages::MessagePtr &msg); + private: bool initialized = false; diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp index 6a6843f5..41d42cb7 100644 --- a/src/providers/twitch/twitchserver.cpp +++ b/src/providers/twitch/twitchserver.cpp @@ -1,6 +1,7 @@ #include "twitchserver.hpp" #include "application.hpp" +#include "controllers/highlights/highlightcontroller.hpp" #include "providers/twitch/ircmessagehandler.hpp" #include "providers/twitch/twitchaccount.hpp" #include "providers/twitch/twitchhelpers.hpp" @@ -93,12 +94,13 @@ void TwitchServer::privateMessageReceived(IrcPrivateMessage *message) TwitchMessageBuilder builder(chan.get(), message, args); if (!builder.isIgnored()) { - messages::MessagePtr _message = builder.build(); - if (_message->flags & messages::Message::Highlighted) { - this->mentionsChannel->addMessage(_message); + messages::MessagePtr msg = builder.build(); + if (msg->flags & messages::Message::Highlighted) { + this->mentionsChannel->addMessage(msg); + getApp()->highlights->addHighlight(msg); } - chan->addMessage(_message); + chan->addMessage(msg); } } diff --git a/src/widgets/basewidget.hpp b/src/widgets/basewidget.hpp index 998befca..6e6702a0 100644 --- a/src/widgets/basewidget.hpp +++ b/src/widgets/basewidget.hpp @@ -26,7 +26,7 @@ public: QSize getScaleIndependantSize() const; int getScaleIndependantWidth() const; int getScaleIndependantHeight() const; - void setScaleIndependantSize(int width, int yOffset); + void setScaleIndependantSize(int width, int height); void setScaleIndependantSize(QSize); void setScaleIndependantWidth(int value); void setScaleIndependantHeight(int value); diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index a1cd1042..50cbd73a 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -32,10 +32,16 @@ namespace chatterino { namespace widgets { -BaseWindow::BaseWindow(QWidget *parent, bool _enableCustomFrame) +BaseWindow::BaseWindow(QWidget *parent, Flags flags) : BaseWidget(parent, Qt::Window) - , enableCustomFrame(_enableCustomFrame) + , enableCustomFrame(flags & EnableCustomFrame) + , frameless(flags & FrameLess) { + if (this->frameless) { + this->enableCustomFrame = false; + this->setWindowFlag(Qt::FramelessWindowHint); + } + this->init(); } @@ -53,55 +59,57 @@ void BaseWindow::init() layout->setSpacing(0); this->setLayout(layout); { - QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout(); - buttonLayout->setMargin(0); - layout->addLayout(buttonLayout); + if (!this->frameless) { + QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout(); + buttonLayout->setMargin(0); + layout->addLayout(buttonLayout); - // title - QLabel *title = new QLabel(" Chatterino"); - QObject::connect(this, &QWidget::windowTitleChanged, - [title](const QString &text) { title->setText(" " + text); }); + // title + QLabel *title = new QLabel(" Chatterino"); + QObject::connect(this, &QWidget::windowTitleChanged, + [title](const QString &text) { title->setText(" " + text); }); - QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred); - policy.setHorizontalStretch(1); - // title->setBaseSize(0, 0); - title->setScaledContents(true); - title->setSizePolicy(policy); - buttonLayout->addWidget(title); - this->ui.titleLabel = title; + QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred); + policy.setHorizontalStretch(1); + // title->setBaseSize(0, 0); + title->setScaledContents(true); + title->setSizePolicy(policy); + buttonLayout->addWidget(title); + this->ui.titleLabel = title; - // buttons - TitleBarButton *_minButton = new TitleBarButton; - _minButton->setButtonStyle(TitleBarButton::Minimize); - TitleBarButton *_maxButton = new TitleBarButton; - _maxButton->setButtonStyle(TitleBarButton::Maximize); - TitleBarButton *_exitButton = new TitleBarButton; - _exitButton->setButtonStyle(TitleBarButton::Close); + // buttons + TitleBarButton *_minButton = new TitleBarButton; + _minButton->setButtonStyle(TitleBarButton::Minimize); + TitleBarButton *_maxButton = new TitleBarButton; + _maxButton->setButtonStyle(TitleBarButton::Maximize); + TitleBarButton *_exitButton = new TitleBarButton; + _exitButton->setButtonStyle(TitleBarButton::Close); - QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] { - this->setWindowState(Qt::WindowMinimized | this->windowState()); - }); - QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] { - this->setWindowState(this->windowState() == Qt::WindowMaximized - ? Qt::WindowActive - : Qt::WindowMaximized); - }); - QObject::connect(_exitButton, &TitleBarButton::clicked, this, - [this] { this->close(); }); + QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] { + this->setWindowState(Qt::WindowMinimized | this->windowState()); + }); + QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] { + this->setWindowState(this->windowState() == Qt::WindowMaximized + ? Qt::WindowActive + : Qt::WindowMaximized); + }); + QObject::connect(_exitButton, &TitleBarButton::clicked, this, + [this] { this->close(); }); - this->ui.minButton = _minButton; - this->ui.maxButton = _maxButton; - this->ui.exitButton = _exitButton; + this->ui.minButton = _minButton; + this->ui.maxButton = _maxButton; + this->ui.exitButton = _exitButton; - this->ui.buttons.push_back(_minButton); - this->ui.buttons.push_back(_maxButton); - this->ui.buttons.push_back(_exitButton); + this->ui.buttons.push_back(_minButton); + this->ui.buttons.push_back(_maxButton); + this->ui.buttons.push_back(_exitButton); - // buttonLayout->addStretch(1); - buttonLayout->addWidget(_minButton); - buttonLayout->addWidget(_maxButton); - buttonLayout->addWidget(_exitButton); - buttonLayout->setSpacing(0); + // buttonLayout->addStretch(1); + buttonLayout->addWidget(_minButton); + buttonLayout->addWidget(_maxButton); + buttonLayout->addWidget(_exitButton); + buttonLayout->setSpacing(0); + } } this->ui.layoutBase = new BaseWidget(this); layout->addWidget(this->ui.layoutBase); @@ -165,10 +173,12 @@ void BaseWindow::themeRefreshEvent() palette.setColor(QPalette::Foreground, this->themeManager->window.text); this->setPalette(palette); - QPalette palette_title; - palette_title.setColor(QPalette::Foreground, - this->themeManager->isLightTheme() ? "#333" : "#ccc"); - this->ui.titleLabel->setPalette(palette_title); + if (this->ui.titleLabel) { + QPalette palette_title; + palette_title.setColor(QPalette::Foreground, + this->themeManager->isLightTheme() ? "#333" : "#ccc"); + this->ui.titleLabel->setPalette(palette_title); + } for (RippleEffectButton *button : this->ui.buttons) { button->setMouseEffectColor(this->themeManager->window.text); @@ -212,7 +222,7 @@ void BaseWindow::changeEvent(QEvent *) TooltipWidget::getInstance()->hide(); #ifdef USEWINSDK - if (this->hasCustomWindowFrame()) { + if (this->ui.maxButton) { this->ui.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized ? TitleBarButton::Unmaximize : TitleBarButton::Maximize); @@ -449,13 +459,19 @@ void BaseWindow::calcButtonsSizes() return; } if ((this->width() / this->getScale()) < 300) { - this->ui.minButton->setScaleIndependantSize(30, 30); - this->ui.maxButton->setScaleIndependantSize(30, 30); - this->ui.exitButton->setScaleIndependantSize(30, 30); + if (this->ui.minButton) + this->ui.minButton->setScaleIndependantSize(30, 30); + if (this->ui.maxButton) + this->ui.maxButton->setScaleIndependantSize(30, 30); + if (this->ui.exitButton) + this->ui.exitButton->setScaleIndependantSize(30, 30); } else { - this->ui.minButton->setScaleIndependantSize(46, 30); - this->ui.maxButton->setScaleIndependantSize(46, 30); - this->ui.exitButton->setScaleIndependantSize(46, 30); + if (this->ui.minButton) + this->ui.minButton->setScaleIndependantSize(46, 30); + if (this->ui.maxButton) + this->ui.maxButton->setScaleIndependantSize(46, 30); + if (this->ui.exitButton) + this->ui.exitButton->setScaleIndependantSize(46, 30); } } } // namespace widgets diff --git a/src/widgets/basewindow.hpp b/src/widgets/basewindow.hpp index c1a65afb..9483f0a4 100644 --- a/src/widgets/basewindow.hpp +++ b/src/widgets/basewindow.hpp @@ -19,7 +19,9 @@ class BaseWindow : public BaseWidget Q_OBJECT public: - explicit BaseWindow(QWidget *parent = nullptr, bool enableCustomFrame = false); + enum Flags { None = 0, EnableCustomFrame = 1, FrameLess = 2 }; + + explicit BaseWindow(QWidget *parent = nullptr, Flags flags = None); QWidget *getLayoutContainer(); bool hasCustomWindowFrame(); @@ -51,16 +53,17 @@ private: void calcButtonsSizes(); bool enableCustomFrame; + bool frameless; bool stayInScreenRect = false; bool shown = false; struct { - QHBoxLayout *titlebarBox; - QWidget *titleLabel; + QHBoxLayout *titlebarBox = nullptr; + QWidget *titleLabel = nullptr; TitleBarButton *minButton = nullptr; TitleBarButton *maxButton = nullptr; TitleBarButton *exitButton = nullptr; - QWidget *layoutBase; + QWidget *layoutBase = nullptr; std::vector buttons; } ui; }; diff --git a/src/widgets/emotepopup.cpp b/src/widgets/emotepopup.cpp index 004cadce..d3787d58 100644 --- a/src/widgets/emotepopup.cpp +++ b/src/widgets/emotepopup.cpp @@ -17,7 +17,7 @@ namespace chatterino { namespace widgets { EmotePopup::EmotePopup() - : BaseWindow(nullptr, true) + : BaseWindow(nullptr, BaseWindow::EnableCustomFrame) { this->viewEmotes = new ChannelView(); this->viewEmojis = new ChannelView(); diff --git a/src/widgets/notificationpopup.cpp b/src/widgets/notificationpopup.cpp new file mode 100644 index 00000000..aa21c5ec --- /dev/null +++ b/src/widgets/notificationpopup.cpp @@ -0,0 +1,50 @@ +#include "notificationpopup.hpp" + +#include "widgets/helper/channelview.hpp" + +#include +#include +#include + +namespace chatterino { +namespace widgets { + +NotificationPopup::NotificationPopup() + : BaseWindow((QWidget *)nullptr, BaseWindow::FrameLess) + , channel(std::make_shared("notifications", Channel::None)) + +{ + this->channelView = new ChannelView(this); + + auto *layout = new QVBoxLayout(this); + this->setLayout(layout); + + layout->addWidget(this->channelView); + + this->channelView->setChannel(this->channel); + this->setScaleIndependantSize(300, 150); +} + +void NotificationPopup::updatePosition() +{ + Location location = BottomRight; + + QDesktopWidget *desktop = QApplication::desktop(); + const QRect rect = desktop->availableGeometry(); + + switch (location) { + case BottomRight: { + this->move(rect.right() - this->width(), rect.bottom() - this->height()); + } break; + } +} + +void NotificationPopup::addMessage(messages::MessagePtr msg) +{ + this->channel->addMessage(msg); + + // QTimer::singleShot(5000, this, [this, msg] { this->channel->remove }); +} + +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/notificationpopup.hpp b/src/widgets/notificationpopup.hpp new file mode 100644 index 00000000..ab7409fa --- /dev/null +++ b/src/widgets/notificationpopup.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "channel.hpp" +#include "messages/message.hpp" +#include "widgets/basewindow.hpp" + +namespace chatterino { +namespace widgets { + +class ChannelView; + +class NotificationPopup : public BaseWindow +{ +public: + enum Location { TopLeft, TopRight, BottomLeft, BottomRight }; + NotificationPopup(); + + void addMessage(messages::MessagePtr msg); + void updatePosition(); + +private: + ChannelView *channelView; + ChannelPtr channel; +}; + +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/selectchanneldialog.cpp b/src/widgets/selectchanneldialog.cpp index d444f4a8..6d1a810a 100644 --- a/src/widgets/selectchanneldialog.cpp +++ b/src/widgets/selectchanneldialog.cpp @@ -17,7 +17,7 @@ namespace chatterino { namespace widgets { SelectChannelDialog::SelectChannelDialog() - : BaseWindow((QWidget *)nullptr, true) + : BaseWindow((QWidget *)nullptr, BaseWindow::EnableCustomFrame) , selectedChannel(Channel::getEmpty()) { this->setWindowTitle("Select a channel to join"); diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index a3fa1477..46168a72 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -24,7 +24,7 @@ namespace chatterino { namespace widgets { Window::Window(WindowType _type) - : BaseWindow(nullptr, true) + : BaseWindow(nullptr, BaseWindow::EnableCustomFrame) , type(_type) , dpi(this->getScale()) , notebook(this) From 59110ad4bd4dd86d53161e06055f5e247eddf8d8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 08:58:34 +0200 Subject: [PATCH 040/121] added roommodes to the splitheader --- .../commands/commandcontroller.cpp | 2 +- src/providers/twitch/ircmessagehandler.cpp | 63 ++++++++++++------- src/providers/twitch/twitchchannel.cpp | 31 ++++++++- src/providers/twitch/twitchchannel.hpp | 26 +++++--- src/providers/twitch/twitchhelpers.cpp | 2 +- src/providers/twitch/twitchhelpers.hpp | 2 +- src/providers/twitch/twitchserver.cpp | 2 +- src/singletons/fontmanager.cpp | 1 - src/widgets/helper/splitheader.cpp | 51 ++++++++++++++- src/widgets/helper/splitheader.hpp | 2 + src/widgets/split.cpp | 10 +-- src/widgets/split.hpp | 2 + 12 files changed, 148 insertions(+), 46 deletions(-) diff --git a/src/controllers/commands/commandcontroller.cpp b/src/controllers/commands/commandcontroller.cpp index 9b897f90..4265a35e 100644 --- a/src/controllers/commands/commandcontroller.cpp +++ b/src/controllers/commands/commandcontroller.cpp @@ -110,7 +110,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, return ""; } else if (commandName == "/uptime") { - const auto &streamStatus = twitchChannel->GetStreamStatus(); + const auto &streamStatus = twitchChannel->getStreamStatus(); QString messageText = streamStatus.live ? streamStatus.uptime : "Channel is not live."; diff --git a/src/providers/twitch/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp index 3d199e97..a8b22649 100644 --- a/src/providers/twitch/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -27,34 +27,48 @@ IrcMessageHandler &IrcMessageHandler::getInstance() void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) { const auto &tags = message->tags(); - auto iterator = tags.find("room-id"); + auto app = getApp(); - if (iterator != tags.end()) { - auto roomID = iterator.value().toString(); + // get twitch channel + QString chanName; + if (!trimChannelName(message->parameter(0), chanName)) { + return; + } + auto chan = app->twitch.server->getChannelOrEmpty(chanName); + TwitchChannel *twitchChannel = dynamic_cast(chan.get()); - QStringList words = QString(message->toData()).split("#"); + if (twitchChannel) { + // room-id + decltype(tags.find("xD")) it; - // ensure the format is valid - if (words.length() < 2) { - return; - } + if ((it = tags.find("room-id")) != tags.end()) { + auto roomID = it.value().toString(); - auto app = getApp(); - - QString channelName = words.at(1); - - auto channel = app->twitch.server->getChannelOrEmpty(channelName); - - if (channel->isEmpty()) { - return; - } - - if (auto twitchChannel = dynamic_cast(channel.get())) { - // set the room id of the channel twitchChannel->setRoomID(roomID); + + app->resources->loadChannelData(roomID); } - app->resources->loadChannelData(roomID); + // Room modes + TwitchChannel::RoomModes roomModes = twitchChannel->getRoomModes(); + + if ((it = tags.find("emote-only")) != tags.end()) { + roomModes.emoteOnly = it.value() == "1"; + } + if ((it = tags.find("subs-only")) != tags.end()) { + roomModes.submode = it.value() == "1"; + } + if ((it = tags.find("slow")) != tags.end()) { + roomModes.slowMode = it.value().toInt(); + } + if ((it = tags.find("r9k")) != tags.end()) { + roomModes.r9k = it.value() == "1"; + } + if ((it = tags.find("broadcaster-lang")) != tags.end()) { + roomModes.broadcasterLang = it.value().toString(); + } + + twitchChannel->setRoomModes(roomModes); } } @@ -66,7 +80,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) } QString chanName; - if (!TrimChannelName(message->parameter(0), chanName)) { + if (!trimChannelName(message->parameter(0), chanName)) { return; } @@ -116,7 +130,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message) auto app = getApp(); QString channelName; - if (!TrimChannelName(message->parameter(0), channelName)) { + if (!trimChannelName(message->parameter(0), channelName)) { return; } @@ -203,7 +217,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) // auto channel = app->twitch.server->getChannelOrEmpty(channelName); // if (channel->isEmpty()) { - // debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager", + // debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel + // manager", // channelName); // return; // } diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index 2b7300d2..ab663ad3 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -86,7 +86,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection }; auto doRefreshChatters = [=]() { - const auto streamStatus = this->GetStreamStatus(); + const auto streamStatus = this->getStreamStatus(); if (app->settings->onlyFetchChattersForSmallerStreamers) { if (streamStatus.live && streamStatus.viewerCount > app->settings->smallStreamerLimit) { @@ -213,6 +213,35 @@ void TwitchChannel::addRecentChatter(const std::shared_ptr &m this->completionModel.addUser(message->displayName); } +TwitchChannel::RoomModes TwitchChannel::getRoomModes() +{ + std::lock_guard lock(this->roomModeMutex); + + return this->roomModes; +} + +void TwitchChannel::setRoomModes(const RoomModes &_roomModes) +{ + { + std::lock_guard lock(this->roomModeMutex); + this->roomModes = _roomModes; + } + + this->roomModesChanged.invoke(); +} + +bool TwitchChannel::isLive() const +{ + std::lock_guard lock(this->streamStatusMutex); + return this->streamStatus.live; +} + +TwitchChannel::StreamStatus TwitchChannel::getStreamStatus() const +{ + std::lock_guard lock(this->streamStatusMutex); + return this->streamStatus; +} + void TwitchChannel::setLive(bool newLiveStatus) { bool gotNewLiveStatus = false; diff --git a/src/providers/twitch/twitchchannel.hpp b/src/providers/twitch/twitchchannel.hpp index 4e5531d7..a594bdf7 100644 --- a/src/providers/twitch/twitchchannel.hpp +++ b/src/providers/twitch/twitchchannel.hpp @@ -38,6 +38,15 @@ public: bool broadcaster; }; + struct RoomModes { + bool submode = false; + bool r9k = false; + bool emoteOnly = false; + // int folowerOnly = 0; + int slowMode = 0; + QString broadcasterLang; + }; + ~TwitchChannel() final; void reloadChannelEmotes(); @@ -66,25 +75,20 @@ public: pajlada::Signals::NoArgBoltSignal fetchMessages; pajlada::Signals::NoArgSignal userStateChanged; + pajlada::Signals::NoArgSignal roomModesChanged; QString roomID; + RoomModes getRoomModes(); + void setRoomModes(const RoomModes &roomModes); - StreamStatus GetStreamStatus() const - { - std::lock_guard lock(this->streamStatusMutex); - return this->streamStatus; - } + StreamStatus getStreamStatus() const; struct NameOptions { QString displayName; QString localizedName; }; - bool IsLive() const - { - std::lock_guard lock(this->streamStatusMutex); - return this->streamStatus.live; - } + bool isLive() const; private: explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection); @@ -103,6 +107,8 @@ private: bool mod; QByteArray messageSuffix; QString lastSentMessage; + RoomModes roomModes; + std::mutex roomModeMutex; Communi::IrcConnection *readConnection; diff --git a/src/providers/twitch/twitchhelpers.cpp b/src/providers/twitch/twitchhelpers.cpp index 4f6f663a..98140c6c 100644 --- a/src/providers/twitch/twitchhelpers.cpp +++ b/src/providers/twitch/twitchhelpers.cpp @@ -5,7 +5,7 @@ namespace chatterino { namespace providers { namespace twitch { -bool TrimChannelName(const QString &channelName, QString &outChannelName) +bool trimChannelName(const QString &channelName, QString &outChannelName) { if (channelName.length() < 3) { debug::Log("channel name length below 3"); diff --git a/src/providers/twitch/twitchhelpers.hpp b/src/providers/twitch/twitchhelpers.hpp index 997821d6..56f0bb94 100644 --- a/src/providers/twitch/twitchhelpers.hpp +++ b/src/providers/twitch/twitchhelpers.hpp @@ -6,7 +6,7 @@ namespace chatterino { namespace providers { namespace twitch { -bool TrimChannelName(const QString &channelName, QString &outChannelName); +bool trimChannelName(const QString &channelName, QString &outChannelName); } // namespace twitch } // namespace providers diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp index 41d42cb7..ab184535 100644 --- a/src/providers/twitch/twitchserver.cpp +++ b/src/providers/twitch/twitchserver.cpp @@ -78,7 +78,7 @@ std::shared_ptr TwitchServer::createChannel(const QString &channelName) void TwitchServer::privateMessageReceived(IrcPrivateMessage *message) { QString channelName; - if (!TrimChannelName(message->target(), channelName)) { + if (!trimChannelName(message->target(), channelName)) { return; } diff --git a/src/singletons/fontmanager.cpp b/src/singletons/fontmanager.cpp index b25bc8b2..49d00c1e 100644 --- a/src/singletons/fontmanager.cpp +++ b/src/singletons/fontmanager.cpp @@ -83,7 +83,6 @@ FontManager::FontData &FontManager::getOrCreateFontData(Type type, float scale) if (it != map.end()) { // return if found - qDebug() << it->second.font; return it->second; } diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 7264f81a..617b2265 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -63,6 +63,16 @@ SplitHeader::SplitHeader(Split *_split) layout->addStretch(1); + // mode button + auto mode = layout.emplace(this).assign(&this->modeButton); + + mode->getLabel().setText("dank\nmemes"); + + // QObject::connect(mode.getElement(), &RippleEffectButton::clicked, this, [this] + // { + // // + // }); + // moderation mode auto moderator = layout.emplace(this).assign(&this->moderationButton); @@ -167,7 +177,7 @@ void SplitHeader::updateChannelText() TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel != nullptr) { - const auto &streamStatus = twitchChannel->GetStreamStatus(); + const auto &streamStatus = twitchChannel->getStreamStatus(); if (streamStatus.live) { this->isLive = true; @@ -216,6 +226,45 @@ void SplitHeader::updateModerationModeIcon() this->moderationButton->setVisible(modButtonVisible); } +void SplitHeader::updateModes() +{ + TwitchChannel *tc = dynamic_cast(this->split->getChannel().get()); + if (tc == nullptr) { + return; + } + + TwitchChannel::RoomModes roomModes = tc->getRoomModes(); + + QString text; + + if (roomModes.r9k) { + text += "r9k, "; + } + if (roomModes.slowMode) { + text += QString("slow(%1), ").arg(QString::number(roomModes.slowMode)); + } + if (roomModes.emoteOnly) { + text += "emote, "; + } + if (roomModes.submode) { + text += "sub, "; + } + + if (text.length() > 2) { + text = text.mid(0, text.size() - 2); + } + + qDebug() << text; + + static QRegularExpression commaReplacement("^.+?, .+?,( ).+$"); + QRegularExpressionMatch match = commaReplacement.match(text); + if (match.hasMatch()) { + text = text.mid(0, match.capturedStart(1)) + '\n' + text.mid(match.capturedEnd(1)); + } + + this->modeButton->getLabel().setText(text); +} + void SplitHeader::paintEvent(QPaintEvent *) { QPainter painter(this); diff --git a/src/widgets/helper/splitheader.hpp b/src/widgets/helper/splitheader.hpp index 167d5e7f..ff6e0047 100644 --- a/src/widgets/helper/splitheader.hpp +++ b/src/widgets/helper/splitheader.hpp @@ -34,6 +34,7 @@ public: // Update channel text from chat widget void updateChannelText(); void updateModerationModeIcon(); + void updateModes(); protected: virtual void scaleChangedEvent(float) override; @@ -59,6 +60,7 @@ private: RippleEffectButton *dropdownButton; // Label *titleLabel; SignalLabel *titleLabel; + RippleEffectLabel *modeButton; RippleEffectButton *moderationButton; QMenu dropdownMenu; diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 2f30d4e7..c3f9f1e9 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -129,15 +129,10 @@ Split::Split(QWidget *parent) this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); this->managedConnect(modifierStatusChanged, [this](Qt::KeyboardModifiers status) { - - qDebug() << "xD" << status; - if ((status == Qt::AltModifier || status == (Qt::AltModifier | Qt::ControlModifier)) && this->isMouseOver) { - qDebug() << "show"; this->overlay->show(); } else { - qDebug() << "hide"; this->overlay->hide(); } }); @@ -187,6 +182,7 @@ void Split::setChannel(IndirectChannel newChannel) this->view.setChannel(newChannel.get()); this->usermodeChangedConnection.disconnect(); + this->roomModeChangedConnection.disconnect(); this->indirectChannelChangedConnection.disconnect(); TwitchChannel *tc = dynamic_cast(newChannel.get().get()); @@ -194,6 +190,9 @@ void Split::setChannel(IndirectChannel newChannel) if (tc != nullptr) { this->usermodeChangedConnection = tc->userStateChanged.connect([this] { this->header.updateModerationModeIcon(); }); + + this->roomModeChangedConnection = + tc->roomModesChanged.connect([this] { this->header.updateModes(); }); } this->indirectChannelChangedConnection = newChannel.getChannelChanged().connect([this] { // @@ -202,6 +201,7 @@ void Split::setChannel(IndirectChannel newChannel) this->header.updateModerationModeIcon(); this->header.updateChannelText(); + this->header.updateModes(); this->channelChanged.invoke(); } diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 45a09d88..41257550 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -100,6 +100,8 @@ private: pajlada::Signals::Connection channelIDChangedConnection; pajlada::Signals::Connection usermodeChangedConnection; + pajlada::Signals::Connection roomModeChangedConnection; + pajlada::Signals::Connection indirectChannelChangedConnection; std::vector managedConnections; From abc2b9724f2a3b34e5ef070daa2b0787b58332a7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 10:03:07 +0200 Subject: [PATCH 041/121] fixed tooltips not showing over topmost windows on windows --- src/application.cpp | 4 ++-- src/widgets/basewindow.cpp | 37 ++++++++++++++++++++---------- src/widgets/basewindow.hpp | 5 +++- src/widgets/helper/channelview.cpp | 3 ++- src/widgets/helper/splitheader.cpp | 18 +++++++++------ src/widgets/tooltipwidget.cpp | 19 +++++++++++---- src/widgets/tooltipwidget.hpp | 4 ++++ 7 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/application.cpp b/src/application.cpp index 5d1bb688..7a4bc845 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -61,6 +61,7 @@ void Application::construct() isAppConstructed = true; // 1. Instantiate all classes + this->settings = new singletons::SettingManager; this->paths = new singletons::PathManager(this->argc, this->argv); this->themes = new singletons::ThemeManager; this->windows = new singletons::WindowManager; @@ -70,7 +71,6 @@ void Application::construct() this->ignores = new controllers::ignores::IgnoreController; this->accounts = new singletons::AccountManager; this->emotes = new singletons::EmoteManager; - this->settings = new singletons::SettingManager; this->fonts = new singletons::FontManager; this->resources = new singletons::ResourceManager; @@ -92,12 +92,12 @@ void Application::initialize() // 2. Initialize/load classes this->settings->initialize(); - this->windows->initialize(); this->nativeMessaging->registerHost(); this->settings->load(); this->commands->load(); + this->windows->initialize(); this->resources->initialize(); diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index 50cbd73a..139a42e9 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -32,10 +32,12 @@ namespace chatterino { namespace widgets { -BaseWindow::BaseWindow(QWidget *parent, Flags flags) - : BaseWidget(parent, Qt::Window) - , enableCustomFrame(flags & EnableCustomFrame) - , frameless(flags & FrameLess) +BaseWindow::BaseWindow(QWidget *parent, Flags _flags) + : BaseWidget(parent, + Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : (Qt::WindowFlags)0)) + , enableCustomFrame(_flags & EnableCustomFrame) + , frameless(_flags & FrameLess) + , flags(_flags) { if (this->frameless) { this->enableCustomFrame = false; @@ -45,6 +47,11 @@ BaseWindow::BaseWindow(QWidget *parent, Flags flags) this->init(); } +BaseWindow::Flags BaseWindow::getFlags() +{ + return this->flags; +} + void BaseWindow::init() { auto app = getApp(); @@ -124,14 +131,19 @@ void BaseWindow::init() #endif #ifdef USEWINSDK - app->settings->windowTopMost.connect([this](bool topMost, auto) { - ::SetWindowPos((HWND)this->winId(), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE); + // fourtf: don't ask me why we need to delay this + QTimer::singleShot(1, this, [this] { + if (!(this->flags & Flags::TopMost)) { + getApp()->settings->windowTopMost.connect([this](bool topMost, auto) { + ::SetWindowPos((HWND)this->winId(), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, + 0, 0, SWP_NOMOVE | SWP_NOSIZE); + }); + } }); #else - if (app->settings->windowTopMost.getValue()) { - this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint); - } +// if (getApp()->settings->windowTopMost.getValue()) { +// this->setWindowFlag(Qt::WindowStaysOnTopHint); +// } #endif } @@ -425,8 +437,9 @@ void BaseWindow::showEvent(QShowEvent *event) { if (!this->shown && this->isVisible() && this->hasCustomWindowFrame()) { this->shown = true; - SetWindowLongPtr((HWND)this->winId(), GWL_STYLE, - WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX); + // SetWindowLongPtr((HWND)this->winId(), GWL_STYLE, + // WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | + // WS_MINIMIZEBOX); const MARGINS shadow = {8, 8, 8, 8}; DwmExtendFrameIntoClientArea((HWND)this->winId(), &shadow); diff --git a/src/widgets/basewindow.hpp b/src/widgets/basewindow.hpp index 9483f0a4..2aef9020 100644 --- a/src/widgets/basewindow.hpp +++ b/src/widgets/basewindow.hpp @@ -19,7 +19,7 @@ class BaseWindow : public BaseWidget Q_OBJECT public: - enum Flags { None = 0, EnableCustomFrame = 1, FrameLess = 2 }; + enum Flags { None = 0, EnableCustomFrame = 1, FrameLess = 2, TopMost = 4 }; explicit BaseWindow(QWidget *parent = nullptr, Flags flags = None); @@ -33,6 +33,8 @@ public: void moveTo(QWidget *widget, QPoint point); + Flags getFlags(); + protected: #ifdef USEWINSDK void showEvent(QShowEvent *) override; @@ -56,6 +58,7 @@ private: bool frameless; bool stayInScreenRect = false; bool shown = false; + Flags flags; struct { QHBoxLayout *titlebarBox = nullptr; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 67393db8..ecabe9fc 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -29,7 +29,7 @@ #define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale()) #define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3 -#define CHAT_HOVER_PAUSE_DURATION 300 +#define CHAT_HOVER_PAUSE_DURATION 400 using namespace chatterino::messages; using namespace chatterino::providers::twitch; @@ -800,6 +800,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) tooltipWidget->moveTo(this, event->globalPos()); tooltipWidget->setText(tooltip); tooltipWidget->show(); + tooltipWidget->raise(); } // check if word has a link diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 617b2265..72d8b07a 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -254,15 +254,18 @@ void SplitHeader::updateModes() text = text.mid(0, text.size() - 2); } - qDebug() << text; + if (text.isEmpty()) { + this->modeButton->hide(); + } else { + static QRegularExpression commaReplacement("^.+?, .+?,( ).+$"); + QRegularExpressionMatch match = commaReplacement.match(text); + if (match.hasMatch()) { + text = text.mid(0, match.capturedStart(1)) + '\n' + text.mid(match.capturedEnd(1)); + } - static QRegularExpression commaReplacement("^.+?, .+?,( ).+$"); - QRegularExpressionMatch match = commaReplacement.match(text); - if (match.hasMatch()) { - text = text.mid(0, match.capturedStart(1)) + '\n' + text.mid(match.capturedEnd(1)); + this->modeButton->getLabel().setText(text); + this->modeButton->show(); } - - this->modeButton->getLabel().setText(text); } void SplitHeader::paintEvent(QPaintEvent *) @@ -326,6 +329,7 @@ void SplitHeader::mouseMoveEvent(QMouseEvent *event) tooltipWidget->moveTo(this, event->globalPos()); tooltipWidget->setText(tooltip); tooltipWidget->show(); + tooltipWidget->raise(); } if (this->dragging) { diff --git a/src/widgets/tooltipwidget.cpp b/src/widgets/tooltipwidget.cpp index 6baa8052..e960bbd4 100644 --- a/src/widgets/tooltipwidget.cpp +++ b/src/widgets/tooltipwidget.cpp @@ -9,11 +9,15 @@ #include #include +#ifdef USEWINSDK +#include +#endif + namespace chatterino { namespace widgets { TooltipWidget::TooltipWidget(BaseWidget *parent) - : BaseWindow(parent) + : BaseWindow(parent, BaseWindow::TopMost) , displayText(new QLabel()) { auto app = getApp(); @@ -24,9 +28,8 @@ TooltipWidget::TooltipWidget(BaseWidget *parent) this->setStayInScreenRect(true); this->setAttribute(Qt::WA_ShowWithoutActivating); - this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | - Qt::X11BypassWindowManagerHint | Qt::BypassWindowManagerHint | - Qt::SubWindow); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | + Qt::BypassWindowManagerHint); displayText->setAlignment(Qt::AlignHCenter); displayText->setText("tooltip text"); @@ -43,6 +46,14 @@ 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::themeRefreshEvent() { this->setStyleSheet("color: #fff; background: #000"); diff --git a/src/widgets/tooltipwidget.hpp b/src/widgets/tooltipwidget.hpp index 90259e7b..fd7aa41d 100644 --- a/src/widgets/tooltipwidget.hpp +++ b/src/widgets/tooltipwidget.hpp @@ -28,6 +28,10 @@ public: return tooltipWidget; } +#ifdef USEWINSDK + void raise(); +#endif + protected: void changeEvent(QEvent *) override; void leaveEvent(QEvent *) override; From 2ac9b4d0e7592ec3c9674a668e507133a413c3ef Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 10:07:31 +0200 Subject: [PATCH 042/121] minor addition to the last commit --- src/widgets/helper/splitheader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 72d8b07a..0dc58e4e 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -66,7 +66,7 @@ SplitHeader::SplitHeader(Split *_split) // mode button auto mode = layout.emplace(this).assign(&this->modeButton); - mode->getLabel().setText("dank\nmemes"); + mode->hide(); // QObject::connect(mode.getElement(), &RippleEffectButton::clicked, this, [this] // { @@ -230,6 +230,7 @@ void SplitHeader::updateModes() { TwitchChannel *tc = dynamic_cast(this->split->getChannel().get()); if (tc == nullptr) { + this->modeButton->hide(); return; } @@ -310,6 +311,7 @@ void SplitHeader::mouseReleaseEvent(QMouseEvent *event) widget->setAttribute(Qt::WA_DeleteOnClose); widget->move(pos); widget->show(); + widget->raise(); QTimer::singleShot(3000, widget, [this, widget] { widget->close(); From 4de2a6b65fc3fc0915065f95148da0907ede7850 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 11:35:50 +0200 Subject: [PATCH 043/121] added a setting to collapse long messages by default --- src/messages/layouts/messagelayout.cpp | 20 ++++++-- src/messages/layouts/messagelayout.hpp | 4 +- .../layouts/messagelayoutcontainer.cpp | 48 +++++++++++++++++-- .../layouts/messagelayoutcontainer.hpp | 9 +++- src/providers/twitch/twitchmessagebuilder.cpp | 14 +++--- src/singletons/settingsmanager.hpp | 1 + src/widgets/helper/channelview.cpp | 10 ++-- src/widgets/settingspages/appearancepage.cpp | 6 +++ 8 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index 96151114..f92d4b03 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -107,12 +107,20 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) return true; } -void MessageLayout::actuallyLayout(int width, MessageElement::Flags flags) +void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) { - this->container.begin(width, this->scale, this->message->flags.value); + auto messageFlags = this->message->flags.value; + + if (this->flags & MessageLayout::Expanded || + (_flags & MessageElement::ModeratorTools && + !(this->message->flags & Message::MessageFlags::Disabled))) { + messageFlags = (Message::MessageFlags)(messageFlags & ~Message::MessageFlags::Collapsed); + } + + this->container.begin(width, this->scale, messageFlags); for (const std::unique_ptr &element : this->message->getElements()) { - element->addToContainer(this->container, flags); + element->addToContainer(this->container, _flags); } if (this->height != this->container.getHeight()) { @@ -121,6 +129,12 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags flags) this->container.end(); this->height = this->container.getHeight(); + + // collapsed state + this->flags &= ~Flags::Collapsed; + if (this->container.isCollapsed()) { + this->flags |= Flags::Collapsed; + } } // Painting diff --git a/src/messages/layouts/messagelayout.hpp b/src/messages/layouts/messagelayout.hpp index b6f045a8..9e329fd1 100644 --- a/src/messages/layouts/messagelayout.hpp +++ b/src/messages/layouts/messagelayout.hpp @@ -22,7 +22,9 @@ public: enum Flags : uint8_t { RequiresBufferUpdate = 1 << 1, RequiresLayout = 1 << 2, - AlternateBackground = 1 << 3 + AlternateBackground = 1 << 3, + Collapsed = 1 << 4, + Expanded = 1 << 5, }; MessageLayout(MessagePtr message); diff --git a/src/messages/layouts/messagelayoutcontainer.cpp b/src/messages/layouts/messagelayoutcontainer.cpp index e5e3b474..16b109a3 100644 --- a/src/messages/layouts/messagelayoutcontainer.cpp +++ b/src/messages/layouts/messagelayoutcontainer.cpp @@ -9,6 +9,7 @@ #include #define COMPACT_EMOTES_OFFSET 6 +#define MAX_UNCOLLAPSED_LINES 3 namespace chatterino { namespace messages { @@ -39,6 +40,9 @@ void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFla auto mediumFontMetrics = getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, _scale); this->textLineHeight = mediumFontMetrics.height(); this->spaceWidth = mediumFontMetrics.width(' '); + this->dotdotdotWidth = mediumFontMetrics.width("..."); + this->_canAddMessages = true; + this->_isCollapsed = false; } void MessageLayoutContainer::clear() @@ -71,12 +75,12 @@ void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element bool MessageLayoutContainer::canAddElements() { - return !(this->flags & Message::MessageFlags::Collapsed && line >= 3); + return this->_canAddMessages; } -void MessageLayoutContainer::_addElement(MessageLayoutElement *element) +void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool forceAdd) { - if (!this->canAddElements()) { + if (!this->canAddElements() && !forceAdd) { delete element; return; } @@ -154,12 +158,18 @@ void MessageLayoutContainer::breakLine() this->lineStart = this->elements.size(); // this->currentX = (int)(this->scale * 8); + + if (this->canCollapse() && line + 1 >= MAX_UNCOLLAPSED_LINES) { + this->_canAddMessages = false; + return; + } + this->currentX = 0; this->currentY += this->lineHeight; this->height = this->currentY + (this->margin.bottom * this->scale); this->lineHeight = 0; this->line++; -} // namespace layouts +} bool MessageLayoutContainer::atStartOfLine() { @@ -168,15 +178,32 @@ bool MessageLayoutContainer::atStartOfLine() bool MessageLayoutContainer::fitsInLine(int _width) { - return this->currentX + _width <= this->width - this->margin.left - this->margin.right; + return this->currentX + _width <= + (this->width - this->margin.left - this->margin.right - + (this->line + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth : 0)); } void MessageLayoutContainer::end() { + if (!this->canAddElements()) { + static TextElement dotdotdot("...", MessageElement::Collapsed, MessageColor::Link); + static QString dotdotdotText("..."); + + auto *element = new TextLayoutElement( + dotdotdot, dotdotdotText, QSize(this->dotdotdotWidth, this->textLineHeight), + QColor("#00D80A"), FontStyle::ChatMediumBold, this->scale); + + // getApp()->themes->messages.textColors.system + this->_addElement(element, true); + this->_isCollapsed = true; + } + if (!this->atStartOfLine()) { this->breakLine(); } + this->height += this->lineHeight; + if (this->lines.size() != 0) { this->lines[0].rect.setTop(-100000); this->lines.back().rect.setBottom(100000); @@ -185,6 +212,17 @@ void MessageLayoutContainer::end() } } +bool MessageLayoutContainer::canCollapse() +{ + return getApp()->settings->collapseLongMessages.getValue() && + this->flags & Message::MessageFlags::Collapsed; +} + +bool MessageLayoutContainer::isCollapsed() +{ + return this->_isCollapsed; +} + MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point) { for (std::unique_ptr &element : this->elements) { diff --git a/src/messages/layouts/messagelayoutcontainer.hpp b/src/messages/layouts/messagelayoutcontainer.hpp index b55c1427..91e4df1d 100644 --- a/src/messages/layouts/messagelayoutcontainer.hpp +++ b/src/messages/layouts/messagelayoutcontainer.hpp @@ -76,6 +76,8 @@ struct MessageLayoutContainer { int getLastCharacterIndex() const; void addSelectionText(QString &str, int from, int to); + bool isCollapsed(); + private: struct Line { int startIndex; @@ -86,7 +88,7 @@ private: }; // helpers - void _addElement(MessageLayoutElement *element); + void _addElement(MessageLayoutElement *element, bool forceAdd = false); // variables float scale = 1.f; @@ -101,6 +103,11 @@ private: int lineHeight = 0; int spaceWidth = 4; int textLineHeight = 0; + int dotdotdotWidth = 0; + bool _canAddMessages = true; + bool _isCollapsed = false; + + bool canCollapse(); std::vector> elements; std::vector lines; diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 2c628273..07e2dee0 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -97,12 +97,14 @@ MessagePtr TwitchMessageBuilder::build() // PARSING this->parseUsername(); -#ifdef XD - if (this->originalMessage.length() > 100) { - this->message->flags |= Message::Collapsed; - this->emplace(getApp()->resources->badgeCollapsed, MessageElement::Collapsed); - } -#endif + //#ifdef XD + // if (this->originalMessage.length() > 100) { + // this->message->flags |= Message::Collapsed; + // this->emplace(getApp()->resources->badgeCollapsed, + // MessageElement::Collapsed); + // } + //#endif + this->message->flags |= Message::Collapsed; // PARSING this->parseMessageID(); diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index f9b2fcbd..dcaad852 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -40,6 +40,7 @@ public: BoolSetting hideEmptyInput = {"/appearance/hideEmptyInputBox", false}; BoolSetting showMessageLength = {"/appearance/messages/showMessageLength", false}; BoolSetting seperateMessages = {"/appearance/messages/separateMessages", false}; + BoolSetting collapseLongMessages = {"/appearance/messages/collapseLongMessages", false}; BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground", false}; BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false}; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index ecabe9fc..33d2eb91 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -778,7 +778,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } // message under cursor is collapsed - if (layout->getMessage()->flags & Message::Collapsed) { + if (layout->flags & MessageLayout::Collapsed) { this->setCursor(Qt::PointingHandCursor); tooltipWidget->hide(); return; @@ -856,7 +856,7 @@ void ChannelView::mousePressEvent(QMouseEvent *event) } // check if message is collapsed - if (layout->getMessage()->flags & Message::Collapsed) { + if (layout->flags & MessageLayout::Collapsed) { return; } @@ -923,8 +923,8 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) } // message under cursor is collapsed - if (layout->getMessage()->flags & Message::MessageFlags::Collapsed) { - layout->getMessage()->flags &= ~Message::MessageFlags::Collapsed; + if (layout->flags & MessageLayout::Collapsed) { + layout->flags |= MessageLayout::Expanded; this->layoutMessages(); return; @@ -1038,7 +1038,7 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event) } // message under cursor is collapsed - if (layout->getMessage()->flags & Message::Collapsed) { + if (layout->flags & MessageLayout::Collapsed) { return; } diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp index 0209f031..1b4472da 100644 --- a/src/widgets/settingspages/appearancepage.cpp +++ b/src/widgets/settingspages/appearancepage.cpp @@ -77,6 +77,12 @@ AppearancePage::AppearancePage() } messages.append(this->createCheckBox("Show badges", app->settings->showBadges)); + + auto *collapseMessages = this->createCheckBox("Collapse large messages (3+ lines)", + app->settings->collapseLongMessages); + QObject::connect(collapseMessages, &QCheckBox::toggled, + [] { getApp()->windows->layoutVisibleChatWidgets(); }); + messages.append(collapseMessages); { auto checkbox = this->createCheckBox("Seperate messages", app->settings->seperateMessages); From 02b8c34de8723d51dd60fc4bee1498df3390b42e Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 15:42:06 +0200 Subject: [PATCH 044/121] added tooltip to bit badges --- src/application.cpp | 6 +++--- src/providers/twitch/twitchmessagebuilder.cpp | 14 ++++---------- src/singletons/resourcemanager.hpp | 1 + 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/application.cpp b/src/application.cpp index 7a4bc845..36edd203 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -34,7 +34,7 @@ namespace { bool isBigEndian() { int test = 1; - char *p = (char *)&test; + char *p = reinterpret_cast(&test); return p[0] == 0; } @@ -268,11 +268,11 @@ void Application::runNativeMessagingHost() } #endif - char *b = (char *)malloc(size + 1); + char *b = reinterpret_cast(malloc(size + 1)); std::cin.read(b, size); *(b + size) = '\0'; - app->nativeMessaging->sendToGuiProcess(QByteArray(b, size)); + app->nativeMessaging->sendToGuiProcess(QByteArray(b, static_cast(size))); free(b); } diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 07e2dee0..ca81711f 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -154,15 +154,8 @@ MessagePtr TwitchMessageBuilder::build() this->appendTwitchEmote(ircMessage, emote, twitchEmotes); } - struct { - bool operator()(const std::pair &lhs, - const std::pair &rhs) - { - return lhs.first < rhs.first; - } - } customLess; - - std::sort(twitchEmotes.begin(), twitchEmotes.end(), customLess); + std::sort(twitchEmotes.begin(), twitchEmotes.end(), + [](const auto &a, const auto &b) { return a.first < b.first; }); } auto currentTwitchEmote = twitchEmotes.begin(); @@ -588,7 +581,8 @@ void TwitchMessageBuilder::appendTwitchBadges() // Try to fetch channel-specific bit badge try { const auto &badge = channelResources.badgeSets.at("bits").versions.at(versionKey); - this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity); + this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) + ->setTooltip(QString("Twitch Bits (") + cheerAmountQS + ")"); continue; } catch (const std::out_of_range &) { // Channel does not contain a special bit badge for this version diff --git a/src/singletons/resourcemanager.hpp b/src/singletons/resourcemanager.hpp index 2da50658..487b0f41 100644 --- a/src/singletons/resourcemanager.hpp +++ b/src/singletons/resourcemanager.hpp @@ -2,6 +2,7 @@ #include "util/emotemap.hpp" +#include #include #include From fecca83312436946eff743ff2a5a3586fb63a754 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 16:06:34 +0200 Subject: [PATCH 045/121] followup to the last commit --- src/providers/twitch/twitchmessagebuilder.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index ca81711f..bdfaabf2 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -577,12 +577,13 @@ void TwitchMessageBuilder::appendTwitchBadges() QString cheerAmountQS = badge.mid(5); std::string versionKey = cheerAmountQS.toStdString(); + QString tooltip = QString("Twitch Bits (") + cheerAmountQS + ")"; // Try to fetch channel-specific bit badge try { const auto &badge = channelResources.badgeSets.at("bits").versions.at(versionKey); this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) - ->setTooltip(QString("Twitch Bits (") + cheerAmountQS + ")"); + ->setTooltip(tooltip); continue; } catch (const std::out_of_range &) { // Channel does not contain a special bit badge for this version @@ -591,7 +592,8 @@ void TwitchMessageBuilder::appendTwitchBadges() // Use default bit badge try { const auto &badge = app->resources->badgeSets.at("bits").versions.at(versionKey); - this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity); + this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) + ->setTooltip(tooltip); } catch (const std::out_of_range &) { debug::Log("No default bit badge for version {} found", versionKey); continue; From 835b6d80da5cc8afcdef92c5e908bd31034d0c8e Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 17:13:46 +0200 Subject: [PATCH 046/121] fixed loading issues of saved split layout --- src/singletons/windowmanager.hpp | 4 +--- src/widgets/splitcontainer.cpp | 29 +++++++++++++++++++---------- src/widgets/splitcontainer.hpp | 20 +++++++++++--------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/singletons/windowmanager.hpp b/src/singletons/windowmanager.hpp index 5ff7b2b4..d17452bf 100644 --- a/src/singletons/windowmanager.hpp +++ b/src/singletons/windowmanager.hpp @@ -4,7 +4,7 @@ namespace chatterino { namespace widgets { -class SplitContainer::Node; +struct SplitContainer::Node; } namespace singletons { @@ -46,8 +46,6 @@ private: widgets::Window *selectedWindow = nullptr; void encodeNodeRecusively(widgets::SplitContainer::Node *node, QJsonObject &obj); - void decodeNodeRecusively(widgets::SplitContainer *container, - widgets::SplitContainer::Node *node, QJsonObject &obj, bool vertical); public: static void encodeChannel(IndirectChannel channel, QJsonObject &obj); diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index ec5dd8f6..92dcb703 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -131,6 +131,11 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Node *relati relativeTo->insertSplitRelative(split, direction); } + this->addSplit(split); +} + +void SplitContainer::addSplit(Split *split) +{ split->setParent(this); split->show(); split->giveFocus(Qt::MouseFocusReason); @@ -223,7 +228,7 @@ void SplitContainer::layout() } { - int i = 0; + size_t i = 0; for (ResizeRect &resizeRect : _resizeRects) { ResizeHandle *handle = this->resizeHandles[i].get(); handle->setGeometry(resizeRect.rect); @@ -338,7 +343,7 @@ void SplitContainer::mouseMoveEvent(QMouseEvent *event) this->update(); } -void SplitContainer::leaveEvent(QEvent *event) +void SplitContainer::leaveEvent(QEvent *) { this->mouseOverPoint = QPoint(-10000, -10000); this->update(); @@ -410,12 +415,16 @@ void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node) split->setChannel( singletons::WindowManager::decodeChannel(_obj.value("data").toObject())); - this->insertSplit(split, direction, node); + Node *_node = new Node(); + _node->parent = node; + _node->split = split; + _node->type = Node::_Split; - this->baseNode.findNodeContainingSplit(split)->flexH = - _obj.value("flexh").toDouble(1.0); - this->baseNode.findNodeContainingSplit(split)->flexV = - _obj.value("flexv").toDouble(1.0); + _node->flexH = _obj.value("flexh").toDouble(1.0); + _node->flexV = _obj.value("flexv").toDouble(1.0); + node->children.emplace_back(_node); + + this->addSplit(split); } else { Node *_node = new Node(); _node->parent = node; @@ -571,7 +580,7 @@ void SplitContainer::Node::_insertNextToThis(Split *_split, Direction _direction qreal height = this->parent->geometry.height() / siblings.size(); if (siblings.size() == 1) { - this->geometry = QRect(0, 0, width, height); + this->geometry = QRect(0, 0, int(width), int(height)); } auto it = std::find_if(siblings.begin(), siblings.end(), @@ -797,8 +806,8 @@ SplitContainer::Node::Type SplitContainer::Node::toContainerType(Direction _dir) SplitContainer::DropOverlay::DropOverlay(SplitContainer *_parent) : QWidget(_parent) - , parent(_parent) , mouseOverPoint(-10000, -10000) + , parent(_parent) { this->setMouseTracking(true); this->setAcceptDrops(true); @@ -811,7 +820,7 @@ void SplitContainer::DropOverlay::setRects(std::vector // pajlada::Signals::NoArgSignal dragEnded; -void SplitContainer::DropOverlay::paintEvent(QPaintEvent *event) +void SplitContainer::DropOverlay::paintEvent(QPaintEvent *) { QPainter painter(this); diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index ebbb3122..ec04ddc7 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -39,7 +39,7 @@ public: // fourtf: !!! preserve the order of left, up, right and down enum Direction { Left, Above, Right, Below }; - struct Position { + struct Position final { private: Position() = default; Position(Node *_relativeNode, Direction _direcion) @@ -56,7 +56,7 @@ public: }; private: - struct DropRect { + struct DropRect final { QRect rect; Position position; @@ -67,7 +67,7 @@ private: } }; - struct ResizeRect { + struct ResizeRect final { QRect rect; Node *node; bool vertical; @@ -122,7 +122,7 @@ public: }; private: - class DropOverlay : public QWidget + class DropOverlay final : public QWidget { public: DropOverlay(SplitContainer *_parent = nullptr); @@ -133,10 +133,10 @@ private: protected: void paintEvent(QPaintEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event); - void dragMoveEvent(QDragMoveEvent *event); - void dragLeaveEvent(QDragLeaveEvent *event); - void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; private: std::vector rects; @@ -144,7 +144,7 @@ private: SplitContainer *parent; }; - class ResizeHandle : public QWidget + class ResizeHandle final : public QWidget { public: SplitContainer *parent; @@ -223,6 +223,8 @@ private: } }; + void addSplit(Split *split); + std::vector dropRects; std::vector dropRegions; NotebookPageDropPreview dropPreview; From 16c57045edfcd9cf22347a86d0bfcc8f80b22a84 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 24 May 2018 17:22:51 +0200 Subject: [PATCH 047/121] fixed style issues --- src/widgets/splitcontainer.cpp | 54 ++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 92dcb703..1ea30419 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -31,10 +31,10 @@ Split *SplitContainer::draggingSplit = nullptr; SplitContainer::SplitContainer(Notebook *parent) : BaseWidget(parent) - , tab(nullptr) , dropPreview(this) - , mouseOverPoint(-10000, -10000) , overlay(this) + , mouseOverPoint(-10000, -10000) + , tab(nullptr) { this->refreshTabTitle(); @@ -102,7 +102,7 @@ void SplitContainer::appendSplit(Split *split) void SplitContainer::insertSplit(Split *split, const Position &position) { - this->insertSplit(split, position.direction, (Node *)position.relativeNode); + this->insertSplit(split, position.direction, reinterpret_cast(position.relativeNode)); } void SplitContainer::insertSplit(Split *split, Direction direction, Split *relativeTo) @@ -677,7 +677,7 @@ qreal SplitContainer::Node::getSize(bool isVertical) qreal SplitContainer::Node::getChildrensTotalFlex(bool isVertical) { return std::accumulate( - this->children.begin(), this->children.end(), (qreal)0, + this->children.begin(), this->children.end(), qreal(0), [=](qreal val, std::unique_ptr &node) { return val + node->getFlex(isVertical); }); } @@ -701,11 +701,11 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vectortype == Node::VerticalContainer; // vars - qreal minSize = 48 * _scale; + qreal minSize = qreal(48 * _scale); qreal totalFlex = this->getChildrensTotalFlex(isVertical); qreal totalSize = std::accumulate( - this->children.begin(), this->children.end(), (qreal)0, + this->children.begin(), this->children.end(), qreal(0), [=](int val, std::unique_ptr &node) { return val + std::max(this->getSize(isVertical) / totalFlex * node->getFlex(isVertical), @@ -717,25 +717,29 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vector(this->getSize(!isVertical) * 0.1, _scale * 24); + qreal offset = + std::min(this->getSize(!isVertical) * 0.1, qreal(_scale * 24)); // droprect left / above dropRects.emplace_back( - QRect(this->geometry.left(), this->geometry.top(), - isVertical ? offset : this->geometry.width(), - isVertical ? this->geometry.height() : offset), + QRectF(this->geometry.left(), this->geometry.top(), + isVertical ? offset : this->geometry.width(), + isVertical ? this->geometry.height() : offset) + .toRect(), Position(this, isVertical ? Direction::Left : Direction::Above)); // droprect right / below if (isVertical) { dropRects.emplace_back( - QRect(this->geometry.right() - offset, this->geometry.top(), offset, - this->geometry.height()), + QRectF(this->geometry.right() - offset, this->geometry.top(), offset, + this->geometry.height()) + .toRect(), Position(this, Direction::Right)); } else { dropRects.emplace_back( - QRect(this->geometry.left(), this->geometry.bottom() - offset, - this->geometry.width(), offset), + QRectF(this->geometry.left(), this->geometry.bottom() - offset, + this->geometry.width(), offset) + .toRect(), Position(this, Direction::Below)); } @@ -774,11 +778,11 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vectorchildren.front()) { - QRect r = isVertical ? QRect(this->geometry.left(), child->geometry.top() - 4, - this->geometry.width(), 8) - : QRect(child->geometry.left() - 4, this->geometry.top(), - 8, this->geometry.height()); - resizeRects.push_back(ResizeRect(r, child.get(), isVertical)); + QRectF r = isVertical ? QRectF(this->geometry.left(), child->geometry.top() - 4, + this->geometry.width(), 8) + : QRectF(child->geometry.left() - 4, this->geometry.top(), + 8, this->geometry.height()); + resizeRects.push_back(ResizeRect(r.toRect(), child.get(), isVertical)); } // normalize flex @@ -856,7 +860,7 @@ void SplitContainer::DropOverlay::dragMoveEvent(QDragMoveEvent *event) this->update(); } -void SplitContainer::DropOverlay::dragLeaveEvent(QDragLeaveEvent *event) +void SplitContainer::DropOverlay::dragLeaveEvent(QDragLeaveEvent *) { this->mouseOverPoint = QPoint(-10000, -10000); this->close(); @@ -900,19 +904,19 @@ SplitContainer::ResizeHandle::ResizeHandle(SplitContainer *_parent) this->setMouseTracking(true); } -void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *event) +void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *) { QPainter painter(this); painter.fillRect(this->rect(), "#999"); } -void SplitContainer::ResizeHandle::mousePressEvent(QMouseEvent *event) +void SplitContainer::ResizeHandle::mousePressEvent(QMouseEvent *) { this->isMouseDown = true; } -void SplitContainer::ResizeHandle::mouseReleaseEvent(QMouseEvent *event) +void SplitContainer::ResizeHandle::mouseReleaseEvent(QMouseEvent *) { this->isMouseDown = false; } @@ -955,7 +959,7 @@ void SplitContainer::ResizeHandle::mouseMoveEvent(QMouseEvent *event) this->parent->layout(); // move handle - this->move(this->x(), (int)before->geometry.bottom() - 4); + this->move(this->x(), int(before->geometry.bottom() - 4)); } else { qreal totalFlexH = this->node->flexH + before->flexH; before->flexH = @@ -965,7 +969,7 @@ void SplitContainer::ResizeHandle::mouseMoveEvent(QMouseEvent *event) this->parent->layout(); // move handle - this->move((int)before->geometry.right() - 4, this->y()); + this->move(int(before->geometry.right() - 4), this->y()); } } From 6ee4945715286d5bb0478da692abf57091e1be2e Mon Sep 17 00:00:00 2001 From: nuuls Date: Thu, 24 May 2018 22:58:07 +0200 Subject: [PATCH 048/121] fixed mac stuff and added more debug code --- chatterino.pro | 5 +++++ src/messages/layouts/messagelayout.cpp | 5 +++++ src/singletons/fontmanager.cpp | 10 ++++++++-- src/singletons/windowmanager.hpp | 7 ++++--- src/util/benchmark.hpp | 21 +++++++++++++++++++++ src/widgets/helper/channelview.cpp | 8 ++++---- src/widgets/helper/notebooktab.cpp | 8 ++++---- 7 files changed, 51 insertions(+), 13 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index 8a5651d2..f99c77bf 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -26,6 +26,11 @@ equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { macx:ICON = resources/images/chatterino2.icns win32:RC_FILE = resources/windows.rc + +macx { + LIBS += -L/usr/local/lib +} + # Submodules include(dependencies/rapidjson.pri) include(dependencies/settings.pri) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index f92d4b03..d9a0f484 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -3,6 +3,7 @@ #include "application.hpp" #include "singletons/emotemanager.hpp" #include "singletons/settingsmanager.hpp" +#include "util/benchmark.hpp" #include #include @@ -47,6 +48,8 @@ int MessageLayout::getHeight() const // return true if redraw is required bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) { + BenchmarkGuard benchmark("MessageLayout::layout()"); + auto app = getApp(); bool layoutRequired = false; @@ -120,6 +123,8 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) this->container.begin(width, this->scale, messageFlags); for (const std::unique_ptr &element : this->message->getElements()) { + BenchmarkGuard guard("addelement"); + element->addToContainer(this->container, _flags); } diff --git a/src/singletons/fontmanager.cpp b/src/singletons/fontmanager.cpp index 49d00c1e..d57649ff 100644 --- a/src/singletons/fontmanager.cpp +++ b/src/singletons/fontmanager.cpp @@ -115,10 +115,16 @@ FontManager::FontData FontManager::createFontData(Type type, float scale) // normal Ui font (use pt size) { +#ifdef Q_OS_MAC + constexpr float multiplier = 0.8f; +#else + constexpr float multiplier = 1.f; +#endif + static std::unordered_map defaultSize{ {Tiny, {8, "Monospace", false, QFont::Normal}}, - {UiMedium, {12, DEFAULT_FONT_FAMILY, false, QFont::Normal}}, - {UiTabs, {9, "Segoe UI", false, QFont::Normal}}, + {UiMedium, {int(12 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, + {UiTabs, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, }; UiFontData &data = defaultSize[type]; diff --git a/src/singletons/windowmanager.hpp b/src/singletons/windowmanager.hpp index d17452bf..28c2cace 100644 --- a/src/singletons/windowmanager.hpp +++ b/src/singletons/windowmanager.hpp @@ -1,11 +1,12 @@ #pragma once #include "widgets/window.hpp" +#include "widgets/splitcontainer.hpp" namespace chatterino { -namespace widgets { -struct SplitContainer::Node; -} +//namespace widgets { +//struct SplitContainer::Node; +//} namespace singletons { class WindowManager diff --git a/src/util/benchmark.hpp b/src/util/benchmark.hpp index 828f6d73..0093bd76 100644 --- a/src/util/benchmark.hpp +++ b/src/util/benchmark.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #define BENCH(x) \ QElapsedTimer x; \ @@ -11,3 +12,23 @@ #define MARK(x) \ qDebug() << BOOST_CURRENT_FUNCTION << __LINE__ \ << static_cast(x.nsecsElapsed()) / 100000.0 << "ms"; + +class BenchmarkGuard : boost::noncopyable { + QElapsedTimer timer; + QString name; + +public: + BenchmarkGuard(const QString &_name) + :name(_name) + { + timer.start(); + } + + ~BenchmarkGuard() { + qDebug() << this->name << float(timer.nsecsElapsed()) / 100000.0 << "ms"; + } + + qreal getElapsedMs() { + return qreal(timer.nsecsElapsed()) / 100000.0; + } +}; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 33d2eb91..a7a08605 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -177,7 +177,7 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) { auto app = getApp(); - // BENCH(timer) + BENCH(timer) auto messagesSnapshot = this->getMessagesSnapshot(); if (messagesSnapshot.getLength() == 0) { @@ -264,7 +264,7 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) this->messageWasAdded = false; } - // MARK(timer); + MARK(timer); if (redrawRequired) { this->queueUpdate(); @@ -580,7 +580,7 @@ bool ChannelView::isPaused() void ChannelView::paintEvent(QPaintEvent * /*event*/) { - // BENCH(timer); + BENCH(timer); QPainter painter(this); @@ -589,7 +589,7 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/) // draw messages this->drawMessages(painter); - // MARK(timer); + MARK(timer); } // if overlays is false then it draws the message, if true then it draws things such as the grey diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 621a37fc..179cc47c 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -86,7 +86,7 @@ void NotebookTab::updateSize() float scale = getScale(); int width; - QFontMetrics metrics = getApp()->fonts->getFontMetrics(FontStyle::UiTabs, this->getScale()); + QFontMetrics metrics = getApp()->fonts->getFontMetrics(FontStyle::UiTabs, this->getScale() * this->devicePixelRatioF()); if (this->hasXButton()) { width = (int)((metrics.width(this->title) + 32) * scale); @@ -188,7 +188,7 @@ void NotebookTab::paintEvent(QPaintEvent *) QPainter painter(this); float scale = this->getScale(); - painter.setFont(getApp()->fonts->getFont(FontStyle::UiTabs, scale)); + painter.setFont(getApp()->fonts->getFont(FontStyle::UiTabs, scale * this->devicePixelRatioF())); int height = (int)(scale * NOTEBOOK_TAB_HEIGHT); // int fullHeight = (int)(scale * 48); @@ -248,13 +248,13 @@ void NotebookTab::paintEvent(QPaintEvent *) if (true) { // legacy // painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter)); int offset = (int)(scale * 8); - QRect textRect(offset, this->selected ? 0 : 1, this->width() - offset - offset, height); + QRect textRect(offset, this->selected ? 1 : 2, this->width() - offset - offset, height); if (this->shouldDrawXButton()) { textRect.setRight(textRect.right() - this->height() / 2); } - QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); + QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter); option.setWrapMode(QTextOption::NoWrap); painter.drawText(textRect, this->getTitle(), option); } else { From ec03bc2e8cfa8674ac3b262a9c0ede8d9e6ba3a5 Mon Sep 17 00:00:00 2001 From: nuuls Date: Thu, 24 May 2018 23:12:50 +0200 Subject: [PATCH 049/121] fixed performance --- src/messages/layouts/messagelayout.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index d9a0f484..860fb243 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -76,6 +76,7 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) // check if timestamp format changed bool timestampFormatChanged = this->timestampFormat != app->settings->timestampFormat; + this->timestampFormat = app->settings->timestampFormat.getValue(); layoutRequired |= timestampFormatChanged; @@ -86,6 +87,8 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) imagesChanged |= scaleChanged; textChanged |= scaleChanged; +// assert(layoutRequired); + // update word sizes if needed if (imagesChanged) { // this->container.updateImages(); @@ -100,6 +103,8 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) } // return if no layout is required + qDebug() << layoutRequired; + if (!layoutRequired) { return false; } @@ -123,8 +128,6 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) this->container.begin(width, this->scale, messageFlags); for (const std::unique_ptr &element : this->message->getElements()) { - BenchmarkGuard guard("addelement"); - element->addToContainer(this->container, _flags); } From f72e1b5d823be1276b50820db186c861af731b2f Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 12:45:18 +0200 Subject: [PATCH 050/121] fixed message expanding not working --- src/messages/layouts/messagelayout.cpp | 10 +++++++--- src/util/benchmark.hpp | 17 ++++++++++------- src/widgets/helper/channelview.cpp | 16 +++++++--------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index 860fb243..be2f211c 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -80,6 +80,10 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) layoutRequired |= timestampFormatChanged; + // check if layout was requested manually + layoutRequired |= bool(this->flags & RequiresLayout); + this->flags &= ~RequiresLayout; + // check if dpi changed bool scaleChanged = this->scale != scale; layoutRequired |= scaleChanged; @@ -87,7 +91,7 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) imagesChanged |= scaleChanged; textChanged |= scaleChanged; -// assert(layoutRequired); + // assert(layoutRequired); // update word sizes if needed if (imagesChanged) { @@ -122,7 +126,7 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) if (this->flags & MessageLayout::Expanded || (_flags & MessageElement::ModeratorTools && !(this->message->flags & Message::MessageFlags::Disabled))) { - messageFlags = (Message::MessageFlags)(messageFlags & ~Message::MessageFlags::Collapsed); + messageFlags = Message::MessageFlags(messageFlags & ~Message::MessageFlags::Collapsed); } this->container.begin(width, this->scale, messageFlags); @@ -209,7 +213,7 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection this->bufferValid = true; } -void MessageLayout::updateBuffer(QPixmap *buffer, int messageIndex, Selection &selection) +void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selection & /*selection*/) { auto app = getApp(); diff --git a/src/util/benchmark.hpp b/src/util/benchmark.hpp index 0093bd76..c41caa41 100644 --- a/src/util/benchmark.hpp +++ b/src/util/benchmark.hpp @@ -11,24 +11,27 @@ #define MARK(x) \ qDebug() << BOOST_CURRENT_FUNCTION << __LINE__ \ - << static_cast(x.nsecsElapsed()) / 100000.0 << "ms"; + << static_cast(x.nsecsElapsed()) / 1000000.0 << "ms"; -class BenchmarkGuard : boost::noncopyable { +class BenchmarkGuard : boost::noncopyable +{ QElapsedTimer timer; QString name; public: BenchmarkGuard(const QString &_name) - :name(_name) + : name(_name) { timer.start(); } - ~BenchmarkGuard() { - qDebug() << this->name << float(timer.nsecsElapsed()) / 100000.0 << "ms"; + ~BenchmarkGuard() + { + qDebug() << this->name << float(timer.nsecsElapsed()) / 1000000.0f << "ms"; } - qreal getElapsedMs() { - return qreal(timer.nsecsElapsed()) / 100000.0; + qreal getElapsedMs() + { + return qreal(timer.nsecsElapsed()) / 1000000.0; } }; diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index a7a08605..38c1e963 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -175,9 +175,10 @@ void ChannelView::layoutMessages() void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) { + BenchmarkGuard benchmark("layout messages"); + auto app = getApp(); - BENCH(timer) auto messagesSnapshot = this->getMessagesSnapshot(); if (messagesSnapshot.getLength() == 0) { @@ -264,8 +265,6 @@ void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) this->messageWasAdded = false; } - MARK(timer); - if (redrawRequired) { this->queueUpdate(); } @@ -580,7 +579,7 @@ bool ChannelView::isPaused() void ChannelView::paintEvent(QPaintEvent * /*event*/) { - BENCH(timer); + BenchmarkGuard benchmark("paint event"); QPainter painter(this); @@ -588,8 +587,6 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/) // draw messages this->drawMessages(painter); - - MARK(timer); } // if overlays is false then it draws the message, if true then it draws things such as the grey @@ -600,14 +597,14 @@ void ChannelView::drawMessages(QPainter &painter) auto messagesSnapshot = this->getMessagesSnapshot(); - size_t start = this->scrollBar.getCurrentValue(); + size_t start = size_t(this->scrollBar.getCurrentValue()); if (start >= messagesSnapshot.getLength()) { return; } - int y = -(messagesSnapshot[start].get()->getHeight() * - (fmod(this->scrollBar.getCurrentValue(), 1))); + int y = int(-(messagesSnapshot[start].get()->getHeight() * + (fmod(this->scrollBar.getCurrentValue(), 1)))); messages::MessageLayout *end = nullptr; bool windowFocused = this->window() == QApplication::activeWindow(); @@ -925,6 +922,7 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) // message under cursor is collapsed if (layout->flags & MessageLayout::Collapsed) { layout->flags |= MessageLayout::Expanded; + layout->flags |= MessageLayout::RequiresLayout; this->layoutMessages(); return; From b68b7ecb101fa6a6dfaa2cab4629816251887d47 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 13:02:14 +0200 Subject: [PATCH 051/121] fixed accountpopup background color --- src/messages/layouts/messagelayout.cpp | 95 ++++++++++++------------ src/messages/layouts/messagelayout.hpp | 30 ++++---- src/widgets/accountpopup.cpp | 4 +- src/widgets/accountpopup.hpp | 3 +- src/widgets/accountswitchpopupwidget.cpp | 7 +- 5 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index be2f211c..a55d94a2 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -21,9 +21,9 @@ namespace chatterino { namespace messages { namespace layouts { -MessageLayout::MessageLayout(MessagePtr _message) - : message(_message) - , buffer(nullptr) +MessageLayout::MessageLayout(MessagePtr message) + : m_message(message) + , m_buffer(nullptr) { util::DebugCount::increase("message layout"); } @@ -35,13 +35,13 @@ MessageLayout::~MessageLayout() Message *MessageLayout::getMessage() { - return this->message.get(); + return this->m_message.get(); } // Height int MessageLayout::getHeight() const { - return container.getHeight(); + return m_container.getHeight(); } // Layout @@ -55,28 +55,28 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) bool layoutRequired = false; // check if width changed - bool widthChanged = width != this->currentLayoutWidth; + bool widthChanged = width != this->m_currentLayoutWidth; layoutRequired |= widthChanged; - this->currentLayoutWidth = width; + this->m_currentLayoutWidth = width; // check if emotes changed - bool imagesChanged = this->emoteGeneration != app->emotes->getGeneration(); + bool imagesChanged = this->m_emoteGeneration != app->emotes->getGeneration(); layoutRequired |= imagesChanged; - this->emoteGeneration = app->emotes->getGeneration(); + this->m_emoteGeneration = app->emotes->getGeneration(); // check if text changed - bool textChanged = this->fontGeneration != app->fonts->getGeneration(); + bool textChanged = this->m_fontGeneration != app->fonts->getGeneration(); layoutRequired |= textChanged; - this->fontGeneration = app->fonts->getGeneration(); + this->m_fontGeneration = app->fonts->getGeneration(); // check if work mask changed - bool wordMaskChanged = this->currentWordFlags != flags; // app->settings->getWordTypeMask(); + bool wordMaskChanged = this->m_currentWordFlags != flags; // app->settings->getWordTypeMask(); layoutRequired |= wordMaskChanged; - this->currentWordFlags = flags; // app->settings->getWordTypeMask(); + this->m_currentWordFlags = flags; // app->settings->getWordTypeMask(); // check if timestamp format changed - bool timestampFormatChanged = this->timestampFormat != app->settings->timestampFormat; - this->timestampFormat = app->settings->timestampFormat.getValue(); + bool timestampFormatChanged = this->m_timestampFormat != app->settings->timestampFormat; + this->m_timestampFormat = app->settings->timestampFormat.getValue(); layoutRequired |= timestampFormatChanged; @@ -85,9 +85,9 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) this->flags &= ~RequiresLayout; // check if dpi changed - bool scaleChanged = this->scale != scale; + bool scaleChanged = this->m_scale != scale; layoutRequired |= scaleChanged; - this->scale = scale; + this->m_scale = scale; imagesChanged |= scaleChanged; textChanged |= scaleChanged; @@ -121,30 +121,30 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) { - auto messageFlags = this->message->flags.value; + auto messageFlags = this->m_message->flags.value; if (this->flags & MessageLayout::Expanded || (_flags & MessageElement::ModeratorTools && - !(this->message->flags & Message::MessageFlags::Disabled))) { + !(this->m_message->flags & Message::MessageFlags::Disabled))) { messageFlags = Message::MessageFlags(messageFlags & ~Message::MessageFlags::Collapsed); } - this->container.begin(width, this->scale, messageFlags); + this->m_container.begin(width, this->m_scale, messageFlags); - for (const std::unique_ptr &element : this->message->getElements()) { - element->addToContainer(this->container, _flags); + for (const std::unique_ptr &element : this->m_message->getElements()) { + element->addToContainer(this->m_container, _flags); } - if (this->height != this->container.getHeight()) { + if (this->m_height != this->m_container.getHeight()) { this->deleteBuffer(); } - this->container.end(); - this->height = this->container.getHeight(); + this->m_container.end(); + this->m_height = this->m_container.getHeight(); // collapsed state this->flags &= ~Flags::Collapsed; - if (this->container.isCollapsed()) { + if (this->m_container.isCollapsed()) { this->flags |= Flags::Collapsed; } } @@ -154,7 +154,7 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection bool isLastReadMessage, bool isWindowFocused) { auto app = getApp(); - QPixmap *pixmap = this->buffer.get(); + QPixmap *pixmap = this->m_buffer.get(); // create new buffer if required if (!pixmap) { @@ -164,15 +164,16 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection (int)(this->container.getHeight() * painter.device()->devicePixelRatioF())); pixmap->setDevicePixelRatio(painter.device()->devicePixelRatioF()); #else - pixmap = new QPixmap(this->container.getWidth(), std::max(16, this->container.getHeight())); + pixmap = + new QPixmap(this->m_container.getWidth(), std::max(16, this->m_container.getHeight())); #endif - this->buffer = std::shared_ptr(pixmap); - this->bufferValid = false; + this->m_buffer = std::shared_ptr(pixmap); + this->m_bufferValid = false; util::DebugCount::increase("message drawing buffers"); } - if (!this->bufferValid || !selection.isEmpty()) { + if (!this->m_bufferValid || !selection.isEmpty()) { this->updateBuffer(pixmap, messageIndex, selection); } @@ -181,21 +182,21 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection // painter.drawPixmap(0, y, this->container.width, this->container.getHeight(), *pixmap); // draw gif emotes - this->container.paintAnimatedElements(painter, y); + this->m_container.paintAnimatedElements(painter, y); // draw disabled - if (this->message->flags.HasFlag(Message::Disabled)) { + if (this->m_message->flags.HasFlag(Message::Disabled)) { painter.fillRect(0, y, pixmap->width(), pixmap->height(), app->themes->messages.disabled); } // draw selection if (!selection.isEmpty()) { - this->container.paintSelection(painter, messageIndex, selection, y); + this->m_container.paintSelection(painter, messageIndex, selection, y); } // draw message seperation line if (app->settings->seperateMessages.getValue()) { - painter.fillRect(0, y + this->container.getHeight() - 1, this->container.getWidth(), 1, + painter.fillRect(0, y + this->m_container.getHeight() - 1, this->m_container.getWidth(), 1, app->themes->splits.messageSeperator); } @@ -206,11 +207,11 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection QBrush brush(color, Qt::VerPattern); - painter.fillRect(0, y + this->container.getHeight() - 1, this->container.getWidth(), 1, + painter.fillRect(0, y + this->m_container.getHeight() - 1, this->m_container.getWidth(), 1, brush); } - this->bufferValid = true; + this->m_bufferValid = true; } void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selection & /*selection*/) @@ -223,7 +224,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selectio // draw background QColor backgroundColor; - if (this->message->flags & Message::Highlighted) { + if (this->m_message->flags & Message::Highlighted) { backgroundColor = app->themes->messages.backgrounds.highlighted; } else if (app->settings->alternateMessageBackground.getValue() && this->flags & MessageLayout::AlternateBackground) { @@ -234,7 +235,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selectio painter.fillRect(buffer->rect(), backgroundColor); // draw message - this->container.paintElements(painter); + this->m_container.paintElements(painter); #ifdef FOURTF // debug @@ -252,15 +253,15 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selectio void MessageLayout::invalidateBuffer() { - this->bufferValid = false; + this->m_bufferValid = false; } void MessageLayout::deleteBuffer() { - if (this->buffer != nullptr) { + if (this->m_buffer != nullptr) { util::DebugCount::decrease("message drawing buffers"); - this->buffer = nullptr; + this->m_buffer = nullptr; } } @@ -269,7 +270,7 @@ void MessageLayout::deleteCache() this->deleteBuffer(); #ifdef XD - this->container.clear(); + this->m_container.clear(); #endif } @@ -282,22 +283,22 @@ void MessageLayout::deleteCache() const MessageLayoutElement *MessageLayout::getElementAt(QPoint point) { // go through all words and return the first one that contains the point. - return this->container.getElementAt(point); + return this->m_container.getElementAt(point); } int MessageLayout::getLastCharacterIndex() const { - return this->container.getLastCharacterIndex(); + return this->m_container.getLastCharacterIndex(); } int MessageLayout::getSelectionIndex(QPoint position) { - return this->container.getSelectionIndex(position); + return this->m_container.getSelectionIndex(position); } void MessageLayout::addSelectionText(QString &str, int from, int to) { - this->container.addSelectionText(str, from, to); + this->m_container.addSelectionText(str, from, to); } } // namespace layouts diff --git a/src/messages/layouts/messagelayout.hpp b/src/messages/layouts/messagelayout.hpp index 9e329fd1..3289e912 100644 --- a/src/messages/layouts/messagelayout.hpp +++ b/src/messages/layouts/messagelayout.hpp @@ -27,7 +27,7 @@ public: Expanded = 1 << 5, }; - MessageLayout(MessagePtr message); + MessageLayout(MessagePtr m_message); ~MessageLayout(); Message *getMessage(); @@ -39,7 +39,7 @@ public: util::FlagsEnum flags; // Layout - bool layout(int width, float scale, MessageElement::Flags flags); + bool layout(int width, float m_scale, MessageElement::Flags flags); // Painting void paint(QPainter &painter, int y, int messageIndex, Selection &selection, @@ -59,23 +59,23 @@ public: private: // variables - MessagePtr message; - MessageLayoutContainer container; - std::shared_ptr buffer = nullptr; - bool bufferValid = false; + MessagePtr m_message; + MessageLayoutContainer m_container; + std::shared_ptr m_buffer = nullptr; + bool m_bufferValid = false; - int height = 0; + int m_height = 0; - int currentLayoutWidth = -1; - int fontGeneration = -1; - int emoteGeneration = -1; - QString timestampFormat; - float scale = -1; - unsigned int bufferUpdatedCount = 0; + int m_currentLayoutWidth = -1; + int m_fontGeneration = -1; + int m_emoteGeneration = -1; + QString m_timestampFormat; + float m_scale = -1; + unsigned int m_bufferUpdatedCount = 0; - MessageElement::Flags currentWordFlags = MessageElement::None; + MessageElement::Flags m_currentWordFlags = MessageElement::None; - int collapsedHeight = 32; + int m_collapsedHeight = 32; // methods void actuallyLayout(int width, MessageElement::Flags flags); diff --git a/src/widgets/accountpopup.cpp b/src/widgets/accountpopup.cpp index 76263f2e..847343f6 100644 --- a/src/widgets/accountpopup.cpp +++ b/src/widgets/accountpopup.cpp @@ -273,9 +273,9 @@ void AccountPopupWidget::loadAvatar(const QUrl &avatarUrl) void AccountPopupWidget::scaleChangedEvent(float newDpi) { this->setStyleSheet(QString("* { font-size: px; }") - .replace("", QString::number((int)(12 * newDpi)))); + .replace("", QString::number(int(12 * newDpi)))); - this->ui->lblAvatar->setFixedSize((int)(100 * newDpi), (int)(100 * newDpi)); + this->ui->lblAvatar->setFixedSize(int(100 * newDpi), int(100 * newDpi)); } void AccountPopupWidget::updateButtons(QWidget *layout, bool state) diff --git a/src/widgets/accountpopup.hpp b/src/widgets/accountpopup.hpp index 05d6ef0e..b35c6d16 100644 --- a/src/widgets/accountpopup.hpp +++ b/src/widgets/accountpopup.hpp @@ -19,9 +19,10 @@ class Channel; namespace widgets { -class AccountPopupWidget : public BaseWindow +class AccountPopupWidget final : public BaseWindow { Q_OBJECT + public: AccountPopupWidget(ChannelPtr _channel); diff --git a/src/widgets/accountswitchpopupwidget.cpp b/src/widgets/accountswitchpopupwidget.cpp index 922834d2..bc29116e 100644 --- a/src/widgets/accountswitchpopupwidget.cpp +++ b/src/widgets/accountswitchpopupwidget.cpp @@ -36,6 +36,8 @@ AccountSwitchPopupWidget::AccountSwitchPopupWidget(QWidget *parent) }); this->setLayout(vbox); + + // this->setStyleSheet("background: #333"); } void AccountSwitchPopupWidget::refresh() @@ -48,11 +50,12 @@ void AccountSwitchPopupWidget::focusOutEvent(QFocusEvent *) this->hide(); } -void AccountSwitchPopupWidget::paintEvent(QPaintEvent *event) +void AccountSwitchPopupWidget::paintEvent(QPaintEvent *) { QPainter painter(this); - painter.fillRect(this->rect(), QColor(255, 255, 255)); + painter.setPen(QColor("#999")); + painter.drawRect(0, 0, this->width() - 1, this->height() - 1); } } // namespace widgets From 9aa9b90267e438b81df4b74b6debe424bbe1bea0 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 13:53:55 +0200 Subject: [PATCH 052/121] fixed /r and tab text alignment --- chatterino.pro | 3 +- src/channel.cpp | 5 ++ src/channel.hpp | 2 + src/providers/twitch/ircmessagehandler.cpp | 2 + src/providers/twitch/twitchchannel.hpp | 2 + src/providers/twitch/twitchserver.cpp | 14 +++++ src/providers/twitch/twitchserver.hpp | 10 ++++ src/util/mutexvalue.h | 40 ++++++++++++++ src/widgets/helper/notebooktab.cpp | 17 ++++-- src/widgets/helper/splitinput.cpp | 63 ++++++++++++---------- src/widgets/helper/splitinput.hpp | 2 +- src/widgets/window.cpp | 19 ++++--- src/widgets/window.hpp | 2 + 13 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 src/util/mutexvalue.h diff --git a/chatterino.pro b/chatterino.pro index f99c77bf..a611e711 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -358,7 +358,8 @@ HEADERS += \ src/widgets/helper/splitoverlay.hpp \ src/widgets/helper/dropoverlay.hpp \ src/widgets/helper/splitnode.hpp \ - src/widgets/notificationpopup.hpp + src/widgets/notificationpopup.hpp \ + src/util/mutexvalue.h RESOURCES += \ resources/resources.qrc diff --git a/src/channel.cpp b/src/channel.cpp index 2e340665..5c264c83 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -45,6 +45,11 @@ Channel::Type Channel::getType() const return this->type; } +bool Channel::isTwitchChannel() const +{ + return this->type >= Twitch && this->type < TwitchEnd; +} + bool Channel::isEmpty() const { return this->name.isEmpty(); diff --git a/src/channel.hpp b/src/channel.hpp index 91d2ad4f..7b6f1715 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -29,6 +29,7 @@ public: TwitchWhispers, TwitchWatching, TwitchMentions, + TwitchEnd, }; explicit Channel(const QString &_name, Type type); @@ -43,6 +44,7 @@ public: pajlada::Signals::NoArgSignal destroyed; Type getType() const; + bool isTwitchChannel() const; virtual bool isEmpty() const; messages::LimitedQueueSnapshot getMessageSnapshot(); diff --git a/src/providers/twitch/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp index a8b22649..6fbb95b4 100644 --- a/src/providers/twitch/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -166,6 +166,8 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message) app->twitch.server->mentionsChannel->addMessage(_message); } + app->twitch.server->lastUserThatWhisperedMe.set(builder.userName); + c->addMessage(_message); if (app->settings->inlineWhispers) { diff --git a/src/providers/twitch/twitchchannel.hpp b/src/providers/twitch/twitchchannel.hpp index a594bdf7..8a8e5727 100644 --- a/src/providers/twitch/twitchchannel.hpp +++ b/src/providers/twitch/twitchchannel.hpp @@ -7,6 +7,7 @@ #include "singletons/emotemanager.hpp" #include "singletons/ircmanager.hpp" #include "util/concurrentmap.hpp" +#include "util/mutexvalue.h" #include @@ -78,6 +79,7 @@ public: pajlada::Signals::NoArgSignal roomModesChanged; QString roomID; + RoomModes getRoomModes(); void setRoomModes(const RoomModes &roomModes); diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp index ab184535..9b10df7b 100644 --- a/src/providers/twitch/twitchserver.cpp +++ b/src/providers/twitch/twitchserver.cpp @@ -197,6 +197,20 @@ std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &chan return Channel::getEmpty(); } +// QString TwitchServer::getLastWhisperedPerson() const +//{ +// std::lock_guard guard(this->lastWhisperedPersonMutex); + +// return this->lastWhisperedPerson; +//} + +// void TwitchServer::setLastWhisperedPerson(const QString &person) +//{ +// std::lock_guard guard(this->lastWhisperedPersonMutex); + +// this->lastWhisperedPerson = person; +//} + QString TwitchServer::cleanChannelName(const QString &dirtyChannelName) { return dirtyChannelName.toLower(); diff --git a/src/providers/twitch/twitchserver.hpp b/src/providers/twitch/twitchserver.hpp index 99852197..d143856e 100644 --- a/src/providers/twitch/twitchserver.hpp +++ b/src/providers/twitch/twitchserver.hpp @@ -3,6 +3,7 @@ #include "providers/irc/abstractircserver.hpp" #include "providers/twitch/twitchaccount.hpp" #include "providers/twitch/twitchchannel.hpp" +#include "util/mutexvalue.h" #include @@ -22,6 +23,11 @@ public: std::shared_ptr getChannelOrEmptyByID(const QString &channelID); + util::MutexValue lastUserThatWhisperedMe; + + // QString getLastWhisperedPerson() const; + // void setLastWhisperedPerson(const QString &person); + const ChannelPtr whispersChannel; const ChannelPtr mentionsChannel; IndirectChannel watchingChannel; @@ -38,6 +44,10 @@ protected: std::shared_ptr getCustomChannel(const QString &channelname) override; QString cleanChannelName(const QString &dirtyChannelName) override; + +private: + // mutable std::mutex lastWhisperedPersonMutex; + // QString lastWhisperedPerson; }; } // namespace twitch diff --git a/src/util/mutexvalue.h b/src/util/mutexvalue.h new file mode 100644 index 00000000..9b0f37a4 --- /dev/null +++ b/src/util/mutexvalue.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace chatterino { +namespace util { + +template +class MutexValue +{ + mutable std::mutex mutex; + T value; + +public: + MutexValue() + { + } + + MutexValue(T &&val) + : value(val) + { + } + + T get() const + { + std::lock_guard guard(this->mutex); + + return this->value; + } + + void set(const T &val) + { + std::lock_guard guard(this->mutex); + + this->value = val; + } +}; + +} // namespace util +} // namespace chatterino diff --git a/src/widgets/helper/notebooktab.cpp b/src/widgets/helper/notebooktab.cpp index 179cc47c..d992d4f7 100644 --- a/src/widgets/helper/notebooktab.cpp +++ b/src/widgets/helper/notebooktab.cpp @@ -86,7 +86,8 @@ void NotebookTab::updateSize() float scale = getScale(); int width; - QFontMetrics metrics = getApp()->fonts->getFontMetrics(FontStyle::UiTabs, this->getScale() * this->devicePixelRatioF()); + QFontMetrics metrics = getApp()->fonts->getFontMetrics( + FontStyle::UiTabs, this->getScale() * this->devicePixelRatioF()); if (this->hasXButton()) { width = (int)((metrics.width(this->title) + 32) * scale); @@ -189,8 +190,10 @@ void NotebookTab::paintEvent(QPaintEvent *) float scale = this->getScale(); painter.setFont(getApp()->fonts->getFont(FontStyle::UiTabs, scale * this->devicePixelRatioF())); + QFontMetrics metrics = + app->fonts->getFontMetrics(FontStyle::UiTabs, scale * this->devicePixelRatioF()); - int height = (int)(scale * NOTEBOOK_TAB_HEIGHT); + int height = int(scale * NOTEBOOK_TAB_HEIGHT); // int fullHeight = (int)(scale * 48); // select the right tab colors @@ -241,20 +244,24 @@ void NotebookTab::paintEvent(QPaintEvent *) painter.setPen(colors.text); // set area for text - int rectW = (!app->settings->showTabCloseButton ? 0 : static_cast(16) * scale); + int rectW = (!app->settings->showTabCloseButton ? 0 : int(16 * scale)); QRect rect(0, 0, this->width() - rectW, height); // draw text if (true) { // legacy // painter.drawText(rect, this->getTitle(), QTextOption(Qt::AlignCenter)); - int offset = (int)(scale * 8); + int offset = int(scale * 8); QRect textRect(offset, this->selected ? 1 : 2, this->width() - offset - offset, height); if (this->shouldDrawXButton()) { textRect.setRight(textRect.right() - this->height() / 2); } - QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter); + int width = metrics.width(this->getTitle()); + Qt::Alignment alignment = width > textRect.width() ? Qt::AlignLeft | Qt::AlignVCenter + : Qt::AlignHCenter | Qt::AlignVCenter; + + QTextOption option(alignment); option.setWrapMode(QTextOption::NoWrap); painter.drawText(textRect, this->getTitle(), option); } else { diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index f08ddfc0..b0644473 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -2,6 +2,8 @@ #include "application.hpp" #include "controllers/commands/commandcontroller.hpp" +#include "providers/twitch/twitchchannel.hpp" +#include "providers/twitch/twitchserver.hpp" #include "singletons/ircmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp" @@ -19,15 +21,15 @@ namespace widgets { SplitInput::SplitInput(Split *_chatWidget) : BaseWidget(_chatWidget) - , chatWidget(_chatWidget) + , split(_chatWidget) { this->initLayout(); - auto completer = new QCompleter(&this->chatWidget->getChannel().get()->completionModel); + auto completer = new QCompleter(&this->split->getChannel().get()->completionModel); this->ui.textEdit->setCompleter(completer); - this->chatWidget->channelChanged.connect([this] { - auto completer = new QCompleter(&this->chatWidget->getChannel()->completionModel); + this->split->channelChanged.connect([this] { + auto completer = new QCompleter(&this->split->getChannel()->completionModel); this->ui.textEdit->setCompleter(completer); }); @@ -83,16 +85,16 @@ void SplitInput::initLayout() }); } - this->emotePopup->resize((int)(300 * this->emotePopup->getScale()), - (int)(500 * this->emotePopup->getScale())); - this->emotePopup->loadChannel(this->chatWidget->getChannel()); + this->emotePopup->resize(int(300 * this->emotePopup->getScale()), + int(500 * this->emotePopup->getScale())); + this->emotePopup->loadChannel(this->split->getChannel()); this->emotePopup->show(); }); // 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->split->view.clearSelection(); } }); @@ -106,13 +108,13 @@ void SplitInput::scaleChangedEvent(float scale) { // update the icon size of the emote button QString text = ""; - text.replace("xD", QString::number((int)12 * scale)); + text.replace("xD", QString::number(int(12 * scale))); this->ui.emoteButton->getLabel().setText(text); - this->ui.emoteButton->setFixedHeight((int)18 * scale); + this->ui.emoteButton->setFixedHeight(int(18 * scale)); // set maximum height - this->setMaximumHeight((int)(150 * this->getScale())); + this->setMaximumHeight(int(150 * this->getScale())); } void SplitInput::themeRefreshEvent() @@ -125,7 +127,7 @@ void SplitInput::themeRefreshEvent() this->ui.textEdit->setStyleSheet(this->themeManager->splits.input.styleSheet); - this->ui.hbox->setMargin((this->themeManager->isLightTheme() ? 4 : 2) * this->getScale()); + this->ui.hbox->setMargin(int((this->themeManager->isLightTheme() ? 4 : 2) * this->getScale())); } void SplitInput::installKeyPressedEvent() @@ -134,7 +136,7 @@ void SplitInput::installKeyPressedEvent() this->ui.textEdit->keyPressed.connect([this, app](QKeyEvent *event) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { - auto c = this->chatWidget->getChannel(); + auto c = this->split->getChannel(); if (c == nullptr) { return; } @@ -241,8 +243,7 @@ void SplitInput::installKeyPressedEvent() } } else if (event->key() == Qt::Key_Tab) { if (event->modifiers() == Qt::ControlModifier) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = static_cast(this->split->parentWidget()); Notebook *notebook = static_cast(page->parentWidget()); @@ -250,16 +251,15 @@ void SplitInput::installKeyPressedEvent() } } else if (event->key() == Qt::Key_Backtab) { if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { - SplitContainer *page = - static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = static_cast(this->split->parentWidget()); Notebook *notebook = static_cast(page->parentWidget()); notebook->selectPreviousTab(); } } else if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) { - if (this->chatWidget->view.hasSelection()) { - this->chatWidget->doCopy(); + if (this->split->view.hasSelection()) { + this->split->doCopy(); event->accept(); } } @@ -293,13 +293,22 @@ void SplitInput::editTextChanged() // set textLengthLabel value QString text = this->ui.textEdit->toPlainText(); - this->textChanged.invoke(text); + if (text.startsWith("/r ") && this->split->getChannel()->isTwitchChannel()) // + { + QString lastUser = app->twitch.server->lastUserThatWhisperedMe.get(); + if (!lastUser.isEmpty()) { + this->ui.textEdit->setPlainText("/w " + lastUser + text.mid(2)); + this->ui.textEdit->moveCursor(QTextCursor::EndOfBlock); + } + } else { + this->textChanged.invoke(text); - text = text.trimmed(); - static QRegularExpression spaceRegex("\\s\\s+"); - text = text.replace(spaceRegex, " "); + text = text.trimmed(); + static QRegularExpression spaceRegex("\\s\\s+"); + text = text.replace(spaceRegex, " "); - text = app->commands->execCommand(text, this->chatWidget->getChannel(), true); + text = app->commands->execCommand(text, this->split->getChannel(), true); + } QString labelText; @@ -317,7 +326,7 @@ void SplitInput::paintEvent(QPaintEvent *) QPainter painter(this); if (this->themeManager->isLightTheme()) { - int s = (int)(3 * this->getScale()); + int s = int(3 * this->getScale()); QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s)); painter.fillRect(rect, this->themeManager->splits.input.background); @@ -325,7 +334,7 @@ void SplitInput::paintEvent(QPaintEvent *) painter.setPen(QColor("#ccc")); painter.drawRect(rect); } else { - int s = (int)(1 * this->getScale()); + int s = int(1 * this->getScale()); QRect rect = this->rect().marginsRemoved(QMargins(s, s, s, s)); painter.fillRect(rect, this->themeManager->splits.input.background); @@ -346,7 +355,7 @@ void SplitInput::resizeEvent(QResizeEvent *) void SplitInput::mousePressEvent(QMouseEvent *) { - this->chatWidget->giveFocus(Qt::MouseFocusReason); + this->split->giveFocus(Qt::MouseFocusReason); } } // namespace widgets diff --git a/src/widgets/helper/splitinput.hpp b/src/widgets/helper/splitinput.hpp index a4c9e45c..b73a6421 100644 --- a/src/widgets/helper/splitinput.hpp +++ b/src/widgets/helper/splitinput.hpp @@ -40,7 +40,7 @@ protected: virtual void mousePressEvent(QMouseEvent *event) override; private: - Split *const chatWidget; + Split *const split; std::unique_ptr emotePopup; struct { diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 46168a72..60396d5d 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -43,18 +43,21 @@ Window::Window(WindowType _type) this->addTitleBarButton(TitleBarButton::Settings, [app] { app->windows->showSettingsDialog(); // }); - auto user = this->addTitleBarLabel([app] { - app->windows->showAccountSelectPopup(QCursor::pos()); // + + this->userLabel = this->addTitleBarLabel([this, app] { + app->windows->showAccountSelectPopup( + this->userLabel->mapToGlobal(this->userLabel->rect().bottomLeft())); // }); - app->accounts->Twitch.currentUserChanged.connect( - [=] { user->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); }); + app->accounts->Twitch.currentUserChanged.connect([=] { + this->userLabel->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); + }); } if (_type == Window::Main) { - this->resize((int)(600 * this->getScale()), (int)(500 * this->getScale())); + this->resize(int(600 * this->getScale()), int(500 * this->getScale())); } else { - this->resize((int)(300 * this->getScale()), (int)(500 * this->getScale())); + this->resize(int(300 * this->getScale()), int(500 * this->getScale())); } QVBoxLayout *layout = new QVBoxLayout(this); @@ -162,11 +165,13 @@ bool Window::event(QEvent *event) } } } break; + + default:; }; return BaseWindow::event(event); } -void Window::closeEvent(QCloseEvent *event) +void Window::closeEvent(QCloseEvent *) { if (this->type == Window::Main) { auto app = getApp(); diff --git a/src/widgets/window.hpp b/src/widgets/window.hpp index fe3d68b3..702bd946 100644 --- a/src/widgets/window.hpp +++ b/src/widgets/window.hpp @@ -42,6 +42,8 @@ protected: bool event(QEvent *event) override; private: + RippleEffectLabel *userLabel; + WindowType type; float dpi; From abd46d0bb8fd078097af3833070bfcb1f9c4c610 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 13:55:55 +0200 Subject: [PATCH 053/121] added spaces when sending whispers --- src/controllers/commands/commandcontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/commands/commandcontroller.cpp b/src/controllers/commands/commandcontroller.cpp index 4265a35e..f1b299f2 100644 --- a/src/controllers/commands/commandcontroller.cpp +++ b/src/controllers/commands/commandcontroller.cpp @@ -169,7 +169,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, QString rest = ""; for (int i = 2; i < words.length(); i++) { - rest += words[i]; + rest += words[i] + " "; } b.emplace(rest, messages::MessageElement::Text); From 50a2454cc640d8be9816712074973de585f14b0e Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 14:57:17 +0200 Subject: [PATCH 054/121] added alt+arrowkeys back --- src/widgets/helper/resizingtextedit.cpp | 11 ++- src/widgets/helper/resizingtextedit.hpp | 3 + src/widgets/helper/splitinput.cpp | 49 ++++------- src/widgets/helper/splitinput.hpp | 6 -- src/widgets/split.cpp | 2 + src/widgets/split.hpp | 1 + src/widgets/splitcontainer.cpp | 112 +++++++++++++++++++++++- src/widgets/splitcontainer.hpp | 26 +++--- 8 files changed, 154 insertions(+), 56 deletions(-) diff --git a/src/widgets/helper/resizingtextedit.cpp b/src/widgets/helper/resizingtextedit.cpp index 4b88ec29..752b1f23 100644 --- a/src/widgets/helper/resizingtextedit.cpp +++ b/src/widgets/helper/resizingtextedit.cpp @@ -137,10 +137,19 @@ void ResizingTextEdit::keyPressEvent(QKeyEvent *event) } } +void ResizingTextEdit::focusInEvent(QFocusEvent *event) +{ + QTextEdit::focusInEvent(event); + + if (event->gotFocus()) { + this->focused.invoke(); + } +} + void ResizingTextEdit::setCompleter(QCompleter *c) { if (this->completer) { - QObject::disconnect(this->completer, 0, this, 0); + QObject::disconnect(this->completer, nullptr, this, nullptr); } this->completer = c; diff --git a/src/widgets/helper/resizingtextedit.hpp b/src/widgets/helper/resizingtextedit.hpp index d68ea0eb..73661977 100644 --- a/src/widgets/helper/resizingtextedit.hpp +++ b/src/widgets/helper/resizingtextedit.hpp @@ -15,6 +15,7 @@ public: bool hasHeightForWidth() const override; pajlada::Signals::Signal keyPressed; + pajlada::Signals::NoArgSignal focused; void setCompleter(QCompleter *c); QCompleter *getCompleter() const; @@ -23,6 +24,8 @@ protected: int heightForWidth(int) const override; void keyPressEvent(QKeyEvent *event) override; + void focusInEvent(QFocusEvent *event) override; + private: QCompleter *completer = nullptr; bool completionInProgress = false; diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index b0644473..803c3608 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -165,16 +165,11 @@ void SplitInput::installKeyPressedEvent() return; } if (event->modifiers() == Qt::AltModifier) { - // SplitContainer *page = - // static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = this->split->getContainer(); - // page->requestFocus(); - // int reqX = page->currentX; - // int reqY = page->lastRequestedY[reqX] - 1; - - // qDebug() << "Alt+Down to" << reqX << "/" << reqY; - - // page->requestFocus(reqX, reqY); + if (page != nullptr) { + page->selectNextSplit(SplitContainer::Above); + } } else { if (this->prevMsg.size() && this->prevIndex) { if (this->prevIndex == (this->prevMsg.size())) { @@ -194,15 +189,11 @@ void SplitInput::installKeyPressedEvent() return; } if (event->modifiers() == Qt::AltModifier) { - // SplitContainer *page = - // static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = this->split->getContainer(); - // int reqX = page->currentX; - // int reqY = page->lastRequestedY[reqX] + 1; - - // qDebug() << "Alt+Down to" << reqX << "/" << reqY; - - // page->requestFocus(reqX, reqY); + if (page != nullptr) { + page->selectNextSplit(SplitContainer::Below); + } } else { if (this->prevIndex != (this->prevMsg.size() - 1) && this->prevIndex != this->prevMsg.size()) { @@ -219,27 +210,19 @@ void SplitInput::installKeyPressedEvent() } } else if (event->key() == Qt::Key_Left) { if (event->modifiers() == Qt::AltModifier) { - // SplitContainer *page = - // static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = this->split->getContainer(); - // int reqX = page->currentX - 1; - // int reqY = page->lastRequestedY[reqX]; - - // qDebug() << "Alt+Left to" << reqX << "/" << reqY; - - // page->requestFocus(reqX, reqY); + if (page != nullptr) { + page->selectNextSplit(SplitContainer::Left); + } } } else if (event->key() == Qt::Key_Right) { if (event->modifiers() == Qt::AltModifier) { - // SplitContainer *page = - // static_cast(this->chatWidget->parentWidget()); + SplitContainer *page = this->split->getContainer(); - // int reqX = page->currentX + 1; - // int reqY = page->lastRequestedY[reqX]; - - // qDebug() << "Alt+Right to" << reqX << "/" << reqY; - - // page->requestFocus(reqX, reqY); + if (page != nullptr) { + page->selectNextSplit(SplitContainer::Right); + } } } else if (event->key() == Qt::Key_Tab) { if (event->modifiers() == Qt::ControlModifier) { diff --git a/src/widgets/helper/splitinput.hpp b/src/widgets/helper/splitinput.hpp index b73a6421..9f7d7fdb 100644 --- a/src/widgets/helper/splitinput.hpp +++ b/src/widgets/helper/splitinput.hpp @@ -52,12 +52,6 @@ private: } ui; std::vector managedConnections; - // QHBoxLayout hbox; - // QVBoxLayout vbox; - // QHBoxLayout editContainer; - // ResizingTextEdit textInput; - // QLabel textLengthLabel; - // RippleEffectLabel emotesLabel; QStringList prevMsg; QString currMsg; int prevIndex = 0; diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index c3f9f1e9..dda289b3 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -136,6 +136,8 @@ Split::Split(QWidget *parent) this->overlay->hide(); } }); + + this->input.ui.textEdit->focused.connect([this] { this->focused.invoke(); }); } Split::~Split() diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 41257550..35b97485 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -46,6 +46,7 @@ public: ~Split() override; pajlada::Signals::NoArgSignal channelChanged; + pajlada::Signals::NoArgSignal focused; ChannelView &getChannelView(); SplitContainer *getContainer(); diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 1ea30419..ad909dfa 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -3,6 +3,7 @@ #include "common.hpp" #include "singletons/thememanager.hpp" #include "singletons/windowmanager.hpp" +#include "util/assertinguithread.hpp" #include "util/helpers.hpp" #include "util/layoutcreator.hpp" #include "widgets/helper/notebooktab.hpp" @@ -83,6 +84,8 @@ void SplitContainer::setTab(NotebookTab *_tab) void SplitContainer::appendNewSplit(bool openChannelNameDialog) { + util::assertInGuiThread(); + Split *split = new Split(this); this->appendSplit(split); @@ -115,6 +118,8 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Split *relat void SplitContainer::insertSplit(Split *split, Direction direction, Node *relativeTo) { + util::assertInGuiThread(); + split->setContainer(this); if (relativeTo == nullptr) { @@ -136,6 +141,8 @@ void SplitContainer::insertSplit(Split *split, Direction direction, Node *relati void SplitContainer::addSplit(Split *split) { + util::assertInGuiThread(); + split->setParent(this); split->show(); split->giveFocus(Qt::MouseFocusReason); @@ -148,12 +155,33 @@ void SplitContainer::addSplit(Split *split) this->tab->setHighlightState(state); } }); + split->focused.connect([this, split] { this->setSelected(split); }); this->layout(); } +void SplitContainer::setSelected(Split *split) +{ + this->selected = split; + + if (Node *node = this->baseNode.findNodeContainingSplit(split)) { + this->setPreferedTargetRecursive(node); + } +} + +void SplitContainer::setPreferedTargetRecursive(Node *node) +{ + if (node->parent != nullptr) { + node->parent->preferedFocusTarget = node; + + this->setPreferedTargetRecursive(node->parent); + } +} + SplitContainer::Position SplitContainer::releaseSplit(Split *split) { + util::assertInGuiThread(); + Node *node = this->baseNode.findNodeContainingSplit(split); assert(node != nullptr); @@ -161,7 +189,9 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) split->setParent(nullptr); Position position = node->releaseSplit(); this->layout(); - if (splits.size() != 0) { + if (splits.size() == 0) { + this->setSelected(nullptr); + } else { this->splits.front()->giveFocus(Qt::MouseFocusReason); } @@ -174,12 +204,77 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) SplitContainer::Position SplitContainer::deleteSplit(Split *split) { + util::assertInGuiThread(); assert(split != nullptr); split->deleteLater(); return releaseSplit(split); } +void SplitContainer::selectNextSplit(Direction direction) +{ + util::assertInGuiThread(); + + if (Node *node = this->baseNode.findNodeContainingSplit(this->selected)) { + this->selectSplitRecursive(node, direction); + } +} + +void SplitContainer::selectSplitRecursive(Node *node, Direction direction) +{ + if (node->parent != nullptr) { + if (node->parent->type == Node::toContainerType(direction)) { + auto &siblings = node->parent->children; + + auto it = std::find_if(siblings.begin(), siblings.end(), + [node](const auto &other) { return other.get() == node; }); + assert(it != siblings.end()); + + if (direction == Direction::Left || direction == Direction::Above) { + if (it == siblings.begin()) { + this->selectSplitRecursive(node->parent, direction); + } else { + this->focusSplitRecursive(siblings[it - siblings.begin() - 1].get(), direction); + } + } else { + if (it->get() == siblings.back().get()) { + this->selectSplitRecursive(node->parent, direction); + } else { + this->focusSplitRecursive(siblings[it - siblings.begin() + 1].get(), direction); + } + } + } else { + this->selectSplitRecursive(node->parent, direction); + } + } +} + +void SplitContainer::focusSplitRecursive(Node *node, Direction direction) +{ + switch (node->type) { + case Node::_Split: { + node->split->giveFocus(Qt::OtherFocusReason); + } break; + + case Node::HorizontalContainer: + case Node::VerticalContainer: { + auto &children = node->children; + + auto it = std::find_if(children.begin(), children.end(), [node](const auto &other) { + return node->preferedFocusTarget == other.get(); + }); + + if (it != children.end()) { + this->focusSplitRecursive(it->get(), direction); + } else { + this->focusSplitRecursive(node->children.front().get(), direction); + } + } break; + + default:; + } +} + void SplitContainer::layout() { this->baseNode.geometry = this->rect(); @@ -383,6 +478,21 @@ void SplitContainer::refreshTabTitle() this->tab->setTitle(newTitle); } +int SplitContainer::getSplitCount() +{ + return 0; +} + +const std::vector SplitContainer::getSplits() const +{ + return this->splits; +} + +SplitContainer::Node *SplitContainer::getBaseNode() +{ + return &this->baseNode; +} + void SplitContainer::decodeFromJson(QJsonObject &obj) { assert(this->baseNode.type == Node::EmptyRoot); diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index ec04ddc7..00459430 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -94,6 +94,7 @@ public: private: Type type; Split *split; + Node *preferedFocusTarget; Node *parent; QRectF geometry; qreal flexH = 1; @@ -175,25 +176,15 @@ public: Position releaseSplit(Split *split); Position deleteSplit(Split *split); + void selectNextSplit(Direction direction); + void decodeFromJson(QJsonObject &obj); - int getSplitCount() - { - return 0; - } - - const std::vector getSplits() const - { - return this->splits; - } - + int getSplitCount(); + const std::vector getSplits() const; void refreshTabTitle(); - NotebookTab *getTab() const; - Node *getBaseNode() - { - return &this->baseNode; - } + Node *getBaseNode(); void setTab(NotebookTab *tab); @@ -235,6 +226,11 @@ private: void layout(); Node baseNode; + Split *selected; + void setSelected(Split *selected); + void selectSplitRecursive(Node *node, Direction direction); + void focusSplitRecursive(Node *node, Direction direction); + void setPreferedTargetRecursive(Node *node); NotebookTab *tab; std::vector splits; From 77630d5c85ef72eabd2754303919a1b0e7739eeb Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 15:03:58 +0200 Subject: [PATCH 055/121] changed resize handle color --- src/widgets/splitcontainer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index ad909dfa..d62d7ec1 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -1017,8 +1017,15 @@ SplitContainer::ResizeHandle::ResizeHandle(SplitContainer *_parent) void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *) { QPainter painter(this); + painter.setPen(QPen(getApp()->themes->splits.dropPreviewBorder, 2)); - painter.fillRect(this->rect(), "#999"); + painter.fillRect(this->rect(), getApp()->themes->splits.dropPreview); + + if (this->vertical) { + painter.drawLine(0, this->height() / 2, this->width(), this->height() / 2); + } else { + painter.drawLine(this->width() / 2, 0, this->width() / 2, this->height()); + } } void SplitContainer::ResizeHandle::mousePressEvent(QMouseEvent *) From f654528e281a8465a13d0ea50985b0543bc7f7dd Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 15:04:41 +0200 Subject: [PATCH 056/121] fixed resizehandles showing when switching tabs --- src/widgets/splitcontainer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index d62d7ec1..fbc7687f 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -1012,6 +1012,7 @@ SplitContainer::ResizeHandle::ResizeHandle(SplitContainer *_parent) , parent(_parent) { this->setMouseTracking(true); + this->hide(); } void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *) From afb5a1e5bf81a7af4adafcd0b5bf6205805bc50b Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 16:11:03 +0200 Subject: [PATCH 057/121] Fixes #309 alt tabbing doesn't hide resize handles --- resources/images/split/splitdown.png | Bin 793 -> 361 bytes resources/images/split/splitleft.png | Bin 812 -> 437 bytes resources/images/split/splitmove.png | Bin 1241 -> 772 bytes resources/images/split/splitright.png | Bin 811 -> 494 bytes resources/images/split/splitup.png | Bin 793 -> 361 bytes src/widgets/helper/splitoverlay.cpp | 29 ++++++++++++++++++-------- src/widgets/splitcontainer.cpp | 14 +++++++++++++ src/widgets/splitcontainer.hpp | 1 + src/widgets/window.cpp | 4 ++++ 9 files changed, 39 insertions(+), 9 deletions(-) diff --git a/resources/images/split/splitdown.png b/resources/images/split/splitdown.png index 3891ca3987cf9dcf52055670823088c1dd67bff8..042f1f4c2e51437cb34afdcf120219e02706162a 100644 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJtM=93Yk-Zif($kIEG|6znx;pb;Lk``S1T<|6a#Tc0C%G{6>0$s04@h1qCL_$Hvb? zx45U@oBrN-&dCH`S)%cS?2P_%CG+pk{Px19x@4KH)#UwKY~Od+mAM;5I0NzF$x~*0 zN=iD_-KnXswrtM~FsIrprpa|nHv84um<=6eZx`(9=N7xTPB!Fw;X$QaTbp0mss3<9 sRu9$uUo1FfLt9_U2h%O?(a&laHU}T5oWGXo0??BTp00i_>zopr0DxbMkpKVy literal 793 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXJq(MA#*AN1Jg85 z7srr_xVKXsv!nw=j?{dPTlC#`w*<>iO&-0^Z&_3>9E<|3hTet0t7f|`urQtx?HM)BzNy;STzMKkFlkL4eg^fKTx=Iyj=B-QGGY>Le zn-&K&YQrGaa-M&|`N}UoK;PV!vz;s{^I)6%d7uw=yuD~Vb0PnNV35#JHRB(`C75Ah zaUd5IOy~Iv>ekQ45ek3qFxI_@1qZ+#Z~&bqmGj{*o5lU;-|Nk%Px}rI=t_NXs3JJ_ z54S#D2M+dk;AkP4^J6D-O*oF2BQk)%Dd>k$!yYkM@>*L{^M^4_>i(Oi9rl-j$(X^@ L)z4*}Q$iB}{<&M) diff --git a/resources/images/split/splitleft.png b/resources/images/split/splitleft.png index 8d1cb0e199051fc8a141e4358dd9f4b83ce8edc5..210b74b48f864b4e19caf703a639540732f0e9a8 100644 GIT binary patch literal 437 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJtM=93Yk-ZrmA|nIEG|6zwI~VVm9PqdH=uk-)ooF7B2l`kIH7}Bq(rr7k{>yzjex= zI{)Z65N;|33%YiN)WnXRv-+@s}^H(I#tubEHE5t@jCH1cmJayASWR zM55Aq7^2NepCA;&lwGYbvnY}D52;y)1 z5`FOPpIkIWKU{SAzW>tMvDaADmJh{TVjY9-^#A!Ay8a0se8)TW?F5g0-m?6PHD6hG X1RSW$J-JvN7^)1Ou6{1-oD!M!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXJq(MA#*AN1Jg=R z7srr_xVKXsvy=lxj?{eKdE}1y-u%a@PpP^u x**5HpK}tu>jC`lhJalHT?1rYe=*sHf>@I2d-_)ibodrzU44$rjF6*2UngBT^H=zIk diff --git a/resources/images/split/splitmove.png b/resources/images/split/splitmove.png index 82bf5382bfb0030eea42e2b9e432e87db63d1252..e4ce85ceadb440dd2d7ee301a2a29f60e14e7a98 100644 GIT binary patch delta 724 zcmcb~*}|sR8Q|y6%O%Cdz`(%k>ERLtq-{W$gAGVp{kd5Lq!^2X+?^QKos)SvQBj)f zFarbMVaDV6D^e!9sn;`=c)B=-WH`T_aWPlMkcV~sk|_@+1vPaS$ZH;4n(N_gW3cOH z?8i0g7TMA>=0^rTI?Vj;@b=u>`$RR?^PJLd{&Z?V%)z(0zkTC)y-dZv9lg;L|0R|a z$o#UG^`GqdBiOvFHx~|`Q{PVK#Vo{^z(4_PvMz`C%OMr@6b>nl* zCv5F065Cd>^{xZZOjFC4Z`uwkpQ(Rv3JBYH^zZSPbN2~OJy5s)qtpF)j88T8c|@~UpQR;>5SminKfnYK?PG9HfN=Ul0CKN5B~=+!AeO!K$kky`NPWB0ArFBj(86uYF>tIfZgsFBf7#J_)stpopW z`Ey@E4(SWJ!0=S^UqJPT`Kq60Dr*`4%z3;ZdG6Nkv7;vZXI zNMB^?VVfsp;;?d=P=%Ss)juq89H$~97PQBIa%`*TU$p=jQt#~3L|*h{+6UPJ1MqZs z^t7$77tHCbXI-&PC;Pgir8KwUtxa2E*`MaaI^FnxnCjybuV_Pz~N^D!*?!P2)@8sQ0vALXBB r1;j~C31@pc)v+=)Aa45GZ{mIrPgte%tGR9mrcee?S3j3^P6!lvI6;>1s;*b z3=Dh+L6~vJ#O${~LCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33JtM=93Yk-Z28DUL zIEGZjy}e`DC*mp4`p}a3(2)mh9ug519Ngl3TgCk5Gce0{8$VEDInKbmfBU3gpMB5! z?0>l`r>o=I5%ty!c1g46O`pnhSTAO3keBA&eK}pTY`>jyTX8daeNGFE>ECf$Axk8w z-&Rqtc|~A{?YEX4Jd4~EzugdiWYi(mdL!AtqxjZadH>Ik&u{k(Tr5!ihO^MF_~-rQ zH~l}WzE%u?itPTYed_(^uJ@j;P(3?;e);ZM$qg~mV)}0X`uQc>8z7ROE&c%|%VCoI z|AD+x7_a^J{C{>U^B7IJHXffR@VwyH%lDI?i9zkzlL|D)1ZK`--TGg@m+WA@qH#p7 z+DRt+?&bUHM|BrKTp#foXwE~p(jMEtwTJSdrXH6BYHYd31o3#E@%@G}j_uFinzXxa zueaM4Zk`}qytAEs!Yl0ueNY$Qd>enys$qJKzZS%@IsUQ^8>|`uuldYXl%R?78#t5&!di(XCe*@6_G3b7pt!b$YW={mAmUdi%~V zm2;RM3KO~a>Cx|)UyNTOOQ2e7uK#-Ud(X^ewG*EERnM*exNG^dd(F^bnO*i<{Lg{o z_bxcky>^^iN(d4J`?lS-f68wCO-=4s&Fp8KzJH;XU4O;!Zua`iIW-Kve?>~F>-;}w z&U$X50&!tYc&$)ga`$^k9N(y(v7g5_vc?_iOkMj9Z@Y%0N6TT}dD*<<2y;X%Ou=he zpn{)gcg)}Otl*<8B)0o!+b{UP?5D-q>`R|>OBv4o*>77nSt)vSiv{MBj0XW?eY(c8>l{Xv1&7984Rl}J}rv%CES-tu{;yHnfhGl0bq NgQu&X%Q~loCIFysR_y=) diff --git a/resources/images/split/splitright.png b/resources/images/split/splitright.png index 173537331602ebd35222e3c96ab9c596e2246bdc..88963fb353acdb959ce1c41dbf403576f805972a 100644 GIT binary patch literal 494 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJtM=93Yk-Zrp9@?IEG|6zwJNRb<{w_>G%KFKkuJQDfAYbxG2Q@aWiwjOYy7Hzjw+f z?Z3aZ_*8_YoYc+ZdVZ4HpVR+yu%DRxt$YLb6VKn)H&{O{`78T|sq)f4zOqJ}%zF0S z4)eC`Z;n;)zjgnh-URix?-RsPlyCTV^0e)?6-_p4>OU3r>#kDZe?5QCo#bw<6Ni7P z>wmwoi0c!ElJmEBp89v~(N5>a^^Wtl7f=1W_GocCQ+%^c{&TJ0(cRDaSog72-a8li zZM)ccS*{vhOcRK(1eb#e`0xMz=HNH-8!b}T$nf{RNfuw;)BpP)tKyCWm2EYj9s{F; N!PC{xWt~$(69A=w<^2Ev literal 811 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXJq(MA#*AN1Jepm z7srr_xVKXsvyuY^m}@`BD19&8Be1A*qtAoY@lLB8x=*%0UzTOK-CCxVphNqsJ6%Tw`a`Oqp{Qmm?&;D+;mIn#<@-DxW!?9uG!;o3L zjAmwYyf*)gZ20;1NZj4euih^gdp8lN=GY-ypqdL2JKTY4q`1F(Y*utgekhk|#*(3P zM;T~AZ)^3En*s|q7WA7)Gnvp$(;3}8Alr+%-veEA;Lagypeqfwf9L}`f=H>K@{IfH z_gB5I%dY_ihW592-;bFC{k00#LT=By}Z;C1rt33 zJtM=93Yk-Zif($kIEG|6znx;pb;Lk``S1T<|6a#Tc0C%G{6>0$s04@h1qCL_$Hvb? zx45U@oBrN-&dCH`Ss?ZgN;=iusj08FY|o4tpOV0w_nL*AVi(uRhI}tPsB~*<^D8^O z2$$Vu0e6qHc4u7YTlIU&194R(^-#_KqJ1VFl(L$1@cIS0xi9VZ8^66c^VJqj3 t%<6mMf8X?_Y-l4Me=yzR9{sF_L2c`SO74;yaX?Qpc)I$ztaD0e0ssVxl#~Df literal 793 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXJq(MA#*AN1Jg85 z7srr_xVKXsv!nw=j?{dPTlC#`w*<>iO&-0^Z&_3>9E<|3hTet0t7f|`urQtx?HM)BzNy;STzMOjILoxf0h2^S1<8Bo}q)6o){x1EX zCjagJdofY>yg^nx-J8P39ubY;{BOBFZ8zA$o!T=GGF~H@^YA&}hggK0A24KluLs*v zr3&<2gWdCs#z1rPs=xRE?Y}K&J6TfZ!8Z5vDQ$KQe$YUA;C~^PtN=o>3>Zq6AYOsetFocusPolicy(Qt::NoFocus); down->setFocusPolicy(Qt::NoFocus); - move->setCursor(Qt::PointingHandCursor); + move->setCursor(Qt::SizeAllCursor); left->setCursor(Qt::PointingHandCursor); right->setCursor(Qt::PointingHandCursor); up->setCursor(Qt::PointingHandCursor); down->setCursor(Qt::PointingHandCursor); this->managedConnect(this->scaleChanged, [=](float _scale) { - int a = _scale * 40; + int a = int(_scale * 30); QSize size(a, a); move->setIconSize(size); @@ -83,9 +83,11 @@ SplitOverlay::SplitOverlay(Split *parent) }); this->setMouseTracking(true); + + this->setCursor(Qt::ArrowCursor); } -void SplitOverlay::paintEvent(QPaintEvent *event) +void SplitOverlay::paintEvent(QPaintEvent *) { QPainter painter(this); painter.fillRect(this->rect(), QColor(0, 0, 0, 150)); @@ -95,16 +97,22 @@ void SplitOverlay::paintEvent(QPaintEvent *event) case SplitLeft: { rect = QRect(0, 0, this->width() / 2, this->height()); } break; + case SplitRight: { rect = QRect(this->width() / 2, 0, this->width() / 2, this->height()); } break; + case SplitUp: { rect = QRect(0, 0, this->width(), this->height() / 2); } break; + case SplitDown: { rect = QRect(0, this->height() / 2, this->width(), this->height() / 2); } break; + + default:; } + if (!rect.isNull()) { painter.setPen(getApp()->themes->splits.dropPreviewBorder); painter.setBrush(getApp()->themes->splits.dropPreview); @@ -174,19 +182,22 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched, QEvent *even this->parent->split->drag(); } return true; - } else { + } + } break; + case QEvent::MouseButtonRelease: { + if (this->hoveredElement != HoveredElement::SplitMove) { SplitContainer *container = this->parent->split->getContainer(); if (container != nullptr) { auto *_split = new Split(container); - container->insertSplit( - _split, - (SplitContainer::Direction)(this->hoveredElement + SplitContainer::Left - - SplitLeft), - this->parent->split); + auto dir = SplitContainer::Direction(this->hoveredElement + + SplitContainer::Left - SplitLeft); + container->insertSplit(_split, dir, this->parent->split); + this->parent->hide(); } } } break; + default:; } return QObject::eventFilter(watched, event); } diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index fbc7687f..386eecc4 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -82,6 +82,15 @@ void SplitContainer::setTab(NotebookTab *_tab) this->refreshTabTitle(); } +void SplitContainer::hideResizeHandles() +{ + this->overlay.hide(); + + for (std::unique_ptr &handle : this->resizeHandles) { + handle->hide(); + } +} + void SplitContainer::appendNewSplit(bool openChannelNameDialog) { util::assertInGuiThread(); @@ -155,6 +164,7 @@ void SplitContainer::addSplit(Split *split) this->tab->setHighlightState(state); } }); + split->focused.connect([this, split] { this->setSelected(split); }); this->layout(); @@ -197,6 +207,10 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) this->refreshTabTitle(); + // fourtf: really bad + split->getChannelView().tabHighlightRequested.disconnectAll(); + split->focused.disconnectAll(); + split->getChannelView().tabHighlightRequested.disconnectAll(); return position; diff --git a/src/widgets/splitcontainer.hpp b/src/widgets/splitcontainer.hpp index 00459430..eb7e7a00 100644 --- a/src/widgets/splitcontainer.hpp +++ b/src/widgets/splitcontainer.hpp @@ -187,6 +187,7 @@ public: Node *getBaseNode(); void setTab(NotebookTab *tab); + void hideResizeHandles(); static bool isDraggingSplit; static Split *draggingSplit; diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 60396d5d..331e143f 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -164,6 +164,10 @@ bool Window::event(QEvent *event) split->updateLastReadMessage(); } } + + if (SplitContainer *container = dynamic_cast(page)) { + container->hideResizeHandles(); + } } break; default:; From 8e8990b339b17a0c90ea9d40c1bcf7416a0a71e1 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 16:20:39 +0200 Subject: [PATCH 058/121] fixes #400 --- src/messages/layouts/messagelayout.cpp | 2 +- src/widgets/splitcontainer.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index a55d94a2..b28e16f3 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -196,7 +196,7 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection // draw message seperation line if (app->settings->seperateMessages.getValue()) { - painter.fillRect(0, y + this->m_container.getHeight() - 1, this->m_container.getWidth(), 1, + painter.fillRect(0, y, this->m_container.getWidth(), 1, app->themes->splits.messageSeperator); } diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index 386eecc4..580ebae0 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -155,6 +155,7 @@ void SplitContainer::addSplit(Split *split) split->setParent(this); split->show(); split->giveFocus(Qt::MouseFocusReason); + this->unsetCursor(); this->splits.push_back(split); this->refreshTabTitle(); @@ -201,6 +202,7 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) this->layout(); if (splits.size() == 0) { this->setSelected(nullptr); + this->setCursor(Qt::PointingHandCursor); } else { this->splits.front()->giveFocus(Qt::MouseFocusReason); } @@ -209,7 +211,6 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) // fourtf: really bad split->getChannelView().tabHighlightRequested.disconnectAll(); - split->focused.disconnectAll(); split->getChannelView().tabHighlightRequested.disconnectAll(); From 5f76903849dc8b7e65627802aec2c3475103dbde Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 16:24:24 +0200 Subject: [PATCH 059/121] Fixes #395 dropdown for search --- src/widgets/helper/splitheader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 0dc58e4e..904d2af2 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -109,6 +109,7 @@ void SplitHeader::addDropdownItems(RippleEffectButton *label) // this->dropdownMenu.addAction("Move split", this, SLOT(menuMoveSplit())); this->dropdownMenu.addAction("Popup", this->split, &Split::doPopup); this->dropdownMenu.addAction("Open viewer list", this->split, &Split::doOpenViewerList); + this->dropdownMenu.addAction("Search in messages", this->split, &Split::doSearch, QKeySequence(tr("Ctrl+F"))); this->dropdownMenu.addSeparator(); #ifdef USEWEBENGINE this->dropdownMenu.addAction("Start watching", this, [this]{ From 8ccd00a4d87a2c537beac193b609a8c0780a60e1 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 16:48:35 +0200 Subject: [PATCH 060/121] fixed building on mac --- src/messages/layouts/messagelayout.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index b28e16f3..323f510e 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -160,8 +160,8 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection if (!pixmap) { #ifdef Q_OS_MACOS pixmap = - new QPixmap((int)(this->container.getWidth() * painter.device()->devicePixelRatioF()), - (int)(this->container.getHeight() * painter.device()->devicePixelRatioF())); + new QPixmap(int(this->m_container.getWidth() * painter.device()->devicePixelRatioF()), + int(this->m_container.getHeight() * painter.device()->devicePixelRatioF())); pixmap->setDevicePixelRatio(painter.device()->devicePixelRatioF()); #else pixmap = From 302af3aaa117f60a113d17ec1acbb9f372d57e20 Mon Sep 17 00:00:00 2001 From: LajamerrMittesdine Date: Thu, 24 May 2018 11:48:25 -0400 Subject: [PATCH 061/121] Changed bit badge overlay text to be consistent with Twitch Currently it uses "Twitch Bit(x)". Changed to "cheer x" to be consistent with Twitch text overlay. --- src/providers/twitch/twitchmessagebuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index bdfaabf2..9898b91f 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -577,7 +577,7 @@ void TwitchMessageBuilder::appendTwitchBadges() QString cheerAmountQS = badge.mid(5); std::string versionKey = cheerAmountQS.toStdString(); - QString tooltip = QString("Twitch Bits (") + cheerAmountQS + ")"; + QString tooltip = QString("cheer ") + cheerAmountQS; // Try to fetch channel-specific bit badge try { From 0c9a079bd535c3a95fd9a659f1e809e59d75ff25 Mon Sep 17 00:00:00 2001 From: LajamerrMittesdine Date: Fri, 25 May 2018 09:45:34 -0400 Subject: [PATCH 062/121] Corrected style for Twitch Bit Badge overlay text to be consistent with Chatterino Changed from "cheer x" to "Twitch cheer x" --- src/providers/twitch/twitchmessagebuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 9898b91f..be8f2a1e 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -577,7 +577,7 @@ void TwitchMessageBuilder::appendTwitchBadges() QString cheerAmountQS = badge.mid(5); std::string versionKey = cheerAmountQS.toStdString(); - QString tooltip = QString("cheer ") + cheerAmountQS; + QString tooltip = QString("Twitch cheer ") + cheerAmountQS; // Try to fetch channel-specific bit badge try { From c31cccb8b3b43dd3c2d3939322e8f3c5470a3532 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 17:08:09 +0200 Subject: [PATCH 063/121] moved building instructions into different md files --- BUILDING_ON_LINUX.md | 19 ++++++++ BUILDING_ON_MAC.md | 22 +++++++++ BUILDING_ON_WINDOWS.md | 47 ++++++++++++++++++ README.md | 106 +++-------------------------------------- 4 files changed, 95 insertions(+), 99 deletions(-) create mode 100644 BUILDING_ON_LINUX.md create mode 100644 BUILDING_ON_MAC.md create mode 100644 BUILDING_ON_WINDOWS.md diff --git a/BUILDING_ON_LINUX.md b/BUILDING_ON_LINUX.md new file mode 100644 index 00000000..fed82527 --- /dev/null +++ b/BUILDING_ON_LINUX.md @@ -0,0 +1,19 @@ +# Linux +## Ubuntu 16.04.2 LTS +*most likely works the same for other Debian-like distros* +1. install QT Creator `sudo apt-get install qtcreator qtmultimedia5-dev` +2. install boost-dev `sudo apt-get install libboost-dev` +3. open `chatterino.pro` with QT Creator and build + +## Ubuntu 18.04 +*most likely works the same for other Debian-like distros* +1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev` +2. Install rapidjson to `/usr/local/` like this: From the Chatterino2 root folder: `sudo cp -r lib/rapidjson/include/rapidjson /usr/local/include`. If you want to install it to another place, you have to make sure it's in the chatterino.pro include path +3. open `chatterino.pro` with QT Creator and build + +## Arch Linux +install [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) from the aur or build manually as follows: +1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson` +2. go into project directory +3. create build folder `mkdir build && cd build` +4. `qmake .. && make` diff --git a/BUILDING_ON_MAC.md b/BUILDING_ON_MAC.md new file mode 100644 index 00000000..f4def52a --- /dev/null +++ b/BUILDING_ON_MAC.md @@ -0,0 +1,22 @@ +# Building on Mac OSX +1. install Xcode and Xcode Command Line Utilites +2. start Xcode, settings -> Locations, activate your Command Line Tools +3. install Qt Creator +4. install brew https://brew.sh/ +5. `brew install boost openssl rapidjson` +6. build the project using Qt Creator + +If the Project does not build at this point, you might need to add additional Paths/Libs, because brew does not install openssl and boost in the common path. You can get their path using + +`brew info openssl` +`brew info boost` + +The lines which you need to add to your project file should look similar to this + +``` +INCLUDEPATH += /usr/local/opt/openssl/include +LIBS += -L/usr/local/opt/openssl/lib + +INCLUDEPATH += "/usr/local/Cellar/boost/1.67.0_1/include" +LIBS += -L"/usr/local/Cellar/boost/1.67.0_1/lib" +``` diff --git a/BUILDING_ON_WINDOWS.md b/BUILDING_ON_WINDOWS.md new file mode 100644 index 00000000..aa1b7746 --- /dev/null +++ b/BUILDING_ON_WINDOWS.md @@ -0,0 +1,47 @@ +# Building on Windows (Recommended) +## Using Qt Creator +### Visual Studio 2017 +1. Install Visual Studio 2017 and select "Desktop development with C++" and "Universal Windows Platform development. + +### Boost +1. Visual Studio 2017 64-bit: https://dl.bintray.com/boostorg/release/1.66.0/binaries/boost_1_66_0-msvc-14.1-64.exe +2. When prompted, install boost to C:\local\boost +3. When the installation is finished, go to C:\local\boost and rename the "lib64-msvc-14.1" folder to "lib" + +### OpenSSL +#### For our websocket library, we need OpenSSL 1.1 +1. Download OpenSSL development library: https://slproweb.com/download/Win64OpenSSL-1_1_0h.exe +2. When prompted, install openssl to C:\local\openssl +3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory" +#### For Qt SSL, we need OpenSSL 1.0 +1. Download OpenSSL light: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2o.exe +2. When prompted, install it anywhere +3. When prompted, copy the OpenSSL DLLS to "The OpenSSL binaries (/bin) directory" +4. Copy the OpenSSL 1.0 files from its /bin folder to C:/local/bin (You will need to create the folder) +5. Then copy the OpenSSL 1.1 files from its /bin folder to C:/local/bin (Overwrite any duplicate files) +6. Add C:/local/bin to your path folder (Follow guide here if you don't know how to do it: https://www.computerhope.com/issues/ch000549.htm#windows8 ) + +### Qt +1. Download Qt: https://www.qt.io/download +2. Select "Open source" at the bottom of this page +3. Then select "Download" +#### When prompted which components to install: +1. Under the latest Qt version: + - Select MSVC 2017 64-bit (or MSVC 2015 64-bit if you still use Visual Studio 2015) + - Optionally, enable Qt WebEngine +2. Under Tools: + - Select Qt Creator, and Qt Creator CDB Debugger Support + +# Windows (Using MSYS2, not recommended) +Note: This guide is currently out of date and will not work as is. +Note: This build will have some features missing from the build. + +Building using MSYS2 can be quite easier process. Check out MSYS2 at [msys2.org](http://www.msys2.org/). + +Be sure to add `-j ` as a make argument so it will use all your cpu cores to build. [example setup](https://i.imgur.com/qlESlS1.png) + +You can also add `-o2` to optimize the final binary size but increase compilation time, and add `-pipe` to use more ram in compilation but increase compilation speed +1. open appropriate MSYS2 terminal and do `pacman -S mingw-w64--boost mingw-w64--qt5 mingw-w64--rapidjson` where `` is `x86_64` or `i686` +2. go into the project directory +3. create build folder `mkdir build && cd build` +4. `qmake .. && mingw32-make` diff --git a/README.md b/README.md index 08c94852..6004c971 100644 --- a/README.md +++ b/README.md @@ -4,107 +4,15 @@ Chatterino 2 Chatterino 2 is the second installment of the Twitch chat client series "Chatterino". For now you can check out Chatterino 1 at [https://chatterino.com](https://chatterino.com). +## Building +Before building run `git submodule update --init --recursive` to get required submodules. + +[Building on windows](../blob/master/BUILDING_ON_WINDOWS.md) +[Building on Linux](../blob/master/BUILDING_ON_LINUX.md) +[Building on Mac](../blob/master/BUILDING_ON_MAC.md) + ## Code style The code is normally formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format. To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b). -## Building -Before building run `git submodule update --init --recursive` to get required submodules. - -### Windows -#### Using Qt Creator -##### Visual Studio 2017 -1. Install Visual Studio 2017 and select "Desktop development with C++" and "Universal Windows Platform development. - -##### Boost -1. Visual Studio 2017 64-bit: https://dl.bintray.com/boostorg/release/1.66.0/binaries/boost_1_66_0-msvc-14.1-64.exe -2. When prompted, install boost to C:\local\boost -3. When the installation is finished, go to C:\local\boost and rename the "lib64-msvc-14.1" folder to "lib" - -##### OpenSSL -###### For our websocket library, we need OpenSSL 1.1 -1. Download OpenSSL development library: https://slproweb.com/download/Win64OpenSSL-1_1_0h.exe -2. When prompted, install openssl to C:\local\openssl -3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory" -###### For Qt SSL, we need OpenSSL 1.0 -1. Download OpenSSL light: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2o.exe -2. When prompted, install it anywhere -3. When prompted, copy the OpenSSL DLLS to "The OpenSSL binaries (/bin) directory" -4. Copy the OpenSSL 1.0 files from its /bin folder to C:/local/bin (You will need to create the folder) -5. Then copy the OpenSSL 1.1 files from its /bin folder to C:/local/bin (Overwrite any duplicate files) -6. Add C:/local/bin to your path folder (Follow guide here if you don't know how to do it: https://www.computerhope.com/issues/ch000549.htm#windows8 ) - -##### Qt -1. Download Qt: https://www.qt.io/download -2. Select "Open source" at the bottom of this page -3. Then select "Download" -###### When prompted which components to install: -1. Under the latest Qt version: - - Select MSVC 2017 64-bit (or MSVC 2015 64-bit if you still use Visual Studio 2015) - - Optionally, enable Qt WebEngine -2. Under Tools: - - Select Qt Creator, and Qt Creator CDB Debugger Support - - -### Windows (Using MSYS2) -Note: This guide is currently out of date and will not work as is. -Note: This build will have some features missing from the build. - -Building using MSYS2 can be quite easier process. Check out MSYS2 at [msys2.org](http://www.msys2.org/). - -Be sure to add `-j ` as a make argument so it will use all your cpu cores to build. [example setup](https://i.imgur.com/qlESlS1.png) - -You can also add `-o2` to optimize the final binary size but increase compilation time, and add `-pipe` to use more ram in compilation but increase compilation speed -1. open appropriate MSYS2 terminal and do `pacman -S mingw-w64--boost mingw-w64--qt5 mingw-w64--rapidjson` where `` is `x86_64` or `i686` -2. go into the project directory -3. create build folder `mkdir build && cd build` -4. `qmake .. && mingw32-make` - -### - -### Linux -#### Ubuntu 16.04.2 LTS -*most likely works the same for other Debian-like distros* -1. install QT Creator `sudo apt-get install qtcreator qtmultimedia5-dev` -2. install boost-dev `sudo apt-get install libboost-dev` -3. open `chatterino.pro` with QT Creator and build - -#### Ubuntu 18.04 -*most likely works the same for other Debian-like distros* -1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev` -2. Install rapidjson to `/usr/local/` like this: From the Chatterino2 root folder: `sudo cp -r lib/rapidjson/include/rapidjson /usr/local/include`. If you want to install it to another place, you have to make sure it's in the chatterino.pro include path -3. open `chatterino.pro` with QT Creator and build - -#### Arch Linux -install [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) from the aur or build manually as follows: -1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson` -2. go into project directory -3. create build folder `mkdir build && cd build` -4. `qmake .. && make` - -### Mac OSX -1. install Xcode and Xcode Command Line Utilites -2. start Xcode, settings -> Locations, activate your Command Line Tools -3. install Qt Creator -4. install brew https://brew.sh/ -5. `brew install boost openssl rapidjson` -6. build the project using Qt Creator - -If the Project does not build at this point, you might need to add additional Paths/Libs, because brew does not install openssl and boost in the common path. You can get their path using - -`brew info openssl` -`brew info boost` - -The lines which you need to add to your project file should look similar to this - -``` -INCLUDEPATH += /usr/local/opt/openssl/include -LIBS += -L/usr/local/opt/openssl/lib - -INCLUDEPATH += "/usr/local/Cellar/boost/1.67.0_1/include" -LIBS += -L"/usr/local/Cellar/boost/1.67.0_1/lib" -``` - - -Test 1 From e5be1d128633ffba4a5d59290c5a894b78f0c9ba Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 17:19:02 +0200 Subject: [PATCH 064/121] fixed minor issues in the readme --- README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6004c971..02329cff 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,24 @@ Chatterino 2 is the second installment of the Twitch chat client series "Chatter ## Building Before building run `git submodule update --init --recursive` to get required submodules. -[Building on windows](../blob/master/BUILDING_ON_WINDOWS.md) -[Building on Linux](../blob/master/BUILDING_ON_LINUX.md) -[Building on Mac](../blob/master/BUILDING_ON_MAC.md) +[Building on Windows](../master/BUILDING_ON_WINDOWS.md) + +[Building on Linux](../master/BUILDING_ON_LINUX.md) + +[Building on Mac](../master/BUILDING_ON_MAC.md) ## Code style -The code is normally formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format. +The code is formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format. To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b). +### Get it automated with QT Creator + Beautifier + Clang Format +1. Download LLVM: http://releases.llvm.org/5.0.1/LLVM-5.0.1-win64.exe +2. During the installation, make sure to add it to your path +3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin +4. Restart QT Creator +5. Select `Tools` > `Options` > `Beautifier` +6. Under `General` select `Tool: ClangFormat` and enable `Automatic Formatting on File Save` +7. Under `Clang Format` select `Use predefined style: File` and `Fallback style: None` + +Qt creator should now format the documents when saving it. From 90551acf3717bbd6982c5bf1d172356d0785882f Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 25 May 2018 18:23:13 +0200 Subject: [PATCH 065/121] changed "justinfanXXX" to "anonymous" --- src/widgets/window.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 331e143f..d90d3118 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -31,11 +31,17 @@ Window::Window(WindowType _type) { auto app = getApp(); - app->accounts->Twitch.currentUsername.connect([this](const std::string &newUsername, auto) { - if (newUsername.empty()) { + app->accounts->Twitch.currentUserChanged.connect([this] { + auto user = getApp()->accounts->Twitch.getCurrent(); + + if (user->isAnon()) { this->refreshWindowTitle("Not logged in"); + + this->userLabel->getLabel().setText("anonymous"); } else { - this->refreshWindowTitle(QString::fromStdString(newUsername)); + this->refreshWindowTitle(user->getUserName()); + + this->userLabel->getLabel().setText(user->getUserName()); } }); @@ -48,10 +54,6 @@ Window::Window(WindowType _type) app->windows->showAccountSelectPopup( this->userLabel->mapToGlobal(this->userLabel->rect().bottomLeft())); // }); - - app->accounts->Twitch.currentUserChanged.connect([=] { - this->userLabel->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); - }); } if (_type == Window::Main) { From 08cf701af36d058157248fbb3cdb0472579cc94f Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 26 May 2018 13:38:25 +0200 Subject: [PATCH 066/121] quick linux fix --- src/widgets/window.cpp | 4 ++++ src/widgets/window.hpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index d90d3118..c5f84dc6 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -32,6 +32,10 @@ Window::Window(WindowType _type) auto app = getApp(); app->accounts->Twitch.currentUserChanged.connect([this] { + if (this->userLabel == nullptr) { + return; + } + auto user = getApp()->accounts->Twitch.getCurrent(); if (user->isAnon()) { diff --git a/src/widgets/window.hpp b/src/widgets/window.hpp index 702bd946..75c3b2cd 100644 --- a/src/widgets/window.hpp +++ b/src/widgets/window.hpp @@ -42,7 +42,7 @@ protected: bool event(QEvent *event) override; private: - RippleEffectLabel *userLabel; + RippleEffectLabel *userLabel = nullptr; WindowType type; float dpi; From 6b24f249f71046c5bc4057fd8d2da7af21e13683 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sat, 26 May 2018 16:31:43 +0200 Subject: [PATCH 067/121] fixed split header tooltips not showing --- src/messages/layouts/messagelayout.cpp | 3 +- src/providers/twitch/twitchchannel.cpp | 5 +++ src/providers/twitch/twitchchannel.hpp | 1 + src/widgets/basewidget.hpp | 2 +- src/widgets/basewindow.cpp | 4 +-- src/widgets/helper/channelview.cpp | 4 +-- src/widgets/helper/splitheader.cpp | 50 ++++++++++++++++---------- src/widgets/helper/splitheader.hpp | 5 +-- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index 323f510e..7be83b95 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -48,7 +48,7 @@ int MessageLayout::getHeight() const // return true if redraw is required bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) { - BenchmarkGuard benchmark("MessageLayout::layout()"); + // BenchmarkGuard benchmark("MessageLayout::layout()"); auto app = getApp(); @@ -107,7 +107,6 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) } // return if no layout is required - qDebug() << layoutRequired; if (!layoutRequired) { return false; diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index ab663ad3..0243bae4 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -325,6 +325,11 @@ void TwitchChannel::refreshLiveStatus() QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; channel->streamStatus.rerun = false; + if (stream.HasMember("stream_type")) { + channel->streamStatus.streamType = stream["stream_type"].GetString(); + } else { + channel->streamStatus.streamType = QString(); + } if (stream.HasMember("broadcast_platform")) { const auto &broadcastPlatformValue = stream["broadcast_platform"]; diff --git a/src/providers/twitch/twitchchannel.hpp b/src/providers/twitch/twitchchannel.hpp index 8a8e5727..52a3e172 100644 --- a/src/providers/twitch/twitchchannel.hpp +++ b/src/providers/twitch/twitchchannel.hpp @@ -32,6 +32,7 @@ public: QString title; QString game; QString uptime; + QString streamType; }; struct UserState { diff --git a/src/widgets/basewidget.hpp b/src/widgets/basewidget.hpp index 6e6702a0..f6967600 100644 --- a/src/widgets/basewidget.hpp +++ b/src/widgets/basewidget.hpp @@ -18,7 +18,7 @@ class BaseWidget : public QWidget public: explicit BaseWidget(QWidget *parent, Qt::WindowFlags f = Qt::WindowFlags()); - virtual ~BaseWidget(); + virtual ~BaseWidget() override; float getScale() const; pajlada::Signals::Signal scaleChanged; diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index 139a42e9..2e8abe3b 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -253,8 +253,8 @@ void BaseWindow::leaveEvent(QEvent *) void BaseWindow::moveTo(QWidget *parent, QPoint point) { - point.rx() += 16; - point.ry() += 16; + // point.rx() += 16; + // point.ry() += 16; this->move(point); this->moveIntoDesktopRect(parent); diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 38c1e963..3172fc1c 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -175,7 +175,7 @@ void ChannelView::layoutMessages() void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) { - BenchmarkGuard benchmark("layout messages"); + // BenchmarkGuard benchmark("layout messages"); auto app = getApp(); @@ -579,7 +579,7 @@ bool ChannelView::isPaused() void ChannelView::paintEvent(QPaintEvent * /*event*/) { - BenchmarkGuard benchmark("paint event"); + // BenchmarkGuard benchmark("paint event"); QPainter painter(this); diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 904d2af2..f7fb6af1 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -31,7 +31,6 @@ SplitHeader::SplitHeader(Split *_split) , split(_split) { auto app = getApp(); - this->setMouseTracking(true); util::LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); @@ -53,13 +52,13 @@ SplitHeader::SplitHeader(Split *_split) // channel name label // auto title = layout.emplacefourtf"); - created->setTextFormat(Qt::RichText); - created->setTextInteractionFlags(Qt::TextBrowserInteraction | - Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByKeyboard); - created->setOpenExternalLinks(true); - // created->setPalette(palette); + { + created->setText( + "Twitch Chat Client created by fourtf"); + created->setTextFormat(Qt::RichText); + created->setTextInteractionFlags(Qt::TextBrowserInteraction | + Qt::LinksAccessibleByKeyboard | + Qt::LinksAccessibleByKeyboard); + created->setOpenExternalLinks(true); + // created->setPalette(palette); + } auto github = layout.emplace(); - github->setText( - "Chatterino on Github"); - github->setTextFormat(Qt::RichText); - github->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByKeyboard); - github->setOpenExternalLinks(true); - // github->setPalette(palette); + { + github->setText( + "Chatterino on Github"); + github->setTextFormat(Qt::RichText); + github->setTextInteractionFlags(Qt::TextBrowserInteraction | + Qt::LinksAccessibleByKeyboard | + Qt::LinksAccessibleByKeyboard); + github->setOpenExternalLinks(true); + // github->setPalette(palette); + } + + auto licenses = layout.emplace("Open source software used"); + { + auto form = licenses.emplace(); + + addLicense(*form, "Qt Framework", "https://www.qt.io", ":/licenses/qt_lgpl-3.0.txt"); + addLicense(*form, "Boost", "https://www.boost.org/", ":/licenses/boost_boost.txt"); + addLicense(*form, "Fmt", "http://fmtlib.net/", ":/licenses/fmt_bsd2.txt"); + addLicense(*form, "LibCommuni", "https://github.com/communi/libcommuni", + ":/licenses/libcommuni_BSD3.txt"); + addLicense(*form, "OpenSSL", "https://www.openssl.org/", ":/licenses/openssl.txt"); + addLicense(*form, "RapidJson", "http://rapidjson.org/", ":/licenses/rapidjson.txt"); + addLicense(*form, "Pajlada/Settings", "https://github.com/pajlada/settings", + ":/licenses/pajlada_settings.txt"); + addLicense(*form, "Pajlada/Signals", "https://github.com/pajlada/signals", + ":/licenses/pajlada_signals.txt"); + addLicense(*form, "Websocketpp", "https://www.zaphoyd.com/websocketpp/", + ":/licenses/websocketpp.txt"); + } } layout->addStretch(1); } +void AboutPage::addLicense(QFormLayout *form, const QString &name, const QString &website, + const QString &licenseLink) +{ + auto *a = new QLabel("" + name + ""); + a->setOpenExternalLinks(true); + auto *b = new SignalLabel(); + b->setText("show license"); + QObject::connect(b, &SignalLabel::mouseUp, [licenseLink] { + auto *edit = new QTextEdit; + + QFile file(licenseLink); + file.open(QIODevice::ReadOnly); + edit->setText(file.readAll()); + edit->setReadOnly(true); + edit->show(); + }); + + form->addRow(a, b); +} + } // namespace settingspages } // namespace widgets } // namespace chatterino diff --git a/src/widgets/settingspages/aboutpage.hpp b/src/widgets/settingspages/aboutpage.hpp index f482244b..20d3e082 100644 --- a/src/widgets/settingspages/aboutpage.hpp +++ b/src/widgets/settingspages/aboutpage.hpp @@ -3,6 +3,7 @@ #include "widgets/settingspages/settingspage.hpp" class QLabel; +class QFormLayout; namespace chatterino { namespace widgets { @@ -15,6 +16,9 @@ public: private: QLabel *logo; + + void addLicense(QFormLayout *form, const QString &name, const QString &website, + const QString &licenseLink); }; } // namespace settingspages From 5e7fc909e7b32e64fbe6bd015d110d842bef5b83 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 4 Jun 2018 21:02:51 +0200 Subject: [PATCH 108/121] fixed an issue --- src/singletons/emotemanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index 5a53caf6..cd70831b 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -130,15 +130,18 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, QString code = emoteObject.value("code").toString(); // emoteObject.value("imageType").toString(); - QString link = linkTemplate; - link.detach(); - auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&] { util::EmoteData emoteData; + QString link = linkTemplate; + link.detach(); emoteData.image1x = new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"), 1, code, code + "
Channel BTTV Emote"); + link = linkTemplate; + link.detach(); emoteData.image2x = new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"), 0.5, code, code + "
Channel BTTV Emote"); + link = linkTemplate; + link.detach(); emoteData.image3x = new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"), 0.25, code, code + "
Channel BTTV Emote"); emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; From 8de0a595337f0529ec8259d3707f8d69710ba700 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 4 Jun 2018 21:05:18 +0200 Subject: [PATCH 109/121] ircconnection and about page --- chatterino.pro | 856 ++++++++++++------------ src/providers/irc/abstractircserver.cpp | 8 +- src/providers/irc/abstractircserver.hpp | 13 +- src/providers/irc/ircconnection2.cpp | 14 + src/providers/irc/ircconnection2.hpp | 17 + src/providers/twitch/twitchserver.cpp | 42 +- src/providers/twitch/twitchserver.hpp | 2 +- src/widgets/settingspages/aboutpage.cpp | 49 +- 8 files changed, 516 insertions(+), 485 deletions(-) create mode 100644 src/providers/irc/ircconnection2.cpp create mode 100644 src/providers/irc/ircconnection2.hpp diff --git a/chatterino.pro b/chatterino.pro index bb6d032f..309ce1a1 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -1,427 +1,429 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-12-28T18:23:35 -# -#------------------------------------------------- - -message(----) - -QT += widgets core gui network multimedia svg -CONFIG += communi -COMMUNI += core model util -CONFIG += c++14 -INCLUDEPATH += src/ -TARGET = chatterino -TEMPLATE = app -DEFINES += QT_DEPRECATED_WARNINGS -PRECOMPILED_HEADER = src/precompiled_header.hpp -CONFIG += precompile_header - -# https://bugreports.qt.io/browse/QTBUG-27018 -equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { - TARGET = bin/chatterino -} - -# Icons -macx:ICON = resources/images/chatterino2.icns -win32:RC_FILE = resources/windows.rc - - -macx { - LIBS += -L/usr/local/lib -} - -# Submodules -include(dependencies/rapidjson.pri) -include(dependencies/settings.pri) -include(dependencies/signals.pri) -include(dependencies/humanize.pri) -include(dependencies/fmt.pri) -DEFINES += IRC_NAMESPACE=Communi -include(dependencies/libcommuni.pri) -include(dependencies/websocketpp.pri) -include(dependencies/openssl.pri) -include(dependencies/boost.pri) - -# Optional feature: QtWebEngine -#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { -# message(Using QWebEngine) -# QT += webenginewidgets -# DEFINES += "USEWEBENGINE" -#} - -linux { - LIBS += -lrt -} - -win32 { - LIBS += -luser32 -} - -# OSX include directory -macx { - INCLUDEPATH += /usr/local/include -} - -# Optional dependency on Windows SDK 7 -!contains(QMAKE_TARGET.arch, x86_64) { - win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) { - LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ - -ldwmapi - - DEFINES += "USEWINSDK" - message(Using Windows SDK 7) - } -} - -# Optional dependency on Windows SDK 10 -contains(QMAKE_TARGET.arch, x86_64) { - WIN_SDK_VERSION = $$(WindowsSDKVersion) - !isEmpty(WIN_SDK_VERSION) { - !equals(WIN_SDK_VERSION, "\\") { - DEFINES += "USEWINSDK" - message(Using Windows SDK 10) - } - } -} - -werr { - QMAKE_CXXFLAGS += -Werror - - message("Enabling error on warning") -} - -# src -SOURCES += \ - src/main.cpp \ - src/application.cpp \ - src/channel.cpp \ - src/channeldata.cpp \ - src/messages/image.cpp \ - src/messages/layouts/messagelayout.cpp \ - src/messages/layouts/messagelayoutcontainer.cpp \ - src/messages/layouts/messagelayoutelement.cpp \ - src/messages/link.cpp \ - src/messages/message.cpp \ - src/messages/messagebuilder.cpp \ - src/messages/messagecolor.cpp \ - src/messages/messageelement.cpp \ - src/providers/irc/abstractircserver.cpp \ - src/providers/twitch/ircmessagehandler.cpp \ - src/providers/twitch/twitchaccount.cpp \ - src/providers/twitch/twitchaccountmanager.cpp \ - src/providers/twitch/twitchchannel.cpp \ - src/providers/twitch/twitchmessagebuilder.cpp \ - src/providers/twitch/twitchserver.cpp \ - src/providers/twitch/pubsub.cpp \ - src/singletons/commandmanager.cpp \ - src/singletons/emotemanager.cpp \ - src/singletons/fontmanager.cpp \ - src/util/completionmodel.cpp \ - src/singletons/helper/loggingchannel.cpp \ - src/singletons/helper/moderationaction.cpp \ - src/singletons/helper/chatterinosetting.cpp \ - src/singletons/loggingmanager.cpp \ - src/singletons/pathmanager.cpp \ - src/singletons/resourcemanager.cpp \ - src/singletons/settingsmanager.cpp \ - src/singletons/thememanager.cpp \ - src/singletons/windowmanager.cpp \ - src/util/networkmanager.cpp \ - src/util/networkrequest.cpp \ - src/widgets/accountpopup.cpp \ - src/widgets/accountswitchpopupwidget.cpp \ - src/widgets/accountswitchwidget.cpp \ - src/widgets/basewidget.cpp \ - src/widgets/basewindow.cpp \ - src/widgets/emotepopup.cpp \ - src/widgets/helper/channelview.cpp \ - src/widgets/helper/droppreview.cpp \ - src/widgets/helper/label.cpp \ - src/widgets/helper/notebookbutton.cpp \ - src/widgets/helper/notebooktab.cpp \ - src/widgets/helper/resizingtextedit.cpp \ - src/widgets/helper/rippleeffectbutton.cpp \ - src/widgets/helper/rippleeffectlabel.cpp \ - src/widgets/helper/scrollbarhighlight.cpp \ - src/widgets/helper/searchpopup.cpp \ - src/widgets/helper/settingsdialogtab.cpp \ - src/widgets/helper/splitcolumn.cpp \ - src/widgets/helper/splitheader.cpp \ - src/widgets/helper/splitinput.cpp \ - src/widgets/helper/titlebarbutton.cpp \ - src/widgets/logindialog.cpp \ - src/widgets/notebook.cpp \ - src/widgets/qualitypopup.cpp \ - src/widgets/scrollbar.cpp \ - src/widgets/settingsdialog.cpp \ - src/widgets/settingspages/aboutpage.cpp \ - src/widgets/settingspages/accountspage.cpp \ - src/widgets/settingspages/appearancepage.cpp \ - src/widgets/settingspages/behaviourpage.cpp \ - src/widgets/settingspages/commandpage.cpp \ - src/widgets/settingspages/emotespage.cpp \ - src/widgets/settingspages/highlightingpage.cpp \ - src/widgets/settingspages/ignoreuserspage.cpp \ - src/widgets/settingspages/keyboardsettingspage.cpp \ - src/widgets/settingspages/logspage.cpp \ - src/widgets/settingspages/moderationpage.cpp \ - src/widgets/settingspages/settingspage.cpp \ - src/widgets/settingspages/specialchannelspage.cpp \ - src/widgets/split.cpp \ - src/widgets/splitcontainer.cpp \ - src/widgets/streamview.cpp \ - src/widgets/textinputdialog.cpp \ - src/widgets/tooltipwidget.cpp \ - src/widgets/window.cpp \ - src/providers/irc/ircaccount.cpp \ - src/providers/irc/ircserver.cpp \ - src/providers/irc/ircchannel2.cpp \ - src/util/streamlink.cpp \ - src/providers/twitch/twitchhelpers.cpp \ - src/widgets/helper/signallabel.cpp \ - src/widgets/helper/debugpopup.cpp \ - src/util/debugcount.cpp \ - src/singletons/nativemessagingmanager.cpp \ - src/util/rapidjson-helpers.cpp \ - src/providers/twitch/pubsubhelpers.cpp \ - src/providers/twitch/pubsubactions.cpp \ - src/providers/twitch/twitchuser.cpp \ - src/widgets/selectchanneldialog.cpp \ - src/singletons/updatemanager.cpp \ - src/widgets/lastruncrashdialog.cpp \ - src/widgets/attachedwindow.cpp \ - src/widgets/settingspages/externaltoolspage.cpp \ - src/widgets/helper/comboboxitemdelegate.cpp \ - src/controllers/commands/command.cpp \ - src/controllers/commands/commandmodel.cpp \ - src/controllers/commands/commandcontroller.cpp \ - src/controllers/highlights/highlightcontroller.cpp \ - src/controllers/highlights/highlightmodel.cpp \ - src/controllers/ignores/ignorecontroller.cpp \ - src/controllers/ignores/ignoremodel.cpp \ - src/widgets/helper/editablemodelview.cpp \ - src/controllers/accounts/accountcontroller.cpp \ - src/controllers/accounts/accountmodel.cpp \ - src/controllers/accounts/account.cpp \ - src/widgets/helper/splitoverlay.cpp \ - src/widgets/helper/dropoverlay.cpp \ - src/widgets/helper/splitnode.cpp \ - src/widgets/notificationpopup.cpp \ - src/controllers/taggedusers/taggeduserscontroller.cpp \ - src/controllers/taggedusers/taggeduser.cpp \ - src/controllers/taggedusers/taggedusersmodel.cpp \ - src/util/emotemap.cpp - -HEADERS += \ - src/precompiled_header.hpp \ - src/channel.hpp \ - src/const.hpp \ - src/debug/log.hpp \ - src/emojis.hpp \ - src/messages/image.hpp \ - src/messages/layouts/messagelayout.hpp \ - src/messages/layouts/messagelayoutcontainer.hpp \ - src/messages/layouts/messagelayoutelement.hpp \ - src/messages/limitedqueue.hpp \ - src/messages/limitedqueuesnapshot.hpp \ - src/messages/link.hpp \ - src/messages/message.hpp \ - src/messages/messagebuilder.hpp \ - src/messages/messagecolor.hpp \ - src/messages/messageelement.hpp \ - src/messages/messageparseargs.hpp \ - src/messages/selection.hpp \ - src/providers/twitch/emotevalue.hpp \ - src/providers/twitch/ircmessagehandler.hpp \ - src/providers/twitch/twitchaccount.hpp \ - src/providers/twitch/twitchaccountmanager.hpp \ - src/providers/twitch/twitchchannel.hpp \ - src/providers/twitch/twitchmessagebuilder.hpp \ - src/providers/twitch/twitchserver.hpp \ - src/providers/twitch/pubsub.hpp \ - src/singletons/commandmanager.hpp \ - src/singletons/emotemanager.hpp \ - src/singletons/fontmanager.hpp \ - src/singletons/helper/chatterinosetting.hpp \ - src/util/completionmodel.hpp \ - src/singletons/helper/loggingchannel.hpp \ - src/singletons/helper/moderationaction.hpp \ - src/singletons/loggingmanager.hpp \ - src/singletons/pathmanager.hpp \ - src/singletons/resourcemanager.hpp \ - src/singletons/thememanager.hpp \ - src/singletons/windowmanager.hpp \ - src/util/benchmark.hpp \ - src/util/concurrentmap.hpp \ - src/util/distancebetweenpoints.hpp \ - src/util/emotemap.hpp \ - src/util/flagsenum.hpp \ - src/util/helpers.hpp \ - src/util/irchelpers.hpp \ - src/util/layoutcreator.hpp \ - src/util/nativeeventhelper.hpp \ - src/util/networkmanager.hpp \ - src/util/networkrequest.hpp \ - src/util/networkrequester.hpp \ - src/util/networkworker.hpp \ - src/util/posttothread.hpp \ - src/util/property.hpp \ - src/util/serialize-custom.hpp \ - src/util/urlfetch.hpp \ - src/widgets/accountpopup.hpp \ - src/widgets/accountswitchpopupwidget.hpp \ - src/widgets/accountswitchwidget.hpp \ - src/widgets/basewidget.hpp \ - src/widgets/basewindow.hpp \ - src/widgets/emotepopup.hpp \ - src/widgets/helper/channelview.hpp \ - src/widgets/helper/droppreview.hpp \ - src/widgets/helper/label.hpp \ - src/widgets/helper/notebookbutton.hpp \ - src/widgets/helper/notebooktab.hpp \ - src/widgets/helper/resizingtextedit.hpp \ - src/widgets/helper/rippleeffectbutton.hpp \ - src/widgets/helper/rippleeffectlabel.hpp \ - src/widgets/helper/scrollbarhighlight.hpp \ - src/widgets/helper/searchpopup.hpp \ - src/widgets/helper/settingsdialogtab.hpp \ - src/widgets/helper/shortcut.hpp \ - src/widgets/helper/signallabel.hpp \ - src/widgets/helper/splitcolumn.hpp \ - src/widgets/helper/splitheader.hpp \ - src/widgets/helper/splitinput.hpp \ - src/widgets/helper/titlebarbutton.hpp \ - src/widgets/notebook.hpp \ - src/widgets/qualitypopup.hpp \ - src/widgets/scrollbar.hpp \ - src/widgets/settingsdialog.hpp \ - src/widgets/settingspages/aboutpage.hpp \ - src/widgets/settingspages/accountspage.hpp \ - src/widgets/settingspages/appearancepage.hpp \ - src/widgets/settingspages/behaviourpage.hpp \ - src/widgets/settingspages/commandpage.hpp \ - src/widgets/settingspages/emotespage.hpp \ - src/widgets/settingspages/highlightingpage.hpp \ - src/widgets/settingspages/ignoreuserspage.hpp \ - src/widgets/settingspages/keyboardsettingspage.hpp \ - src/widgets/settingspages/logspage.hpp \ - src/widgets/settingspages/moderationpage.hpp \ - src/widgets/settingspages/settingspage.hpp \ - src/widgets/settingspages/specialchannelspage.hpp \ - src/widgets/split.hpp \ - src/widgets/splitcontainer.hpp \ - src/widgets/streamview.hpp \ - src/widgets/textinputdialog.hpp \ - src/widgets/tooltipwidget.hpp \ - src/widgets/window.hpp \ - src/common.hpp \ - src/providers/irc/abstractircserver.hpp \ - src/providers/irc/ircaccount.hpp \ - src/providers/irc/ircserver.hpp \ - src/providers/irc/ircchannel2.hpp \ - src/util/streamlink.hpp \ - src/providers/twitch/twitchhelpers.hpp \ - src/util/debugcount.hpp \ - src/widgets/helper/debugpopup.hpp \ - src/version.hpp \ - src/singletons/settingsmanager.hpp \ - src/singletons/nativemessagingmanager.hpp \ - src/util/rapidjson-helpers.hpp \ - src/providers/twitch/pubsubhelpers.hpp \ - src/providers/twitch/pubsubactions.hpp \ - src/providers/twitch/twitchuser.hpp \ - src/widgets/selectchanneldialog.hpp \ - src/singletons/updatemanager.hpp \ - src/widgets/lastruncrashdialog.hpp \ - src/widgets/attachedwindow.hpp \ - src/widgets/settingspages/externaltoolspage.hpp \ - src/util/removescrollareabackground.hpp \ - src/util/standarditemhelper.hpp \ - src/widgets/helper/comboboxitemdelegate.hpp \ - src/util/assertinguithread.hpp \ - src/util/signalvector2.hpp \ - src/util/signalvectormodel.hpp \ - src/controllers/commands/command.hpp \ - src/controllers/commands/commandmodel.hpp \ - src/controllers/commands/commandcontroller.hpp \ - src/controllers/highlights/highlightcontroller.hpp \ - src/controllers/highlights/highlightphrase.hpp \ - src/controllers/highlights/highlightmodel.hpp \ - src/controllers/ignores/ignorecontroller.hpp \ - src/controllers/ignores/ignorephrase.hpp \ - src/controllers/ignores/ignoremodel.hpp \ - src/widgets/helper/editablemodelview.hpp \ - src/controllers/accounts/accountcontroller.hpp \ - src/controllers/accounts/accountmodel.hpp \ - src/controllers/accounts/account.hpp \ - src/util/sharedptrelementless.hpp \ - src/widgets/helper/splitoverlay.hpp \ - src/widgets/helper/dropoverlay.hpp \ - src/widgets/helper/splitnode.hpp \ - src/widgets/notificationpopup.hpp \ - src/controllers/taggedusers/taggeduserscontroller.hpp \ - src/controllers/taggedusers/taggeduser.hpp \ - src/providerid.hpp \ - src/controllers/taggedusers/taggedusersmodel.hpp \ - src/util/qstringhash.hpp \ - src/util/mutexvalue.hpp - -RESOURCES += \ - resources/resources.qrc - -DISTFILES += - -FORMS += \ - forms/accountpopupform.ui - -# Define warning flags for Chatterino -win32-msvc* { - QMAKE_CXXFLAGS_WARN_ON = /W4 - # 4714 - function marked as __forceinline not inlined - # 4996 - occurs when the compiler encounters a function or variable that is marked as deprecated. - # These functions may have a different preferred name, may be insecure or have - # a more secure variant, or may be obsolete. - # 4505 - unreferenced local version has been removed - # 4127 - conditional expression is constant - # 4503 - decorated name length exceeded, name was truncated - # 4100 - unreferences formal parameter - # 4305 - possible truncation of data - # 4267 - possible loss of data in return - QMAKE_CXXFLAGS_WARN_ON += /wd4714 - QMAKE_CXXFLAGS_WARN_ON += /wd4996 - QMAKE_CXXFLAGS_WARN_ON += /wd4505 - QMAKE_CXXFLAGS_WARN_ON += /wd4127 - QMAKE_CXXFLAGS_WARN_ON += /wd4503 - QMAKE_CXXFLAGS_WARN_ON += /wd4100 - QMAKE_CXXFLAGS_WARN_ON += /wd4305 - QMAKE_CXXFLAGS_WARN_ON += /wd4267 - -} else { - QMAKE_CXXFLAGS_WARN_ON = -Wall - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function - QMAKE_CXXFLAGS_WARN_ON += -Wno-switch - QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations - QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable - - # Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future - QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing - - equals(QMAKE_CXX, "clang++") { - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef - } else { - QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess - } -} - -# do not use windows min/max macros -#win32 { -# DEFINES += NOMINMAX -#} - -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -linux { - QMAKE_LFLAGS += -lrt -} +#------------------------------------------------- +# +# Project created by QtCreator 2016-12-28T18:23:35 +# +#------------------------------------------------- + +message(----) + +QT += widgets core gui network multimedia svg +CONFIG += communi +COMMUNI += core model util +CONFIG += c++14 +INCLUDEPATH += src/ +TARGET = chatterino +TEMPLATE = app +DEFINES += QT_DEPRECATED_WARNINGS +PRECOMPILED_HEADER = src/precompiled_header.hpp +CONFIG += precompile_header + +# https://bugreports.qt.io/browse/QTBUG-27018 +equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { + TARGET = bin/chatterino +} + +# Icons +macx:ICON = resources/images/chatterino2.icns +win32:RC_FILE = resources/windows.rc + + +macx { + LIBS += -L/usr/local/lib +} + +# Submodules +include(dependencies/rapidjson.pri) +include(dependencies/settings.pri) +include(dependencies/signals.pri) +include(dependencies/humanize.pri) +include(dependencies/fmt.pri) +DEFINES += IRC_NAMESPACE=Communi +include(dependencies/libcommuni.pri) +include(dependencies/websocketpp.pri) +include(dependencies/openssl.pri) +include(dependencies/boost.pri) + +# Optional feature: QtWebEngine +#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { +# message(Using QWebEngine) +# QT += webenginewidgets +# DEFINES += "USEWEBENGINE" +#} + +linux { + LIBS += -lrt +} + +win32 { + LIBS += -luser32 +} + +# OSX include directory +macx { + INCLUDEPATH += /usr/local/include +} + +# Optional dependency on Windows SDK 7 +!contains(QMAKE_TARGET.arch, x86_64) { + win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) { + LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ + -ldwmapi + + DEFINES += "USEWINSDK" + message(Using Windows SDK 7) + } +} + +# Optional dependency on Windows SDK 10 +contains(QMAKE_TARGET.arch, x86_64) { + WIN_SDK_VERSION = $$(WindowsSDKVersion) + !isEmpty(WIN_SDK_VERSION) { + !equals(WIN_SDK_VERSION, "\\") { + DEFINES += "USEWINSDK" + message(Using Windows SDK 10) + } + } +} + +werr { + QMAKE_CXXFLAGS += -Werror + + message("Enabling error on warning") +} + +# src +SOURCES += \ + src/main.cpp \ + src/application.cpp \ + src/channel.cpp \ + src/channeldata.cpp \ + src/messages/image.cpp \ + src/messages/layouts/messagelayout.cpp \ + src/messages/layouts/messagelayoutcontainer.cpp \ + src/messages/layouts/messagelayoutelement.cpp \ + src/messages/link.cpp \ + src/messages/message.cpp \ + src/messages/messagebuilder.cpp \ + src/messages/messagecolor.cpp \ + src/messages/messageelement.cpp \ + src/providers/irc/abstractircserver.cpp \ + src/providers/twitch/ircmessagehandler.cpp \ + src/providers/twitch/twitchaccount.cpp \ + src/providers/twitch/twitchaccountmanager.cpp \ + src/providers/twitch/twitchchannel.cpp \ + src/providers/twitch/twitchmessagebuilder.cpp \ + src/providers/twitch/twitchserver.cpp \ + src/providers/twitch/pubsub.cpp \ + src/singletons/commandmanager.cpp \ + src/singletons/emotemanager.cpp \ + src/singletons/fontmanager.cpp \ + src/util/completionmodel.cpp \ + src/singletons/helper/loggingchannel.cpp \ + src/singletons/helper/moderationaction.cpp \ + src/singletons/helper/chatterinosetting.cpp \ + src/singletons/loggingmanager.cpp \ + src/singletons/pathmanager.cpp \ + src/singletons/resourcemanager.cpp \ + src/singletons/settingsmanager.cpp \ + src/singletons/thememanager.cpp \ + src/singletons/windowmanager.cpp \ + src/util/networkmanager.cpp \ + src/util/networkrequest.cpp \ + src/widgets/accountpopup.cpp \ + src/widgets/accountswitchpopupwidget.cpp \ + src/widgets/accountswitchwidget.cpp \ + src/widgets/basewidget.cpp \ + src/widgets/basewindow.cpp \ + src/widgets/emotepopup.cpp \ + src/widgets/helper/channelview.cpp \ + src/widgets/helper/droppreview.cpp \ + src/widgets/helper/label.cpp \ + src/widgets/helper/notebookbutton.cpp \ + src/widgets/helper/notebooktab.cpp \ + src/widgets/helper/resizingtextedit.cpp \ + src/widgets/helper/rippleeffectbutton.cpp \ + src/widgets/helper/rippleeffectlabel.cpp \ + src/widgets/helper/scrollbarhighlight.cpp \ + src/widgets/helper/searchpopup.cpp \ + src/widgets/helper/settingsdialogtab.cpp \ + src/widgets/helper/splitcolumn.cpp \ + src/widgets/helper/splitheader.cpp \ + src/widgets/helper/splitinput.cpp \ + src/widgets/helper/titlebarbutton.cpp \ + src/widgets/logindialog.cpp \ + src/widgets/notebook.cpp \ + src/widgets/qualitypopup.cpp \ + src/widgets/scrollbar.cpp \ + src/widgets/settingsdialog.cpp \ + src/widgets/settingspages/aboutpage.cpp \ + src/widgets/settingspages/accountspage.cpp \ + src/widgets/settingspages/appearancepage.cpp \ + src/widgets/settingspages/behaviourpage.cpp \ + src/widgets/settingspages/commandpage.cpp \ + src/widgets/settingspages/emotespage.cpp \ + src/widgets/settingspages/highlightingpage.cpp \ + src/widgets/settingspages/ignoreuserspage.cpp \ + src/widgets/settingspages/keyboardsettingspage.cpp \ + src/widgets/settingspages/logspage.cpp \ + src/widgets/settingspages/moderationpage.cpp \ + src/widgets/settingspages/settingspage.cpp \ + src/widgets/settingspages/specialchannelspage.cpp \ + src/widgets/split.cpp \ + src/widgets/splitcontainer.cpp \ + src/widgets/streamview.cpp \ + src/widgets/textinputdialog.cpp \ + src/widgets/tooltipwidget.cpp \ + src/widgets/window.cpp \ + src/providers/irc/ircaccount.cpp \ + src/providers/irc/ircserver.cpp \ + src/providers/irc/ircchannel2.cpp \ + src/util/streamlink.cpp \ + src/providers/twitch/twitchhelpers.cpp \ + src/widgets/helper/signallabel.cpp \ + src/widgets/helper/debugpopup.cpp \ + src/util/debugcount.cpp \ + src/singletons/nativemessagingmanager.cpp \ + src/util/rapidjson-helpers.cpp \ + src/providers/twitch/pubsubhelpers.cpp \ + src/providers/twitch/pubsubactions.cpp \ + src/providers/twitch/twitchuser.cpp \ + src/widgets/selectchanneldialog.cpp \ + src/singletons/updatemanager.cpp \ + src/widgets/lastruncrashdialog.cpp \ + src/widgets/attachedwindow.cpp \ + src/widgets/settingspages/externaltoolspage.cpp \ + src/widgets/helper/comboboxitemdelegate.cpp \ + src/controllers/commands/command.cpp \ + src/controllers/commands/commandmodel.cpp \ + src/controllers/commands/commandcontroller.cpp \ + src/controllers/highlights/highlightcontroller.cpp \ + src/controllers/highlights/highlightmodel.cpp \ + src/controllers/ignores/ignorecontroller.cpp \ + src/controllers/ignores/ignoremodel.cpp \ + src/widgets/helper/editablemodelview.cpp \ + src/controllers/accounts/accountcontroller.cpp \ + src/controllers/accounts/accountmodel.cpp \ + src/controllers/accounts/account.cpp \ + src/widgets/helper/splitoverlay.cpp \ + src/widgets/helper/dropoverlay.cpp \ + src/widgets/helper/splitnode.cpp \ + src/widgets/notificationpopup.cpp \ + src/controllers/taggedusers/taggeduserscontroller.cpp \ + src/controllers/taggedusers/taggeduser.cpp \ + src/controllers/taggedusers/taggedusersmodel.cpp \ + src/util/emotemap.cpp \ + src/providers/irc/ircconnection2.cpp + +HEADERS += \ + src/precompiled_header.hpp \ + src/channel.hpp \ + src/const.hpp \ + src/debug/log.hpp \ + src/emojis.hpp \ + src/messages/image.hpp \ + src/messages/layouts/messagelayout.hpp \ + src/messages/layouts/messagelayoutcontainer.hpp \ + src/messages/layouts/messagelayoutelement.hpp \ + src/messages/limitedqueue.hpp \ + src/messages/limitedqueuesnapshot.hpp \ + src/messages/link.hpp \ + src/messages/message.hpp \ + src/messages/messagebuilder.hpp \ + src/messages/messagecolor.hpp \ + src/messages/messageelement.hpp \ + src/messages/messageparseargs.hpp \ + src/messages/selection.hpp \ + src/providers/twitch/emotevalue.hpp \ + src/providers/twitch/ircmessagehandler.hpp \ + src/providers/twitch/twitchaccount.hpp \ + src/providers/twitch/twitchaccountmanager.hpp \ + src/providers/twitch/twitchchannel.hpp \ + src/providers/twitch/twitchmessagebuilder.hpp \ + src/providers/twitch/twitchserver.hpp \ + src/providers/twitch/pubsub.hpp \ + src/singletons/commandmanager.hpp \ + src/singletons/emotemanager.hpp \ + src/singletons/fontmanager.hpp \ + src/singletons/helper/chatterinosetting.hpp \ + src/util/completionmodel.hpp \ + src/singletons/helper/loggingchannel.hpp \ + src/singletons/helper/moderationaction.hpp \ + src/singletons/loggingmanager.hpp \ + src/singletons/pathmanager.hpp \ + src/singletons/resourcemanager.hpp \ + src/singletons/thememanager.hpp \ + src/singletons/windowmanager.hpp \ + src/util/benchmark.hpp \ + src/util/concurrentmap.hpp \ + src/util/distancebetweenpoints.hpp \ + src/util/emotemap.hpp \ + src/util/flagsenum.hpp \ + src/util/helpers.hpp \ + src/util/irchelpers.hpp \ + src/util/layoutcreator.hpp \ + src/util/nativeeventhelper.hpp \ + src/util/networkmanager.hpp \ + src/util/networkrequest.hpp \ + src/util/networkrequester.hpp \ + src/util/networkworker.hpp \ + src/util/posttothread.hpp \ + src/util/property.hpp \ + src/util/serialize-custom.hpp \ + src/util/urlfetch.hpp \ + src/widgets/accountpopup.hpp \ + src/widgets/accountswitchpopupwidget.hpp \ + src/widgets/accountswitchwidget.hpp \ + src/widgets/basewidget.hpp \ + src/widgets/basewindow.hpp \ + src/widgets/emotepopup.hpp \ + src/widgets/helper/channelview.hpp \ + src/widgets/helper/droppreview.hpp \ + src/widgets/helper/label.hpp \ + src/widgets/helper/notebookbutton.hpp \ + src/widgets/helper/notebooktab.hpp \ + src/widgets/helper/resizingtextedit.hpp \ + src/widgets/helper/rippleeffectbutton.hpp \ + src/widgets/helper/rippleeffectlabel.hpp \ + src/widgets/helper/scrollbarhighlight.hpp \ + src/widgets/helper/searchpopup.hpp \ + src/widgets/helper/settingsdialogtab.hpp \ + src/widgets/helper/shortcut.hpp \ + src/widgets/helper/signallabel.hpp \ + src/widgets/helper/splitcolumn.hpp \ + src/widgets/helper/splitheader.hpp \ + src/widgets/helper/splitinput.hpp \ + src/widgets/helper/titlebarbutton.hpp \ + src/widgets/notebook.hpp \ + src/widgets/qualitypopup.hpp \ + src/widgets/scrollbar.hpp \ + src/widgets/settingsdialog.hpp \ + src/widgets/settingspages/aboutpage.hpp \ + src/widgets/settingspages/accountspage.hpp \ + src/widgets/settingspages/appearancepage.hpp \ + src/widgets/settingspages/behaviourpage.hpp \ + src/widgets/settingspages/commandpage.hpp \ + src/widgets/settingspages/emotespage.hpp \ + src/widgets/settingspages/highlightingpage.hpp \ + src/widgets/settingspages/ignoreuserspage.hpp \ + src/widgets/settingspages/keyboardsettingspage.hpp \ + src/widgets/settingspages/logspage.hpp \ + src/widgets/settingspages/moderationpage.hpp \ + src/widgets/settingspages/settingspage.hpp \ + src/widgets/settingspages/specialchannelspage.hpp \ + src/widgets/split.hpp \ + src/widgets/splitcontainer.hpp \ + src/widgets/streamview.hpp \ + src/widgets/textinputdialog.hpp \ + src/widgets/tooltipwidget.hpp \ + src/widgets/window.hpp \ + src/common.hpp \ + src/providers/irc/abstractircserver.hpp \ + src/providers/irc/ircaccount.hpp \ + src/providers/irc/ircserver.hpp \ + src/providers/irc/ircchannel2.hpp \ + src/util/streamlink.hpp \ + src/providers/twitch/twitchhelpers.hpp \ + src/util/debugcount.hpp \ + src/widgets/helper/debugpopup.hpp \ + src/version.hpp \ + src/singletons/settingsmanager.hpp \ + src/singletons/nativemessagingmanager.hpp \ + src/util/rapidjson-helpers.hpp \ + src/providers/twitch/pubsubhelpers.hpp \ + src/providers/twitch/pubsubactions.hpp \ + src/providers/twitch/twitchuser.hpp \ + src/widgets/selectchanneldialog.hpp \ + src/singletons/updatemanager.hpp \ + src/widgets/lastruncrashdialog.hpp \ + src/widgets/attachedwindow.hpp \ + src/widgets/settingspages/externaltoolspage.hpp \ + src/util/removescrollareabackground.hpp \ + src/util/standarditemhelper.hpp \ + src/widgets/helper/comboboxitemdelegate.hpp \ + src/util/assertinguithread.hpp \ + src/util/signalvector2.hpp \ + src/util/signalvectormodel.hpp \ + src/controllers/commands/command.hpp \ + src/controllers/commands/commandmodel.hpp \ + src/controllers/commands/commandcontroller.hpp \ + src/controllers/highlights/highlightcontroller.hpp \ + src/controllers/highlights/highlightphrase.hpp \ + src/controllers/highlights/highlightmodel.hpp \ + src/controllers/ignores/ignorecontroller.hpp \ + src/controllers/ignores/ignorephrase.hpp \ + src/controllers/ignores/ignoremodel.hpp \ + src/widgets/helper/editablemodelview.hpp \ + src/controllers/accounts/accountcontroller.hpp \ + src/controllers/accounts/accountmodel.hpp \ + src/controllers/accounts/account.hpp \ + src/util/sharedptrelementless.hpp \ + src/widgets/helper/splitoverlay.hpp \ + src/widgets/helper/dropoverlay.hpp \ + src/widgets/helper/splitnode.hpp \ + src/widgets/notificationpopup.hpp \ + src/controllers/taggedusers/taggeduserscontroller.hpp \ + src/controllers/taggedusers/taggeduser.hpp \ + src/providerid.hpp \ + src/controllers/taggedusers/taggedusersmodel.hpp \ + src/util/qstringhash.hpp \ + src/util/mutexvalue.hpp \ + src/providers/irc/ircconnection2.hpp + +RESOURCES += \ + resources/resources.qrc + +DISTFILES += + +FORMS += \ + forms/accountpopupform.ui + +# Define warning flags for Chatterino +win32-msvc* { + QMAKE_CXXFLAGS_WARN_ON = /W4 + # 4714 - function marked as __forceinline not inlined + # 4996 - occurs when the compiler encounters a function or variable that is marked as deprecated. + # These functions may have a different preferred name, may be insecure or have + # a more secure variant, or may be obsolete. + # 4505 - unreferenced local version has been removed + # 4127 - conditional expression is constant + # 4503 - decorated name length exceeded, name was truncated + # 4100 - unreferences formal parameter + # 4305 - possible truncation of data + # 4267 - possible loss of data in return + QMAKE_CXXFLAGS_WARN_ON += /wd4714 + QMAKE_CXXFLAGS_WARN_ON += /wd4996 + QMAKE_CXXFLAGS_WARN_ON += /wd4505 + QMAKE_CXXFLAGS_WARN_ON += /wd4127 + QMAKE_CXXFLAGS_WARN_ON += /wd4503 + QMAKE_CXXFLAGS_WARN_ON += /wd4100 + QMAKE_CXXFLAGS_WARN_ON += /wd4305 + QMAKE_CXXFLAGS_WARN_ON += /wd4267 + +} else { + QMAKE_CXXFLAGS_WARN_ON = -Wall + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + QMAKE_CXXFLAGS_WARN_ON += -Wno-switch + QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations + QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable + + # Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future + QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing + + equals(QMAKE_CXX, "clang++") { + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef + } else { + QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess + } +} + +# do not use windows min/max macros +#win32 { +# DEFINES += NOMINMAX +#} + +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +linux { + QMAKE_LFLAGS += -lrt +} diff --git a/src/providers/irc/abstractircserver.cpp b/src/providers/irc/abstractircserver.cpp index 070fd5e2..f83ec743 100644 --- a/src/providers/irc/abstractircserver.cpp +++ b/src/providers/irc/abstractircserver.cpp @@ -4,6 +4,8 @@ #include "messages/limitedqueuesnapshot.hpp" #include "messages/message.hpp" +#include + using namespace chatterino::messages; namespace chatterino { @@ -13,14 +15,14 @@ namespace irc { AbstractIrcServer::AbstractIrcServer() { // Initialize the connections - this->writeConnection.reset(new Communi::IrcConnection); + this->writeConnection.reset(new IrcConnection); this->writeConnection->moveToThread(QCoreApplication::instance()->thread()); QObject::connect(this->writeConnection.get(), &Communi::IrcConnection::messageReceived, [this](auto msg) { this->writeConnectionMessageReceived(msg); }); // Listen to read connection message signals - this->readConnection.reset(new Communi::IrcConnection); + this->readConnection.reset(new IrcConnection); this->readConnection->moveToThread(QCoreApplication::instance()->thread()); QObject::connect(this->readConnection.get(), &Communi::IrcConnection::messageReceived, @@ -33,7 +35,7 @@ AbstractIrcServer::AbstractIrcServer() [this] { this->onDisconnected(); }); } -Communi::IrcConnection *AbstractIrcServer::getReadConnection() const +IrcConnection *AbstractIrcServer::getReadConnection() const { return this->readConnection.get(); } diff --git a/src/providers/irc/abstractircserver.hpp b/src/providers/irc/abstractircserver.hpp index fb41b131..3ac77d88 100644 --- a/src/providers/irc/abstractircserver.hpp +++ b/src/providers/irc/abstractircserver.hpp @@ -2,9 +2,9 @@ #include "channel.hpp" -#include #include #include +#include #include #include @@ -19,7 +19,7 @@ public: virtual ~AbstractIrcServer() = default; // connection - Communi::IrcConnection *getReadConnection() const; + IrcConnection *getReadConnection() const; void connect(); void disconnect(); @@ -43,8 +43,7 @@ public: protected: AbstractIrcServer(); - virtual void initializeConnection(Communi::IrcConnection *connection, bool isRead, - bool isWrite) = 0; + virtual void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) = 0; virtual std::shared_ptr createChannel(const QString &channelName) = 0; virtual void privateMessageReceived(Communi::IrcPrivateMessage *message); @@ -64,10 +63,12 @@ protected: private: void initConnection(); - std::unique_ptr writeConnection = nullptr; - std::unique_ptr readConnection = nullptr; + std::unique_ptr writeConnection = nullptr; + std::unique_ptr readConnection = nullptr; std::mutex connectionMutex; + + QTimer pingTimer; }; } // namespace irc diff --git a/src/providers/irc/ircconnection2.cpp b/src/providers/irc/ircconnection2.cpp new file mode 100644 index 00000000..c2cb7aa6 --- /dev/null +++ b/src/providers/irc/ircconnection2.cpp @@ -0,0 +1,14 @@ +#include "ircconnection2.hpp" + +namespace chatterino { +namespace providers { +namespace irc { + +IrcConnection::IrcConnection(QObject *parent) + : Communi::IrcConnection(parent) +{ +} + +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/irc/ircconnection2.hpp b/src/providers/irc/ircconnection2.hpp new file mode 100644 index 00000000..85c9bfe7 --- /dev/null +++ b/src/providers/irc/ircconnection2.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace chatterino { +namespace providers { +namespace irc { + +class IrcConnection : public Communi::IrcConnection +{ +public: + IrcConnection(QObject *parent = nullptr); +}; + +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp index 62fd410c..bcb028fd 100644 --- a/src/providers/twitch/twitchserver.cpp +++ b/src/providers/twitch/twitchserver.cpp @@ -9,9 +9,10 @@ #include "providers/twitch/twitchmessagebuilder.hpp" #include "util/posttothread.hpp" +#include #include -using namespace Communi; +// using namespace Communi; using namespace chatterino::singletons; namespace chatterino { @@ -32,7 +33,8 @@ void TwitchServer::initialize() [this]() { util::postToThread([this] { this->connect(); }); }); } -void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) +void TwitchServer::initializeConnection(providers::irc::IrcConnection *connection, bool isRead, + bool isWrite) { std::shared_ptr account = getApp()->accounts->twitch.getCurrent(); @@ -57,9 +59,9 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, // this->refreshIgnoredUsers(username, oauthClient, oauthToken); } - connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/membership")); - connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands")); - connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags")); + connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); + connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands")); + connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags")); connection->setHost("irc.chat.twitch.tv"); connection->setPort(6667); @@ -75,15 +77,15 @@ std::shared_ptr TwitchServer::createChannel(const QString &channelName) return std::shared_ptr(channel); } -void TwitchServer::privateMessageReceived(IrcPrivateMessage *message) +void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message) { IrcMessageHandler::getInstance().handlePrivMessage(message, *this); } -void TwitchServer::messageReceived(IrcMessage *message) +void TwitchServer::messageReceived(Communi::IrcMessage *message) { // this->readConnection - if (message->type() == IrcMessage::Type::Private) { + if (message->type() == Communi::IrcMessage::Type::Private) { // We already have a handler for private messages return; } @@ -105,7 +107,7 @@ void TwitchServer::messageReceived(IrcMessage *message) } else if (command == "MODE") { handler.handleModeMessage(message); } else if (command == "NOTICE") { - handler.handleNoticeMessage(static_cast(message)); + handler.handleNoticeMessage(static_cast(message)); } else if (command == "JOIN") { handler.handleJoinMessage(message); } else if (command == "PART") { @@ -113,13 +115,15 @@ void TwitchServer::messageReceived(IrcMessage *message) } } -void TwitchServer::writeConnectionMessageReceived(IrcMessage *message) +void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message) { switch (message->type()) { - case IrcMessage::Type::Notice: { + case Communi::IrcMessage::Type::Notice: { IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage( - static_cast(message)); + static_cast(message)); } break; + + default:; } } @@ -178,20 +182,6 @@ std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &chan return Channel::getEmpty(); } -// QString TwitchServer::getLastWhisperedPerson() const -//{ -// std::lock_guard guard(this->lastWhisperedPersonMutex); - -// return this->lastWhisperedPerson; -//} - -// void TwitchServer::setLastWhisperedPerson(const QString &person) -//{ -// std::lock_guard guard(this->lastWhisperedPersonMutex); - -// this->lastWhisperedPerson = person; -//} - QString TwitchServer::cleanChannelName(const QString &dirtyChannelName) { return dirtyChannelName.toLower(); diff --git a/src/providers/twitch/twitchserver.hpp b/src/providers/twitch/twitchserver.hpp index 070c9b4c..caa93fe2 100644 --- a/src/providers/twitch/twitchserver.hpp +++ b/src/providers/twitch/twitchserver.hpp @@ -33,7 +33,7 @@ public: IndirectChannel watchingChannel; protected: - void initializeConnection(Communi::IrcConnection *connection, bool isRead, + void initializeConnection(providers::irc::IrcConnection *connection, bool isRead, bool isWrite) override; std::shared_ptr createChannel(const QString &channelName) override; diff --git a/src/widgets/settingspages/aboutpage.cpp b/src/widgets/settingspages/aboutpage.cpp index 387b8b1c..88effe52 100644 --- a/src/widgets/settingspages/aboutpage.cpp +++ b/src/widgets/settingspages/aboutpage.cpp @@ -36,31 +36,35 @@ AboutPage::AboutPage() // palette.setColor(QPalette::Link, "#a5cdff"); // palette.setColor(QPalette::LinkVisited, "#a5cdff"); - auto created = layout.emplace(); + /*auto xd = layout.emplace("Created by..."); { - created->setText( - "Twitch Chat Client created by fourtf"); - created->setTextFormat(Qt::RichText); - created->setTextInteractionFlags(Qt::TextBrowserInteraction | - Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByKeyboard); - created->setOpenExternalLinks(true); - // created->setPalette(palette); - } + auto created = xd.emplace(); + { + created->setText("Created by fourtf
" + "with big help from pajlada."); + created->setTextFormat(Qt::RichText); + created->setTextInteractionFlags(Qt::TextBrowserInteraction | + Qt::LinksAccessibleByKeyboard | + Qt::LinksAccessibleByKeyboard); + created->setOpenExternalLinks(true); + // created->setPalette(palette); + } - auto github = layout.emplace(); - { - github->setText( - "Chatterino on Github"); - github->setTextFormat(Qt::RichText); - github->setTextInteractionFlags(Qt::TextBrowserInteraction | - Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByKeyboard); - github->setOpenExternalLinks(true); - // github->setPalette(palette); - } + // auto github = xd.emplace(); + // { + // github->setText( + // "Chatterino on + // Github"); + // github->setTextFormat(Qt::RichText); + // github->setTextInteractionFlags(Qt::TextBrowserInteraction | + // Qt::LinksAccessibleByKeyboard | + // Qt::LinksAccessibleByKeyboard); + // github->setOpenExternalLinks(true); + // // github->setPalette(palette); + // } + }*/ - auto licenses = layout.emplace("Open source software used"); + auto licenses = layout.emplace("Open source software used..."); { auto form = licenses.emplace(); @@ -90,6 +94,7 @@ void AboutPage::addLicense(QFormLayout *form, const QString &name, const QString a->setOpenExternalLinks(true); auto *b = new SignalLabel(); b->setText("show license"); + b->setCursor(Qt::PointingHandCursor); QObject::connect(b, &SignalLabel::mouseUp, [licenseLink] { auto *edit = new QTextEdit; From d9cb8093cbe6318322d3cbfc975fefc173b64afc Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 4 Jun 2018 21:37:19 +0200 Subject: [PATCH 110/121] added a shitty reconnect implementation --- src/providers/irc/abstractircserver.cpp | 4 ++++ src/providers/irc/ircconnection2.cpp | 27 +++++++++++++++++++++++++ src/providers/irc/ircconnection2.hpp | 10 +++++++++ 3 files changed, 41 insertions(+) diff --git a/src/providers/irc/abstractircserver.cpp b/src/providers/irc/abstractircserver.cpp index f83ec743..aa4ac746 100644 --- a/src/providers/irc/abstractircserver.cpp +++ b/src/providers/irc/abstractircserver.cpp @@ -33,6 +33,10 @@ AbstractIrcServer::AbstractIrcServer() [this] { this->onConnected(); }); QObject::connect(this->readConnection.get(), &Communi::IrcConnection::disconnected, [this] { this->onDisconnected(); }); + + // listen to reconnect request + this->readConnection->reconnectRequested.connect([this] { this->connect(); }); + // this->writeConnection->reconnectRequested.connect([this] { this->connect(); }); } IrcConnection *AbstractIrcServer::getReadConnection() const diff --git a/src/providers/irc/ircconnection2.cpp b/src/providers/irc/ircconnection2.cpp index c2cb7aa6..5d3aefcb 100644 --- a/src/providers/irc/ircconnection2.cpp +++ b/src/providers/irc/ircconnection2.cpp @@ -7,6 +7,33 @@ namespace irc { IrcConnection::IrcConnection(QObject *parent) : Communi::IrcConnection(parent) { + this->pingTimer_.setInterval(10000); + this->pingTimer_.start(); + QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] { + if (!this->recentlyReceivedMessage_.load()) { + this->sendRaw("PING"); + this->reconnectTimer_.start(); + } + this->recentlyReceivedMessage_ = false; + }); + + this->reconnectTimer_.setInterval(10000); + this->reconnectTimer_.setSingleShot(true); + QObject::connect(&this->reconnectTimer_, &QTimer::timeout, + [this] { reconnectRequested.invoke(); }); + + QObject::connect(this, &Communi::IrcConnection::messageReceived, + [this](Communi::IrcMessage *message) { + if (message->command() == "PONG") { + qDebug() << "PONG"; + } + this->recentlyReceivedMessage_ = true; + + if (this->reconnectTimer_.isActive()) { + this->reconnectTimer_.stop(); + qDebug() << "reconnect stopped"; + } + }); } } // namespace irc diff --git a/src/providers/irc/ircconnection2.hpp b/src/providers/irc/ircconnection2.hpp index 85c9bfe7..c9a468b7 100644 --- a/src/providers/irc/ircconnection2.hpp +++ b/src/providers/irc/ircconnection2.hpp @@ -1,6 +1,9 @@ #pragma once +#include + #include +#include namespace chatterino { namespace providers { @@ -10,6 +13,13 @@ class IrcConnection : public Communi::IrcConnection { public: IrcConnection(QObject *parent = nullptr); + + pajlada::Signals::NoArgSignal reconnectRequested; + +private: + QTimer pingTimer_; + QTimer reconnectTimer_; + std::atomic recentlyReceivedMessage_{true}; }; } // namespace irc From b693779c55bc8269624c09fe235b3dea832d506c Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 4 Jun 2018 21:44:03 +0200 Subject: [PATCH 111/121] fixed clicking to select split --- src/providers/irc/ircconnection2.cpp | 21 ++++++++------------- src/widgets/helper/channelview.cpp | 4 ++-- src/widgets/split.cpp | 5 ++++- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/providers/irc/ircconnection2.cpp b/src/providers/irc/ircconnection2.cpp index 5d3aefcb..8469bed1 100644 --- a/src/providers/irc/ircconnection2.cpp +++ b/src/providers/irc/ircconnection2.cpp @@ -7,7 +7,7 @@ namespace irc { IrcConnection::IrcConnection(QObject *parent) : Communi::IrcConnection(parent) { - this->pingTimer_.setInterval(10000); + this->pingTimer_.setInterval(5000); this->pingTimer_.start(); QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] { if (!this->recentlyReceivedMessage_.load()) { @@ -17,23 +17,18 @@ IrcConnection::IrcConnection(QObject *parent) this->recentlyReceivedMessage_ = false; }); - this->reconnectTimer_.setInterval(10000); + this->reconnectTimer_.setInterval(5000); this->reconnectTimer_.setSingleShot(true); QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] { reconnectRequested.invoke(); }); - QObject::connect(this, &Communi::IrcConnection::messageReceived, - [this](Communi::IrcMessage *message) { - if (message->command() == "PONG") { - qDebug() << "PONG"; - } - this->recentlyReceivedMessage_ = true; + QObject::connect(this, &Communi::IrcConnection::messageReceived, [this](Communi::IrcMessage *) { + this->recentlyReceivedMessage_ = true; - if (this->reconnectTimer_.isActive()) { - this->reconnectTimer_.stop(); - qDebug() << "reconnect stopped"; - } - }); + if (this->reconnectTimer_.isActive()) { + this->reconnectTimer_.stop(); + } + }); } } // namespace irc diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 9d06843f..75a9a886 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -813,6 +813,8 @@ void ChannelView::mousePressEvent(QMouseEvent *event) { auto app = getApp(); + this->mouseDown.invoke(event); + std::shared_ptr layout; QPoint relativePos; int messageIndex; @@ -856,8 +858,6 @@ void ChannelView::mousePressEvent(QMouseEvent *event) auto selectionItem = SelectionItem(messageIndex, index); this->setSelection(selectionItem, selectionItem); - - this->mouseDown.invoke(event); } break; case Qt::RightButton: { diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 52a408ee..e9a14c64 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -95,7 +95,10 @@ Split::Split(QWidget *parent) this->input.ui.textEdit->installEventFilter(parent); - this->view.mouseDown.connect([this](QMouseEvent *) { this->giveFocus(Qt::MouseFocusReason); }); + this->view.mouseDown.connect([this](QMouseEvent *) { + // + this->giveFocus(Qt::MouseFocusReason); + }); this->view.selectionChanged.connect([this]() { if (view.hasSelection()) { this->input.clearSelection(); From 4b52a98778f420996f79eb5f4fea54363e4e1a2d Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Tue, 5 Jun 2018 01:13:53 +0200 Subject: [PATCH 112/121] Fix mentions tab --- src/providers/twitch/ircmessagehandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp index e7fb4795..a7f1799d 100644 --- a/src/providers/twitch/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -61,7 +61,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message, const QString & msg->flags |= messages::Message::Subscription; msg->flags &= ~messages::Message::Highlighted; } else { - if (msg->flags & messages::Message::Subscription) { + if (msg->flags & messages::Message::Highlighted) { server.mentionsChannel->addMessage(msg); getApp()->highlights->addHighlight(msg); } From 10b0466052f217c72bde4de95086cbe4d7a6c212 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 5 Jun 2018 00:14:20 +0200 Subject: [PATCH 113/121] changed scrollbar bg --- src/messages/layouts/messagelayout.cpp | 3 +-- src/singletons/thememanager.cpp | 5 +++-- src/widgets/settingspages/appearancepage.cpp | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index d99de85d..3e690e06 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -179,8 +179,7 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, QBrush brush(color, Qt::VerPattern); - painter.fillRect(0, y + this->container_.getHeight() - 1, this->container_.getWidth(), 1, - brush); + painter.fillRect(0, y + this->container_.getHeight() - 1, pixmap->width(), 1, brush); } this->bufferValid_ = true; diff --git a/src/singletons/thememanager.cpp b/src/singletons/thememanager.cpp index 983d4711..5c626603 100644 --- a/src/singletons/thememanager.cpp +++ b/src/singletons/thememanager.cpp @@ -183,8 +183,9 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier) // this->messages.seperatorInner = // Scrollbar - this->scrollbars.background = splits.background; - this->scrollbars.background.setAlphaF(qreal(0.2)); + this->scrollbars.background = QColor(0, 0, 0, 0); + // this->scrollbars.background = splits.background; + // this->scrollbars.background.setAlphaF(qreal(0.2)); this->scrollbars.thumb = getColor(0, sat, 0.80); this->scrollbars.thumbSelected = getColor(0, sat, 0.7); diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp index af4f13fe..9c839f98 100644 --- a/src/widgets/settingspages/appearancepage.cpp +++ b/src/widgets/settingspages/appearancepage.cpp @@ -108,6 +108,7 @@ AppearancePage::AppearancePage() auto emotes = layout.emplace("Emotes").setLayoutType(); { + /* emotes.append( this->createCheckBox("Enable Twitch emotes", app->settings->enableTwitchEmotes)); emotes.append(this->createCheckBox("Enable BetterTTV emotes for Twitch", @@ -115,6 +116,7 @@ AppearancePage::AppearancePage() emotes.append(this->createCheckBox("Enable FrankerFaceZ emotes for Twitch", app->settings->enableFfzEmotes)); emotes.append(this->createCheckBox("Enable emojis", app->settings->enableEmojis)); + */ emotes.append( this->createCheckBox("Enable animations", app->settings->enableGifAnimations)); From 3c8bb5e9bb978a84a3a11165318b1435d3f32a29 Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Mon, 4 Jun 2018 17:24:54 -0400 Subject: [PATCH 114/121] Fix a few issues with text emotes. 1. Fix the heart emote text overlay. 2. Added a missing text emote to the replacements. 3. Replace > with > --- src/singletons/emotemanager.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index cd70831b..2de509bf 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -547,13 +547,14 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa { QString _emoteName = emoteName; _emoteName.replace("<", "<"); + _emoteName.replace(">", ">"); static QMap emoteNameReplacements{ - {"[oO](_|\\.)[oO]", "o_O"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, - {"\\:-?[z|Z|\\|]", ":z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, - {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, - {"R-?\\)", "R-)"}, + {"\\:-?\\)", ":-)"}, {"\\:-?\\(", ":-("}, {"\\:-?D", ":-D"}, + {"\\>\\;\\(", ">("}, {"\\:-?[z|Z|\\|]", ":-Z"}, {"[oO](_|\\.)[oO]", "O_o"}, + {"B-?\\)", "B-)"}, {"\\:-?(o|O)", ":-O"}, {"\\<\\;3", "<3"}, + {"\\:-?[\\\\/]", ":-/"}, {"\\;-?\\)", ";-)"}, {"\\:-?(p|P)", ":-P"}, + {"\\;-?(p|P)", ";-P"}, {"R-?\\)", "R-)"}, }; auto it = emoteNameReplacements.find(_emoteName); From 4430f0b9a91cc90ddc18fb2522f61f5e77262e99 Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Mon, 4 Jun 2018 18:06:55 -0400 Subject: [PATCH 115/121] Revisement of fixing text emotes Fixing the order of text emotes. --- src/singletons/emotemanager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index 2de509bf..172a5cbe 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -550,11 +550,11 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa _emoteName.replace(">", ">"); static QMap emoteNameReplacements{ - {"\\:-?\\)", ":-)"}, {"\\:-?\\(", ":-("}, {"\\:-?D", ":-D"}, - {"\\>\\;\\(", ">("}, {"\\:-?[z|Z|\\|]", ":-Z"}, {"[oO](_|\\.)[oO]", "O_o"}, - {"B-?\\)", "B-)"}, {"\\:-?(o|O)", ":-O"}, {"\\<\\;3", "<3"}, - {"\\:-?[\\\\/]", ":-/"}, {"\\;-?\\)", ";-)"}, {"\\:-?(p|P)", ":-P"}, - {"\\;-?(p|P)", ";-P"}, {"R-?\\)", "R-)"}, + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, + {"\\:-?(o|O)", ":-O"}, {"\\:-?(p|P)", ":-P"}, {"\\:-?[\\\\/]", ":-/"}, + {"\\:-?[z|Z|\\|]", ":-Z"}, {"\\:-?\\(", ":-("}, {"\\:-?\\)", ":-)"}, + {"\\:-?D", ":-D"}, {"\\;-?(p|P)", ";-P"}, {"\\;-?\\)", ";-)"}, + {"R-?\\)", "R-)"}, {"B-?\\)", "B-)"}, }; auto it = emoteNameReplacements.find(_emoteName); From b2d049c78256447b2f9b9f2bcdc1add7a6b61114 Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Mon, 4 Jun 2018 18:09:41 -0400 Subject: [PATCH 116/121] Revisement 2 of Fixing emote text Fixing the fix of the fix. --- src/singletons/emotemanager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index 172a5cbe..3bb56271 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -550,11 +550,11 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa _emoteName.replace(">", ">"); static QMap emoteNameReplacements{ - {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":-O"}, {"\\:-?(p|P)", ":-P"}, {"\\:-?[\\\\/]", ":-/"}, - {"\\:-?[z|Z|\\|]", ":-Z"}, {"\\:-?\\(", ":-("}, {"\\:-?\\)", ":-)"}, - {"\\:-?D", ":-D"}, {"\\;-?(p|P)", ";-P"}, {"\\;-?\\)", ";-)"}, - {"R-?\\)", "R-)"}, {"B-?\\)", "B-)"}, + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, + {"\\:-?(o|O)", ":-O"}, {"\\:-?(p|P)", ":-P"}, {"\\:-?[\\\\/]", ":-/"}, + {"\\:-?[z|Z|\\|]", ":-Z"}, {"\\:-?\\(", ":-("}, {"\\:-?\\)", ":-)"}, + {"\\:-?D", ":-D"}, {"\\;-?(p|P)", ";-P"}, {"\\;-?\\)", ";-)"}, + {"R-?\\)", "R-)"}, {"B-?\\)", "B-)"}, }; auto it = emoteNameReplacements.find(_emoteName); From 819812c4580d9435956be3d565e04ef01a521be9 Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Mon, 4 Jun 2018 18:29:51 -0400 Subject: [PATCH 117/121] Revisement 3 of Fixing the text emotes Fix of a fix of a fix of a fix. --- src/singletons/emotemanager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index 3bb56271..aeddc38b 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -551,10 +551,10 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa static QMap emoteNameReplacements{ {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":-O"}, {"\\:-?(p|P)", ":-P"}, {"\\:-?[\\\\/]", ":-/"}, - {"\\:-?[z|Z|\\|]", ":-Z"}, {"\\:-?\\(", ":-("}, {"\\:-?\\)", ":-)"}, - {"\\:-?D", ":-D"}, {"\\;-?(p|P)", ";-P"}, {"\\;-?\\)", ";-)"}, - {"R-?\\)", "R-)"}, {"B-?\\)", "B-)"}, + {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, + {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, }; auto it = emoteNameReplacements.find(_emoteName); From 791187e6886c03921dc4dfff6c33db2c739940b1 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Tue, 5 Jun 2018 14:08:55 +0200 Subject: [PATCH 118/121] Re-implement F5 for fake messages (debug mode only) --- src/providers/irc/abstractircserver.cpp | 2 +- src/widgets/window.cpp | 41 +++++++++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/providers/irc/abstractircserver.cpp b/src/providers/irc/abstractircserver.cpp index aa4ac746..52e97e56 100644 --- a/src/providers/irc/abstractircserver.cpp +++ b/src/providers/irc/abstractircserver.cpp @@ -237,7 +237,7 @@ void AbstractIrcServer::addFakeMessage(const QString &data) { auto fakeMessage = Communi::IrcMessage::fromData(data.toUtf8(), this->readConnection.get()); - this->privateMessageReceived(qobject_cast(fakeMessage)); + this->messageReceived(fakeMessage); } void AbstractIrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index bd83e5f7..cb8c3e16 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -2,6 +2,7 @@ #include "application.hpp" #include "controllers/accounts/accountcontroller.hpp" +#include "providers/twitch/twitchserver.hpp" #include "singletons/ircmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp" @@ -100,7 +101,8 @@ Window::Window(WindowType _type) // CTRL+SHIFT+W: Close current tab CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); }); - std::vector cheerMessages; +#ifdef QT_DEBUG + std::vector cheerMessages, subMessages; // clang-format off cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=2000;color=#B22222;display-name=arzenhuz;emotes=185989:33-37;id=1ae336ac-8e1a-4d6b-8b00-9fcee26e8337;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783470139;turbo=0;user-id=111553331;user-type= :arzenhuz!arzenhuz@arzenhuz.tmi.twitch.tv PRIVMSG #pajlada :pajacheer2000 Buy pizza for both pajaH)"); cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=37;color=#3FBF72;display-name=VADIKUS007;emotes=;id=eedd95fd-2a17-4da1-879c-a1e76ffce582;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783184352;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :cheer37)"); @@ -111,13 +113,40 @@ Window::Window(WindowType _type) cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=1;color=#3FBF72;display-name=VADIKUS007;emotes=;id=c4c5061b-f5c6-464b-8bff-7f1ac816caa7;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782817171;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :trihard1)"); cheerMessages.emplace_back(R"(@badges=;bits=1;color=#FF0000;display-name=?????;emotes=;id=979b6b4f-be9a-42fb-a54c-88fcb0aca18d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1515782819084;turbo=0;user-id=70656218;user-type= :stels_tv!stels_tv@stels_tv.tmi.twitch.tv PRIVMSG #pajlada :trihard1)"); cheerMessages.emplace_back(R"(@badges=subscriber/3,premium/1;bits=1;color=#FF0000;display-name=kalvarenga;emotes=;id=4744d6f0-de1d-475d-a3ff-38647113265a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782860740;turbo=0;user-id=108393131;user-type= :kalvarenga!kalvarenga@kalvarenga.tmi.twitch.tv PRIVMSG #pajlada :trihard1)"); + + 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!)"); + subMessages.emplace_back(R"(@badges=staff/1,premium/1;color=#0000FF;display-name=TWW2;emotes=;id=e9176cd8-5e22-4684-ad40-ce53c2561c5e;login=tww2;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=Mr_Woodchuck;msg-param-recipient-id=89614178;msg-param-recipient-name=mr_woodchuck;msg-param-sub-plan-name=House\sof\sNyoro~n;msg-param-sub-plan=1000;room-id=19571752;subscriber=0;system-msg=TWW2\sgifted\sa\sTier\s1\ssub\sto\sMr_Woodchuck!;tmi-sent-ts=1521159445153;turbo=0;user-id=13405587;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada)"); + + // hyperbolicxd gifted a sub to quote_if_nam + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#00FF7F;display-name=hyperbolicxd;emotes=;id=b20ef4fe-cba8-41d0-a371-6327651dc9cc;login=hyperbolicxd;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=quote_if_nam;msg-param-recipient-id=217259245;msg-param-recipient-user-name=quote_if_nam;msg-param-sender-count=1;msg-param-sub-plan-name=Channel\sSubscription\s(nymn_hs);msg-param-sub-plan=1000;room-id=62300805;subscriber=1;system-msg=hyperbolicxd\sgifted\sa\sTier\s1\ssub\sto\squote_if_nam!\sThis\sis\stheir\sfirst\sGift\sSub\sin\sthe\schannel!;tmi-sent-ts=1528190938558;turbo=0;user-id=111534250;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); + + // first time sub + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#0000FF;display-name=byebyeheart;emotes=;id=fe390424-ab89-4c33-bb5a-53c6e5214b9f;login=byebyeheart;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=byebyeheart\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190963670;turbo=0;user-id=131956000;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); + + // first time sub + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=vJoeyzz;emotes=;id=b2476df5-fffe-4338-837b-380c5dd90051;login=vjoeyzz;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=vJoeyzz\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190995089;turbo=0;user-id=78945903;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); + + // first time sub + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=Lennydog3;emotes=;id=44feb1eb-df60-45f6-904b-7bf0d5375a41;login=lennydog3;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=Lennydog3\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528191098733;turbo=0;user-id=175759335;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); + + // resub with message + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#1E90FF;display-name=OscarLord;emotes=;id=376529fd-31a8-4da9-9c0d-92a9470da2cd;login=oscarlord;mod=0;msg-id=resub;msg-param-months=2;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=1000;room-id=39298218;subscriber=1;system-msg=OscarLord\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sOscarLord\ssubscribed\sfor\s2\smonths\sin\sa\srow!;tmi-sent-ts=1528191154801;turbo=0;user-id=162607810;user-type= :tmi.twitch.tv USERNOTICE #pajlada :Hey dk love to watch your streams keep up the good work)"); + + // resub with message + subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=samewl;emotes=9:22-23;id=599fda87-ca1e-41f2-9af7-6a28208daf1c;login=samewl;mod=0;msg-id=resub;msg-param-months=5;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol);msg-param-sub-plan=Prime;room-id=22484632;subscriber=1;system-msg=samewl\sjust\ssubscribed\swith\sTwitch\sPrime.\ssamewl\ssubscribed\sfor\s5\smonths\sin\sa\srow!;tmi-sent-ts=1528191317948;turbo=0;user-id=70273207;user-type= :tmi.twitch.tv USERNOTICE #pajlada :lot of love sebastian <3)"); + + // resub without message + subMessages.emplace_back(R"(@badges=subscriber/12;color=#CC00C2;display-name=cspice;emotes=;id=6fc4c3e0-ca61-454a-84b8-5669dee69fc9;login=cspice;mod=0;msg-id=resub;msg-param-months=12;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol):\s$9.99\sSub;msg-param-sub-plan=2000;room-id=22484632;subscriber=1;system-msg=cspice\sjust\ssubscribed\swith\sa\sTier\s2\ssub.\scspice\ssubscribed\sfor\s12\smonths\sin\sa\srow!;tmi-sent-ts=1528192510808;turbo=0;user-id=47894662;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); // clang-format on - // CreateWindowShortcut(this, "F5", [cheerMessages] { - // auto &ircManager = singletons::IrcManager::getInstance(); - // static int index = 0; - // ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]); - // }); + CreateWindowShortcut(this, "F5", [=] { + const auto &messages = subMessages; + static int index = 0; + auto app = getApp(); + const auto &msg = messages[index++ % messages.size()]; + app->twitch.server->addFakeMessage(msg); + }); +#endif this->refreshWindowTitle(""); From 7c81477c3562520b87ef44cb7614a25e41529703 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Tue, 5 Jun 2018 14:14:00 +0200 Subject: [PATCH 119/121] Improve sub/resub message parsing Instead of regexping out the username, use the existing ircv3 login-tag --- src/providers/twitch/ircmessagehandler.cpp | 24 ++++++++++++------- src/providers/twitch/twitchmessagebuilder.cpp | 11 +-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/providers/twitch/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp index a7f1799d..09753cac 100644 --- a/src/providers/twitch/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -228,16 +228,24 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, TwitchServer &server) { auto data = message->toData(); - static QRegularExpression findMessage(" USERNOTICE (#\\w+) :(.+)$"); - - auto match = findMessage.match(data); - auto target = match.captured(1); - - if (match.hasMatch()) { - this->addMessage(message, target, match.captured(2), server, true); - } auto tags = message->tags(); + auto parameters = message->parameters(); + + auto target = parameters[0]; + QString msgType = tags.value("msg-id", "").toString(); + QString content; + if (parameters.size() >= 2) { + content = parameters[1]; + } + + if (msgType == "sub" || msgType == "resub" || msgType == "subgift") { + // Sub-specific message. I think it's only allowed for "resub" messages atm + if (!content.isEmpty()) { + this->addMessage(message, target, content, server, true); + } + } + auto it = tags.find("system-msg"); if (it != tags.end()) { diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index 8f1ca38b..98631704 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -288,19 +288,10 @@ void TwitchMessageBuilder::parseUsername() // username this->userName = this->ircMessage->nick(); - if (this->userName.isEmpty()) { + if (this->userName.isEmpty() || this->args.trimSubscriberUsername) { this->userName = this->tags.value(QLatin1String("login")).toString(); } - if (this->args.trimSubscriberUsername) { - static QRegularExpression fixName("^tmi.twitch.tv\\((\\w+)\\)$"); - - auto match = fixName.match(this->userName); - if (match.hasMatch()) { - this->userName = match.captured(1); - } - } - // display name // auto displayNameVariant = this->tags.value("display-name"); // if (displayNameVariant.isValid()) { From c81df989c51fce6a4824d4ba12b4bf1d72dffca0 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Tue, 5 Jun 2018 15:01:40 +0200 Subject: [PATCH 120/121] Fix rare crash when clicking in a ChannelView --- src/widgets/helper/channelview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 75a9a886..de7af454 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -835,9 +835,9 @@ void ChannelView::mousePressEvent(QMouseEvent *event) SelectionItem selectionItem(lastMessageIndex, lastCharacterIndex); this->setSelection(selectionItem, selectionItem); - - return; } + + return; } // check if message is collapsed From ac40bede37f434a29ce751d0e8541c9b699ffa68 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Tue, 5 Jun 2018 15:03:34 +0200 Subject: [PATCH 121/121] Implement the "Reload channel emotes" menu option --- src/widgets/helper/splitheader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 3510c67a..8f57ca7c 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -392,6 +392,12 @@ void SplitHeader::menuMoveSplit() void SplitHeader::menuReloadChannelEmotes() { + auto channel = this->split->getChannel(); + TwitchChannel *twitchChannel = dynamic_cast(channel.get()); + + if (twitchChannel) { + twitchChannel->reloadChannelEmotes(); + } } void SplitHeader::menuManualReconnect()