mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
added split resizing and splitting
This commit is contained in:
parent
28fb877020
commit
5b26cdaa07
9 changed files with 856 additions and 629 deletions
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue