added split resizing and splitting

This commit is contained in:
fourtf 2018-05-16 14:55:45 +02:00
parent 28fb877020
commit 5b26cdaa07
9 changed files with 856 additions and 629 deletions

View file

@ -10,6 +10,7 @@
#include "widgets/accountswitchpopupwidget.hpp" #include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/settingsdialog.hpp" #include "widgets/settingsdialog.hpp"
#include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -20,6 +21,9 @@
namespace chatterino { namespace chatterino {
namespace singletons { namespace singletons {
using SplitNode = widgets::SplitContainer::Node;
using SplitDirection = widgets::SplitContainer::Direction;
void WindowManager::showSettingsDialog() void WindowManager::showSettingsDialog()
{ {
QTimer::singleShot(80, [] { widgets::SettingsDialog::showDialog(); }); QTimer::singleShot(80, [] { widgets::SettingsDialog::showDialog(); });
@ -201,15 +205,24 @@ void WindowManager::initialize()
} }
// load splits // load splits
QJsonObject splitRoot = tab_obj.value("splits2").toObject();
if (!splitRoot.isEmpty()) {
tab->decodeFromJson(splitRoot);
continue;
}
// fallback load splits (old)
int colNr = 0; int colNr = 0;
for (QJsonValue column_val : tab_obj.value("splits").toArray()) { for (QJsonValue column_val : tab_obj.value("splits").toArray()) {
for (QJsonValue split_val : column_val.toArray()) { for (QJsonValue split_val : column_val.toArray()) {
widgets::Split *split = new widgets::Split(tab); widgets::Split *split = new widgets::Split(tab);
QJsonObject split_obj = split_val.toObject(); 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++; colNr++;
} }
@ -270,23 +283,11 @@ void WindowManager::save()
} }
// splits // splits
QJsonArray columns_arr; QJsonObject splits;
// std::vector<std::vector<widgets::Split *>> columns = tab->getColumns();
// for (std::vector<widgets::Split *> &cells : columns) { this->encodeNodeRecusively(tab->getBaseNode(), splits);
// QJsonArray cells_arr;
// for (widgets::Split *cell : cells) { tab_obj.insert("splits2", splits);
// 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);
tabs_arr.append(tab_obj); tabs_arr.append(tab_obj);
} }
@ -302,10 +303,46 @@ void WindowManager::save()
QString settingsPath = app->paths->settingsFolderPath + SETTINGS_FILENAME; QString settingsPath = app->paths->settingsFolderPath + SETTINGS_FILENAME;
QFile file(settingsPath); QFile file(settingsPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate); 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(); 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<SplitNode> &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) void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
{ {
util::assertInGuiThread(); util::assertInGuiThread();

View file

@ -3,6 +3,9 @@
#include "widgets/window.hpp" #include "widgets/window.hpp"
namespace chatterino { namespace chatterino {
namespace widgets {
class SplitContainer::Node;
}
namespace singletons { namespace singletons {
class WindowManager class WindowManager
@ -42,8 +45,13 @@ private:
widgets::Window *mainWindow = nullptr; widgets::Window *mainWindow = nullptr;
widgets::Window *selectedWindow = nullptr; widgets::Window *selectedWindow = nullptr;
void encodeChannel(IndirectChannel channel, QJsonObject &obj); void encodeNodeRecusively(widgets::SplitContainer::Node *node, QJsonObject &obj);
IndirectChannel decodeChannel(const 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 } // namespace singletons

View file

@ -27,7 +27,6 @@ BaseWidget::~BaseWidget()
float BaseWidget::getScale() const float BaseWidget::getScale() const
{ {
// return 1.f;
BaseWidget *baseWidget = dynamic_cast<BaseWidget *>(this->window()); BaseWidget *baseWidget = dynamic_cast<BaseWidget *>(this->window());
if (baseWidget == nullptr) { if (baseWidget == nullptr) {

View file

@ -227,9 +227,47 @@ void SplitHeader::paintEvent(QPaintEvent *)
void SplitHeader::mousePressEvent(QMouseEvent *event) 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 <ctrl+r> 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) void SplitHeader::mouseMoveEvent(QMouseEvent *event)
@ -242,27 +280,27 @@ void SplitHeader::mouseMoveEvent(QMouseEvent *event)
} }
if (this->dragging) { if (this->dragging) {
if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || if (std::abs(this->dragStart.x() - event->pos().x()) > (int)(12 * this->getScale()) ||
std::abs(this->dragStart.y() - event->pos().y()) > 12) { std::abs(this->dragStart.y() - event->pos().y()) > (int)(12 * this->getScale())) {
this->split->drag(); this->split->drag();
this->dragging = false; this->dragging = false;
} }
} }
} }
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
BaseWidget::leaveEvent(event);
}
void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event) void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->split->doChangeChannel(); this->split->doChangeChannel();
} }
this->doubleClicked = true;
} }
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
BaseWidget::leaveEvent(event);
}
void SplitHeader::rightButtonClicked() void SplitHeader::rightButtonClicked()
{ {
} }

