使用责任链来完成鼠标事件的责任处理——MouseManager与MouseProcessor

Qt6 使用责任链来完成鼠标事件的责任处理 —— MouseManagerMouseProcessor

Documentation: v1.0.0 Specified for Version v2.2.1,First Release in 2025/8/1
Documenation belongs to Projects: Charliechen114514/CCIMXDesktop: This is a Simple Desktop with Common Utilities for Embedded Device System using Qt6

前言

​ 这个部分是笔者设计的,用于提供一定优先级办法的鼠标事件处理的文档,主要介绍在library文件夹下的鼠标设计处理的文档。

​ 大家都是看过很多Demo的人,都可以注意到,很多人写的MousePressEvent和MouseReleaseEvent简直是惨不忍睹,大长串的一片,搞得代码乱七八糟,非常不好维护,笔者的代码利用一定的设计后,化简成了这样:

void DesktopMainWindow::mousePressEvent(QMouseEvent* event) {
    mouseManager->install_mousepoint(
        event->pos(),
        MouseManager::PointType::Press);
    event->accept();
}

void DesktopMainWindow::mouseReleaseEvent(QMouseEvent* event) {
    mouseManager->install_mousepoint(
        event->pos(),
        MouseManager::PointType::Release);

    if (!mouseManager->run_processor()) {
        event->ignore();
        return;
    }
    qDebug() << "Ok, handled!";
    event->accept();
}

​ 其他处理器的部分依次由模块提供进行注册,可以说是非常的方便了。

             MouseProcessor* updown_processor = new MouseProcessor(
                 [mainWindow](MouseRecorder* recorder) -> bool {
                              const QPoint diff = recorder->press - recorder->release;
                              return qAbs(diff.y()) >= mainWindow->tool_settings_bar_slide_limitive; },
                 [mainWindow](MouseRecorder* recorder) -> bool {
                                const QPoint diff = recorder->press - recorder->release;
                                if(diff.y() < 0){
                                    mainWindow->fastSettingsWidget->slideDown();
                                }else{
                                    mainWindow->fastSettingsWidget->slideUp();
                                }
                              return true; });
             updown_processor->setName("ToolSettingsProcessor");
             mainWindow->mouseManager->register_processor(updown_processor);

​ 我们的目的很简单:我们知道在 GUI 应用程序中,鼠标事件的处理往往涉及多种组件与行为逻辑。传统事件分发机制(如事件过滤器)在复杂业务中容易导致耦合度高、可测试性差、可扩展性不足等问题。为了解耦事件处理逻辑、增强处理策略的可配置性与可插拔性,我们很自然的想到设计模式中的“责任链”模式,来帮助我们处理这个事情。

什么是责任链模式(Chain of Responsibility)?

责任链模式是一种行为型设计模式,其主要目的是:使多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合关系。这些对象被连成一条链,请求沿着链传递,直到某个对象处理它为止。本质上,它是一种将请求处理与具体处理逻辑解耦的设计策略。

​ 我们先来看我们的MouseManager,MouseManager收集可靠的鼠标信息后,将我们的鼠标信息转发到我们的处理链上,如下图所示👇

+------------------+
|   MouseManager   |
+------------------+
         |
         v
+------------------+       +------------------+       +------------------+
|  MouseProcessor  | ----> |  MouseProcessor  | ----> |  MouseProcessor  |
+------------------+       +------------------+       +------------------+
       ^                           ^                            ^
       | tryProcess()              |                            |
       | directProcess()           |                            |
       | setNext() / next()        |                            |

​ 你可以看到,在这个处理链条上,我们能抓出来三个角色,这里列一张表:

模式角色当前实现中的类说明
Handler(抽象处理者)MouseProcessor定义处理请求的接口(try_and_process)和设置下一个处理者的方法(setNext)
ConcreteHandler(具体处理者)继承/实例化的 MouseProcessor 对象实现具体的 try + process 逻辑
Client(请求发起者)MouseManager构建处理链并发起处理请求

​ 你突然发现,这个时候,我们的逻辑就没有写死了,我们可以动态的注册我们的模块接口,他不关心是从哪里来的数据,只需要把数据接受进来,发现是自己可以处理的,就进行处理,希望截至处理的,就返回处理完毕的信号,反之,就返回false表达这个事件还需要继续被其他Processor进行处理。这样的话:

  • 解耦发送者与处理者MouseManager 不需要知道哪个处理器能处理请求,职责完全委托给链;
  • 提高可扩展性:增加或修改处理器不影响其他逻辑,只需按需插入链条;
  • 运行时灵活配置:处理器链可以在运行时动态调整顺序或启用状态;
  • 天然支持优先级:链条顺序即优先级,先注册的处理器优先尝试处理。
典型处理流程(序列图)
MouseManager      ProcessorA     ProcessorB     ProcessorC
     |                 |              |              |
     | run_processor() |              |              |
     |---------------->|              |              |
     | try_and_process |              |              |
     |   tryProcess?   |              |              |
     |     false       |              |              |
     |-----------------------------> try_and_process |
                       |              tryProcess?    |
                       |                true         |
                       |            directProcess?   |
                       |               true          |
                       |           <-- handled OK ---|
     |<---------------------------------------------|

核心类结构说明

1. MouseProcessor —— 事件处理器节点

每一个 MouseProcessor 表示一个独立的鼠标事件处理节点,它拥有两个策略:

  • tryProcess(recorder):判断是否有资格处理该事件;
  • directProcess(recorder):执行实际处理逻辑。

若当前节点未启用、或 tryProcess 不满足条件、或 directProcess 执行失败,则会尝试交由下一个处理节点继续尝试,体现出典型的责任链传播模式。

bool MouseProcessor::try_and_process(MouseRecorder* recorder) {
    if (!enabled) {
        return next_one ? next_one->try_and_process(recorder) : false;
    }

    if (tryProcess(recorder)) {
        if (directProcess(recorder)) {
            qInfo() << processor_name << ": handle OK!";
            return true;
        } else {
            qInfo() << processor_name << ": processing failed, pass to next!";
            return next_one ? next_one->try_and_process(recorder) : false;
        }
    } else {
        qInfo() << processor_name << ": tryProcess failed, pass to next!";
        return next_one ? next_one->try_and_process(recorder) : false;
    }
}

该类的职责是抽象一个处理能力单位,通过链式结构组织成完整的处理流程。处理器链的顺序由注册时决定,内部维护 next_one 指针指向下一个处理器。

2. MouseManager —— 管理器与入口

MouseManager 负责管理整个责任链,它负责:

  • 注册处理器(register_processor)并构建链结构;
  • 接收与记录鼠标事件的关键点位(install_mousepoint);
  • 触发链处理入口(run_processor)。
bool MouseManager::run_processor() {
    if (!head_processor) {
        qCritical() << "No processor is registered, manager do nothing!";
        return false;
    }

    bool isHandled = head_processor->try_and_process(recorder.get());
    if (!isHandled) {
        qCritical() << "Current Chain failed to process the events!";
    } else {
        qDebug() << "Ok, event is processed!";
    }

    return isHandled;
}

该类本身并不处理具体的鼠标事件逻辑,而是以管理者的身份,组织、传递并控制处理链的执行。


数据结构辅助:MouseRecorder

MouseRecorder 是辅助结构,用于记录当前一次鼠标事件的 Press/Release 两个点位,是各处理器共享的上下文数据结构,支持:

recorder->press
recorder->release

处理器根据这些点位判断自身是否有能力处理该事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值