2017-06-07 10:09:24 +02:00
|
|
|
#pragma once
|
2016-12-29 17:31:07 +01:00
|
|
|
|
2017-06-26 16:41:20 +02:00
|
|
|
#include "widgets/basewidget.hpp"
|
2017-11-12 17:21:50 +01:00
|
|
|
#include "widgets/helper/droppreview.hpp"
|
|
|
|
#include "widgets/helper/notebooktab.hpp"
|
|
|
|
#include "widgets/split.hpp"
|
2016-12-30 12:20:26 +01:00
|
|
|
|
2017-01-18 04:52:47 +01:00
|
|
|
#include <QDragEnterEvent>
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
#include <QRect>
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
#include <QVector>
|
|
|
|
#include <QWidget>
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <functional>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <pajlada/signals/signalholder.hpp>
|
|
|
|
|
2017-01-18 21:30:23 +01:00
|
|
|
namespace chatterino {
|
|
|
|
namespace widgets {
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder
|
2016-12-29 17:31:07 +01:00
|
|
|
{
|
2017-01-11 18:52:09 +01:00
|
|
|
Q_OBJECT
|
2016-12-29 17:31:07 +01:00
|
|
|
|
|
|
|
public:
|
2018-05-10 19:50:31 +02:00
|
|
|
// fourtf: !!! preserve the order of left, up, right and down
|
|
|
|
enum Direction { Left, Above, Right, Below };
|
|
|
|
|
|
|
|
struct Node;
|
|
|
|
|
|
|
|
struct Position {
|
|
|
|
private:
|
|
|
|
Position() = default;
|
|
|
|
Position(Node *_relativeNode, Direction _direcion)
|
|
|
|
: relativeNode(_relativeNode)
|
|
|
|
, direction(_direcion)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *relativeNode;
|
|
|
|
Direction direction;
|
|
|
|
|
|
|
|
friend struct Node;
|
|
|
|
friend class SplitContainer;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DropRect {
|
|
|
|
QRect rect;
|
|
|
|
Position position;
|
|
|
|
|
|
|
|
DropRect(const QRect &_rect, const Position &_position)
|
|
|
|
: rect(_rect)
|
|
|
|
, position(_position)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Node final {
|
|
|
|
enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer };
|
|
|
|
|
|
|
|
Type getType()
|
|
|
|
{
|
|
|
|
return this->type;
|
|
|
|
}
|
|
|
|
Split *getSplit()
|
|
|
|
{
|
|
|
|
return this->split;
|
|
|
|
}
|
|
|
|
const std::vector<std::unique_ptr<Node>> &getChildren()
|
|
|
|
{
|
|
|
|
return this->children;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Type type;
|
|
|
|
Split *split;
|
|
|
|
Node *parent;
|
|
|
|
QRectF geometry;
|
|
|
|
std::vector<std::unique_ptr<Node>> children;
|
|
|
|
|
|
|
|
Node()
|
|
|
|
: type(Type::EmptyRoot)
|
|
|
|
, split(nullptr)
|
|
|
|
, parent(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node(Split *_split, Node *_parent)
|
|
|
|
: type(Type::_Split)
|
|
|
|
, split(_split)
|
|
|
|
, parent(_parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isOrContainsNode(Node *_node)
|
|
|
|
{
|
|
|
|
if (this == _node) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::any_of(
|
|
|
|
this->children.begin(), this->children.end(),
|
|
|
|
[_node](std::unique_ptr<Node> &n) { return n->isOrContainsNode(_node); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *findNodeContainingSplit(Split *_split)
|
|
|
|
{
|
|
|
|
if (this->type == Type::_Split && this->split == _split) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::unique_ptr<Node> &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> &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<Node>(clone));
|
|
|
|
this->type = toContainerType(_direction);
|
|
|
|
this->split = nullptr;
|
|
|
|
|
|
|
|
clone->_insertNextToThis(_split, _direction);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _insertNextToThis(Split *_split, Direction _direction)
|
|
|
|
{
|
|
|
|
auto &siblings = this->parent->children;
|
|
|
|
|
|
|
|
qreal size =
|
|
|
|
// std::accumulate(this->parent->children.begin(),
|
|
|
|
// this->parent->children.end(), 0,
|
|
|
|
// [_direction](qreal val, Node *node) {
|
|
|
|
// if (toContainerType(_direction) ==
|
|
|
|
// Type::VerticalContainer) {
|
|
|
|
// return val + node->geometry.height();
|
|
|
|
// } else {
|
|
|
|
// return val + node->geometry.width();
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
|
|
|
|
this->parent->geometry.width() / siblings.size();
|
|
|
|
|
|
|
|
if (siblings.size() == 1) {
|
|
|
|
this->geometry = QRect(0, 0, size, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto it = std::find_if(siblings.begin(), siblings.end(),
|
|
|
|
[this](auto &node) { return this == node.get(); });
|
|
|
|
|
|
|
|
assert(it != siblings.end());
|
|
|
|
if (_direction == Direction::Right || _direction == Direction::Below) {
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *node = new Node(_split, this->parent);
|
|
|
|
node->geometry = QRectF(0, 0, size, size);
|
|
|
|
siblings.insert(it, std::unique_ptr<Node>(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<Node> &sibling = siblings.front();
|
|
|
|
_parent->type = sibling->type;
|
|
|
|
_parent->split = sibling->split;
|
|
|
|
std::vector<std::unique_ptr<Node>> nodes = std::move(sibling->children);
|
|
|
|
for (auto &node : nodes) {
|
|
|
|
node->parent = _parent;
|
|
|
|
}
|
|
|
|
_parent->children = std::move(nodes);
|
|
|
|
} else {
|
|
|
|
if (this == siblings.back().get()) {
|
|
|
|
position.direction = this->parent->type == Type::VerticalContainer
|
|
|
|
? Direction::Below
|
|
|
|
: Direction::Right;
|
|
|
|
siblings.erase(it);
|
|
|
|
position.relativeNode = siblings.back().get();
|
|
|
|
} else {
|
|
|
|
position.relativeNode = (it + 1)->get();
|
|
|
|
position.direction = this->parent->type == Type::VerticalContainer
|
|
|
|
? Direction::Above
|
|
|
|
: Direction::Left;
|
|
|
|
siblings.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void layout(bool addSpacing, std::vector<DropRect> &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> &node) {
|
|
|
|
return val + node->geometry.height();
|
|
|
|
});
|
|
|
|
|
|
|
|
qreal childX = this->geometry.left();
|
|
|
|
qreal childWidth = this->geometry.width();
|
|
|
|
|
|
|
|
if (addSpacing) {
|
|
|
|
qreal offset = this->geometry.width() * 0.1;
|
|
|
|
dropRects.emplace_back(
|
|
|
|
QRect(childX, this->geometry.top(), offset, this->geometry.height()),
|
|
|
|
Position(this, Direction::Left));
|
|
|
|
dropRects.emplace_back(
|
|
|
|
QRect(childX + this->geometry.width() - offset, this->geometry.top(),
|
|
|
|
offset, this->geometry.height()),
|
|
|
|
Position(this, Direction::Right));
|
|
|
|
|
|
|
|
childX += offset;
|
|
|
|
childWidth -= offset * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal scaleFactor = this->geometry.height() / totalHeight;
|
|
|
|
|
|
|
|
qreal y = this->geometry.top();
|
|
|
|
for (std::unique_ptr<Node> &child : this->children) {
|
|
|
|
child->geometry =
|
|
|
|
QRectF(childX, y, childWidth, child->geometry.height() * scaleFactor);
|
|
|
|
|
|
|
|
child->layout(addSpacing, dropRects);
|
|
|
|
y += child->geometry.height();
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case Node::HorizontalContainer: {
|
|
|
|
qreal totalWidth =
|
|
|
|
std::accumulate(this->children.begin(), this->children.end(), 0,
|
|
|
|
[](qreal val, std::unique_ptr<Node> &node) {
|
|
|
|
return val + node->geometry.width();
|
|
|
|
});
|
|
|
|
|
|
|
|
qreal childY = this->geometry.top();
|
|
|
|
qreal childHeight = this->geometry.height();
|
|
|
|
|
|
|
|
if (addSpacing) {
|
|
|
|
qreal offset = this->geometry.height() * 0.1;
|
|
|
|
dropRects.emplace_back(
|
|
|
|
QRect(this->geometry.left(), childY, this->geometry.width(), offset),
|
|
|
|
Position(this, Direction::Above));
|
|
|
|
dropRects.emplace_back(
|
|
|
|
QRect(this->geometry.left(), childY + this->geometry.height() - offset,
|
|
|
|
this->geometry.width(), offset),
|
|
|
|
Position(this, Direction::Below));
|
|
|
|
|
|
|
|
childY += this->geometry.height() * 0.1;
|
|
|
|
childHeight -= this->geometry.height() * 0.2;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal scaleFactor = this->geometry.width() / totalWidth;
|
|
|
|
|
|
|
|
qreal x = this->geometry.left();
|
|
|
|
for (std::unique_ptr<Node> &child : this->children) {
|
|
|
|
child->geometry =
|
|
|
|
QRectF(x, childY, child->geometry.width() * scaleFactor, childHeight);
|
|
|
|
|
|
|
|
child->layout(addSpacing, dropRects);
|
|
|
|
x += child->geometry.width();
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static Type toContainerType(Direction _dir)
|
|
|
|
{
|
|
|
|
return _dir == Direction::Left || _dir == Direction::Right ? Type::HorizontalContainer
|
|
|
|
: Type::VerticalContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend class SplitContainer;
|
|
|
|
};
|
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
SplitContainer(Notebook *parent, NotebookTab *_tab);
|
2017-06-26 16:41:20 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void appendNewSplit(bool openChannelNameDialog);
|
|
|
|
void appendSplit(Split *split);
|
|
|
|
void insertSplit(Split *split, const Position &position);
|
|
|
|
void insertSplit(Split *split, Direction direction, Split *relativeTo);
|
|
|
|
void insertSplit(Split *split, Direction direction, Node *relativeTo = nullptr);
|
|
|
|
Position releaseSplit(Split *split);
|
|
|
|
Position deleteSplit(Split *split);
|
|
|
|
|
|
|
|
int getSplitCount()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<Split *> getSplits() const
|
|
|
|
{
|
|
|
|
return this->splits;
|
|
|
|
}
|
|
|
|
|
|
|
|
void refreshTabTitle();
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
NotebookTab *getTab() const;
|
2018-05-10 19:50:31 +02:00
|
|
|
const Node *getBaseNode()
|
|
|
|
{
|
|
|
|
return &this->baseNode;
|
|
|
|
}
|
2017-01-16 03:15:07 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// std::pair<int, int> removeFromLayout(Split *widget);
|
|
|
|
// void addToLayout(Split *widget, std::pair<int, int> position = std::pair<int, int>(-1,
|
|
|
|
// -1));
|
2017-05-29 21:26:55 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// const std::vector<Split *> &getSplits() const;
|
|
|
|
// std::vector<std::vector<Split *>> getColumns() const;
|
|
|
|
|
|
|
|
// void addChat(bool openChannelNameDialog = false);
|
|
|
|
|
|
|
|
// static std::pair<int, int> dropPosition;
|
2016-12-30 18:00:25 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// int currentX = 0;
|
|
|
|
// int currentY = 0;
|
|
|
|
// std::map<int, int> lastRequestedY;
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false);
|
|
|
|
// void requestFocus(int x, int y);
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// void updateFlexValues();
|
|
|
|
// int splitCount() const;
|
|
|
|
|
|
|
|
// void loadSplits();
|
|
|
|
|
|
|
|
// void save();
|
|
|
|
|
|
|
|
static bool isDraggingSplit;
|
|
|
|
static Split *draggingSplit;
|
|
|
|
// static Position dragOriginalPosition;
|
2017-11-12 17:21:50 +01:00
|
|
|
|
2016-12-30 18:00:25 +01:00
|
|
|
protected:
|
2018-05-10 19:50:31 +02:00
|
|
|
// bool eventFilter(QObject *object, QEvent *event) override;
|
2018-04-07 12:53:10 +02:00
|
|
|
void paintEvent(QPaintEvent *event) override;
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// void showEvent(QShowEvent *event) override;
|
2017-08-12 15:41:14 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// void enterEvent(QEvent *event) override;
|
|
|
|
// void leaveEvent(QEvent *event) override;
|
2018-04-07 12:53:10 +02:00
|
|
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
2017-01-01 13:07:36 +01:00
|
|
|
|
2018-04-07 12:53:10 +02:00
|
|
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
|
|
|
void dragMoveEvent(QDragMoveEvent *event) override;
|
|
|
|
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
|
|
|
void dropEvent(QDropEvent *event) override;
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void resizeEvent(QResizeEvent *event) override;
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
private:
|
2017-01-11 18:52:09 +01:00
|
|
|
struct DropRegion {
|
2017-01-01 02:30:42 +01:00
|
|
|
QRect rect;
|
|
|
|
std::pair<int, int> position;
|
|
|
|
|
|
|
|
DropRegion(QRect rect, std::pair<int, int> position)
|
|
|
|
{
|
|
|
|
this->rect = rect;
|
|
|
|
this->position = position;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
std::vector<DropRect> dropRects;
|
|
|
|
std::vector<DropRegion> dropRegions;
|
|
|
|
NotebookPageDropPreview dropPreview;
|
2017-01-16 03:15:07 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
void layout();
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
Node baseNode;
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
NotebookTab *tab;
|
2018-01-16 18:55:30 +01:00
|
|
|
std::vector<Split *> splits;
|
2017-07-02 13:10:06 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
bool isDragging = false;
|
2017-01-01 02:30:42 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// struct {
|
|
|
|
// QVBoxLayout parentLayout;
|
2017-01-28 22:35:23 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// QHBoxLayout hbox;
|
|
|
|
// } ui;
|
2017-01-29 12:26:22 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// std::vector<Split *> splits;
|
2017-06-26 16:41:20 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// void setPreviewRect(QPoint mousePos);
|
2017-08-13 16:52:16 +02:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// std::pair<int, int> getChatPosition(const Split *chatWidget);
|
2017-12-22 14:44:31 +01:00
|
|
|
|
2018-05-10 19:50:31 +02:00
|
|
|
// Split *createChatWidget();
|
2016-12-29 17:31:07 +01:00
|
|
|
};
|
2017-01-28 22:35:23 +01:00
|
|
|
|
2017-04-14 17:52:22 +02:00
|
|
|
} // namespace widgets
|
|
|
|
} // namespace chatterino
|