扁平窗口实现(二) 无边框可缩放控件

扁平窗口实现之无边框可缩放控件

开发步骤

  • 计算控件可变范围区域
  • 判断鼠标在区域内改变鼠标对应形状
  • 根据扩展方向改变大小

计算可变区域范围

一般来讲窗口都是在鼠标处于窗口的8个方向上时才能对窗体进行大小改变,因此,需要给控件设置一个虚拟的部区域,在内部区域外是改变控件的操作,在内部区域中则是移动窗口,示意图如下:

响应鼠标拖拽区域示意图

因此,只需要使用adjusted()基于控件自身获取一个小一号的区域然后判断鼠标是否在该区域内,示例代码如下:

bool JQFlatBase::judgeOnEdge(const QPoint & _current_pos) {
    /* 获取当前窗口大小,获取一个根据检测边界宽度改变的区域 */
    QRect inner_rect = rect().adjusted(edge_width_, edge_width_, -edge_width_, -edge_width_);
    if (inner_rect.contains(_current_pos)) {
        return false;
    }	
    // ......
}

这个edge_width_是一个边界的宽度,鼠标在这个区域内按下会视为要进行控件大小的改变,这个值越大响应的范围就越大,也就是示意图中的蓝色区域部分越小,而橘黄色部分越宽

这个目前只能识别出鼠标点击时是要改变大小还是移动,在改变大小时还不能区分到底是往8个方向的哪个方向变化。

目前,我想到的方法是将窗体划分成8个小的区域,然后依次判断鼠标是否在该区域之内,由此来确定是朝着哪个方向扩展,示意图如下:

扩展方向示意图

根据示意图可以得到区域计算代码

QMap<QString, QRect> JQFlatBase::divideAreas() {
    QMap<QString, QRect> divide_area_results;
    /* 分割扩展方向区域 */
    /* 左上角区域 */
    QRect top_left_rect = QRect(rect().topLeft(), QSize(edge_width_, edge_width_));
    divide_area_results[TOP_LEFT_EDGE] = top_left_rect;
    /* 上边区域 */
    QRect top_rect = QRect(top_left_rect.topRight(),
                           QSize(rect().width() - edge_width_ * 2, edge_width_));
    divide_area_results[TOP_EDGE] = top_rect;
    /* 右上角区域 */
    QRect top_right_rect = QRect(top_rect.topRight(),
                                 QSize(edge_width_, edge_width_));
    divide_area_results[TOP_RIGHT_EDGE] = top_right_rect;
    /* 右侧边区域 */
    QRect right_rect = QRect(top_right_rect.bottomLeft(),
                             QSize(edge_width_, rect().height() - edge_width_ * 2));
    divide_area_results[RIGHT_EDGE] = right_rect;
    /* 右下边区域 */
    QRect bottom_right_rect = QRect(right_rect.bottomLeft(),
                                    QSize(edge_width_, edge_width_));
    divide_area_results[RIGHT_BOTTOM_EDGE] = bottom_right_rect;
    /* 左边区域 */
    QRect left_rect = QRect(top_left_rect.bottomLeft(),
                            QSize(edge_width_, rect().height() - edge_width_ * 2));
    divide_area_results[LEFT_EDGE] = left_rect;
    /* 左下边区域 */
    QRect bottom_left_rect = QRect(left_rect.bottomLeft(),
                                   QSize(edge_width_, edge_width_));
    divide_area_results[LEFT_BOTTOM_EDGE] = bottom_left_rect;
    /* 下边区域 */
    QRect bottom_rect = QRect(bottom_left_rect.topRight(),
                              QSize(rect().width() - edge_width_ * 2, edge_width_));
    divide_area_results[BOTTOM_EDGE] = bottom_rect;

    return divide_area_results;
}

改变鼠标的形状

区域分割完成后就是根据区域在鼠标移动到该区域之内时,改变对应方向的形状,形状改变是成对出现,分别是左右[水平]上下[垂直]左上右下[斜左右]右上左下[斜右左]

void JQFlatBase::changedMouseShape() {
    if (edge_flags_[TOP_LEFT_EDGE] || edge_flags_[RIGHT_BOTTOM_EDGE]) {
        setCursor(Qt::SizeFDiagCursor);
    } else if (edge_flags_[TOP_EDGE] || edge_flags_[BOTTOM_EDGE]) {
        setCursor(Qt::SizeVerCursor);
    } else if (edge_flags_[TOP_RIGHT_EDGE] || edge_flags_[LEFT_BOTTOM_EDGE]) {
        setCursor(Qt::SizeBDiagCursor);
    } else if (edge_flags_[RIGHT_EDGE] || edge_flags_[LEFT_EDGE]) {
        setCursor(Qt::SizeHorCursor);
    }
}

至此,鼠标判断悬浮区域以及改变鼠标形状的功能基本完成,后边就是点击鼠标进行大小和位置的改变。

控件大小改变计算

先判断当前点击鼠标的功能到底是移动还是改变大小,因此,定义两个变量is_changed_size_is_move_在鼠标点击事件函数中设置这两个标志,代码如下:

