From 5b26cdaa0777562a0b3c663a203528eca56bd5df Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 16 May 2018 14:55:45 +0200 Subject: [PATCH] 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 ec0778950..a7716aae2 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 814f92318..5ff7b2b40 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 4dcc1a6d3..5a1c5b0c3 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 42fef1c01..845f037b7 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 3bfa55415..167d5e7f4 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 53395e45a..dd6b517ea 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 8feb137a3..de203833f 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 9637e8284..26026b6bd 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 ff3a7bf0e..90259e7b0 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: