#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;
}