设为首页 加入收藏

TOP

高仿富途牛牛-组件化(二)-磁力吸附(一)
2019-06-18 00:07:59 】 浏览:228
Tags:高仿富 牛牛 组件 磁力 吸附

一、概述

上一篇文章高仿富途牛牛-组件化(一)-支持页签拖拽、增删、小工具我们讲述了组件化的一些基础东西,并有了一个基本的雏形,使用过富途牛牛的同学应该对其中的gif图比较熟悉了。虽然效果糙了一点儿,但是该有的基础功能是已经有了。

  • 工具栏页签拖拽
  • 工具栏之间页签拖拽
  • 小工具
  • 多页签架构
  • 小窗口

上述几个功能在上一篇文章中都已经有了,今天我们来讲述下第二个关键功能--磁力吸附和一些其他小功能

二、效果展示

磁力吸附,顾名思义就是说窗口移动时,快要接近另一个窗口边缘时,会有一种磁性,把正在拖拽的窗口直接吸过去,效果图如下图所示。






三、磁力吸附

高仿富途牛牛-组件化(一)-支持页签拖拽、增删、小工具文章最后,我列出了工程中所有的类,并做了每个类的功能说明。

本篇文章的工程代码在上一版本的基础上进行了一些优化,代码的结构也更加的清晰,阅读起来更容易,主要是增加了磁力吸附和一些同步功能。

下面来思考下磁力吸附这个功能。

首先我们来考虑下磁力吸附,什么是磁力吸附,明白我们自己的需求是什么样子的?

磁力表现出来可能像下面这样:

  1. 不同子窗口之间希望进行磁力吸附,也就是说窗口移动时,可以被吸附到邻近的窗口边框上
  2. 不同页签之间不需要关联
  3. 鼠标不能移动到subPanel之外

别名:被拖拽窗口(A)、吸附窗口(B)、事件处理(C)

有了清晰的需求之后,我们下面就来考虑怎么实现我们的需求,既然要做到小窗口之间进行吸附,想一想,这个事件处理不管写到A窗口还是B窗口都不是那么合适。那么可想而知,除过被拖拽的窗口A和将要的吸附窗口B之外,必然需要引入一个第三者C,进行事件处理,他不一定是一个窗口,主要是要能代理A和B的事件,并且进行各种处理即可。

有了第三者C之后,接下来我们在第三者C中去处理A的移动事件,循环去判断是否和其中某个窗口满足了吸附条件。一旦满足吸附条件,我们就触发吸附后操作

处理吸附事件时,可能像下面这样

假设我们有10个窗口,分别是A1、A2、A3、A4...A9、A10等

  1. 当我们拖拽A1窗口时,其他窗口都是吸附窗口(B)
  2. 当我们拖拽A2窗口时,A1和其他窗口都是吸附窗口(B)
  3. 同理,当我们拖拽其他An窗口时,除过An的窗口都是吸附窗口(B)

当要引入第三者窗口时,我们可能需要思考如下几个问题

  • 怎么样引入第三者事件处理类呢?
  • 他是怎么初始化的?
  • 他的作用范围?

思考如上3个问题,怎么去解决他们!我第一时间就想到了Qt中提供的QButtonGroup类,这个类的作用是用于管理其中的按钮,在他里边包含的按钮不允许有两个同时选中。 是不是很相似,也是管理一堆相同的控件,但是他们中,其中一个控件的操作会对其他所有的控件产生相同的效果。

也就是说:我们可以新增一个SmallGroup类,专门负责处理移动的窗口和其他窗口之间的事件

这个类可能就像这边这样!他提供了新增一个小窗口和移除一个小窗口的接口,添加进来的小窗口我们都可以进行磁力吸附管理。

class SmallGroup : public QObject
{
public:
    SmallGroup(QObject * object = nullptr);
    ~SmallGroup(){}

public:
    void AddSmall(SmallWidget *);
    void RemoveSmall(SmallWidget *);

    void MagneticEnable(bool);

    void LimitCursor(bool);//限制鼠标移动范围
    void MoveStart(SmallWidget *, const QPoint &);//开始移动 
    void MovingDistance(SmallWidget *, const QPoint &);//距离开始移动时的偏差距离

protected:
    virtual bool eventFilter(QObject *, QEvent *) override;

private:
    QPoint MagneticPos(SmallWidget *, const QRect &);

private:
    bool m_bMagnetic;
    QPoint m_startPos;
    QVector<SmallWidget *> m_smallVec;
    SmallWidget * m_pMoveWidget;
};

这个类的思路不难,只是里边有一些比较繁杂的实现,这里我主要说3点

  1. 限制鼠标区域
  2. 修正窗口可以移动的区域
  3. 获取最邻近的可被吸附的窗口

1、限制鼠标区域

限制鼠标可移动区域的接口上边已经列出来来了,根据参数动态的去限制鼠标移动区域,或者不限制

LimitCursor(bool)

当进行拖拽小窗口时,我们需要限制鼠标不能移除subPanel,如果不理解subPanel是什么东西,需要仔细去阅读下上一篇文章高仿富途牛牛-组件化(一)-支持页签拖拽、增删、小工具

限制鼠标移动区域的代码如下所示,主要是使用了ClipCursor这个win32接口,代码比较简单,这里就不做详细说明了。

void SmallGroup::LimitCursor(bool limit)
{
#ifdef Q_OS_WIN
    if (limit)
    {
        if (QWidget * subPanel = dynamic_cast<QWidget *>(parent()))
        {
            QRect q_rect = subPanel->geometry();
            QPoint g_pos = subPanel->mapToGlobal(QPoint(0, 0));

            CRect w_rect;
            w_rect.left = g_pos.x();
            w_rect.top = g_pos.y();
            w_rect.right = g_pos.x() + q_rect.width();
            w_rect.bottom = g_pos.y() + q_rect.height();

            ClipCursor(&w_rect);
        }
    }
    else
    {
        ClipCursor(nullptr);
    }
#endif 
}

2、修正窗口可以移动的区域

看到这个标题是不是有点儿蒙圈,其实这个也很简单,这里主要说明的是,我们移动小窗口时,小窗口不能移出subPanel,也就是说当subPanel显示时,其中的小窗口都可以全部显示出来,或者被其他小窗口遮挡。

当然了,这个也是需要根据需求来定的,我最开始做的就是4个边都不能出subPanel,但是后来发现,富途牛牛的代码是只有顶部不能出去。因此代码里我注释了3个if修正操作,大家可以根据自家的需求进行修改。

QRect CorrentRect(const QRect & rect, const QRect & subPanel)
{
    QRect correntRect = rect;
    //if (correntRect.left() < subPanel.left())
    //{
    //  correntRect.moveLeft
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高仿富途牛牛-组件化(三)-界面美化 下一篇[hdu517] 小奇的集合

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目