zoukankan      html  css  js  c++  java
  • 一步一步实现自己的模拟控件(6)——控件树及控件区域

    控件树

    一步一步实现自己的模拟控件(1)中的图上我们可以看到,我们的控件体系其实就是一个控件树。每一个窗口关联一个根控件,所有控件都在这个根控件之下,父控件包容并管理子控件,那么我们的Widget就应该是一个树结点。一个树结点至少有对Parent和Chilren的设置和访问接口:

    void SetParent(Widget* const pNewParent);
    Widget
    * GetParent() const;

    bool InsertChild(Widget* const pChild);
    bool RemoveChild(Widget* const pChild);

    在父控件销毁的时候它要负责销毁其下所有的子控件(类似窗口销毁也会销毁其子窗口):

    Widget::~Widget()
    {
    // 销毁所有子控件
    WidgetSet temp(std::move(pImpl_->children_));
    std::for_each(temp.begin(), temp.end(), std::mem_fn(
    &Widget::Destroy));

    if (IsRoot())
    {
    // 作为根控件,同时销毁驱动
    delete pImpl_->pDriver_;
    }
    else
    {
    // 不是根控件,则脱离父控件
    SetParent(0);
    }

    delete pImpl_;
    }

    实现中我们使用std::set来保存子控件,这样便于防止子控件重复设置,也便于移除子控件,缺点就是不能对子控件进行排序。如果以后我们提供控件的z-order概念,那么我们就会使用能够进行排序的容器来容纳子控件。

    控件区域:

    windows下,我们使用RECT结构来保存控件自身相对于窗口客户区的区域,那么窗口客户区尺寸改变时也就是我们控件进行布局的时机,那我们就要在消息过滤中处理WM_SIZE消息了。

    case WM_SIZE: // 让根控件适应真个客户区
    {
    RECT clientRect;
    ::GetClientRect(param.hWnd,
    &clientRect);
    pRootWidget
    ->SetAbsoluteRect(clientRect, false);
    }
    break;

    我们将控件的布局交由父控件管理,也就是说我们只需要更新根控件区域便可。根控件负责对其子控件进行布局,如此递归。

    控件更新:

    当控件区域改变了,那么相应的其显示也应相应的进行更新,所以我们的SetAbsoluteRect接口有一个update参数用于控制是否让窗口产生无效区域激活绘制。

    // 此处的update作用是控制是否立即更新显示。
    // 因为模拟控件只是窗口客户区的一个区域,当区域改变时应该产生原区域和新区域or运算后区域的脏矩形
    // 以使得窗口去重绘这部分区域。
    // 可能有些批量性质的操作会在操作多个控件后进行整体更新,所以在对单个控件设置新区域的时候可能不会想要更新。
    // 所以才加上这个是否立即更新的开关。
    void SetAbsoluteRect(const RECT& rect, bool update = true);

    既然提到了绘制,那么我们也应该让我们的控件展示在窗口上了。

    控件绘制:

    通常我们的窗口程序都是在WM_PAINT消息中进行绘制,我们的控件系统当然也需要处理此消息。

    case WM_PAINT:
    {
    // 使用内存DC来缓冲绘制
    // 目前没有计算脏矩形区域
    wnd_msg_assistant::OnPaint opAssistant(param.hWnd);
    pRootWidget
    ->Draw(opAssistant.GetMemDC());
    }
    return S_OK;

    这里引入了一个辅助对象帮助我们产生内存DC,优化我们的绘制效率。我们直接return了这个消息,也就是说我们将这个消息过滤掉了。前面WM_SIZE和WM_DESTROY我们都没有过滤,只是在这个时机对控件进行了通知或者操作。之所以要过滤WM_PAINT消息是因为外部的绘制和控件的绘制难以协调,那么我们干脆就接管了窗口客户区的绘制了。

    当然,控件也需要负责绘制其子控件,那么Draw接口中便会调用子控件的Draw,如此递归使得每个控件都能够得以绘制。

    首次直观的看到我们的控件:

    我们在调试版本中,为每个控件生成了一个随机的颜色,根据控件的区域绘制了其边框,这样我们就第一次直观的在窗口中看到了我们的控件。

    

    迫不及待,具有了区域的控件,我们已经急切的想要对其布局进行控制,绘制进行定制了。布局控制和绘制定制当然属于扩展部分,那么下面就将要引入我们的扩展体系了,尽请期待。

    下载测试工程源码

    By Evil.Ghost
  • 相关阅读:
    干货分享!用户级爬虫,怎敢封IP
    一篇就够,网站数据提取,数据使用
    scrapy 解决爬虫IP代理池,数据轻松爬。
    使用代理ip的作用是什么?
    爬虫工作怎样选择代理ip
    爬取VIP视频
    python爬虫数据追加至excel中
    使用geany编辑器时python输出中文问题
    在Windows系统中从终端运行Python程序
    Windows 安装 Python 及配置环境变量
  • 原文地址:https://www.cnblogs.com/EvilGhost/p/Abstract_Widget_6.html
Copyright © 2011-2022 走看看