QtNovelUI/libSplitView/splitpanel.cpp

587 lines
18 KiB
C++
Raw Normal View History

2023-08-26 12:40:57 +00:00
#include "splitpanel.h"
#include "baseview.h"
#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>
namespace SplitFrame {
/**
* @brief
*/
class SPLITVIEW_EXPORT AttachPanel : public QWidget {
enum class ActiveArea { LEFT, RIGHT, TOP, BOTTOM, CENTER, NONE };
public:
AttachPanel(ViewPresent *host);
void bindAttachment(RectCom *widget);
RectCom *attachmentTarget() const;
virtual void paintEvent(QPaintEvent *event) override;
virtual void dragMoveEvent(QDragMoveEvent *event) override;
virtual void dragEnterEvent(QDragEnterEvent *event) override;
virtual void dropEvent(QDropEvent *event) override;
private:
ViewPresent *const adjust_host;
RectCom *current_target;
ActiveArea active_comp;
std::tuple<QRectF, QRectF, QRectF, QRectF, QRectF> view_rects() const;
};
} // namespace SplitFrame
using namespace SplitFrame;
using namespace Config;
AttachPanel::AttachPanel(ViewPresent *host) : QWidget(host->bind()), adjust_host(host) {
setWindowOpacity(50);
setAcceptDrops(true);
}
void AttachPanel::bindAttachment(RectCom *widget) { this->current_target = widget; }
RectCom *AttachPanel::attachmentTarget() const { return this->current_target; }
void AttachPanel::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);
p.setPen(QPen(Qt::black, 1));
auto x_views = view_rects();
QVector<QRectF> rects;
rects << std::get<0>(x_views);
rects << std::get<1>(x_views);
rects << std::get<2>(x_views);
rects << std::get<3>(x_views);
rects << std::get<4>(x_views);
p.drawRects(rects);
}
void AttachPanel::dragMoveEvent(QDragMoveEvent *event) {
QWidget::dragMoveEvent(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();
}
void AttachPanel::dragEnterEvent(QDragEnterEvent *event) {
QWidget::dragEnterEvent(event);
if (adjust_host->adjustState())
event->acceptProposedAction();
}
void AttachPanel::dropEvent(QDropEvent *event) {
QWidget::dropEvent(event);
event->acceptProposedAction();
auto xfrom = adjust_host->adjustView();
auto target = this->attachmentTarget();
if (target != xfrom && active_comp != ActiveArea::NONE) {
static_cast<SplitRect *>(xfrom->parentRes())->removeComp(xfrom);
adjust_host->objsRelateRebuild();
auto ptarget = static_cast<SplitRect *>(target->parentRes());
auto target_rect = target->outline();
RectCom *new_split = nullptr;
switch (this->active_comp) {
case ActiveArea::LEFT: {
new_split = new SplitView(xfrom, target, adjust_host->bind());
static_cast<SplitView *>(new_split)->setSplitInfo(SplitType::SPLIT_H, target_rect.width() / 2, adjust_host->splitterWidth(), false);
} break;
case ActiveArea::RIGHT: {
new_split = new SplitView(target, xfrom, adjust_host->bind());
static_cast<SplitView *>(new_split)->setSplitInfo(SplitType::SPLIT_H, target_rect.width() / 2, adjust_host->splitterWidth(), false);
} break;
case ActiveArea::TOP: {
new_split = new SplitView(xfrom, target, adjust_host->bind());
static_cast<SplitView *>(new_split)->setSplitInfo(SplitType::SPLIT_V, target_rect.height() / 2, adjust_host->splitterWidth(), false);
} break;
case ActiveArea::BOTTOM: {
new_split = new SplitView(target, xfrom, adjust_host->bind());
static_cast<SplitView *>(new_split)->setSplitInfo(SplitType::SPLIT_V, target_rect.height() / 2, adjust_host->splitterWidth(), false);
} break;
case ActiveArea::CENTER: {
new_split = xfrom;
adjust_host->markFreedom(target);
} break;
default:
return;
}
ptarget->replaceComp(new_split, target);
new_split->relayout(target_rect);
adjust_host->objsRelateRebuild();
}
adjust_host->setAdjustView();
this->setVisible(false);
}
std::tuple<QRectF, QRectF, QRectF, QRectF, QRectF> AttachPanel::view_rects() const {
auto rect = this->rect();
auto boder_len = std::min(rect.width() - 4, rect.height() - 4);
auto rect_length = std::min(boder_len * 4 / 5, 300);
auto rect_height = rect_length / 3;
auto lt0 = rect.center() - QPoint(rect_height / 2, rect_length / 2);
auto lt1 = rect.center() - QPoint(rect_length / 2, rect_height / 2);
return std::make_tuple(QRectF(lt1 - QPointF(3, 0), QSizeF(rect_height, rect_height)),
QRectF(lt1 + QPoint(rect_height * 2 + 3, 0), QSizeF(rect_height, rect_height)),
QRectF(lt0 - QPointF(0, 3), QSizeF(rect_height, rect_height)),
QRectF(lt0 + QPointF(0, rect_height * 2 + 3), QSizeF(rect_height, rect_height)),
QRectF(lt1 + QPointF(rect_height, 0), QSizeF(rect_height, rect_height)));
}
ViewPresent::ViewPresent(QWidget *parent) : QWidget(parent), accept_panel(new AttachPanel(this)) {
accept_panel->hide();
setMouseTracking(true);
}
ViewPresent::~ViewPresent() { accept_panel->deleteLater(); }
void ViewPresent::addListener(FreedomViewsListener *lsn) {
if (lsn_list.contains(lsn))
return;
lsn_list << lsn;
}
void ViewPresent::removeListener(FreedomViewsListener *lsn) { lsn_list.removeAll(lsn); }
void ViewPresent::regist_basepanel(RectCom *it) {
if (this->all_func_views.contains(it))
return;
dynamic_cast<BaseView *>(it)->installPerceptionHandle(this);
all_func_views << it;
}
void ViewPresent::markFreedom(ViewRes *view) {
if (!view)
return;
auto cinst = static_cast<RectCom *>(view);
if (!freedom_views.contains(cinst))
freedom_views << cinst;
cinst->bind()->setVisible(false);
cinst->setParentRes(nullptr);
}
void ViewPresent::purge(ViewRes *target) {
if (!target)
return;
auto cinst = static_cast<RectCom *>(target);
if (!all_func_views.contains(cinst))
throw new SimpleException<bool>("参数非法", "目标实例不属于本ViewPresent");
cinst->bind()->setVisible(false);
target->setParentRes(nullptr);
if (temp_visible_view == cinst)
temp_visible_view = nullptr;
all_func_views.removeAll(cinst);
freedom_views.removeAll(cinst);
display_members.removeAll(cinst);
objsRelateRebuild();
delete cinst;
}
void ViewPresent::temporaryVisible(RectCom *target) {
if (target != nullptr && !this->freedom_views.contains(target))
throw new SimpleException<QString>("参数异常", "非闲置视图不可以设置为临时视图");
if (this->temp_visible_view) {
markFreedom(temp_visible_view);
}
temp_visible_view = target;
if (temp_visible_view) {
temp_visible_view->setParentRes(this);
temp_visible_view->bind()->setVisible(true);
objsRelateRebuild();
} else
relayout();
}
void ViewPresent::setAdjustView(RectCom *one) {
if (one) {
this->adjust_view_temp = one;
dynamic_cast<ViewRes *>(adjust_view_temp)->bind()->setVisible(false);
dynamic_cast<ViewRes *>(adjust_view_temp)->bind()->repaint();
} else {
dynamic_cast<ViewRes *>(adjust_view_temp)->bind()->setVisible(true);
dynamic_cast<ViewRes *>(adjust_view_temp)->bind()->repaint();
this->adjust_view_temp = one;
}
}
bool ViewPresent::adjustState() const { return this->adjust_view_temp != nullptr; }
RectCom *ViewPresent::adjustView() const { return this->adjust_view_temp; }
QObject *ViewPresent::globalEventFilter() const { return dynamic_cast<QObject *>(const_cast<ViewPresent *>(this)); }
SplitRect *ViewPresent::createSplit(RectCom *first, RectCom *next, SplitType sp, float pos, float split_width) {
auto inst = new SplitView(first, next, this);
inst->setSplitInfo(sp, pos, split_width, false);
return inst;
}
const QList<RectCom *> ViewPresent::freedomViews() const {
QList<RectCom *> ret_list;
for (auto &vit : freedom_views)
ret_list << vit;
return ret_list;
}
void ViewPresent::resetEngross(RectCom *view) {
for (auto &vit : all_func_views)
vit->pRelease();
temp_visible_view = nullptr;
if (display_members.size())
display_members.clear();
if (freedom_views.contains(view))
freedom_views.removeAll(view);
view->setParentRes(this);
view->bind()->setVisible(true);
display_members << view;
objsRelateRebuild();
}
bool ViewPresent::eventFilter(QObject *watched, QEvent *event) {
auto adjust_state = adjustState();
if (adjust_state && event->type() == QEvent::DragEnter) {
for (auto &v : all_func_views) {
if (v->contains((QWidget *)watched)) {
auto outline = v->outline();
QPoint gpos(v->bind()->mapToGlobal(outline.topLeft().toPoint()));
QPoint localpos(mapFromGlobal(gpos));
accept_panel->raise();
accept_panel->setGeometry(QRect(localpos, outline.size().toSize()));
accept_panel->setVisible(true);
accept_panel->bindAttachment(v);
event->accept();
return true;
}
}
}
if (event->type() == QEvent::Drop) {
accept_panel->hide();
accept_panel->lower();
}
if (this->temp_visible_view && event->type() == QEvent::MouseButtonPress) {
if (!temp_visible_view->contains((QWidget *)watched)) {
temporaryVisible(nullptr);
}
}
return QWidget::eventFilter(watched, event);
}
QWidget *ViewPresent::bind() const { return const_cast<ViewPresent *>(this); }
bool ViewPresent::contains(QWidget *) const { return false; }
ViewPresent *ViewPresent::viewPanel() const { return const_cast<ViewPresent *>(this); }
void ViewPresent::objsRelateRebuild() {
setParentRes();
relayout();
update();
}
std::pair<RectCom *, RectCom *> ViewPresent::child() const {
RectCom *inst0 = nullptr, *inst1 = nullptr;
if (display_members.size() > 1)
inst1 = display_members[1];
if (display_members.size() > 0)
inst0 = display_members[0];
return std::make_pair(inst0, inst1);
}
void ViewPresent::replaceComp(RectCom *view, RectCom *old) {
if (!display_members.contains(old))
throw new SimpleException<QString>("参数错误", "指定替换的界面不属于此界面");
if (display_members.contains(view))
return;
view->setParentRes(this);
display_members.replace(display_members.indexOf(old), view);
objsRelateRebuild();
}
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);
if (relayout)
this->relayout();
}
QRectF ViewPresent::outline() const { return this->rect(); }
void ViewPresent::relayout(const QRectF &outline) {
setGeometry(outline.toRect());
relayout();
}
void ViewPresent::pRelease() { throw new SimpleException<bool>("非法操作", "不允许对ViewPanel调用pRelease"); }
void ViewPresent::pClose() { throw new SimpleException<bool>("非法操作", "不允许对ViewPanel调用pClose"); }
void ViewPresent::registElement(QWidget *) { throw new SimpleException<bool>("非法操作", "不允许对ViewPanel调用registElement"); }
void ViewPresent::setParentRes(ViewRes *) {
auto pair = child();
if (pair.first) {
pair.first->setParentRes(this);
}
if (pair.second) {
pair.second->setParentRes(this);
}
if (temp_visible_view) {
temp_visible_view->setParentRes(this);
}
}
ViewRes *ViewPresent::parentRes() const { return nullptr; }
void ViewPresent::paintEvent(QPaintEvent *ev) {
QWidget::paintEvent(ev);
if (display_members.size() == 2) {
QPainter p(this);
auto handle_rect = this->handle_rect();
p.fillRect(handle_rect, QBrush(Qt::gray));
}
for (auto &it : display_members) {
it->bind()->setVisible(true);
it->bind()->update();
}
if (temp_visible_view) {
temp_visible_view->bind()->setVisible(true);
temp_visible_view->bind()->update();
}
for (auto &it : freedom_views) {
if (it == temp_visible_view)
return;
it->bind()->setVisible(false);
it->bind()->update();
}
}
void ViewPresent::removeComp(RectCom *child_inst) {
if (!child_inst)
return;
if (temp_visible_view == child_inst) {
temp_visible_view = nullptr;
freedom_views.removeAll(child_inst);
for (auto &lsn : lsn_list)
lsn->freedomRemoved(child_inst);
} else {
if (!display_members.contains(child_inst))
throw new SimpleException<QString>("参数非法", "不允许移除隐藏的自由视图");
display_members.removeAll(child_inst);
}
child_inst->setParentRes(nullptr);
objsRelateRebuild();
}
void ViewPresent::resizeEvent(QResizeEvent *ev) {
QWidget::resizeEvent(ev);
relayout();
}
void ViewPresent::mousePressEvent(QMouseEvent *event) {
QWidget::mousePressEvent(event);
if (event->button() == Qt::MouseButton::LeftButton) {
if (handle_rect().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 = handle_rect();
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 = view_rects();
if (display_members.size() == 2) {
display_members[0]->relayout(rects.first.toRect());
display_members[1]->relayout(rects.second.toRect());
} else if (display_members.size() == 1) {
display_members[0]->relayout(this->rect());
}
if (temp_visible_view) {
temp_visible_view->relayout(QRectF(QPointF(), QSizeF(300, this->height())));
}
update();
}
std::pair<QRectF, QRectF> ViewPresent::view_rects() {
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::handle_rect() 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;
}
RectCom *ViewPresent::create_base_panel(QWidget *bview, const QString &title, bool empty_flag) { return new BaseView(title, bview, empty_flag); }