void JQFlatBase::mousePressEvent(QMouseEvent *event) {
    if (event->button() != Qt::LeftButton) {
        /* 不是鼠标左键则退出 */
        return;
    }
    is_mouse_press_ = true;

    /* 判断是否在是改变大小 */
    is_changed_size_ = judgeOnEdge(event->pos());
    if (!is_changed_size_)
        is_move_ = true;

    /* 记录当前状态 */
    last_mouse_point_ = event->pos();
    last_press_size_ = size();
}

调用judgeOnEdge()函数,并将当前鼠标位置传入,判断是否点击在改变区域内

bool JQFlatBase::judgeOnEdge(const QPoint & _current_pos) {
    /* 将全部边界检测标志进行置位 */
    restEdgeFlags();
    /* 获取当前窗口大小,获取一个根据检测边界宽度改变的区域 */
    QRect inner_rect = rect().adjusted(edge_width_, edge_width_, -edge_width_, -edge_width_);

    if (inner_rect.contains(_current_pos)) {
        setCursor(Qt::ArrowCursor);
        return false;
    }

    QMap<QString, QRect> divide_areas = divideAreas();
    for (const QString & _area_name : divide_areas.keys()) {
        QRect current_area = divide_areas[_area_name];
        if (current_area.contains(_current_pos)) {
            edge_flags_[_area_name] = true;
            changedMouseShape();
            return true;
            // qInfo() << _area_name << current_area;
        }
    }

    return false;
}

在该函数还会更新edge_flags_的变量,该变量是QMap<QString, bool>用于给当前类提供鼠标是否在某个可变区域内。

点击了某个区域后就会到鼠标移动事件函数中,根据不同的移动方向更新控件大小,代码如下所示:

void JQFlatBase::mouseMoveEvent(QMouseEvent * event) {
    /* 在点击鼠标后不能再次判断鼠标位置 */
    if (!is_mouse_press_) {
        judgeOnEdge(event->pos());
    }
    if (is_move_ && is_mouse_press_) {
        move(event->pos() - last_mouse_point_ + pos());
    } else if (is_changed_size_ && is_mouse_press_) {
        updateWidget(event->pos());
    }
}

调用udateWidegt()函数将当前鼠标点击后相对控件的坐标传入,该函数内部算法如下:

void JQFlatBase::updateWidget(const QPoint & _update_pos) {

    QRect changed_rect = geometry();

    if (edge_flags_[RIGHT_EDGE]) {
        /* 向右拖动边框时,坐标点不动,更新 x轴方向 大小 */
        changed_rect.setRight(_update_pos.x() + pos().x());
    } else if (edge_flags_[LEFT_EDGE]) {
        /* 向左拖动边框时,坐标 x轴 方向移动,更新 x轴方向 大小 */
        changed_rect.setLeft(_update_pos.x() + pos().x());
    } else if (edge_flags_[TOP_EDGE]) {
        /* 向上拖动边框时,坐标 y轴 方向移动,更新 y轴方向 大小 */
        changed_rect.setTop(_update_pos.y() + pos().y());
    } else if (edge_flags_[BOTTOM_EDGE]) {
        /* 向下拖动边框时,坐标不动,更新 y轴方向 大小 */
        changed_rect.setBottom(_update_pos.y() + pos().y());
    } else if (edge_flags_[TOP_LEFT_EDGE]) {
        /* 向左上拖动边框时,坐标 x,y轴 方向移动,更新 -x,-y轴方向 大小 */
        changed_rect.setTopLeft(QPoint(_update_pos.x() + pos().x(), _update_pos.y() + pos().y()));
    } else if (edge_flags_[TOP_RIGHT_EDGE]) {
        /* 向右上拖动边框时,坐标 -y轴 方向移动,更新 x, -y轴方向 大小 */
        changed_rect.setTopRight(QPoint(_update_pos.x() + pos().x(), _update_pos.y() + pos().y()));
    } else if (edge_flags_[RIGHT_BOTTOM_EDGE]) {
        /* 向右下拖动边框时,坐标不动,更新 x, y轴方向 大小 */
        changed_rect.setBottomRight(QPoint(_update_pos.x() + pos().x(), _update_pos.y() + pos().y()));
    } else if (edge_flags_[LEFT_BOTTOM_EDGE]) {
        /* 向左下拖动边框时,坐标 x 轴 方向移动,更新 -x, y轴方向 大小 */
        changed_rect.setBottomLeft(QPoint(_update_pos.x() + pos().x(), _update_pos.y() + pos().y()));
    }
    setGeometry(changed_rect);
}

计算比较简单基本就是对应QRect的8个函数setRight()setLeft()setTop()setBottom()setTopLeft()setTopLeft()setTopRight()setBottomRight()

其中对于需要更新x轴方向的计算都是_update_pos.x() + pos().x(),而涉及更新y轴方向的计算都是_update_pos.y() + pos().y()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biBDcZ0C-1686904253794)(自定义可拖拽改变大小控件.gif)]
tom()setTopLeft()setTopLeft()setTopRight()setBottomRight()`。

其中对于需要更新x轴方向的计算都是_update_pos.x() + pos().x(),而涉及更新y轴方向的计算都是_update_pos.y() + pos().y()

效果

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值