#include "splitview.h" #include #include #include #include #include #include #include #include #include #include #include using namespace SplitFrame; using namespace Config; 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; if (child != this) child->setParent(this); } ViewPresent *BaseView::viewPanel() const { return parentRect()->viewPanel(); } void BaseView::closeView() { } bool BaseView::contains(QWidget *ptr) const { return comps_list.contains(ptr); } QWidget *BaseView::bind() const { return const_cast(this); } SplitRect *BaseView::parentRect() const { return parent_store; } void BaseView::setParentRect(SplitRect *pinst) { this->parent_store = pinst; this->setParent(pinst->bind()); } QRectF BaseView::outline() const { return this->rect(); } void BaseView::relayout(const QRectF &outline) { this->setGeometry(outline.toRect()); update(); } 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); } AttachPanel::AttachPanel(ViewPresent *host) : QWidget(host->bind()), adjust_host(host) { setWindowOpacity(50); setAcceptDrops(true); } void AttachPanel::bindAttachment(BaseView *widget) { this->current_target = widget; } BaseView *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); 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 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) { RectCom *newsplit = nullptr; xfrom->parentRect()->removeComp(xfrom); adjust_host->objsRelateRebuild(); auto ptarget = target->parentRect(); auto target_rect = target->outline(); switch (this->active_comp) { case ActiveArea::LEFT: { newsplit = new SplitView(xfrom, target, adjust_host); newsplit->relayout(target_rect); static_cast(newsplit)->setSplitInfo(SplitType::SPLIT_H, target_rect.width() / 2, adjust_host->splitterWidth(), true); } break; case ActiveArea::RIGHT: { newsplit = new SplitView(target, xfrom, adjust_host); newsplit->relayout(target_rect); static_cast(newsplit)->setSplitInfo(SplitType::SPLIT_H, target_rect.width() / 2, adjust_host->splitterWidth(), true); } break; case ActiveArea::TOP: { newsplit = new SplitView(xfrom, target, adjust_host); newsplit->relayout(target_rect); static_cast(newsplit)->setSplitInfo(SplitType::SPLIT_V, target_rect.height() / 2, adjust_host->splitterWidth(), true); } break; case ActiveArea::BOTTOM: { newsplit = new SplitView(target, xfrom, adjust_host); newsplit->relayout(target_rect); static_cast(newsplit)->setSplitInfo(SplitType::SPLIT_V, target_rect.height() / 2, adjust_host->splitterWidth(), true); } break; case ActiveArea::CENTER: { newsplit = xfrom; } break; default: return; } ptarget->replaceComp(newsplit, target); adjust_host->objsRelateRebuild(); adjust_host->unusedResRelease(); } adjust_host->setViewAdjust(); this->setVisible(false); } std::tuple AttachPanel::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(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::registViewComp(BaseView *it) { if (this->all_sep_views.contains(it)) return; it->installPerceptionHandle(this); all_sep_views << it; } void ViewPresent::markFreedom(BaseView *view) { if (!freedom_views.contains(view)) freedom_views << view; } // void ViewPresent::collectFreedomView(BaseView *invisible) { // if (!freedom_views.contains(invisible)) { // freedom_views << invisible; // for (auto &it : lsn_list) // it->freedomAppended(invisible); // } // invisible->setParentRect(nullptr); // invisible->bind()->setVisible(false); //} void ViewPresent::setTempVisible(BaseView *target) { if (target != nullptr && !this->freedom_views.contains(target)) throw new SimpleException("参数异常", "非闲置视图不可以设置为临时视图"); if (this->temp_visible_view) { // collectFreedomView(temp_visible_view); } temp_visible_view = target; if (temp_visible_view) { temp_visible_view->setParentRect(this); temp_visible_view->bind()->setVisible(true); temp_visible_view->bind()->raise(); } relayout(); } void ViewPresent::setViewAdjust(BaseView *one) { this->adjust_view_temp = one; } bool ViewPresent::adjustState() const { return this->adjust_view_temp != nullptr; } BaseView *ViewPresent::adjustView() const { return this->adjust_view_temp; } void ViewPresent::resetViews(RectCom *center, RectCom *extend) { view_anchors << center; if (extend) { view_anchors << extend; } this->objsRelateRebuild(); this->relayout(); } bool ViewPresent::eventFilter(QObject *watched, QEvent *event) { auto adjust_state = adjustState(); 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 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)) { setTempVisible(nullptr); } } return QWidget::eventFilter(watched, event); } QWidget *ViewPresent::bind() const { return const_cast(this); } bool ViewPresent::contains(QWidget *ptr) const { return false; } ViewPresent *ViewPresent::viewPanel() const { return const_cast(this); } SplitRect *ViewPresent::parentRect() const { return nullptr; } void ViewPresent::objsRelateRebuild() { setParentRect(nullptr); } void ViewPresent::markUnused(SplitRect *item) { if (!this->unused_splits.contains(item)) unused_splits << item; } void ViewPresent::unusedResRelease() { for (auto &it : unused_splits) delete it; unused_splits.clear(); } void ViewPresent::setParentRect(SplitRect *) { auto pair = child(); pair.first->setParentRect(this); if (pair.second) pair.second->setParentRect(this); } std::pair ViewPresent::child() const { RectCom *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::replaceComp(RectCom *view, RectCom *old) { if (!view_anchors.contains(old)) throw new SimpleException("参数错误", "指定替换的界面不属于此界面"); if (view_anchors.contains(view)) return; view_anchors.replace(view_anchors.indexOf(old), view); objsRelateRebuild(); update(); } 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()); } 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()->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) { it->bind()->setVisible(false); it->bind()->update(); } } void ViewPresent::removeComp(BaseView *child_inst) { if (temp_visible_view == child_inst) { temp_visible_view = nullptr; freedom_views.removeAll(child_inst); for (auto &lsn : lsn_list) lsn->freedomRemoved(static_cast(child_inst)); } else { if (!view_anchors.contains(child_inst)) throw new SimpleException("参数非法", "回收的指定视图不属于本ViewPresent"); markFreedom(child_inst); view_anchors.removeAll(child_inst); } viewPanel()->objsRelateRebuild(); viewPanel()->unusedResRelease(); update(); } 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()); } if (temp_visible_view) { temp_visible_view->relayout(QRectF(QPointF(), QSizeF(300, this->height()))); } update(); } std::pair 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(RectCom *first, RectCom *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); } SplitFrame::SplitView::~SplitView() { } QWidget *SplitView::bind() const { return const_cast(this); } ViewPresent *SplitView::viewPanel() const { return parentRect()->viewPanel(); } SplitRect *SplitView::parentRect() const { return parent_inst; } void SplitView::setParentRect(SplitRect *pinst) { QWidget::setParent(pinst->bind()); auto pair = child(); pair.first->setParentRect(this); pair.second->setParentRect(this); } std::pair 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::replaceComp(RectCom *view, RectCom *old) { if (!child_store.contains(old) || child_store.contains(view)) { throw new SimpleException("参数非法", "要替换的视图不属于本构件,或者新视图本就属于本构件"); } auto index = child_store.indexOf(old); child_store.replace(index, view); viewPanel()->objsRelateRebuild(); } QRectF SplitView::outline() const { return this->rect(); } void SplitView::relayout(const QRectF &outlinex) { this->setGeometry(outlinex.toRect()); update(); } void SplitView::removeComp(BaseView *child_inst) { auto pinst = parentRect(); auto sib = this->child().first; if (child_inst == sib) sib = this->child().second; pinst->replaceComp(sib, this); viewPanel()->markUnused(this); viewPanel()->markFreedom(child_inst); viewPanel()->objsRelateRebuild(); viewPanel()->unusedResRelease(); } 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(); } bool SplitView::contains(QWidget *ptr) const { return false; } 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(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(); pair.first->relayout(std::get<0>(rects)); pair.second->relayout(std::get<1>(rects)); update(); } std::pair 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->removeComp(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) { auto distance = QLineF(std::get<1>(press_flag), event->pos()).length(); if (std::get<0>(press_flag) && distance > QApplication::startDragDistance()) { auto panel = view_core->viewPanel(); panel->setViewAdjust(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(); } }