123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- #pragma execution_character_set("utf-8")
- #include "selectwidget.h"
- SelectWidget::SelectWidget(QWidget *parent) : QWidget(parent)
- {
- drawPoint = false;
- pointSize = 10;
- padding = pointSize / 2;
- borderWidth = 1;
-
- pointColor = QColor(34, 163, 168);
- pointStyle = PointStyle::PointStyle_Rect;
- m_Hitted = false;
- pressed = false;
- pressedLeft = false;
- pressedRight = false;
- pressedTop = false;
- pressedBottom = false;
- pressedLeftTop = false;
- pressedRightTop = false;
- pressedLeftBottom = false;
- pressedRightBottom = false;
- m_StretchDir = STRETCH_DIRECTION::DIR_NONE;
- m_pWidget = nullptr;
- // 增加:控件的类型的和指针
- m_Type = VALUE_TYPE::Type_Unknown;
- m_pProperty = nullptr;
- // 扩展属性
- m_pPropertyEx = nullptr;
- //设置鼠标追踪为真,并绑定事件过滤器,可以获取焦点用于按键移动位置
- this->setMouseTracking(true);
- //安装事件过滤器,识别鼠标拖动和拉伸大小
- this->installEventFilter(this);
- //设置焦点策略,以便鼠标按下获取焦点并手柄可见
- this->setFocusPolicy(Qt::StrongFocus);
- }
- /// <summary>
- /// 拦截所属的控件事件
- /// </summary>
- /// <param name="watched"></param>
- /// <param name="event"></param>
- /// <returns></returns>
- bool SelectWidget::eventFilter(QObject *watched, QEvent *event)
- {
- //// 拦截键盘事件
- //if (event->type() == QEvent::KeyPress)
- //{
- // QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
- //if (keyEvent->key() == Qt::Key_Left)
- //{
- // this->move(this->pos() - QPoint(1, 0));
- //} else if (keyEvent->key() == Qt::Key_Right)
- //{
- // this->move(this->pos() + QPoint(1, 0));
- //} else if (keyEvent->key() == Qt::Key_Up)
- //{
- // this->move(this->pos() - QPoint(0, 1));
- //} else if (keyEvent->key() == Qt::Key_Down)
- //{
- // this->move(this->pos() + QPoint(0, 1));
- //} else
- //if (keyEvent->key() == Qt::Key_Delete)
- //{
- // emit widgetDelete(m_pWidget);
- // m_pWidget->deleteLater();
- // this->deleteLater();
- // m_pWidget = nullptr;
- //}
- ////重新设置主控件的位置和大小
- //if (m_pWidget != nullptr)
- //{
- // m_pWidget->setGeometry(this->x() + padding, this->y() + padding, this->width() - padding * 2, this->height() - padding * 2);
- //}
- // return QWidget::eventFilter(watched, event);
- //}
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- if (mouseEvent->type() == QEvent::MouseButtonPress)
- {
- // qDebug() << "QEvent::MouseButtonPress";
- //记住当前控件坐标和宽高以及鼠标按下的坐标
- rectX = this->x();
- rectY = this->y();
- rectW = this->width();
- rectH = this->height();
- lastPos = mouseEvent->pos();
- //判断按下的手柄的区域位置
- if (rectLeft.contains(lastPos)) {
- pressedLeft = true;
- } else if (rectRight.contains(lastPos)) {
- pressedRight = true;
- } else if (rectTop.contains(lastPos)) {
- pressedTop = true;
- } else if (rectBottom.contains(lastPos)) {
- pressedBottom = true;
- } else if (rectLeftTop.contains(lastPos)) {
- pressedLeftTop = true;
- } else if (rectRightTop.contains(lastPos)) {
- pressedRightTop = true;
- } else if (rectLeftBottom.contains(lastPos)) {
- pressedLeftBottom = true;
- } else if (rectRightBottom.contains(lastPos)) {
- pressedRightBottom = true;
- } else {
- pressed = true;
- }
- m_Hitted = true;
- if (m_pWidget != nullptr)
- {
- emit widgetPressed(m_pWidget);
- }
- }
- // 鼠标移动时
- else if (mouseEvent->type() == QEvent::MouseMove)
- {
- // qDebug() << "QEvent::MouseMove";
- //根据当前鼠标位置,计算XY轴移动了多少
- QPoint pos = mouseEvent->pos();
- int dx = pos.x() - lastPos.x();
- int dy = pos.y() - lastPos.y();
- // 重置控件被拉伸的方向
- m_StretchDir = STRETCH_DIRECTION::DIR_NONE;
- //根据按下处的位置判断是否是移动控件还是拉伸控件
- if (pressed)
- {
- QPoint curPos = QPoint(this->x() + dx, this->y() + dy);
- // 坐标对齐后再移动
- this->alignToGrid(curPos);
- this->move(curPos);
- }
- // 向左拖动
- else if (pressedLeft)
- {
- // 计算拖动后的位置和尺寸
- int fixX = this->alignToGrid(this->x() + dx);
- int dx2 = fixX - this->x();
- int resizeW = this->width() - dx2;
- if (this->minimumWidth() <= resizeW)
- {
- this->setGeometry(fixX, rectY, resizeW, rectH);
- m_StretchDir = STRETCH_DIRECTION::DIR_LEFT;
- }
- }
- // 向右拖动
- else if (pressedRight)
- {
- // 重新计算对齐后的右边界坐标
- int fixRight = this->alignToGrid(rectX + rectW + dx);
- int resizeW = fixRight - rectX;
- this->setGeometry(rectX, rectY, resizeW, rectH);
- m_StretchDir = STRETCH_DIRECTION::DIR_RIGHT;
- }
-
- // 向上拖动
- else if (pressedTop)
- {
- // 计算拖动后的位置和尺寸
- int fixY = this->alignToGrid(this->y() + dy);
- int dy2 = fixY - this->y();
- int resizeH = this->height() - dy2;
- if (this->minimumHeight() <= resizeH)
- {
- this->setGeometry(rectX, fixY, rectW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_TOP;
- }
- }
- // 向下拖动
- else if (pressedBottom)
- {
- // 重新计算对齐后的下边界坐标
- int fixBottom = this->alignToGrid(rectY + rectH + dy);
- int resizeH = fixBottom - rectY;
- this->setGeometry(rectX, rectY, rectW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_BOTTOM;
- }
- // 向左上拖动
- else if (pressedLeftTop)
- {
- // 计算拖动后的位置和尺寸
- int fixX = this->alignToGrid(this->x() + dx);
- int dx2 = fixX - this->x();
- int resizeW = this->width() - dx2;
- // 计算拖动后的位置和尺寸
- int fixY = this->alignToGrid(this->y() + dy);
- int dy2 = fixY - this->y();
- int resizeH = this->height() - dy2;
- if (this->minimumWidth() <= resizeW)
- {
- this->setGeometry(fixX, this->y(), resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_LEFTTOP;
- }
- if (this->minimumHeight() <= resizeH)
- {
- this->setGeometry(this->x(), fixY, resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_LEFTTOP;
- }
- }
- // 向右上拖动
- else if (pressedRightTop)
- {
- // 重新计算对齐后的右边界坐标
- int fixRight = this->alignToGrid(rectX + rectW + dx);
- int resizeW = fixRight - rectX;
- // 计算拖动后的位置和尺寸
- int fixY = this->alignToGrid(this->y() + dy);
- int dy2 = fixY - this->y();
- int resizeH = this->height() - dy2;
- if (this->minimumHeight() <= resizeH)
- {
- this->setGeometry(this->x(), fixY, resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_RIGHTTOP;
- }
- }
- // 向左下拖动
- else if (pressedLeftBottom)
- {
- // 计算拖动后的位置和尺寸
- int fixX = this->alignToGrid(this->x() + dx);
- int dx2 = fixX - this->x();
- int resizeW = this->width() - dx2;
- // 重新计算对齐后的下边界坐标
- int fixBottom = this->alignToGrid(rectY + rectH + dy);
- int resizeH = fixBottom - rectY;
- if (this->minimumWidth() <= resizeW)
- {
- this->setGeometry(fixX, this->y(), resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_LEFTBOTTOM;
- }
- if (this->minimumHeight() <= resizeH)
- {
- this->setGeometry(this->x(), this->y(), resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_LEFTBOTTOM;
- }
- }
- // 向右下拖动
- else if (pressedRightBottom)
- {
- // 重新计算对齐后的右边界坐标
- int fixRight = this->alignToGrid(rectX + rectW + dx);
- int resizeW = fixRight - rectX;
- // 重新计算对齐后的下边界坐标
- int fixBottom = this->alignToGrid(rectY + rectH + dy);
- int resizeH = fixBottom - rectY;
- this->setGeometry(this->x(), this->y(), resizeW, resizeH);
- m_StretchDir = STRETCH_DIRECTION::DIR_RIGHTBOTTOM;
- }
- // 防止鼠标没有做任何操作,控件却被移动的情况
- if (m_Hitted)
- {
- //重新设置主控件的位置和大小
- if (m_pWidget != nullptr)
- {
- m_pWidget->setGeometry(this->x() + padding, this->y() + padding, this->width() - padding * 2, this->height() - padding * 2);
- }
- }
- }
- // 鼠标释放时,发送鼠标释放信号
- else if (mouseEvent->type() == QEvent::MouseButtonRelease)
- {
- m_Hitted = false;
- pressed = false;
- pressedLeft = false;
- pressedRight = false;
- pressedTop = false;
- pressedBottom = false;
- pressedLeftTop = false;
- pressedRightTop = false;
- pressedLeftBottom = false;
- pressedRightBottom = false;
- // 重置控件被拉伸的方向
- m_StretchDir = STRETCH_DIRECTION::DIR_NONE;
- if (m_pWidget != nullptr)
- {
- emit widgetRelease(m_pWidget);
- }
- }
- return QWidget::eventFilter(watched, event);
- }
- /// <summary>
- /// 在区域改变时计算对应的锚点区域
- /// </summary>
- /// <param name=""></param>
- void SelectWidget::resizeEvent(QResizeEvent *)
- {
- // qDebug() << "SelectWidget::resizeEvent";
- //重新计算八个锚点的区域,锚点区域的作用还有就是计算鼠标坐标是否在某一个区域内
- double width = this->width();
- double height = this->height();
- //左侧锚点区域
- rectLeft = QRectF(0, int( height / 2.0 - pointSize / 2.0), pointSize, pointSize);
- //上侧锚点区域
- rectTop = QRectF(int(width / 2 - pointSize / 2), 0, pointSize, pointSize);
- //右侧锚点区域
- rectRight = QRectF(int(width - pointSize),int( height / 2 - pointSize / 2), pointSize, pointSize);
- //下侧锚点区域
- rectBottom = QRectF(int(width / 2 - pointSize / 2), height - pointSize, pointSize, pointSize);
- //左上角锚点区域
- rectLeftTop = QRectF(0, 0, pointSize, pointSize);
- //右上角锚点区域
- rectRightTop = QRectF(int(width - pointSize), 0, pointSize, pointSize);
- //左下角锚点区域
- rectLeftBottom = QRectF(0, int(height - pointSize), pointSize, pointSize);
- //右下角锚点区域
- rectRightBottom = QRectF(width - pointSize, height - pointSize, pointSize, pointSize);
- }
- /// <summary>
- /// 在鼠标拖拽时显示对应的鼠标
- /// </summary>
- /// <param name="e"></param>
- void SelectWidget::mouseMoveEvent(QMouseEvent *e)
- {
- // qDebug() << "SelectWidget::mouseMoveEvent";
- // 计算当前鼠标位置是否在某个区域内,自动更新鼠标形状
- // 2022-9-8,由于目前处于自动对齐状态,矩形区域并非和鼠标位置完全吻合,所以此处要把锚点判断区域扩大
- QRectF rectLeftTmp = rectLeft.adjusted(-3, -3, 3, 3);
- QRectF rectTopTmp = rectTop.adjusted(-3, -3, 3, 3);
- QRectF rectRightTmp = rectRight.adjusted(-3, -3, 3, 3);
- QRectF rectBottomTmp = rectBottom.adjusted(-3, -3, 3, 3);
- QRectF rectLeftTopTmp = rectLeftTop.adjusted(-3, -3, 3, 3);
- QRectF rectRightTopTmp = rectRightTop.adjusted(-3, -3, 3, 3);
- QRectF rectLeftBottomTmp = rectLeftBottom.adjusted(-3, -3, 3, 3);
- QRectF rectRightBottomTmp = rectRightBottom.adjusted(-3, -3, 3, 3);
- QPoint p = e->pos();
- if (rectLeftTmp.contains(p))
- {
- this->setCursor(Qt::SizeHorCursor);
- }
- else if (rectTopTmp.contains(p))
- {
- this->setCursor(Qt::SizeVerCursor);
- }
- else if (rectRightTmp.contains(p))
- {
- this->setCursor(Qt::SizeHorCursor);
- }
- else if (rectBottomTmp.contains(p))
- {
- this->setCursor(Qt::SizeVerCursor);
- }
- else if (rectLeftTopTmp.contains(p))
- {
- this->setCursor(Qt::SizeFDiagCursor);
- }
- else if (rectRightTopTmp.contains(p))
- {
- this->setCursor(Qt::SizeBDiagCursor);
- }
- else if (rectLeftBottomTmp.contains(p))
- {
- this->setCursor(Qt::SizeBDiagCursor);
- }
- else if (rectRightBottomTmp.contains(p))
- {
- this->setCursor(Qt::SizeFDiagCursor);
- }
- else
- {
- this->setCursor(Qt::ArrowCursor);
- }
- if (m_Hitted)
- {
- // 发出控件移动信号
- emit widgetMove(m_pWidget);
- // 2022-9-16,如果同时处于拉伸控件状态的话,还需要同时发送拉伸消息
- if (m_StretchDir != STRETCH_DIRECTION::DIR_NONE)
- {
- emit widgetStretch(m_pWidget, m_StretchDir);
- }
- }
- }
- /// <summary>
- /// 进行拖拽边框和锚点的绘制
- /// </summary>
- /// <param name=""></param>
- void SelectWidget::paintEvent(QPaintEvent * )
- {
- if (!drawPoint) {
- return;
- }
- QPainter painter(this);
- painter.setRenderHints(QPainter::Antialiasing);
- painter.setPen(Qt::NoPen);
- painter.setBrush(pointColor);
- if (pointStyle == PointStyle::PointStyle_Rect)
- {
- drawRect(&painter);
- }
- else if (pointStyle == PointStyle::PointStyle_Circle)
- {
- drawCircle(&painter);
- }
- if (borderWidth > 0)
- {
- drawBorder(&painter);
- }
- }
- /// <summary>
- /// 绘制矩形的锚点
- /// </summary>
- /// <param name="painter"></param>
- void SelectWidget::drawRect(QPainter *painter)
- {
- //逐个绘制 左上角点+顶边中间点+右上角点+左边中间点+右边中间点+左下角点+底边中间点+右下角点
- painter->save();
- painter->drawRect(rectLeft);
- painter->drawRect(rectRight);
- painter->drawRect(rectTop);
- painter->drawRect(rectBottom);
- painter->drawRect(rectLeftTop);
- painter->drawRect(rectRightTop);
- painter->drawRect(rectLeftBottom);
- painter->drawRect(rectRightBottom);
- painter->restore();
- }
- /// <summary>
- /// 绘制圆形的锚点
- /// </summary>
- /// <param name="painter"></param>
- void SelectWidget::drawCircle(QPainter *painter)
- {
- //逐个绘制 左上角点+顶边中间点+右上角点+左边中间点+右边中间点+左下角点+底边中间点+右下角点
- painter->save();
- painter->drawEllipse(rectLeft);
- painter->drawEllipse(rectRight);
- painter->drawEllipse(rectTop);
- painter->drawEllipse(rectBottom);
- painter->drawEllipse(rectLeftTop);
- painter->drawEllipse(rectRightTop);
- painter->drawEllipse(rectLeftBottom);
- painter->drawEllipse(rectRightBottom);
- painter->restore();
- }
- /// <summary>
- /// 获取用于拖拽的边框
- /// </summary>
- /// <param name="painter"></param>
- void SelectWidget::drawBorder(QPainter *painter)
- {
- painter->save();
- QPen pen;
- pen.setWidth(borderWidth);
- pen.setColor(pointColor);
- painter->setPen(pen);
- painter->setBrush(Qt::NoBrush);
- QRectF borderRect(pointSize / 2, pointSize / 2, width() - pointSize, height() - pointSize);
- painter->drawRect(borderRect);
- painter->restore();
- }
- bool SelectWidget::getDrawPoint() const
- {
- return this->drawPoint;
- }
- int SelectWidget::getPadding() const
- {
- return this->padding;
- }
- int SelectWidget::getBorderWidth() const
- {
- return this->borderWidth;
- }
- int SelectWidget::getPointSize() const
- {
- return this->pointSize;
- }
- QColor SelectWidget::getPointColor() const
- {
- return this->pointColor;
- }
- SelectWidget::PointStyle SelectWidget::getPointStyle() const
- {
- return this->pointStyle;
- }
- QWidget *SelectWidget::getWidget() const
- {
- return m_pWidget;
- }
- QSize SelectWidget::sizeHint() const
- {
- return QSize(200, 200);
- }
- QSize SelectWidget::minimumSizeHint() const
- {
- return QSize(30, 30);
- }
- /// <summary>
- /// 2022-10-21 直接移动控件和外边框的位置(目前用于Redo体系中直接移动控件)
- /// </summary>
- /// <param name="newPos"></param>
- void SelectWidget::updatePos(const QPoint& newPos)
- {
- // 移动内嵌控件
- this->m_pWidget->move(newPos);
- // 移动选中外框
- this->move(this->m_pWidget->pos() - QPoint(padding, padding));
- }
- /// <summary>
- /// 2022-10-21 直接缩放控件和外边框(目前用于Redo体系中直接缩放控件)
- /// </summary>
- /// <param name="newGeometry"></param>
- void SelectWidget::updateGeometry(const QRect& newGeometry)
- {
- // 设置内嵌控件
- m_pWidget->setGeometry(newGeometry);
- // 设置选中外框
- this->setGeometry(
- newGeometry.x() - padding,
- newGeometry.y() - padding,
- newGeometry.width() + padding * 2,
- newGeometry.height() + padding * 2
- );
- // this->setGeometry(this->x(), this->y(), resizeW, resizeH);
- // m_pWidget->setGeometry(this->x() + padding, this->y() + padding, this->width() - padding * 2, this->height() - padding * 2);
- }
- void SelectWidget::setDrawPoint(bool drawPoint)
- {
- if (this->drawPoint != drawPoint) {
- this->drawPoint = drawPoint;
- update();
- }
- }
- void SelectWidget::setPadding(int padding)
- {
- if (this->padding != padding) {
- this->padding = padding;
- update();
- }
- }
- void SelectWidget::setBorderWidth(int borderWidth)
- {
- if (this->borderWidth != borderWidth) {
- this->borderWidth = borderWidth;
- update();
- }
- }
- void SelectWidget::setPointSize(int pointSize)
- {
- if (this->pointSize != pointSize) {
- this->pointSize = pointSize;
- update();
- }
- }
- void SelectWidget::setPointColor(const QColor &pointColor)
- {
- if (this->pointColor != pointColor) {
- this->pointColor = pointColor;
- update();
- }
- }
- void SelectWidget::setPointStyle(const SelectWidget::PointStyle &pointStyle)
- {
- if (this->pointStyle != pointStyle) {
- this->pointStyle = pointStyle;
- update();
- }
- }
- /// <summary>
- /// 将指定的控件与本控件绑定,并且计算边框尺寸
- /// </summary>
- /// <param name="widget"></param>
- void SelectWidget::setWidget(QWidget *widget)
- {
- this->m_pWidget = widget;
- this->m_pWidget->setVisible(true);
- this->setVisible(true);
- // 修正一下控件位置,自动对齐到网格
- int fixX = this->alignToGrid(this->m_pWidget->x()) + padding;
- int fixY = this->alignToGrid(this->m_pWidget->y()) + padding;
- this->m_pWidget->move(fixX, fixY);
- //设置最小尺寸
- this->setMinimumSize(50, 20);
- //设置当前窗体大小为跟随窗体的大小增加部分
- this->resize(this->m_pWidget->size() + QSize(padding * 2, padding * 2));
- //将当前窗体移到偏移位置
- this->move(this->m_pWidget->pos() - QPoint(padding, padding));
- }
- /// <summary>
- /// 自动对齐控件(针对Point)
- /// </summary>
- void SelectWidget::alignToGrid(QPoint& pt)
- {
- int nGridSize = 10;
- // 按四舍五规则来取整,如果采用和Grid一样的方式 std::ceil 向上取整的话,操作会比较奇怪
- int dstX = std::round((double)pt.x() / nGridSize) * nGridSize - padding;
- int dstY = std::round((double)pt.y() / nGridSize) * nGridSize - padding;
- pt.setX(dstX);
- pt.setY(dstY);
- }
- /// <summary>
- /// 自动对齐控件(针对单个坐标)
- /// </summary>
- int SelectWidget::alignToGrid(int x)
- {
- int nGridSize = 10;
- // 按四舍五规则来取整,如果采用和Grid一样的方式 std::ceil 向上取整的话,操作会比较奇怪
- int dstX = std::round((double)x / nGridSize) * nGridSize - padding;
- return dstX;
- }
|