• 一步一步实现自己的模拟控件(9)——消息处理


    这次我们将要给Widget增加一些状态,并使其能够接受出消息处理扩展,测试工程中实现了一个按钮的消息处理扩展。

    Widget状态:

    之前的控件只是绘制了一个边框,并且总是会在窗口中显示。实际上我们往往会希望能够让某个控件显示或者隐藏、可用或者不可用等等,那么控件应该具有能够标识这些状态的属性,于是我们给Widget增加了状态概念。

    // 状态相关
    void AddStates(size_t states);
    void SubStates(size_t states);
    bool CheckState(widget::StateBitField s);

    上面是状态相关的几个接口,包括了增加状态组,减少状态组,检测状态。这里有个状态组的概念,是因为我将状态用位域来实现,那么他们就可以通过or运算来得到多个状态的集合,我就这个集合称为状态组。

    enum StateBitField{
    STATE_VISIBLE
    = 1 << 0, // 控件可见?决定控件是否被绘制
    STATE_ENABLE = 1 << 1, // 控件可用?决定控件是否响应鼠标键盘消息
    STATE_TRANSPARENT = 1 << 2, // 控件透明?决定控件是否响应鼠标键盘消息以及是否显示tooltip
    };

    目前我给Widget定义了3个状态:可见、可用、透明。默认创建的Widget是不可见、不可用、不透明的,需要在创建成功后手动设置,例如:

    // 建立根控件
    auto pRootWidget = ghost::Widget::Create();
    pRootWidget
    ->AddStates(
      ghost::widget::STATE_VISIBLE|ghost::widget::STATE_ENABLE|ghost::widget::STATE_TRANSPARENT);

    消息处理扩展:

    通常对于控件来说,没有消息处理就等于没有生命意义,那么为Widget添加消息处理扩展就意味着使Widget活起来,这次我们就来完成这个任务,期待Widget活起来的那激动人心的一刻。和以往添加扩展支持一样,为扩展编写一个抽象基类,在Widget的关联对象管理中添加这个扩展的关联处理,然后在Widget的SendMessage处理逻辑里增加对消息处理扩展的支持。那么我们的SendMessage实现就变成了这样:

    int Widget::SendMessage(widget::Message message, void* param1/* = 0*/, void* param2/* = 0*/)
    {
    if (!CheckState(widget::STATE_ENABLE))
    {
    // 不响应鼠标键盘消息
    if ((widget::MSG_MOUSE_FIRST <= message
    && widget::MSG_MOUSE_LAST >= message)
    ||(widget::MSG_KEY_FIRST <= message
    && widget::MSG_KEY_LAST >= message))
    {
    return widget::MSG_RESULT_NONE;
    }
    }
    if (CheckState(widget::STATE_TRANSPARENT))
    {
    // 不响应鼠标键盘消息,不显示tooltip
    if ((widget::MSG_MOUSE_FIRST <= message
    && widget::MSG_MOUSE_LAST >= message)
    ||(widget::MSG_KEY_FIRST <= message
    && widget::MSG_KEY_LAST >= message))
    {
    return widget::MSG_RESULT_NONE;
    }
    }

    auto pRelatedObject
    = GetRelatedObject();
    if (pRelatedObject)
    {
    auto pMessageHandle
    = pRelatedObject->GetMessageHandle();
    if (pMessageHandle)
    {
    bool handled = false;
    int res = pMessageHandle->OnMessage(this, message, param1, param2, handled);
    if (handled)
    {
    return res;
    }
    }
    }

    switch (message)
    {
    #ifdef _DEBUG
    case widget::MSG_DRAW:
    {
    HBRUSH hBrush
    = ::CreateSolidBrush(pImpl_->testFrameColor_);
    ::FrameRect((HDC)param1,
    &pImpl_->absoluteRect_, hBrush);
    ::DeleteObject(hBrush);
    }
    break;
    #endif // _DEBUG
    case widget::MSG_HIT_TEST:
    if (::PtInRect(&pImpl_->absoluteRect_, *(const POINT*)param1))
    {
    return ~widget::MSG_RESULT_NONE;
    }
    break;
    }
    return widget::MSG_RESULT_NONE;
    }

    我多次提到了tooltip,但是我们这次并没有为Widget增加其支持,它将在后面被加入到Widget中来。可以看到这里处理有对消息处理扩展的支持,还有状态对于消息的影响。这里出现了一个MSG_HIT_TEST,这是一个新增的消息。这次为Widget添加了很多的消息,包括了鼠标、键盘等,要将鼠标消息准确的发送给正确的控件,那么点击测试是必不可少的,这个MSG_HIT_TEST消息则是用于控件处理点击测试的。

    点击测试:

    当容器产生了鼠标事件的时候,我们能够得到鼠标热点在容器中的坐标。前面我也多次提到,模拟控件是容器中的某个区域,那么当鼠标热点位于某个控件所处的区域的时候,那么这个鼠标事件我们就应当交由这个控件进行处理(这是通常情况,也有可能某个控件作为透明控件,不接受任何点击测试)。于是我们便通过点击测试(HitTest)这个访问接口来找到应该处理鼠标事件的控件。在找到控件之后,我们还将坐标映射到了控件的相对坐标系,这样控件就可以以自身的相对位置来处理鼠标事件了。

    当然,这次的内容非常多,包括坐标映射、区域映射,捕获鼠标的控件、活动控件、焦点控件等概念都未提到,但在代码中还是能够看到这些概念的。如果一一介绍,那文章就会非常冗长,也会使Widget实现进展缓慢,因此我通常都会省略一些内容,这些内容也就只能通过代码阅读来得到了。

    按钮:

    为了测试我们这次实现的内容,我们编写了一个按钮的消息处理扩展。简单起见,我们使其不产生命令、不绘制文本,仅仅只是展示对鼠标消息的处理和状态的变化而已。

    我们将原先测试代码中的中间那个控件关联了按钮的消息处理,那么它就称为了一个按钮控件了。我们可以将鼠标移到它上面点击看看会发生什么。

    下载测试工程代码 因为我一直都在使用VC10来编写Widget,也用到了一些新的特性,所以子啊这次的测试工程我生成了一份release下的程序,没有VC10的人至少能够看到其运行效果。

  • 相关阅读:
    使用a标签制作tooltips
    使用editorconfig配置你的编辑器
    JointJS绘制流程图
    用highcharts展现你的数据
    css段落首字母下沉
    sklearn框架的系统学习
    numpy删除二维数据矩阵的行和列
    sklearn中机器学习算法评价指标
    sklearn调用逻辑回归算法
    sklearn调用多项式回归
  • 原文地址:https://www.cnblogs.com/EvilGhost/p/Abstract_Widget_9.html
Copyright © 2020-2023  润新知