2018-06-26 14:39:22 +02:00
|
|
|
#include "widgets/splits/SplitContainer.hpp"
|
2018-10-07 18:27:40 +02:00
|
|
|
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "Application.hpp"
|
2018-06-26 15:33:51 +02:00
|
|
|
#include "common/Common.hpp"
|
2018-06-26 17:20:03 +02:00
|
|
|
#include "debug/AssertInGuiThread.hpp"
|
2018-06-28 20:03:04 +02:00
|
|
|
#include "singletons/Theme.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "singletons/WindowManager.hpp"
|
|
|
|
#include "util/Helpers.hpp"
|
|
|
|
#include "util/LayoutCreator.hpp"
|
|
|
|
#include "widgets/Notebook.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "widgets/helper/ChannelView.hpp"
|
2018-06-26 17:20:03 +02:00
|
|
|
#include "widgets/helper/NotebookTab.hpp"
|
2018-06-26 14:39:22 +02:00
|
|
|
#include "widgets/splits/Split.hpp"
|
2016-12-29 17:31:07 +01:00
|
|
|
|
2017-08-17 16:43:19 +02:00
|
|
|
#include <QApplication>
|
2017-01-28 22:35:23 +01:00
|
|
|
#include <QDebug>
|
2017-01-18 04:52:47 +01:00
|
|
|
#include <QHBoxLayout>
|
2018-05-16 14:55:45 +02:00
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QJsonObject>
|
2017-01-18 04:52:47 +01:00
|
|
|
#include <QMimeData>
|
2017-01-29 12:26:22 +01:00
|
|
|
#include <QObject>
|
2017-01-18 04:52:47 +01:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
#include <QWidget>
|
2017-07-02 13:10:06 +02:00
|
|
|
#include <algorithm>
|
2018-08-07 09:45:10 +02:00
|
|
|
#include <boost/foreach.hpp>
|
2017-07-02 13:10:06 +02:00
|
|
|
|
2017-04-14 17:52:22 +02:00
|
|
|
namespace chatterino {
|
2017-01-18 21:30:23 +01:00
|
|
|
|
2017-11-12 17:21:50 +01:00
|
|
|
bool SplitContainer::isDraggingSplit = false;
|
|
|
|
Split *SplitContainer::draggingSplit = nullptr;
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-23 11:59:37 +02:00
|
|
|
SplitContainer::SplitContainer(Notebook *parent)
|
2018-04-27 22:11:19 +02:00
|
|
|
: BaseWidget(parent)
|
2018-07-06 19:23:47 +02:00
|
|
|
, overlay_(this)
|
|
|
|
, mouseOverPoint_(-10000, -10000)
|
|
|
|
, tab_(nullptr)
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-05-10 23:58:07 +02:00
|
|
|
this->refreshTabTitle();
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
this->managedConnect(Split::modifierStatusChanged, [this](auto modifiers) {
|
2018-05-10 23:58:07 +02:00
|
|
|
this->layout();
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2018-06-01 14:20:46 +02:00
|
|
|
if (modifiers == showResizeHandlesModifiers) {
|
2018-08-09 16:20:09 +02:00
|
|
|
for (auto &handle : this->resizeHandles_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
handle->show();
|
|
|
|
handle->raise();
|
|
|
|
}
|
|
|
|
} else {
|
2018-08-09 16:20:09 +02:00
|
|
|
for (auto &handle : this->resizeHandles_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
handle->hide();
|
|
|
|
}
|
|
|
|
}
|
2018-06-01 14:20:46 +02:00
|
|
|
|
|
|
|
if (modifiers == showSplitOverlayModifiers) {
|
|
|
|
this->setCursor(Qt::PointingHandCursor);
|
|
|
|
} else {
|
|
|
|
this->unsetCursor();
|
|
|
|
}
|
2018-05-10 23:58:07 +02:00
|
|
|
});
|
2017-05-29 21:26:55 +02:00
|
|
|
|
2018-05-10 23:58:07 +02:00
|
|
|
this->setCursor(Qt::PointingHandCursor);
|
|
|
|
this->setAcceptDrops(true);
|
2017-05-29 21:26:55 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->managedConnect(this->overlay_.dragEnded, [this]() {
|
|
|
|
this->isDragging_ = false;
|
2018-05-10 23:58:07 +02:00
|
|
|
this->layout();
|
|
|
|
});
|
2017-08-17 22:25:41 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->overlay_.hide();
|
2017-11-12 17:21:50 +01:00
|
|
|
|
2018-05-10 23:58:07 +02:00
|
|
|
this->setMouseTracking(true);
|
2018-05-10 19:50:31 +02:00
|
|
|
this->setAcceptDrops(true);
|
2017-11-12 17:21:50 +01:00
|
|
|
}
|
|
|
|
|
2018-05-23 11:59:37 +02:00
|
|
|
NotebookTab *SplitContainer::getTab() const
|
2018-01-16 18:55:30 +01:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->tab_;
|
2018-01-16 18:55:30 +01:00
|
|
|
}
|
|
|
|
|
2018-05-23 11:59:37 +02:00
|
|
|
void SplitContainer::setTab(NotebookTab *_tab)
|
2018-05-23 04:22:17 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->tab_ = _tab;
|
2018-05-23 04:22:17 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->tab_->page = this;
|
2018-05-23 04:22:17 +02:00
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
this->refreshTab();
|
2018-05-23 04:22:17 +02:00
|
|
|
}
|
|
|
|
|
2018-05-25 16:11:03 +02:00
|
|
|
void SplitContainer::hideResizeHandles()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->overlay_.hide();
|
2018-05-25 16:11:03 +02:00
|
|
|
|
2018-08-09 16:20:09 +02:00
|
|
|
for (auto &handle : this->resizeHandles_) {
|
2018-05-25 16:11:03 +02:00
|
|
|
handle->hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 14:46:41 +02:00
|
|
|
void SplitContainer::resetMouseStatus()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = QPoint(-10000, -10000);
|
2018-06-01 14:46:41 +02:00
|
|
|
this->update();
|
|
|
|
}
|
|
|
|
|
2018-09-30 18:55:41 +02:00
|
|
|
Split *SplitContainer::appendNewSplit(bool openChannelNameDialog)
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
Split *split = new Split(this);
|
|
|
|
this->appendSplit(split);
|
2018-04-07 12:43:28 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
if (openChannelNameDialog) {
|
|
|
|
split->showChangeChannelPopup("Open channel name", true, [=](bool ok) {
|
|
|
|
if (!ok) {
|
|
|
|
this->deleteSplit(split);
|
2018-04-07 12:43:28 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
});
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
2018-09-21 20:35:14 +02:00
|
|
|
|
|
|
|
return split;
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::appendSplit(Split *split)
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-05-10 19:50:31 +02:00
|
|
|
this->insertSplit(split, Direction::Right);
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::insertSplit(Split *split, const Position &position)
|
2017-07-02 13:10:06 +02:00
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
this->insertSplit(split, position.direction_,
|
|
|
|
reinterpret_cast<Node *>(position.relativeNode_));
|
2017-07-02 13:10:06 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::insertSplit(Split *split, Direction direction,
|
|
|
|
Split *relativeTo)
|
2018-04-06 23:31:34 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
Node *node = this->baseNode_.findNodeContainingSplit(relativeTo);
|
2018-05-10 19:50:31 +02:00
|
|
|
assert(node != nullptr);
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
this->insertSplit(split, direction, node);
|
|
|
|
}
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::insertSplit(Split *split, Direction direction,
|
|
|
|
Node *relativeTo)
|
2018-05-10 19:50:31 +02:00
|
|
|
{
|
2018-10-07 18:27:40 +02:00
|
|
|
// Queue up save because: Split added
|
|
|
|
getApp()->windows->queueSave();
|
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-05-10 23:58:07 +02:00
|
|
|
split->setContainer(this);
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
if (relativeTo == nullptr) {
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->baseNode_.type_ == Node::EmptyRoot) {
|
|
|
|
this->baseNode_.setSplit(split);
|
|
|
|
} else if (this->baseNode_.type_ == Node::_Split) {
|
|
|
|
this->baseNode_.nestSplitIntoCollection(split, direction);
|
2018-05-10 19:50:31 +02:00
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->baseNode_.insertSplitRelative(split, direction);
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(this->baseNode_.isOrContainsNode(relativeTo));
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
relativeTo->insertSplitRelative(split, direction);
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2018-05-24 17:13:46 +02:00
|
|
|
this->addSplit(split);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::addSplit(Split *split)
|
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
split->setParent(this);
|
|
|
|
split->show();
|
2018-05-10 23:58:07 +02:00
|
|
|
split->giveFocus(Qt::MouseFocusReason);
|
2018-05-25 16:20:39 +02:00
|
|
|
this->unsetCursor();
|
2018-07-06 19:23:47 +02:00
|
|
|
this->splits_.push_back(split);
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
this->refreshTab();
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
split->getChannelView().tabHighlightRequested.connect(
|
|
|
|
[this](HighlightState state) {
|
|
|
|
if (this->tab_ != nullptr) {
|
|
|
|
this->tab_->setHighlightState(state);
|
|
|
|
}
|
|
|
|
});
|
2018-05-25 16:11:03 +02:00
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
split->getChannelView().liveStatusChanged.connect([this]() {
|
|
|
|
this->refreshTabLiveStatus(); //
|
|
|
|
});
|
|
|
|
|
2018-05-25 14:57:17 +02:00
|
|
|
split->focused.connect([this, split] { this->setSelected(split); });
|
2018-05-23 04:22:17 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
this->layout();
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2018-05-25 14:57:17 +02:00
|
|
|
void SplitContainer::setSelected(Split *split)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->selected_ = split;
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (Node *node = this->baseNode_.findNodeContainingSplit(split)) {
|
2018-05-25 14:57:17 +02:00
|
|
|
this->setPreferedTargetRecursive(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::setPreferedTargetRecursive(Node *node)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (node->parent_ != nullptr) {
|
|
|
|
node->parent_->preferedFocusTarget_ = node;
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->setPreferedTargetRecursive(node->parent_);
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
SplitContainer::Position SplitContainer::releaseSplit(Split *split)
|
2017-07-02 13:10:06 +02:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
Node *node = this->baseNode_.findNodeContainingSplit(split);
|
2018-05-10 19:50:31 +02:00
|
|
|
assert(node != nullptr);
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
this->splits_.erase(
|
|
|
|
std::find(this->splits_.begin(), this->splits_.end(), split));
|
2018-05-10 19:50:31 +02:00
|
|
|
split->setParent(nullptr);
|
|
|
|
Position position = node->releaseSplit();
|
|
|
|
this->layout();
|
2018-07-06 19:23:47 +02:00
|
|
|
if (splits_.size() == 0) {
|
2018-05-25 14:57:17 +02:00
|
|
|
this->setSelected(nullptr);
|
2018-05-25 16:20:39 +02:00
|
|
|
this->setCursor(Qt::PointingHandCursor);
|
2018-05-25 14:57:17 +02:00
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->splits_.front()->giveFocus(Qt::MouseFocusReason);
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
this->refreshTab();
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-05-25 16:11:03 +02:00
|
|
|
// fourtf: really bad
|
|
|
|
split->getChannelView().tabHighlightRequested.disconnectAll();
|
|
|
|
|
2018-05-23 04:22:17 +02:00
|
|
|
split->getChannelView().tabHighlightRequested.disconnectAll();
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
return position;
|
2017-07-02 13:10:06 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
SplitContainer::Position SplitContainer::deleteSplit(Split *split)
|
2017-07-02 13:10:06 +02:00
|
|
|
{
|
2018-10-07 18:27:40 +02:00
|
|
|
// Queue up save because: Split removed
|
|
|
|
getApp()->windows->queueSave();
|
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-10 19:50:31 +02:00
|
|
|
assert(split != nullptr);
|
2017-07-02 13:10:06 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
split->deleteLater();
|
|
|
|
return releaseSplit(split);
|
2017-07-02 13:10:06 +02:00
|
|
|
}
|
|
|
|
|
2018-05-25 14:57:17 +02:00
|
|
|
void SplitContainer::selectNextSplit(Direction direction)
|
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (Node *node = this->baseNode_.findNodeContainingSplit(this->selected_)) {
|
2018-05-25 14:57:17 +02:00
|
|
|
this->selectSplitRecursive(node, direction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::selectSplitRecursive(Node *node, Direction direction)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (node->parent_ != nullptr) {
|
|
|
|
if (node->parent_->type_ == Node::toContainerType(direction)) {
|
|
|
|
auto &siblings = node->parent_->children_;
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
auto it = std::find_if(
|
|
|
|
siblings.begin(), siblings.end(),
|
|
|
|
[node](const auto &other) { return other.get() == node; });
|
2018-05-25 14:57:17 +02:00
|
|
|
assert(it != siblings.end());
|
|
|
|
|
|
|
|
if (direction == Direction::Left || direction == Direction::Above) {
|
|
|
|
if (it == siblings.begin()) {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->selectSplitRecursive(node->parent_, direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
this->focusSplitRecursive(
|
|
|
|
siblings[it - siblings.begin() - 1].get(), direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (it->get() == siblings.back().get()) {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->selectSplitRecursive(node->parent_, direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
this->focusSplitRecursive(
|
|
|
|
siblings[it - siblings.begin() + 1].get(), direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->selectSplitRecursive(node->parent_, direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::focusSplitRecursive(Node *node, Direction direction)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
switch (node->type_) {
|
2018-05-25 14:57:17 +02:00
|
|
|
case Node::_Split: {
|
2018-07-06 19:23:47 +02:00
|
|
|
node->split_->giveFocus(Qt::OtherFocusReason);
|
2018-05-25 14:57:17 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case Node::HorizontalContainer:
|
|
|
|
case Node::VerticalContainer: {
|
2018-07-06 19:23:47 +02:00
|
|
|
auto &children = node->children_;
|
2018-05-25 14:57:17 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
auto it = std::find_if(
|
|
|
|
children.begin(), children.end(), [node](const auto &other) {
|
|
|
|
return node->preferedFocusTarget_ == other.get();
|
|
|
|
});
|
2018-05-25 14:57:17 +02:00
|
|
|
|
|
|
|
if (it != children.end()) {
|
|
|
|
this->focusSplitRecursive(it->get(), direction);
|
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
this->focusSplitRecursive(node->children_.front().get(),
|
|
|
|
direction);
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:39:54 +02:00
|
|
|
Split *SplitContainer::getTopRightSplit(Node &node)
|
|
|
|
{
|
|
|
|
switch (node.getType()) {
|
|
|
|
case Node::_Split:
|
|
|
|
return node.getSplit();
|
|
|
|
case Node::VerticalContainer:
|
|
|
|
if (!node.getChildren().empty())
|
|
|
|
return getTopRightSplit(*node.getChildren().front());
|
|
|
|
break;
|
|
|
|
case Node::HorizontalContainer:
|
|
|
|
if (!node.getChildren().empty())
|
|
|
|
return getTopRightSplit(*node.getChildren().back());
|
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::layout()
|
2017-08-12 15:41:14 +02:00
|
|
|
{
|
2018-09-04 21:39:54 +02:00
|
|
|
// update top right split
|
|
|
|
auto topRight = this->getTopRightSplit(this->baseNode_);
|
|
|
|
if (this->topRight_) this->topRight_->setIsTopRightSplit(false);
|
|
|
|
this->topRight_ = topRight;
|
|
|
|
if (topRight) this->topRight_->setIsTopRightSplit(true);
|
|
|
|
|
|
|
|
// layout
|
2018-08-09 16:44:04 +02:00
|
|
|
this->baseNode_.geometry_ = this->rect().adjusted(-1, -1, 0, 0);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
std::vector<DropRect> _dropRects;
|
2018-05-16 14:55:45 +02:00
|
|
|
std::vector<ResizeRect> _resizeRects;
|
2018-08-06 21:17:03 +02:00
|
|
|
this->baseNode_.layout(
|
|
|
|
Split::modifierStatus == showAddSplitRegions || this->isDragging_,
|
|
|
|
this->getScale(), _dropRects, _resizeRects);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->dropRects_ = _dropRects;
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
for (Split *split : this->splits_) {
|
2018-05-10 23:58:07 +02:00
|
|
|
const QRect &g = split->geometry();
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
Node *node = this->baseNode_.findNodeContainingSplit(split);
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-08-08 19:42:14 +02:00
|
|
|
// left
|
2018-05-10 23:58:07 +02:00
|
|
|
_dropRects.push_back(
|
2018-08-07 09:45:10 +02:00
|
|
|
DropRect(QRect(g.left(), g.top(), g.width() / 3, g.height()),
|
2018-08-06 21:17:03 +02:00
|
|
|
Position(node, Direction::Left)));
|
2018-08-08 19:42:14 +02:00
|
|
|
// right
|
|
|
|
_dropRects.push_back(DropRect(QRect(g.right() - g.width() / 3, g.top(),
|
|
|
|
g.width() / 3, g.height()),
|
2018-08-06 21:17:03 +02:00
|
|
|
Position(node, Direction::Right)));
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-08-08 19:42:14 +02:00
|
|
|
// top
|
2018-05-10 23:58:07 +02:00
|
|
|
_dropRects.push_back(
|
2018-08-06 21:17:03 +02:00
|
|
|
DropRect(QRect(g.left(), g.top(), g.width(), g.height() / 2),
|
|
|
|
Position(node, Direction::Above)));
|
2018-08-08 19:42:14 +02:00
|
|
|
// bottom
|
|
|
|
_dropRects.push_back(
|
|
|
|
DropRect(QRect(g.left(), g.bottom() - g.height() / 2, g.width(),
|
|
|
|
g.height() / 2),
|
|
|
|
Position(node, Direction::Below)));
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->splits_.empty()) {
|
2018-05-10 23:58:07 +02:00
|
|
|
QRect g = this->rect();
|
2018-08-06 21:17:03 +02:00
|
|
|
_dropRects.push_back(
|
2018-08-08 19:42:14 +02:00
|
|
|
DropRect(QRect(g.left(), g.top(), g.width() - 1, g.height() - 1),
|
2018-08-06 21:17:03 +02:00
|
|
|
Position(nullptr, Direction::Below)));
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->overlay_.setRects(std::move(_dropRects));
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
// handle resizeHandles
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->resizeHandles_.size() < _resizeRects.size()) {
|
|
|
|
while (this->resizeHandles_.size() < _resizeRects.size()) {
|
2018-08-06 21:17:03 +02:00
|
|
|
this->resizeHandles_.push_back(
|
|
|
|
std::make_unique<ResizeHandle>(this));
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-07-06 19:23:47 +02:00
|
|
|
} else if (this->resizeHandles_.size() > _resizeRects.size()) {
|
|
|
|
this->resizeHandles_.resize(_resizeRects.size());
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-24 17:13:46 +02:00
|
|
|
size_t i = 0;
|
2018-05-16 14:55:45 +02:00
|
|
|
for (ResizeRect &resizeRect : _resizeRects) {
|
2018-07-06 19:23:47 +02:00
|
|
|
ResizeHandle *handle = this->resizeHandles_[i].get();
|
2018-05-16 14:55:45 +02:00
|
|
|
handle->setGeometry(resizeRect.rect);
|
|
|
|
handle->setVertical(resizeRect.vertical);
|
|
|
|
handle->node = resizeRect.node;
|
|
|
|
|
2018-06-01 14:20:46 +02:00
|
|
|
if (Split::modifierStatus == showResizeHandlesModifiers) {
|
2018-05-16 14:55:45 +02:00
|
|
|
handle->show();
|
|
|
|
handle->raise();
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// redraw
|
2018-05-10 19:50:31 +02:00
|
|
|
this->update();
|
|
|
|
}
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::resizeEvent(QResizeEvent *event)
|
|
|
|
{
|
|
|
|
BaseWidget::resizeEvent(event);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
this->layout();
|
|
|
|
}
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::mouseReleaseEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
if (event->button() == Qt::LeftButton) {
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->splits_.size() == 0) {
|
2018-05-10 19:50:31 +02:00
|
|
|
// "Add Chat" was clicked
|
|
|
|
this->appendNewSplit(true);
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = QPoint(-10000, -10000);
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-10 23:58:07 +02:00
|
|
|
// this->setCursor(QCursor(Qt::ArrowCursor));
|
2018-05-10 19:50:31 +02:00
|
|
|
} else {
|
|
|
|
auto it =
|
2018-07-06 19:23:47 +02:00
|
|
|
std::find_if(this->dropRects_.begin(), this->dropRects_.end(),
|
2018-08-06 21:17:03 +02:00
|
|
|
[event](DropRect &rect) {
|
|
|
|
return rect.rect.contains(event->pos());
|
|
|
|
});
|
2018-07-06 19:23:47 +02:00
|
|
|
if (it != this->dropRects_.end()) {
|
2018-05-10 19:50:31 +02:00
|
|
|
this->insertSplit(new Split(this), it->position);
|
2017-08-12 15:41:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void SplitContainer::paintEvent(QPaintEvent *)
|
2017-08-12 15:41:14 +02:00
|
|
|
{
|
2018-05-10 19:50:31 +02:00
|
|
|
QPainter painter(this);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->splits_.size() == 0) {
|
2018-07-06 17:11:37 +02:00
|
|
|
painter.fillRect(rect(), this->theme->splits.background);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-07-06 17:11:37 +02:00
|
|
|
painter.setPen(this->theme->splits.header.text);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
QString text = "Click to add a split";
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-23 11:59:37 +02:00
|
|
|
Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget());
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
if (notebook != nullptr) {
|
2018-05-23 04:22:17 +02:00
|
|
|
if (notebook->getPageCount() > 1) {
|
2018-10-16 16:07:59 +02:00
|
|
|
text += "\n\nAfter adding hold <Ctrl+Alt> to move or split it.";
|
2018-05-10 19:50:31 +02:00
|
|
|
}
|
|
|
|
}
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
painter.drawText(rect(), text, QTextOption(Qt::AlignCenter));
|
|
|
|
} else {
|
2018-06-07 17:43:21 +02:00
|
|
|
if (getApp()->themes->isLightTheme()) {
|
|
|
|
painter.fillRect(rect(), QColor("#999"));
|
|
|
|
} else {
|
|
|
|
painter.fillRect(rect(), QColor("#555"));
|
|
|
|
}
|
2017-08-12 15:41:14 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
for (DropRect &dropRect : this->dropRects_) {
|
2018-06-01 14:20:46 +02:00
|
|
|
QColor border = getApp()->themes->splits.dropTargetRectBorder;
|
|
|
|
QColor background = getApp()->themes->splits.dropTargetRect;
|
2018-05-10 23:58:07 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (!dropRect.rect.contains(this->mouseOverPoint_)) {
|
2018-05-10 23:58:07 +02:00
|
|
|
// border.setAlphaF(0.1);
|
2018-06-01 14:20:46 +02:00
|
|
|
// background.setAlphaF(0.1);
|
|
|
|
} else {
|
|
|
|
// background.setAlphaF(0.1);
|
|
|
|
border.setAlpha(255);
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
painter.setPen(border);
|
|
|
|
painter.setBrush(background);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-06-01 14:20:46 +02:00
|
|
|
auto rect = dropRect.rect.marginsRemoved(QMargins(2, 2, 2, 2));
|
|
|
|
|
|
|
|
painter.drawRect(rect);
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
int s =
|
|
|
|
std::min<int>(dropRect.rect.width(), dropRect.rect.height()) - 12;
|
2018-06-01 14:20:46 +02:00
|
|
|
|
2018-07-06 17:11:37 +02:00
|
|
|
if (this->theme->isLightTheme()) {
|
2018-06-07 17:43:21 +02:00
|
|
|
painter.setPen(QColor(0, 0, 0));
|
|
|
|
} else {
|
|
|
|
painter.setPen(QColor(255, 255, 255));
|
|
|
|
}
|
2018-08-06 21:17:03 +02:00
|
|
|
painter.drawLine(rect.left() + rect.width() / 2 - (s / 2),
|
|
|
|
rect.top() + rect.height() / 2,
|
|
|
|
rect.left() + rect.width() / 2 + (s / 2),
|
|
|
|
rect.top() + rect.height() / 2);
|
|
|
|
painter.drawLine(rect.left() + rect.width() / 2,
|
|
|
|
rect.top() + rect.height() / 2 - (s / 2),
|
|
|
|
rect.left() + rect.width() / 2,
|
|
|
|
rect.top() + rect.height() / 2 + (s / 2));
|
2017-01-01 13:07:36 +01:00
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
QBrush accentColor =
|
|
|
|
(QApplication::activeWindow() == this->window()
|
|
|
|
? this->theme->tabs.selected.backgrounds.regular
|
|
|
|
: this->theme->tabs.selected.backgrounds.unfocused);
|
2017-01-16 03:15:07 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
painter.fillRect(0, 0, width(), 1, accentColor);
|
2017-01-01 13:07:36 +01:00
|
|
|
}
|
|
|
|
|
2017-11-12 17:21:50 +01:00
|
|
|
void SplitContainer::dragEnterEvent(QDragEnterEvent *event)
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-08-02 14:23:27 +02:00
|
|
|
if (!event->mimeData()->hasFormat("chatterino/split")) return;
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-08-02 14:23:27 +02:00
|
|
|
if (!SplitContainer::isDraggingSplit) return;
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->isDragging_ = true;
|
2018-05-10 19:50:31 +02:00
|
|
|
this->layout();
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->overlay_.setGeometry(this->rect());
|
|
|
|
this->overlay_.show();
|
|
|
|
this->overlay_.raise();
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
void SplitContainer::mouseMoveEvent(QMouseEvent *event)
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-06-01 14:46:41 +02:00
|
|
|
if (Split::modifierStatus == showSplitOverlayModifiers) {
|
|
|
|
this->setCursor(Qt::PointingHandCursor);
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = event->pos();
|
2018-05-16 14:55:45 +02:00
|
|
|
this->update();
|
|
|
|
}
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-24 17:13:46 +02:00
|
|
|
void SplitContainer::leaveEvent(QEvent *)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = QPoint(-10000, -10000);
|
2018-05-16 14:55:45 +02:00
|
|
|
this->update();
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
|
|
|
|
2018-05-31 16:02:20 +02:00
|
|
|
void SplitContainer::focusInEvent(QFocusEvent *)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->baseNode_.findNodeContainingSplit(this->selected_) != nullptr) {
|
|
|
|
this->selected_->setFocus();
|
2018-05-31 16:02:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->splits_.size() != 0) {
|
|
|
|
this->splits_.front()->setFocus();
|
2018-05-31 16:02:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
void SplitContainer::refreshTab()
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-10-13 14:20:06 +02:00
|
|
|
this->refreshTabTitle();
|
|
|
|
this->refreshTabLiveStatus();
|
2017-01-01 02:30:42 +01:00
|
|
|
}
|
|
|
|
|
2018-05-25 14:57:17 +02:00
|
|
|
int SplitContainer::getSplitCount()
|
|
|
|
{
|
2018-10-20 10:59:58 +02:00
|
|
|
return this->splits_.size();
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<Split *> SplitContainer::getSplits() const
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->splits_;
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SplitContainer::Node *SplitContainer::getBaseNode()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return &this->baseNode_;
|
2018-05-25 14:57:17 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
void SplitContainer::decodeFromJson(QJsonObject &obj)
|
2017-01-01 02:30:42 +01:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(this->baseNode_.type_ == Node::EmptyRoot);
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->decodeNodeRecusively(obj, &this->baseNode_);
|
2017-08-12 15:41:14 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node)
|
|
|
|
{
|
|
|
|
QString type = obj.value("type").toString();
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
if (type == "split") {
|
|
|
|
auto *split = new Split(this);
|
2018-08-06 21:17:03 +02:00
|
|
|
split->setChannel(
|
|
|
|
WindowManager::decodeChannel(obj.value("data").toObject()));
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
this->appendSplit(split);
|
|
|
|
} else if (type == "horizontal" || type == "vertical") {
|
|
|
|
bool vertical = type == "vertical";
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
Direction direction = vertical ? Direction::Below : Direction::Right;
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
node->type_ =
|
|
|
|
vertical ? Node::VerticalContainer : Node::HorizontalContainer;
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
for (QJsonValue _val : obj.value("items").toArray()) {
|
|
|
|
auto _obj = _val.toObject();
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
auto _type = _obj.value("type");
|
|
|
|
if (_type == "split") {
|
|
|
|
auto *split = new Split(this);
|
2018-08-06 21:17:03 +02:00
|
|
|
split->setChannel(WindowManager::decodeChannel(
|
|
|
|
_obj.value("data").toObject()));
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-24 17:13:46 +02:00
|
|
|
Node *_node = new Node();
|
2018-07-06 19:23:47 +02:00
|
|
|
_node->parent_ = node;
|
|
|
|
_node->split_ = split;
|
|
|
|
_node->type_ = Node::_Split;
|
2018-05-24 17:13:46 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
_node->flexH_ = _obj.value("flexh").toDouble(1.0);
|
|
|
|
_node->flexV_ = _obj.value("flexv").toDouble(1.0);
|
|
|
|
node->children_.emplace_back(_node);
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-24 17:13:46 +02:00
|
|
|
this->addSplit(split);
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
|
|
|
Node *_node = new Node();
|
2018-07-06 19:23:47 +02:00
|
|
|
_node->parent_ = node;
|
|
|
|
node->children_.emplace_back(_node);
|
2018-05-16 14:55:45 +02:00
|
|
|
this->decodeNodeRecusively(_obj, _node);
|
|
|
|
}
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
if (node->getChildren().size() < 2) {
|
|
|
|
auto *split = new Split(this);
|
2018-08-06 21:17:03 +02:00
|
|
|
split->setChannel(
|
|
|
|
WindowManager::decodeChannel(obj.value("data").toObject()));
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
this->insertSplit(split, direction, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-13 14:20:06 +02:00
|
|
|
void SplitContainer::refreshTabTitle()
|
|
|
|
{
|
|
|
|
if (this->tab_ == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString newTitle = "";
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
for (const auto &chatWidget : this->splits_) {
|
|
|
|
auto channelName = chatWidget->getChannel()->getName();
|
|
|
|
if (channelName.isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!first) {
|
|
|
|
newTitle += ", ";
|
|
|
|
}
|
|
|
|
newTitle += channelName;
|
|
|
|
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newTitle.isEmpty()) {
|
|
|
|
newTitle = "empty";
|
|
|
|
}
|
|
|
|
|
|
|
|
this->tab_->setDefaultTitle(newTitle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::refreshTabLiveStatus()
|
|
|
|
{
|
|
|
|
if (this->tab_ == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool liveStatus = false;
|
|
|
|
for (const auto &s : this->splits_) {
|
|
|
|
auto c = s->getChannel();
|
|
|
|
if (c->isLive()) {
|
|
|
|
liveStatus = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->tab_->setLive(liveStatus);
|
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
//
|
|
|
|
// Node
|
|
|
|
//
|
|
|
|
|
|
|
|
SplitContainer::Node::Type SplitContainer::Node::getType()
|
2018-05-10 23:58:07 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->type_;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
Split *SplitContainer::Node::getSplit()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->split_;
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
SplitContainer::Node *SplitContainer::Node::getParent()
|
2018-05-10 23:58:07 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->parent_;
|
2018-05-10 23:58:07 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
qreal SplitContainer::Node::getHorizontalFlex()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->flexH_;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
qreal SplitContainer::Node::getVerticalFlex()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->flexV_;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
const std::vector<std::unique_ptr<SplitContainer::Node>>
|
|
|
|
&SplitContainer::Node::getChildren()
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->children_;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
SplitContainer::Node::Node()
|
2018-07-06 19:23:47 +02:00
|
|
|
: type_(SplitContainer::Node::Type::EmptyRoot)
|
|
|
|
, split_(nullptr)
|
|
|
|
, parent_(nullptr)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
SplitContainer::Node::Node(Split *_split, Node *_parent)
|
2018-07-06 19:23:47 +02:00
|
|
|
: type_(Type::_Split)
|
|
|
|
, split_(_split)
|
|
|
|
, parent_(_parent)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
bool SplitContainer::Node::isOrContainsNode(SplitContainer::Node *_node)
|
|
|
|
{
|
|
|
|
if (this == _node) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
return std::any_of(this->children_.begin(), this->children_.end(),
|
2018-08-06 21:17:03 +02:00
|
|
|
[_node](std::unique_ptr<Node> &n) {
|
|
|
|
return n->isOrContainsNode(_node);
|
|
|
|
});
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
SplitContainer::Node *SplitContainer::Node::findNodeContainingSplit(
|
|
|
|
Split *_split)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->type_ == Type::_Split && this->split_ == _split) {
|
2018-05-16 14:55:45 +02:00
|
|
|
return this;
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
for (std::unique_ptr<Node> &node : this->children_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
Node *a = node->findNodeContainingSplit(_split);
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
if (a != nullptr) {
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::Node::insertSplitRelative(Split *_split,
|
|
|
|
Direction _direction)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->parent_ == nullptr) {
|
|
|
|
switch (this->type_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
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
|
2018-07-06 19:23:47 +02:00
|
|
|
if (parent_->type_ == toContainerType(_direction)) {
|
2018-05-16 14:55:45 +02:00
|
|
|
// hell yeah we'll just insert it next to outselves
|
2018-07-06 19:23:47 +02:00
|
|
|
this->insertNextToThis(_split, _direction);
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
|
|
|
this->nestSplitIntoCollection(_split, _direction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::Node::nestSplitIntoCollection(Split *_split,
|
|
|
|
Direction _direction)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (toContainerType(_direction) == this->type_) {
|
|
|
|
this->children_.emplace_back(new Node(_split, this));
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
|
|
|
// we'll need to nest outselves
|
|
|
|
// move all our data into a new node
|
|
|
|
Node *clone = new Node();
|
2018-07-06 19:23:47 +02:00
|
|
|
clone->type_ = this->type_;
|
|
|
|
clone->children_ = std::move(this->children_);
|
|
|
|
for (std::unique_ptr<Node> &node : clone->children_) {
|
|
|
|
node->parent_ = clone;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-07-06 19:23:47 +02:00
|
|
|
clone->split_ = this->split_;
|
|
|
|
clone->parent_ = this;
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
// add the node to our children and change our type
|
2018-07-06 19:23:47 +02:00
|
|
|
this->children_.push_back(std::unique_ptr<Node>(clone));
|
|
|
|
this->type_ = toContainerType(_direction);
|
|
|
|
this->split_ = nullptr;
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
clone->insertNextToThis(_split, _direction);
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
void SplitContainer::Node::insertNextToThis(Split *_split, Direction _direction)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
auto &siblings = this->parent_->children_;
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
qreal width = this->parent_->geometry_.width() / siblings.size();
|
|
|
|
qreal height = this->parent_->geometry_.height() / siblings.size();
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
if (siblings.size() == 1) {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->geometry_ = QRect(0, 0, int(width), int(height));
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
auto it = std::find_if(siblings.begin(), siblings.end(),
|
|
|
|
[this](auto &node) { return this == node.get(); });
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
assert(it != siblings.end());
|
|
|
|
if (_direction == Direction::Right || _direction == Direction::Below) {
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
Node *node = new Node(_split, this->parent_);
|
|
|
|
node->geometry_ = QRectF(0, 0, width, height);
|
2018-05-16 14:55:45 +02:00
|
|
|
siblings.insert(it, std::unique_ptr<Node>(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::Node::setSplit(Split *_split)
|
2017-08-13 16:52:16 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(this->split_ == nullptr);
|
|
|
|
assert(this->children_.size() == 0);
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->split_ = _split;
|
|
|
|
this->type_ = Type::_Split;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SplitContainer::Position SplitContainer::Node::releaseSplit()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(this->type_ == Type::_Split);
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (parent_ == nullptr) {
|
|
|
|
this->type_ = Type::EmptyRoot;
|
|
|
|
this->split_ = nullptr;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
Position pos;
|
2018-07-06 19:23:47 +02:00
|
|
|
pos.relativeNode_ = nullptr;
|
|
|
|
pos.direction_ = Direction::Right;
|
2018-05-16 14:55:45 +02:00
|
|
|
return pos;
|
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
auto &siblings = this->parent_->children_;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
auto it =
|
|
|
|
std::find_if(begin(siblings), end(siblings),
|
|
|
|
[this](auto &node) { return this == node.get(); });
|
2018-05-16 14:55:45 +02:00
|
|
|
assert(it != siblings.end());
|
|
|
|
|
|
|
|
Position position;
|
|
|
|
if (siblings.size() == 2) {
|
|
|
|
// delete this and move split to parent
|
2018-07-06 19:23:47 +02:00
|
|
|
position.relativeNode_ = this->parent_;
|
|
|
|
if (this->parent_->type_ == Type::VerticalContainer) {
|
2018-08-06 21:17:03 +02:00
|
|
|
position.direction_ = siblings.begin() == it ? Direction::Above
|
|
|
|
: Direction::Below;
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
position.direction_ =
|
|
|
|
siblings.begin() == it ? Direction::Left : Direction::Right;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
Node *_parent = this->parent_;
|
2018-05-16 14:55:45 +02:00
|
|
|
siblings.erase(it);
|
|
|
|
std::unique_ptr<Node> &sibling = siblings.front();
|
2018-07-06 19:23:47 +02:00
|
|
|
_parent->type_ = sibling->type_;
|
|
|
|
_parent->split_ = sibling->split_;
|
2018-08-06 21:17:03 +02:00
|
|
|
std::vector<std::unique_ptr<Node>> nodes =
|
|
|
|
std::move(sibling->children_);
|
2018-05-16 14:55:45 +02:00
|
|
|
for (auto &node : nodes) {
|
2018-07-06 19:23:47 +02:00
|
|
|
node->parent_ = _parent;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2018-07-06 19:23:47 +02:00
|
|
|
_parent->children_ = std::move(nodes);
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
|
|
|
if (this == siblings.back().get()) {
|
2018-08-06 21:17:03 +02:00
|
|
|
position.direction_ =
|
|
|
|
this->parent_->type_ == Type::VerticalContainer
|
|
|
|
? Direction::Below
|
|
|
|
: Direction::Right;
|
2018-05-16 14:55:45 +02:00
|
|
|
siblings.erase(it);
|
2018-07-06 19:23:47 +02:00
|
|
|
position.relativeNode_ = siblings.back().get();
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
position.relativeNode_ = (it + 1)->get();
|
2018-08-06 21:17:03 +02:00
|
|
|
position.direction_ =
|
|
|
|
this->parent_->type_ == Type::VerticalContainer
|
|
|
|
? Direction::Above
|
|
|
|
: Direction::Left;
|
2018-05-16 14:55:45 +02:00
|
|
|
siblings.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal SplitContainer::Node::getFlex(bool isVertical)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return isVertical ? this->flexV_ : this->flexH_;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal SplitContainer::Node::getSize(bool isVertical)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return isVertical ? this->geometry_.height() : this->geometry_.width();
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal SplitContainer::Node::getChildrensTotalFlex(bool isVertical)
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
return std::accumulate(this->children_.begin(), this->children_.end(),
|
|
|
|
qreal(0),
|
|
|
|
[=](qreal val, std::unique_ptr<Node> &node) {
|
|
|
|
return val + node->getFlex(isVertical);
|
|
|
|
});
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::Node::layout(bool addSpacing, float _scale,
|
|
|
|
std::vector<DropRect> &dropRects,
|
2018-05-16 14:55:45 +02:00
|
|
|
std::vector<ResizeRect> &resizeRects)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
for (std::unique_ptr<Node> &node : this->children_) {
|
2018-08-02 14:23:27 +02:00
|
|
|
if (node->flexH_ <= 0) node->flexH_ = 0;
|
|
|
|
if (node->flexV_ <= 0) node->flexV_ = 0;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
switch (this->type_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
case Node::_Split: {
|
2018-07-06 19:23:47 +02:00
|
|
|
QRect rect = this->geometry_.toRect();
|
2018-08-06 21:17:03 +02:00
|
|
|
this->split_->setGeometry(
|
2018-08-09 16:20:09 +02:00
|
|
|
rect.marginsRemoved(QMargins(1, 1, 0, 0)));
|
2018-05-16 14:55:45 +02:00
|
|
|
} break;
|
|
|
|
case Node::VerticalContainer:
|
|
|
|
case Node::HorizontalContainer: {
|
2018-07-06 19:23:47 +02:00
|
|
|
bool isVertical = this->type_ == Node::VerticalContainer;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
// vars
|
2018-05-24 17:22:51 +02:00
|
|
|
qreal minSize = qreal(48 * _scale);
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
qreal totalFlex = this->getChildrensTotalFlex(isVertical);
|
|
|
|
qreal totalSize = std::accumulate(
|
2018-07-06 19:23:47 +02:00
|
|
|
this->children_.begin(), this->children_.end(), qreal(0),
|
2018-05-16 14:55:45 +02:00
|
|
|
[=](int val, std::unique_ptr<Node> &node) {
|
2018-08-06 21:17:03 +02:00
|
|
|
return val + std::max<qreal>(this->getSize(isVertical) /
|
|
|
|
totalFlex *
|
2018-05-16 14:55:45 +02:00
|
|
|
node->getFlex(isVertical),
|
|
|
|
minSize);
|
|
|
|
});
|
|
|
|
|
|
|
|
qreal sizeMultiplier = this->getSize(isVertical) / totalSize;
|
2018-07-06 19:23:47 +02:00
|
|
|
QRectF childRect = this->geometry_;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
// add spacing if reqested
|
|
|
|
if (addSpacing) {
|
2018-08-06 21:17:03 +02:00
|
|
|
qreal offset = std::min<qreal>(this->getSize(!isVertical) * 0.1,
|
|
|
|
qreal(_scale * 24));
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
// droprect left / above
|
|
|
|
dropRects.emplace_back(
|
2018-07-06 19:23:47 +02:00
|
|
|
QRectF(this->geometry_.left(), this->geometry_.top(),
|
|
|
|
isVertical ? offset : this->geometry_.width(),
|
|
|
|
isVertical ? this->geometry_.height() : offset)
|
2018-05-24 17:22:51 +02:00
|
|
|
.toRect(),
|
2018-08-06 21:17:03 +02:00
|
|
|
Position(this,
|
|
|
|
isVertical ? Direction::Left : Direction::Above));
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
// droprect right / below
|
|
|
|
if (isVertical) {
|
|
|
|
dropRects.emplace_back(
|
2018-08-06 21:17:03 +02:00
|
|
|
QRectF(this->geometry_.right() - offset,
|
|
|
|
this->geometry_.top(), offset,
|
2018-07-06 19:23:47 +02:00
|
|
|
this->geometry_.height())
|
2018-05-24 17:22:51 +02:00
|
|
|
.toRect(),
|
2018-05-16 14:55:45 +02:00
|
|
|
Position(this, Direction::Right));
|
|
|
|
} else {
|
|
|
|
dropRects.emplace_back(
|
2018-08-06 21:17:03 +02:00
|
|
|
QRectF(this->geometry_.left(),
|
|
|
|
this->geometry_.bottom() - offset,
|
2018-07-06 19:23:47 +02:00
|
|
|
this->geometry_.width(), offset)
|
2018-05-24 17:22:51 +02:00
|
|
|
.toRect(),
|
2018-05-16 14:55:45 +02:00
|
|
|
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
|
2018-08-09 16:20:09 +02:00
|
|
|
auto pos = int(isVertical ? childRect.top() : childRect.left());
|
2018-07-06 19:23:47 +02:00
|
|
|
for (std::unique_ptr<Node> &child : this->children_) {
|
2018-05-16 14:55:45 +02:00
|
|
|
// set rect
|
2018-08-09 16:20:09 +02:00
|
|
|
QRect rect = childRect.toRect();
|
2018-05-16 14:55:45 +02:00
|
|
|
if (isVertical) {
|
|
|
|
rect.setTop(pos);
|
|
|
|
rect.setHeight(
|
2018-08-06 21:17:03 +02:00
|
|
|
std::max<qreal>(this->geometry_.height() / totalFlex *
|
|
|
|
child->flexV_,
|
2018-05-16 14:55:45 +02:00
|
|
|
minSize) *
|
|
|
|
sizeMultiplier);
|
|
|
|
} else {
|
|
|
|
rect.setLeft(pos);
|
2018-08-06 21:17:03 +02:00
|
|
|
rect.setWidth(std::max<qreal>(this->geometry_.width() /
|
|
|
|
totalFlex * child->flexH_,
|
|
|
|
minSize) *
|
|
|
|
sizeMultiplier);
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 16:20:09 +02:00
|
|
|
if (child == this->children_.back()) {
|
|
|
|
rect.setRight(childRect.right() - 1);
|
|
|
|
rect.setBottom(childRect.bottom() - 1);
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
child->geometry_ = rect;
|
2018-05-16 14:55:45 +02:00
|
|
|
child->layout(addSpacing, _scale, dropRects, resizeRects);
|
|
|
|
|
|
|
|
pos += child->getSize(isVertical);
|
|
|
|
|
|
|
|
// add resize rect
|
2018-07-06 19:23:47 +02:00
|
|
|
if (child != this->children_.front()) {
|
2018-08-06 21:17:03 +02:00
|
|
|
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));
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// normalize flex
|
|
|
|
if (isVertical) {
|
2018-08-06 21:17:03 +02:00
|
|
|
child->flexV_ =
|
|
|
|
child->flexV_ / totalFlex * this->children_.size();
|
2018-07-06 19:23:47 +02:00
|
|
|
child->flexH_ = 1;
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
child->flexH_ =
|
|
|
|
child->flexH_ / totalFlex * this->children_.size();
|
2018-07-06 19:23:47 +02:00
|
|
|
child->flexV_ = 1;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
SplitContainer::Node::Type SplitContainer::Node::toContainerType(Direction _dir)
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
return _dir == Direction::Left || _dir == Direction::Right
|
|
|
|
? Type::HorizontalContainer
|
|
|
|
: Type::VerticalContainer;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// DropOverlay
|
|
|
|
//
|
|
|
|
|
|
|
|
SplitContainer::DropOverlay::DropOverlay(SplitContainer *_parent)
|
|
|
|
: QWidget(_parent)
|
2018-07-06 19:23:47 +02:00
|
|
|
, mouseOverPoint_(-10000, -10000)
|
|
|
|
, parent_(_parent)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
|
|
|
this->setMouseTracking(true);
|
|
|
|
this->setAcceptDrops(true);
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void SplitContainer::DropOverlay::setRects(
|
|
|
|
std::vector<SplitContainer::DropRect> _rects)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->rects_ = std::move(_rects);
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// pajlada::Signals::NoArgSignal dragEnded;
|
|
|
|
|
2018-05-24 17:13:46 +02:00
|
|
|
void SplitContainer::DropOverlay::paintEvent(QPaintEvent *)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
|
|
|
QPainter painter(this);
|
|
|
|
|
|
|
|
// painter.fillRect(this->rect(), QColor("#334"));
|
|
|
|
|
|
|
|
bool foundMover = false;
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
for (DropRect &rect : this->rects_) {
|
|
|
|
if (!foundMover && rect.rect.contains(this->mouseOverPoint_)) {
|
2018-05-16 14:55:45 +02:00
|
|
|
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();
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = event->pos();
|
2018-05-16 14:55:45 +02:00
|
|
|
this->update();
|
|
|
|
}
|
|
|
|
|
2018-05-24 17:22:51 +02:00
|
|
|
void SplitContainer::DropOverlay::dragLeaveEvent(QDragLeaveEvent *)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = QPoint(-10000, -10000);
|
2018-05-16 14:55:45 +02:00
|
|
|
this->close();
|
|
|
|
this->dragEnded.invoke();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::DropOverlay::dropEvent(QDropEvent *event)
|
|
|
|
{
|
|
|
|
Position *position = nullptr;
|
2018-07-06 19:23:47 +02:00
|
|
|
for (DropRect &rect : this->rects_) {
|
|
|
|
if (rect.rect.contains(this->mouseOverPoint_)) {
|
2018-05-16 14:55:45 +02:00
|
|
|
position = &rect.position;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (position != nullptr) {
|
2018-07-06 19:23:47 +02:00
|
|
|
this->parent_->insertSplit(SplitContainer::draggingSplit, *position);
|
2018-05-16 14:55:45 +02:00
|
|
|
event->acceptProposedAction();
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->mouseOverPoint_ = QPoint(-10000, -10000);
|
2018-05-16 14:55:45 +02:00
|
|
|
this->close();
|
|
|
|
this->dragEnded.invoke();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// ResizeHandle
|
|
|
|
//
|
|
|
|
|
|
|
|
void SplitContainer::ResizeHandle::setVertical(bool isVertical)
|
|
|
|
{
|
|
|
|
this->setCursor(isVertical ? Qt::SplitVCursor : Qt::SplitHCursor);
|
2018-07-06 19:23:47 +02:00
|
|
|
this->vertical_ = isVertical;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SplitContainer::ResizeHandle::ResizeHandle(SplitContainer *_parent)
|
|
|
|
: QWidget(_parent)
|
|
|
|
, parent(_parent)
|
|
|
|
{
|
|
|
|
this->setMouseTracking(true);
|
2018-05-25 15:04:41 +02:00
|
|
|
this->hide();
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-05-24 17:22:51 +02:00
|
|
|
void SplitContainer::ResizeHandle::paintEvent(QPaintEvent *)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
|
|
|
QPainter painter(this);
|
2018-06-01 14:20:46 +02:00
|
|
|
painter.setPen(QPen(getApp()->themes->splits.resizeHandle, 2));
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
painter.fillRect(this->rect(),
|
|
|
|
getApp()->themes->splits.resizeHandleBackground);
|
2018-05-25 15:03:58 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->vertical_) {
|
2018-08-06 21:17:03 +02:00
|
|
|
painter.drawLine(0, this->height() / 2, this->width(),
|
|
|
|
this->height() / 2);
|
2018-05-25 15:03:58 +02:00
|
|
|
} else {
|
2018-08-06 21:17:03 +02:00
|
|
|
painter.drawLine(this->width() / 2, 0, this->width() / 2,
|
|
|
|
this->height());
|
2018-05-25 15:03:58 +02:00
|
|
|
}
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-08-07 23:46:00 +02:00
|
|
|
void SplitContainer::ResizeHandle::mousePressEvent(QMouseEvent *event)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->isMouseDown_ = true;
|
2018-08-07 23:46:00 +02:00
|
|
|
|
|
|
|
if (event->button() == Qt::RightButton) {
|
|
|
|
this->resetFlex();
|
|
|
|
}
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-05-24 17:22:51 +02:00
|
|
|
void SplitContainer::ResizeHandle::mouseReleaseEvent(QMouseEvent *)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->isMouseDown_ = false;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::ResizeHandle::mouseMoveEvent(QMouseEvent *event)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
if (!this->isMouseDown_) {
|
2017-08-13 16:52:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
assert(node != nullptr);
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(node->parent_ != nullptr);
|
2017-08-13 16:52:16 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
auto &siblings = node->parent_->getChildren();
|
2018-08-06 21:17:03 +02:00
|
|
|
auto it = std::find_if(siblings.begin(), siblings.end(),
|
|
|
|
[this](const std::unique_ptr<Node> &n) {
|
|
|
|
return n.get() == this->node;
|
|
|
|
});
|
2017-08-17 22:25:41 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
assert(it != siblings.end());
|
|
|
|
Node *before = siblings[it - siblings.begin() - 1].get();
|
2017-08-13 16:52:16 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
QPoint topLeft =
|
|
|
|
this->parent->mapToGlobal(before->geometry_.topLeft().toPoint());
|
|
|
|
QPoint bottomRight = this->parent->mapToGlobal(
|
|
|
|
this->node->geometry_.bottomRight().toPoint());
|
2017-08-13 16:52:16 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
int globalX = topLeft.x() > event->globalX()
|
|
|
|
? topLeft.x()
|
2018-08-06 21:17:03 +02:00
|
|
|
: (bottomRight.x() < event->globalX() ? bottomRight.x()
|
|
|
|
: event->globalX());
|
2018-05-16 14:55:45 +02:00
|
|
|
int globalY = topLeft.y() > event->globalY()
|
|
|
|
? topLeft.y()
|
2018-08-06 21:17:03 +02:00
|
|
|
: (bottomRight.y() < event->globalY() ? bottomRight.y()
|
|
|
|
: event->globalY());
|
2018-05-10 19:50:31 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
QPoint mousePoint(globalX, globalY);
|
2017-08-17 22:25:41 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
if (this->vertical_) {
|
|
|
|
qreal totalFlexV = this->node->flexV_ + before->flexV_;
|
2018-08-06 21:17:03 +02:00
|
|
|
before->flexV_ = totalFlexV * (mousePoint.y() - topLeft.y()) /
|
|
|
|
(bottomRight.y() - topLeft.y());
|
2018-07-06 19:23:47 +02:00
|
|
|
this->node->flexV_ = totalFlexV - before->flexV_;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
this->parent->layout();
|
|
|
|
|
|
|
|
// move handle
|
2018-07-06 19:23:47 +02:00
|
|
|
this->move(this->x(), int(before->geometry_.bottom() - 4));
|
2018-05-16 14:55:45 +02:00
|
|
|
} else {
|
2018-07-06 19:23:47 +02:00
|
|
|
qreal totalFlexH = this->node->flexH_ + before->flexH_;
|
2018-08-06 21:17:03 +02:00
|
|
|
before->flexH_ = totalFlexH * (mousePoint.x() - topLeft.x()) /
|
|
|
|
(bottomRight.x() - topLeft.x());
|
2018-07-06 19:23:47 +02:00
|
|
|
this->node->flexH_ = totalFlexH - before->flexH_;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
this->parent->layout();
|
|
|
|
|
|
|
|
// move handle
|
2018-07-06 19:23:47 +02:00
|
|
|
this->move(int(before->geometry_.right() - 4), this->y());
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2017-06-26 16:41:20 +02:00
|
|
|
}
|
|
|
|
|
2018-07-04 19:52:11 +02:00
|
|
|
void SplitContainer::ResizeHandle::mouseDoubleClickEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
event->accept();
|
|
|
|
|
2018-08-07 23:46:00 +02:00
|
|
|
this->resetFlex();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitContainer::ResizeHandle::resetFlex()
|
|
|
|
{
|
2018-07-04 19:52:11 +02:00
|
|
|
for (auto &sibling : this->node->getParent()->getChildren()) {
|
2018-07-06 19:23:47 +02:00
|
|
|
sibling->flexH_ = 1;
|
|
|
|
sibling->flexV_ = 1;
|
2018-07-04 19:52:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this->parent->layout();
|
|
|
|
}
|
|
|
|
|
2017-04-14 17:52:22 +02:00
|
|
|
} // namespace chatterino
|