#include "splitpanel.h" #include "baseview.h" #include "splitview.h" #include #include #include #include #include #include #include #include #include #include #include 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 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 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(xfrom->parentRes())->removeComp(xfrom); adjust_host->objsRelateRebuild(); auto ptarget = static_cast(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(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(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(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(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 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(it)->installPerceptionHandle(this); all_func_views << it; } void ViewPresent::markFreedom(ViewRes *view, bool add) { if (!view) return; auto cinst = static_cast(view); if (add) { if (!freedom_views.contains(cinst)) { freedom_views << cinst; auto fview = static_cast(view); for (auto &lsn : lsn_list) lsn->freedomAppended(cinst, fview->viewIcon(), fview->title()); } cinst->bind()->setVisible(false); cinst->setParentRes(nullptr); } else { if (freedom_views.contains(cinst)) { freedom_views.removeAll(cinst); for (auto &lsn : lsn_list) lsn->freedomRemoved(cinst); } } } void ViewPresent::purge(ViewRes *target) { if (!target) return; auto cinst = static_cast(target); if (!all_func_views.contains(cinst)) throw new SimpleException("参数非法", "目标实例不属于本ViewPresent"); cinst->bind()->setVisible(false); target->setParentRes(nullptr); if (temp_visible_view.second == cinst) temp_visible_view.second = nullptr; all_func_views.removeAll(cinst); freedom_views.removeAll(cinst); display_members = nullptr; objsRelateRebuild(); delete cinst; } void ViewPresent::temporaryVisible(DockType t, RectCom *target) { if (target != nullptr && !this->freedom_views.contains(target)) throw new SimpleException("参数异常", "非闲置视图不可以设置为临时视图"); if (this->temp_visible_view.second) { markFreedom(temp_visible_view.second); } temp_visible_view = std::make_pair(t, target); if (temp_visible_view.second) { temp_visible_view.second->setParentRes(this); temp_visible_view.second->bind()->setVisible(true); objsRelateRebuild(); } else relayout(); } bool ViewPresent::isTemporaryView(RectCom *view) { return this->temp_visible_view.second == view; } void ViewPresent::setAdjustView(RectCom *one) { if (one) { this->adjust_view_temp = one; dynamic_cast(adjust_view_temp)->bind()->setVisible(false); dynamic_cast(adjust_view_temp)->bind()->repaint(); } else { dynamic_cast(adjust_view_temp)->bind()->setVisible(true); dynamic_cast(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(const_cast(this)); } SplitRect *ViewPresent::createSplit(RectCom *first, RectCom *next) { auto inst = new SplitView(first, next, this); return inst; } const QList ViewPresent::freedomViews() const { QList 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 = std::make_pair(DockType::LEFT, nullptr); display_members = nullptr; 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) && !isTemporaryView(v)) { 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.second && event->type() == QEvent::MouseButtonPress) { if (!temp_visible_view.second->contains((QWidget *)watched)) { temporaryVisible(DockType::LEFT, nullptr); } } return QWidget::eventFilter(watched, event); } QWidget *ViewPresent::bind() const { return const_cast(this); } bool ViewPresent::contains(QWidget *) const { return false; } ViewPresent *ViewPresent::viewPanel() const { return const_cast(this); } void ViewPresent::objsRelateRebuild() { setParentRes(); relayout(); update(); } std::pair ViewPresent::child() const { return std::make_pair(display_members, nullptr); } void ViewPresent::replaceComp(RectCom *view, RectCom *old) { if (display_members != old) throw new SimpleException("参数错误", "指定替换的界面不属于此界面"); if (display_members == view) return; view->setParentRes(this); display_members = 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(); } QString ViewPresent::title() const { return ""; } void ViewPresent::pRelease() { throw new SimpleException("非法操作", "不允许对ViewPanel调用pRelease"); } void ViewPresent::pClose() { throw new SimpleException("非法操作", "不允许对ViewPanel调用pClose"); } void ViewPresent::registElement(QWidget *) { throw new SimpleException("非法操作", "不允许对ViewPanel调用registElement"); } void ViewPresent::setParentRes(ViewRes *) { if (display_members) { display_members->setParentRes(this); } if (temp_visible_view.second) { temp_visible_view.second->setParentRes(this); } } ViewRes *ViewPresent::parentRes() const { return nullptr; } void ViewPresent::paintEvent(QPaintEvent *ev) { QWidget::paintEvent(ev); if (display_members) { display_members->bind()->setVisible(true); display_members->bind()->update(); } if (temp_visible_view.second) { temp_visible_view.second->bind()->setVisible(true); temp_visible_view.second->bind()->update(); } for (auto &it : freedom_views) { if (it == temp_visible_view.second) return; it->bind()->setVisible(false); it->bind()->update(); } } void ViewPresent::removeComp(RectCom *child_inst) { if (!child_inst) return; if (temp_visible_view.second == child_inst) { temp_visible_view = std::make_pair(DockType::LEFT, nullptr); freedom_views.removeAll(child_inst); for (auto &lsn : lsn_list) lsn->freedomRemoved(child_inst); } else { if (display_members != child_inst) throw new SimpleException("参数非法", "不允许移除隐藏的自由视图"); display_members = nullptr; } 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() { if (display_members) { display_members->relayout(this->rect()); } if (temp_visible_view.second) { auto vrect = this->rect(); QRectF cube; switch (temp_visible_view.first) { case DockType::LEFT: cube = QRectF(QPointF(), QSizeF(300, this->height())); break; case DockType::RIGHT: cube = QRectF(vrect.topRight() - QPointF(300, 0), QSizeF(300, this->height())); break; case DockType::TOP: cube = QRectF(QPointF(), QSizeF(this->width(), 300)); break; case DockType::BOTTOM: cube = QRectF(vrect.bottomLeft() - QPointF(0, 300), QSizeF(this->width(), 300)); break; } temp_visible_view.second->relayout(cube); } update(); } std::pair 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 QIcon &icon, const QString &title, bool empty_flag) { auto vinst = new BaseView(title, bview, empty_flag); vinst->setTitle(title); vinst->setIcon(icon); return vinst; }