View file

@ -41,6 +41,7 @@ protected:
virtual void paintEvent(QPaintEvent *) override; virtual void paintEvent(QPaintEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override; virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void leaveEvent(QEvent *event) override; virtual void leaveEvent(QEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
@ -50,6 +51,8 @@ private:
QPoint dragStart; QPoint dragStart;
bool dragging = false; bool dragging = false;
bool doubleClicked = false;
bool showingHelpTooltip = false;
pajlada::Signals::Connection onlineStatusChangedConnection; pajlada::Signals::Connection onlineStatusChangedConnection;

View file

@ -2,6 +2,7 @@
#include "application.hpp" #include "application.hpp"
#include "common.hpp" #include "common.hpp"
#include "singletons/thememanager.hpp" #include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "util/helpers.hpp" #include "util/helpers.hpp"
#include "util/layoutcreator.hpp" #include "util/layoutcreator.hpp"
#include "widgets/helper/notebooktab.hpp" #include "widgets/helper/notebooktab.hpp"
@ -11,6 +12,8 @@
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QMimeData> #include <QMimeData>
#include <QObject> #include <QObject>
#include <QPainter> #include <QPainter>
@ -25,7 +28,6 @@ namespace widgets {
bool SplitContainer::isDraggingSplit = false; bool SplitContainer::isDraggingSplit = false;
Split *SplitContainer::draggingSplit = nullptr; Split *SplitContainer::draggingSplit = nullptr;
// SplitContainer::Position SplitContainer::dragOriginalPosition;
SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab) SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab)
: BaseWidget(parent) : BaseWidget(parent)
@ -38,9 +40,19 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab)
this->refreshTabTitle(); this->refreshTabTitle();
this->managedConnect(Split::modifierStatusChanged, [this](auto) { this->managedConnect(Split::modifierStatusChanged, [this](auto modifiers) {
// fourtf: maybe optimize
this->layout(); this->layout();
if (modifiers == Qt::AltModifier) {
for (std::unique_ptr<ResizeHandle> &handle : this->resizeHandles) {
handle->show();
handle->raise();
}
} else {
for (std::unique_ptr<ResizeHandle> &handle : this->resizeHandles) {
handle->hide();
}
}
}); });
this->setCursor(Qt::PointingHandCursor); this->setCursor(Qt::PointingHandCursor);
@ -83,7 +95,7 @@ void SplitContainer::appendSplit(Split *split)
void SplitContainer::insertSplit(Split *split, const Position &position) 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) 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); split->giveFocus(Qt::MouseFocusReason);
this->splits.push_back(split); this->splits.push_back(split);
// this->setAcceptDrops(false); this->refreshTabTitle();
this->layout(); this->layout();
} }
@ -135,9 +147,7 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split)
this->splits.front()->giveFocus(Qt::MouseFocusReason); this->splits.front()->giveFocus(Qt::MouseFocusReason);
} }
// if (this->splits.empty()) { this->refreshTabTitle();
// this->setAcceptDrops(true);
// }
return position; return position;
} }
@ -155,13 +165,10 @@ void SplitContainer::layout()
this->baseNode.geometry = this->rect(); this->baseNode.geometry = this->rect();
std::vector<DropRect> _dropRects; std::vector<DropRect> _dropRects;
std::vector<ResizeRect> _resizeRects;
this->baseNode.layout( this->baseNode.layout(
Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging, Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging,
this->getScale(), _dropRects); this->getScale(), _dropRects, _resizeRects);
_dropRects.clear();
this->baseNode.layout(
Split::modifierStatus == (Qt::AltModifier | Qt::ControlModifier) || this->isDragging,
this->getScale(), _dropRects);
this->dropRects = _dropRects; this->dropRects = _dropRects;
@ -190,10 +197,37 @@ void SplitContainer::layout()
} }
this->overlay.setRects(std::move(_dropRects)); 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<ResizeHandle>(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(); this->update();
} }
/// EVENTS
void SplitContainer::resizeEvent(QResizeEvent *event) void SplitContainer::resizeEvent(QResizeEvent *event)
{ {
BaseWidget::resizeEvent(event); BaseWidget::resizeEvent(event);
@ -229,14 +263,14 @@ void SplitContainer::paintEvent(QPaintEvent *)
painter.setPen(this->themeManager->splits.header.text); painter.setPen(this->themeManager->splits.header.text);
QString text = "Click to add a <split>"; QString text = "Click to add a split";
Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget()); Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget());
if (notebook != nullptr) { if (notebook != nullptr) {
if (notebook->tabCount() > 1) { if (notebook->tabCount() > 1) {
text += "\n\ntip: you can drag a <split> while holding <Alt>"; text += "\n\nTip: After adding a split you can hold <Alt> to move it or split it "
text += "\nor add another one by pressing <Ctrl+T>"; "further.";
} }
} }
@ -269,110 +303,21 @@ void SplitContainer::paintEvent(QPaintEvent *)
void SplitContainer::dragEnterEvent(QDragEnterEvent *event) void SplitContainer::dragEnterEvent(QDragEnterEvent *event)
{ {
// if (!event->mimeData()->hasFormat("chatterino/split")) if (!event->mimeData()->hasFormat("chatterino/split"))
// return; return;
// if (!SplitContainer::isDraggingSplit) { if (!SplitContainer::isDraggingSplit) {
// return; return;
// } }
this->isDragging = true; this->isDragging = true;
this->layout(); this->layout();
// if (this->splits.empty()) {
// event->acceptProposedAction();
// }
this->overlay.setGeometry(this->rect()); this->overlay.setGeometry(this->rect());
this->overlay.show(); this->overlay.show();
this->overlay.raise(); 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<QVBoxLayout *>(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<Split *>(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) void SplitContainer::mouseMoveEvent(QMouseEvent *event)
{ {
this->mouseOverPoint = event->pos(); this->mouseOverPoint = event->pos();
@ -385,75 +330,6 @@ void SplitContainer::leaveEvent(QEvent *event)
this->update(); 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<QFocusEvent *>(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<int, int> 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<int, int> 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() void SplitContainer::refreshTabTitle()
{ {
assert(this->tab != nullptr); assert(this->tab != nullptr);
@ -462,30 +338,610 @@ void SplitContainer::refreshTabTitle()
return; return;
} }
this->tab->setTitle("default title"); QString newTitle = "";
bool first = true;
// QString newTitle = ""; for (const auto &chatWidget : this->splits) {
// bool first = true; auto channelName = chatWidget->getChannel()->name;
if (channelName.isEmpty()) {
continue;
}
// for (const auto &chatWidget : this->splits) { if (!first) {
// auto channelName = chatWidget->getChannel()->name; newTitle += ", ";
// if (channelName.isEmpty()) { }
// continue; newTitle += channelName;
// }
// if (!first) { first = false;
// newTitle += ", "; }
// }
// newTitle += channelName;
// first = false; if (newTitle.isEmpty()) {
// } newTitle = "empty";
}
// if (newTitle.isEmpty()) { this->tab->setTitle(newTitle);
// newTitle = "empty"; }
// }
// 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<std::unique_ptr<SplitContainer::Node>> &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<Node> &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> &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> &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 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>(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<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;
}
}
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> &node) { return val + node->getFlex(isVertical); });
}
void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vector<DropRect> &dropRects,
std::vector<ResizeRect> &resizeRects)
{
for (std::unique_ptr<Node> &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> &node) {
return val + std::max<qreal>(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<qreal>(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<Node> &child : this->children) {
// set rect
QRectF rect = childRect;
if (isVertical) {
rect.setTop(pos);
rect.setHeight(
std::max<qreal>(this->geometry.height() / totalFlex * child->flexV,
minSize) *
sizeMultiplier);
} else {
rect.setLeft(pos);
rect.setWidth(std::max<qreal>(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<SplitContainer::DropRect> _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<Node> &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 } // namespace widgets

View file

@ -19,23 +19,26 @@
#include <pajlada/signals/signal.hpp> #include <pajlada/signals/signal.hpp>
#include <pajlada/signals/signalholder.hpp> #include <pajlada/signals/signalholder.hpp>
// remove class QJsonObject;
#include "application.hpp"
#include "singletons/thememanager.hpp"
namespace chatterino { namespace chatterino {
namespace widgets { 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 class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder
{ {
Q_OBJECT Q_OBJECT
struct Node;
public: public:
// fourtf: !!! preserve the order of left, up, right and down // fourtf: !!! preserve the order of left, up, right and down
enum Direction { Left, Above, Right, Below }; enum Direction { Left, Above, Right, Below };
struct Node;
struct Position { struct Position {
private: private:
Position() = default; Position() = default;
@ -52,6 +55,7 @@ public:
friend class SplitContainer; friend class SplitContainer;
}; };
private:
struct DropRect { struct DropRect {
QRect rect; QRect rect;
Position position; 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 { struct Node final {
enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer }; enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer };
Type getType() Type getType();
{ Split *getSplit();
return this->type; Node *getParent();
} qreal getHorizontalFlex();
Split *getSplit() qreal getVerticalFlex();
{ const std::vector<std::unique_ptr<Node>> &getChildren();
return this->split;
}
const std::vector<std::unique_ptr<Node>> &getChildren()
{
return this->children;
}
private: private:
Type type; Type type;
Split *split; Split *split;
Node *parent; Node *parent;
QRectF geometry; QRectF geometry;
qreal flexH = 1;
qreal flexV = 1;
std::vector<std::unique_ptr<Node>> children; std::vector<std::unique_ptr<Node>> children;
Node() Node();
: type(Type::EmptyRoot) Node(Split *_split, Node *_parent);
, split(nullptr)
, parent(nullptr)
{
}
Node(Split *_split, Node *_parent) bool isOrContainsNode(Node *_node);
: type(Type::_Split) Node *findNodeContainingSplit(Split *_split);
, split(_split) void insertSplitRelative(Split *_split, Direction _direction);
, parent(_parent) 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<DropRect> &dropRects,
std::vector<ResizeRect> &resizeRects);
bool isOrContainsNode(Node *_node) static Type toContainerType(Direction _dir);
{
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 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>(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, float _scale, 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 = std::min<qreal>(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<Node> &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> &node) {
return val + node->geometry.width();
});
qreal childY = this->geometry.top();
qreal childHeight = this->geometry.height();
if (addSpacing) {
qreal offset = std::min<qreal>(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<Node> &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;
}
friend class SplitContainer; friend class SplitContainer;
}; };
private:
class DropOverlay : public QWidget class DropOverlay : public QWidget
{ {
public: public:
DropOverlay(SplitContainer *_parent = nullptr) DropOverlay(SplitContainer *_parent = nullptr);
: QWidget(_parent)
, parent(_parent)
, mouseOverPoint(-10000, -10000)
{
this->setMouseTracking(true);
this->setAcceptDrops(true);
}
void setRects(std::vector<SplitContainer::DropRect> _rects) void setRects(std::vector<SplitContainer::DropRect> _rects);
{
this->rects = std::move(_rects);
}
pajlada::Signals::NoArgSignal dragEnded; pajlada::Signals::NoArgSignal dragEnded;
protected: protected:
void paintEvent(QPaintEvent *event) override void paintEvent(QPaintEvent *event) override;
{ void dragEnterEvent(QDragEnterEvent *event);
QPainter painter(this); void dragMoveEvent(QDragMoveEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
// painter.fillRect(this->rect(), QColor("#334")); void dropEvent(QDropEvent *event);
bool foundMover = false;
for (DropRect &rect : this->rects) {
if (!foundMover && rect.rect.contains(this->mouseOverPoint)) {
painter.setBrush(getApp()->themes->splits.dropPreview);
painter.setPen(getApp()->themes->splits.dropPreviewBorder);
foundMover = true;
} else {
painter.setBrush(QColor(0, 0, 0, 0));
painter.setPen(QColor(0, 0, 0, 0));
// painter.setPen(getApp()->themes->splits.dropPreviewBorder);
}
painter.drawRect(rect.rect);
}
}
void mouseMoveEvent(QMouseEvent *event)
{
this->mouseOverPoint = event->pos();
}
void leaveEvent(QEvent *event)
{
this->mouseOverPoint = QPoint(-10000, -10000);
}
void dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void dragMoveEvent(QDragMoveEvent *event)
{
event->acceptProposedAction();
this->mouseOverPoint = event->pos();
this->update();
}
void dragLeaveEvent(QDragLeaveEvent *event)
{
this->mouseOverPoint = QPoint(-10000, -10000);
this->close();
this->dragEnded.invoke();
}
void dropEvent(QDropEvent *event)
{
Position *position = nullptr;
for (DropRect &rect : this->rects) {
if (rect.rect.contains(this->mouseOverPoint)) {
position = &rect.position;
break;
}
}
if (position != nullptr) {
this->parent->insertSplit(SplitContainer::draggingSplit, *position);
event->acceptProposedAction();
}
this->mouseOverPoint = QPoint(-10000, -10000);
this->close();
this->dragEnded.invoke();
}
private: private:
std::vector<DropRect> rects; std::vector<DropRect> rects;
@ -468,6 +144,27 @@ public:
SplitContainer *parent; 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); SplitContainer(Notebook *parent, NotebookTab *_tab);
void appendNewSplit(bool openChannelNameDialog); void appendNewSplit(bool openChannelNameDialog);
@ -478,6 +175,8 @@ public:
Position releaseSplit(Split *split); Position releaseSplit(Split *split);
Position deleteSplit(Split *split); Position deleteSplit(Split *split);
void decodeFromJson(QJsonObject &obj);
int getSplitCount() int getSplitCount()
{ {
return 0; return 0;
@ -491,30 +190,22 @@ public:
void refreshTabTitle(); void refreshTabTitle();
NotebookTab *getTab() const; NotebookTab *getTab() const;
const Node *getBaseNode() Node *getBaseNode()
{ {
return &this->baseNode; return &this->baseNode;
} }
static bool isDraggingSplit; static bool isDraggingSplit;
static Split *draggingSplit; static Split *draggingSplit;
// static Position dragOriginalPosition;
protected: protected:
// bool eventFilter(QObject *object, QEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
// void showEvent(QShowEvent *event) override;
// void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override; void leaveEvent(QEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *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; void resizeEvent(QResizeEvent *event) override;
@ -534,6 +225,7 @@ private:
std::vector<DropRegion> dropRegions; std::vector<DropRegion> dropRegions;
NotebookPageDropPreview dropPreview; NotebookPageDropPreview dropPreview;
DropOverlay overlay; DropOverlay overlay;
std::vector<std::unique_ptr<ResizeHandle>> resizeHandles;
QPoint mouseOverPoint; QPoint mouseOverPoint;
void layout(); void layout();
@ -545,19 +237,7 @@ private:
bool isDragging = false; bool isDragging = false;
// struct { void decodeNodeRecusively(QJsonObject &obj, Node *node);
// QVBoxLayout parentLayout;
// QHBoxLayout hbox;
// } ui;
// std::vector<Split *> splits;
// void setPreviewRect(QPoint mousePos);
// std::pair<int, int> getChatPosition(const Split *chatWidget);
// Split *createChatWidget();
}; };
} // namespace widgets } // namespace widgets

View file

@ -43,6 +43,11 @@ TooltipWidget::~TooltipWidget()
this->fontChangedConnection.disconnect(); this->fontChangedConnection.disconnect();
} }
void TooltipWidget::themeRefreshEvent()
{
this->setStyleSheet("color: #fff; background: #000");
}
void TooltipWidget::scaleChangedEvent(float) void TooltipWidget::scaleChangedEvent(float)
{ {
this->updateFont(); this->updateFont();

View file

@ -31,6 +31,7 @@ public:
protected: protected:
void changeEvent(QEvent *) override; void changeEvent(QEvent *) override;
void leaveEvent(QEvent *) override; void leaveEvent(QEvent *) override;
void themeRefreshEvent() override;
void scaleChangedEvent(float) override; void scaleChangedEvent(float) override;
private: private: