#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); } /// /// 拦截所属的控件事件 /// /// /// /// bool SelectWidget::eventFilter(QObject *watched, QEvent *event) { //// 拦截键盘事件 //if (event->type() == QEvent::KeyPress) //{ // QKeyEvent *keyEvent = dynamic_cast(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(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); } /// /// 在区域改变时计算对应的锚点区域 /// /// 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); } /// /// 在鼠标拖拽时显示对应的鼠标 /// /// 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); } } } /// /// 进行拖拽边框和锚点的绘制 /// /// 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); } } /// /// 绘制矩形的锚点 /// /// 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(); } /// /// 绘制圆形的锚点 /// /// 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(); } /// /// 获取用于拖拽的边框 /// /// 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); } /// /// 2022-10-21 直接移动控件和外边框的位置(目前用于Redo体系中直接移动控件) /// /// void SelectWidget::updatePos(const QPoint& newPos) { // 移动内嵌控件 this->m_pWidget->move(newPos); // 移动选中外框 this->move(this->m_pWidget->pos() - QPoint(padding, padding)); } /// /// 2022-10-21 直接缩放控件和外边框(目前用于Redo体系中直接缩放控件) /// /// 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(); } } /// /// 将指定的控件与本控件绑定,并且计算边框尺寸 /// /// 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)); } /// /// 自动对齐控件(针对Point) /// 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); } /// /// 自动对齐控件(针对单个坐标) /// int SelectWidget::alignToGrid(int x) { int nGridSize = 10; // 按四舍五规则来取整,如果采用和Grid一样的方式 std::ceil 向上取整的话,操作会比较奇怪 int dstX = std::round((double)x / nGridSize) * nGridSize - padding; return dstX; }