QtNovelUI/SplitView/splitview.cpp

759 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "splitview.h"
#include <QApplication>
#include <QDebug>
#include <QDrag>
#include <QHBoxLayout>
#include <QLineF>
#include <QMimeData>
#include <QPainter>
#include <QPushButton>
#include <QResizeEvent>
#include <QStyleOption>
#include <libConfig.h>
using namespace SplitFrame;
using namespace Config;
class ViewDragDrapCommon {
public:
BaseView *target;
static QString type_sign;
ViewDragDrapCommon(BaseView *inst) : target(inst) {}
ViewDragDrapCommon() : target(nullptr) {}
};
QString ViewDragDrapCommon::type_sign = "application/x-qt-windows-mime;value=\"ViewDragDrapCommon\"";
Q_DECLARE_METATYPE(ViewDragDrapCommon)
QDataStream &operator<<(QDataStream &out, const ViewDragDrapCommon &myObj) {
out << myObj.target;
return out;
}
QDataStream &operator>>(QDataStream &in, ViewDragDrapCommon &myObj) {
qint64 handle;
in >> handle;
myObj.target = (BaseView *)handle;
return in;
}
BaseView::BaseView(const QString &title, QWidget *view, bool empty, QWidget *parent) : QWidget(parent), title_header(nullptr), parent_store(nullptr) {
auto layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(0);
registComp(this);
if (!empty) {
title_header = new DragHeader(this);
title_header->setText(title);
title_header->setMaximumHeight(22);
layout->addWidget(title_header, 0);
registComp(title_header);
}
registComp(view);
layout->addWidget(view, 1);
setMouseTracking(true);
}
void BaseView::setTitle(const QString &title) {
QWidget::setWindowTitle(title);
title_header->setText(title);
}
QString BaseView::title() const { return this->title_header->text(); }
void BaseView::setIcon(const QIcon &icon) { this->title_header->setIcon(icon); }
QIcon BaseView::viewIcon() const { return title_header->icon(); }
void BaseView::registComp(QWidget *child) {
if (this->comps_list.contains(child))
return;
comps_list << child;
}
ViewPresent *BaseView::viewPanel() const {
if (!parentRect())
return nullptr;
return parentRect()->viewPanel();
}
void BaseView::closeView() {}
bool BaseView::contains(QWidget *ptr) const { return comps_list.contains(ptr); }
QWidget *BaseView::bind() const { return const_cast<BaseView *>(this); }
SplitRect *BaseView::parentRect() const { return parent_store; }
void BaseView::setParentRect(SplitRect *pinst) {
if (pinst) {
auto conv = pinst->bind();
setParent(conv);
} else
setParent(nullptr);
this->parent_store = pinst;
}
std::pair<SplitRect *, SplitRect *> BaseView::child() const { return std::make_pair<SplitRect *, SplitRect *>(nullptr, nullptr); }
void BaseView::replace(SplitRect *view, SplitRect *old) { throw new SimpleException<QString>("异常调用", "不能对ViewBase调用replace函数"); }
float BaseView::splitterWidth() const { return 0; }
float BaseView::splitterPos() const { return 0; }
SplitType BaseView::splitType() const { return SplitType::SPLIT_H; }
void BaseView::setSplitInfo(SplitType type, float pos, float width, bool relayout) {
throw new SimpleException<QString>("异常调用", "不能对ViewBase调用setSplitInfo函数");
}
QRectF BaseView::outline() const { return this->rect(); }
void BaseView::relayout(const QRectF &outline) { this->setGeometry(outline.toRect()); }
void BaseView::installPerceptionHandle(ViewPresent *obj) {
for (auto &it : comps_list) {
it->setAcceptDrops(true);
it->removeEventFilter(obj);
it->installEventFilter(obj);
}
}
void BaseView::paintEvent(QPaintEvent *ev) { QWidget::paintEvent(ev); }
void BaseView::retrieveComp(SplitRect *child_inst) { throw new SimpleException<QString>("非法操作", "BaseView不支持retrieveComp操作"); }
DockPanel::DockPanel(ViewPresent *host) : QWidget(host->bind()), size_host(host) { setWindowOpacity(50); }
void DockPanel::bind(BaseView *widget) { this->current_target = widget; }
BaseView *DockPanel::target() const { return this->current_target; }
void DockPanel::paintEvent(QPaintEvent *event) {
QWidget::paintEvent(event);
QPainter p(this);
p.setBrush(Qt::lightGray);
p.fillRect(this->rect(), Qt::lightGray);
auto rect = this->rect();
p.setPen(QPen(Qt::black, 4));
switch (active_comp) {
case ActiveArea::LEFT:
p.fillRect(QRectF(rect.topLeft(), QSizeF(rect.width() / 2, rect.height())), Qt::gray);
break;
case ActiveArea::RIGHT:
p.fillRect(QRectF(rect.center() - QPointF(0, rect.height() / 2), QSizeF(rect.width() / 2, rect.height())), Qt::gray);
break;
case ActiveArea::TOP:
p.fillRect(QRectF(rect.topLeft(), QSizeF(rect.width(), rect.height() / 2)), Qt::gray);
break;
case ActiveArea::BOTTOM:
p.fillRect(QRectF(rect.center() - QPointF(rect.width() / 2, 0), QSizeF(rect.width(), rect.height() / 2)), Qt::gray);
break;
case ActiveArea::CENTER:
p.fillRect(rect, Qt::gray);
break;
default:
break;
}
p.setBrush(Qt::transparent);
auto lt0 = rect.center() - QPoint(50, 150);
p.drawRoundRect(QRectF(lt0, QSizeF(100, 300)));
auto lt1 = rect.center() - QPoint(150, 50);
p.drawRoundRect(QRectF(lt1, QSizeF(300, 100)));
}
void DockPanel::mouseMoveEvent(QMouseEvent *event) {
auto tuple_list = view_rects();
if (std::get<0>(tuple_list).contains(event->pos())) {
this->active_comp = ActiveArea::LEFT;
} else if (std::get<1>(tuple_list).contains(event->pos())) {
this->active_comp = ActiveArea::RIGHT;
} else if (std::get<2>(tuple_list).contains(event->pos())) {
this->active_comp = ActiveArea::TOP;
} else if (std::get<3>(tuple_list).contains(event->pos())) {
this->active_comp = ActiveArea::BOTTOM;
} else if (std::get<4>(tuple_list).contains(event->pos())) {
this->active_comp = ActiveArea::CENTER;
} else {
this->active_comp = ActiveArea::NONE;
}
this->update();
}
std::tuple<QRectF, QRectF, QRectF, QRectF, QRectF> DockPanel::view_rects() const {
auto rect = this->rect();
auto lt0 = rect.center() - QPoint(50, 150);
auto lt1 = rect.center() - QPoint(150, 50);
return std::make_tuple(QRectF(lt1, QSizeF(100, 100)), QRectF(lt1 + QPoint(200, 0), QSizeF(100, 100)), QRectF(lt0, QSizeF(100, 100)),
QRectF(lt0 + QPointF(0, 200), QSizeF(100, 100)), QRectF(lt1 + QPointF(100, 0), QSizeF(100, 100)));
}
ViewPresent::ViewPresent(SplitRect *initial_view, QWidget *parent) : QWidget(parent), accept_panel(new DockPanel(this)) {
accept_panel->hide();
this->split_info_store = std::make_tuple(SplitType::SPLIT_H, 100, 7, 1);
initial_view->setParentRect(this);
setMouseTracking(true);
view_anchors << initial_view;
qRegisterMetaTypeStreamOperators<ViewDragDrapCommon>("ViewDragDrapCommon");
}
void ViewPresent::registViewComp(BaseView *it) {
if (this->all_sep_views.contains(it))
return;
it->installPerceptionHandle(this);
all_sep_views << it;
}
void ViewPresent::setViewAdjust(bool state, BaseView *one) {
this->adjust_state = state;
if (!state)
one = nullptr;
this->adjust_view_store = one;
}
bool ViewPresent::adjustState() const { return this->adjust_state; }
BaseView *ViewPresent::adjustView() const { return this->adjust_view_store; }
void ViewPresent::append(SplitRect *inst, uint index) {
auto pos = splitterPos();
auto width = splitterWidth();
auto idx = index ? 1 : 0;
auto type = splitType();
if (view_anchors.size() == 2) {
view_anchors[idx]->setParentRect(nullptr);
view_anchors[idx]->bind()->setVisible(false);
view_anchors[idx] = inst;
view_anchors[idx]->setParentRect(this);
view_anchors[idx]->bind()->setVisible(true);
} else if (view_anchors.size() == 1) {
view_anchors.insert(idx, inst);
view_anchors[idx] = inst;
view_anchors[idx]->setParentRect(this);
view_anchors[idx]->bind()->setVisible(true);
} else {
view_anchors << inst;
view_anchors.last()->setParentRect(this);
view_anchors.last()->bind()->setVisible(true);
}
this->split_info_store = std::make_tuple(type, pos, width, idx);
this->relayout();
}
bool ViewPresent::eventFilter(QObject *watched, QEvent *event) {
auto adjust_state = adjustState();
qDebug() << event->type();
if (adjust_state && event->type() == QEvent::DragEnter) {
for (auto &v : all_sep_views) {
if (v->contains((QWidget *)watched)) {
auto outline = v->outline();
QPoint gpos(v->bind()->mapToGlobal(outline.topLeft().toPoint()));
QPoint local_pos(mapFromGlobal(gpos));
QRectF local_geom(local_pos, outline.size());
accept_panel->setGeometry(local_geom.toRect());
accept_panel->setVisible(true);
event->accept();
return true;
}
}
}
return QWidget::eventFilter(watched, event);
}
QWidget *ViewPresent::bind() const { return const_cast<ViewPresent *>(this); }
ViewPresent *ViewPresent::viewPanel() const { return const_cast<ViewPresent *>(this); }
SplitRect *ViewPresent::parentRect() const { return nullptr; }
void ViewPresent::setParentRect(SplitRect *pinst) { throw new SimpleException<QString>("非法操作", "不允许对ViewPresent调用setParentRect"); }
std::pair<SplitRect *, SplitRect *> ViewPresent::child() const {
SplitRect *inst0 = nullptr, *inst1 = nullptr;
if (view_anchors.size() > 1)
inst1 = view_anchors[1];
if (view_anchors.size() > 0)
inst0 = view_anchors[0];
return std::make_pair(inst0, inst1);
}
void ViewPresent::replace(SplitRect *view, SplitRect *old) {
if (!view_anchors.contains(old))
throw new SimpleException<QString>("参数错误", "指定替换的界面不属于此界面");
if (view_anchors.contains(view))
return;
if (old) {
old->bind()->setVisible(false);
old->setParentRect(nullptr);
}
view_anchors.replace(view_anchors.indexOf(old), view);
if (view) {
view->setParentRect(this);
view->bind()->setVisible(true);
}
relayout();
}
float ViewPresent::splitterWidth() const { return std::get<2>(split_info_store); }
float ViewPresent::splitterPos() const { return std::get<1>(split_info_store); }
SplitType ViewPresent::splitType() const { return std::get<0>(split_info_store); }
void ViewPresent::setSplitInfo(SplitType type, float pos, float width, bool relayout) {
this->split_info_store = std::make_tuple(type, pos, width, std::get<3>(split_info_store));
if (relayout)
this->relayout();
}
QRectF ViewPresent::outline() const { return this->rect(); }
void ViewPresent::relayout(const QRectF &outline) { setGeometry(outline.toRect()); }
void ViewPresent::paintEvent(QPaintEvent *ev) {
QWidget::paintEvent(ev);
if (view_anchors.size() == 2) {
QPainter p(this);
auto handle_rect = handleRect();
p.fillRect(handle_rect, QBrush(Qt::gray));
}
for (auto &it : view_anchors)
it->bind()->update();
}
void ViewPresent::retrieveComp(SplitRect *child_inst) {
if (!view_anchors.contains(child_inst))
throw new SimpleException<QString>("参数非法", "回收的指定视图不属于本ViewPresent");
if (child_inst)
child_inst->bind()->setVisible(false);
view_anchors.removeAll(child_inst);
relayout();
}
void ViewPresent::resizeEvent(QResizeEvent *ev) {
QWidget::resizeEvent(ev);
relayout();
}
void ViewPresent::mousePressEvent(QMouseEvent *event) {
QWidget::mousePressEvent(event);
if (event->button() == Qt::MouseButton::LeftButton) {
if (handleRect().contains(event->pos()))
this->press_flags = true;
}
}
void ViewPresent::mouseReleaseEvent(QMouseEvent *event) {
QWidget::mouseReleaseEvent(event);
if (event->button() == Qt::MouseButton::LeftButton) {
this->press_flags = false;
}
}
void ViewPresent::mouseMoveEvent(QMouseEvent *event) {
QWidget::mouseMoveEvent(event);
auto drag_rect = handleRect();
if (drag_rect.contains(event->pos())) {
if (splitType() == SplitType::SPLIT_H)
setCursor(QCursor(Qt::CursorShape::SplitHCursor));
else
setCursor(QCursor(Qt::CursorShape::SplitVCursor));
} else {
setCursor(QCursor(Qt::CursorShape::ArrowCursor));
}
if (this->press_flags) {
auto pos = this->mapFromGlobal(event->globalPos());
auto split_margin = splitterWidth();
if (std::get<0>(split_info_store) == SplitType::SPLIT_H) {
setSplitInfo(splitType(), pos.x() - split_margin / 2, split_margin, true);
} else {
setSplitInfo(splitType(), pos.y() - split_margin / 2, split_margin, true);
}
update();
}
}
void ViewPresent::relayout() {
auto rects = viewRects();
if (view_anchors.size() == 2) {
view_anchors[0]->relayout(rects.first.toRect());
view_anchors[1]->relayout(rects.second.toRect());
} else if (view_anchors.size() == 1) {
view_anchors[0]->relayout(this->rect());
}
update();
}
std::pair<QRectF, QRectF> ViewPresent::viewRects() {
auto xrect = this->rect();
auto type = (int)splitType();
if (!type) {
auto width0 = splitterPos();
if (width0 + splitterWidth() > xrect.width())
width0 = xrect.width() - splitterWidth();
if (width0 < 0)
width0 = 0;
setSplitInfo(splitType(), width0, splitterWidth(), false);
auto rect0 = QRectF(xrect.topLeft(), QSizeF(width0, xrect.height()));
auto rect1 = QRectF(xrect.topLeft() + QPointF(rect0.width() + splitterWidth(), 0),
QSizeF(xrect.width() - rect0.width() - splitterWidth(), xrect.height()));
return std::make_pair(rect0, rect1);
} else {
auto height0 = splitterPos();
if (height0 + splitterWidth() > xrect.height())
height0 = xrect.height() - splitterWidth();
if (height0 < 0)
height0 = 0;
setSplitInfo(splitType(), height0, splitterWidth(), false);
auto rect0 = QRectF(xrect.topLeft(), QSizeF(xrect.width(), height0));
auto rect1 = QRectF(xrect.topLeft() + QPointF(0, rect0.height() + splitterWidth()),
QSizeF(xrect.width(), xrect.height() - splitterWidth() - rect0.height()));
return std::make_pair(rect0, rect1);
}
}
QRectF ViewPresent::handleRect() const {
QRectF rect;
auto width_middle = splitterWidth() - 2;
if (splitType() == SplitType::SPLIT_H) {
rect = QRectF(splitterPos() + 1, 0, width_middle, outline().height());
} else {
rect = QRectF(0, splitterPos() + 1, outline().width(), width_middle);
}
return rect;
}
SplitView::SplitView(SplitRect *first, SplitRect *next, QWidget *parent) : QWidget(parent) {
this->split_info_value = std::make_tuple(SplitType::SPLIT_H, 100, 7);
this->child_store << first;
this->child_store << next;
setMouseTracking(true);
first->setParentRect(this);
next->setParentRect(this);
}
QWidget *SplitView::bind() const { return const_cast<SplitView *>(this); }
ViewPresent *SplitView::viewPanel() const {
if (parentRect() == nullptr)
return nullptr;
return parentRect()->viewPanel();
}
SplitRect *SplitView::parentRect() const { return parent_inst; }
void SplitView::setParentRect(SplitRect *pinst) {
if (pinst) {
auto pwidget = pinst->bind();
this->setParent(pwidget);
} else
this->setParent(nullptr);
this->parent_inst = pinst;
}
std::pair<SplitRect *, SplitRect *> SplitView::child() const { return std::make_pair(child_store[0], child_store[1]); }
float SplitView::splitterWidth() const { return std::get<2>(split_info_value); }
float SplitView::splitterPos() const { return std::get<1>(split_info_value); }
SplitType SplitView::splitType() const { return std::get<0>(split_info_value); }
void SplitView::setSplitInfo(SplitType type, float pos, float width, bool relayout) {
this->split_info_value = std::make_tuple(type, pos, width);
if (relayout)
this->relayout();
}
void SplitView::replace(SplitRect *view, SplitRect *old) {
if (child_store.contains(old) && !child_store.contains(view)) {
auto index = child_store.indexOf(old);
child_store.replace(index, view);
old->setParentRect(nullptr);
view->setParentRect(this);
}
relayout();
}
QRectF SplitView::outline() const { return this->rect(); }
void SplitView::relayout(const QRectF &outlinex) {
this->setGeometry(outlinex.toRect());
update();
}
void SplitView::retrieveComp(SplitRect *child_inst) {
auto pinst = parentRect();
auto sib = this->child().first;
if (child_inst == sib)
sib = this->child().second;
child_inst->bind()->setVisible(false);
pinst->replace(sib, this);
child_inst->setParentRect(nullptr);
delete this;
}
void SplitView::paintEvent(QPaintEvent *ev) {
QWidget::paintEvent(ev);
QPainter p(this);
QRectF rect = handleRect();
p.fillRect(rect, QBrush(Qt::gray, Qt::BrushStyle::SolidPattern));
for (auto &it : child_store)
it->bind()->update();
}
void SplitView::resizeEvent(QResizeEvent *ev) {
QWidget::resizeEvent(ev);
relayout();
}
void SplitView::mousePressEvent(QMouseEvent *event) {
QWidget::mousePressEvent(event);
if (event->button() == Qt::MouseButton::LeftButton) {
if (handleRect().contains(event->pos()))
this->press_flags = true;
}
}
void SplitView::mouseReleaseEvent(QMouseEvent *event) {
QWidget::mouseReleaseEvent(event);
if (event->button() == Qt::MouseButton::LeftButton) {
this->press_flags = false;
}
}
void SplitView::mouseMoveEvent(QMouseEvent *event) {
QWidget::mouseMoveEvent(event);
auto drag_rect = handleRect() + QMargins(2, 2, 2, 2);
if (drag_rect.contains(event->pos())) {
if (splitType() == SplitType::SPLIT_H)
setCursor(QCursor(Qt::CursorShape::SplitHCursor));
else
setCursor(QCursor(Qt::CursorShape::SplitVCursor));
drags_flag = true;
} else {
setCursor(QCursor(Qt::CursorShape::ArrowCursor));
}
if (this->press_flags) {
auto pos = this->mapFromGlobal(event->globalPos());
auto split_margin = splitterWidth();
if (std::get<0>(split_info_value) == SplitType::SPLIT_H) {
setSplitInfo(splitType(), pos.x() - split_margin / 2, split_margin, true);
} else {
setSplitInfo(splitType(), pos.y() - split_margin / 2, split_margin, true);
}
update();
}
}
bool SplitView::eventFilter(QObject *watched, QEvent *event) {
if (watched != this && this->drags_flag && event->type() == QEvent::MouseMove) {
auto x = static_cast<QMouseEvent *>(event);
auto pos = mapFromGlobal(x->globalPos());
if (!handleRect().contains(pos)) {
setCursor(QCursor(Qt::CursorShape::ArrowCursor));
this->drags_flag = false;
}
return true;
}
return QWidget::eventFilter(watched, event);
}
void SplitView::relayout() {
auto rects = viewRects();
auto pair = child();
auto awidget = dynamic_cast<QWidget *>(pair.first);
auto bwidget = dynamic_cast<QWidget *>(pair.second);
if (!awidget->isVisible())
awidget->setVisible(true);
if (!bwidget->isVisible())
bwidget->setVisible(true);
pair.first->relayout(std::get<0>(rects));
pair.second->relayout(std::get<1>(rects));
update();
}
std::pair<QRectF, QRectF> SplitView::viewRects() {
auto xrect = this->rect();
auto type = (int)splitType();
if (!type) {
auto width0 = splitterPos();
if (width0 + splitterWidth() > xrect.width())
width0 = xrect.width() - splitterWidth();
if (width0 < 0)
width0 = 0;
setSplitInfo(splitType(), width0, splitterWidth(), false);
auto rect0 = QRectF(xrect.topLeft(), QSizeF(width0, xrect.height()));
auto rect1 = QRectF(xrect.topLeft() + QPointF(rect0.width() + splitterWidth(), 0),
QSizeF(xrect.width() - rect0.width() - splitterWidth(), xrect.height()));
return std::make_pair(rect0, rect1);
} else {
auto height0 = splitterPos();
if (height0 + splitterWidth() > xrect.height())
height0 = xrect.height() - splitterWidth();
if (height0 < 0)
height0 = 0;
setSplitInfo(splitType(), height0, splitterWidth(), false);
auto rect0 = QRectF(xrect.topLeft(), QSizeF(xrect.width(), height0));
auto rect1 = QRectF(xrect.topLeft() + QPointF(0, rect0.height() + splitterWidth()),
QSizeF(xrect.width(), xrect.height() - splitterWidth() - rect0.height()));
return std::make_pair(rect0, rect1);
}
}
QRectF SplitView::handleRect() const {
QRectF rect;
auto width_middle = splitterWidth() - 2;
if (splitType() == SplitType::SPLIT_H) {
rect = QRectF(splitterPos() + 1, 0, width_middle, outline().height());
} else {
rect = QRectF(0, splitterPos() + 1, outline().width(), width_middle);
}
return rect;
}
DragHeader::DragHeader(BaseView *bind) : QFrame(bind->bind()), icon_store(new QLabel(this)), title_store(new QLabel(this)), view_core(bind) {
setFrameShadow(QFrame::Shadow::Raised);
setFrameShape(QFrame::Shape::Box);
setStyleSheet("background-color: rgb(200, 200, 255);");
auto layout = new QHBoxLayout(this);
layout->setMargin(1);
layout->setSpacing(2);
layout->addWidget(icon_store, 0);
layout->addWidget(title_store, 1);
auto hide = new QPushButton("-", this);
layout->addWidget(hide, 0);
hide->setMaximumSize(20, 20);
auto close = new QPushButton("X", this);
layout->addWidget(close, 0);
close->setMaximumSize(20, 20);
connect(hide, &QPushButton::clicked, [=]() {
auto con = this->view_core->parentRect();
con->retrieveComp(view_core);
});
connect(close, &QPushButton::clicked, [=]() {
hide->click();
view_core->closeView();
});
}
void DragHeader::setIcon(const QIcon &icon) {
icon_inst = icon;
auto len = this->height() - 2;
this->icon_store->setPixmap(icon.pixmap(QSize(len, len)));
}
QIcon DragHeader::icon() const { return this->icon_inst; }
void DragHeader::setText(const QString &title) { this->title_store->setText(title); }
QString DragHeader::text() const { return title_store->text(); }
void DragHeader::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton && !std::get<0>(press_flag)) {
this->press_flag = std::make_tuple(true, event->pos());
}
}
void DragHeader::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
this->press_flag = std::make_tuple(false, QPointF());
}
}
void DragHeader::mouseMoveEvent(QMouseEvent *event) {
if (std::get<0>(press_flag) && QLineF(std::get<1>(press_flag), event->pos()).length() > QApplication::startDragDistance()) {
auto panel = view_core->viewPanel();
panel->setViewAdjust(true, view_core);
QDrag *drag = new QDrag(this);
auto pix = view_core->grab();
drag->setPixmap(pix);
drag->setHotSpot(QPoint(20, 20));
QMimeData *mimeData = new QMimeData;
mimeData->setText("移动视图:" + view_core->title());
drag->setMimeData(mimeData);
drag->exec();
}
}