日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一步一步实现自己的模拟控件(6)——控件树及控件区域

發(fā)布時間:2024/4/17 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一步一步实现自己的模拟控件(6)——控件树及控件区域 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

控件樹

一步一步實(shí)現(xiàn)自己的模擬控件(1)中的圖上我們可以看到,我們的控件體系其實(shí)就是一個控件樹。每一個窗口關(guān)聯(lián)一個根控件,所有控件都在這個根控件之下,父控件包容并管理子控件,那么我們的Widget就應(yīng)該是一個樹結(jié)點(diǎn)。一個樹結(jié)點(diǎn)至少有對Parent和Chilren的設(shè)置和訪問接口:

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

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

在父控件銷毀的時候它要負(fù)責(zé)銷毀其下所有的子控件(類似窗口銷毀也會銷毀其子窗口):

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

if (IsRoot())
{
// 作為根控件,同時銷毀驅(qū)動
delete pImpl_->pDriver_;
}
else
{
// 不是根控件,則脫離父控件
SetParent(0);
}

delete pImpl_;
}

實(shí)現(xiàn)中我們使用std::set來保存子控件,這樣便于防止子控件重復(fù)設(shè)置,也便于移除子控件,缺點(diǎn)就是不能對子控件進(jìn)行排序。如果以后我們提供控件的z-order概念,那么我們就會使用能夠進(jìn)行排序的容器來容納子控件。

控件區(qū)域:

windows下,我們使用RECT結(jié)構(gòu)來保存控件自身相對于窗口客戶區(qū)的區(qū)域,那么窗口客戶區(qū)尺寸改變時也就是我們控件進(jìn)行布局的時機(jī),那我們就要在消息過濾中處理WM_SIZE消息了。

case WM_SIZE: // 讓根控件適應(yīng)真?zhèn)€客戶區(qū)
{
RECT clientRect;
::GetClientRect(param.hWnd,
&clientRect);
pRootWidget
->SetAbsoluteRect(clientRect, false);
}
break;

我們將控件的布局交由父控件管理,也就是說我們只需要更新根控件區(qū)域便可。根控件負(fù)責(zé)對其子控件進(jìn)行布局,如此遞歸。

控件更新:

當(dāng)控件區(qū)域改變了,那么相應(yīng)的其顯示也應(yīng)相應(yīng)的進(jìn)行更新,所以我們的SetAbsoluteRect接口有一個update參數(shù)用于控制是否讓窗口產(chǎn)生無效區(qū)域激活繪制。

// 此處的update作用是控制是否立即更新顯示。
// 因?yàn)槟M控件只是窗口客戶區(qū)的一個區(qū)域,當(dāng)區(qū)域改變時應(yīng)該產(chǎn)生原區(qū)域和新區(qū)域or運(yùn)算后區(qū)域的臟矩形
// 以使得窗口去重繪這部分區(qū)域。
// 可能有些批量性質(zhì)的操作會在操作多個控件后進(jìn)行整體更新,所以在對單個控件設(shè)置新區(qū)域的時候可能不會想要更新。
// 所以才加上這個是否立即更新的開關(guān)。
void SetAbsoluteRect(const RECT& rect, bool update = true);

既然提到了繪制,那么我們也應(yīng)該讓我們的控件展示在窗口上了。

控件繪制:

通常我們的窗口程序都是在WM_PAINT消息中進(jìn)行繪制,我們的控件系統(tǒng)當(dāng)然也需要處理此消息。

case WM_PAINT:
{
// 使用內(nèi)存DC來緩沖繪制
// 目前沒有計(jì)算臟矩形區(qū)域
wnd_msg_assistant::OnPaint opAssistant(param.hWnd);
pRootWidget
->Draw(opAssistant.GetMemDC());
}
return S_OK;

這里引入了一個輔助對象幫助我們產(chǎn)生內(nèi)存DC,優(yōu)化我們的繪制效率。我們直接return了這個消息,也就是說我們將這個消息過濾掉了。前面WM_SIZE和WM_DESTROY我們都沒有過濾,只是在這個時機(jī)對控件進(jìn)行了通知或者操作。之所以要過濾WM_PAINT消息是因?yàn)橥獠康睦L制和控件的繪制難以協(xié)調(diào),那么我們干脆就接管了窗口客戶區(qū)的繪制了。

當(dāng)然,控件也需要負(fù)責(zé)繪制其子控件,那么Draw接口中便會調(diào)用子控件的Draw,如此遞歸使得每個控件都能夠得以繪制。

首次直觀的看到我們的控件:

我們在調(diào)試版本中,為每個控件生成了一個隨機(jī)的顏色,根據(jù)控件的區(qū)域繪制了其邊框,這樣我們就第一次直觀的在窗口中看到了我們的控件。



迫不及待,具有了區(qū)域的控件,我們已經(jīng)急切的想要對其布局進(jìn)行控制,繪制進(jìn)行定制了。布局控制和繪制定制當(dāng)然屬于擴(kuò)展部分,那么下面就將要引入我們的擴(kuò)展體系了,盡請期待。

下載測試工程源碼

轉(zhuǎn)載于:https://www.cnblogs.com/EvilGhost/archive/2011/04/09/Abstract_Widget_6.html

總結(jié)

以上是生活随笔為你收集整理的一步一步实现自己的模拟控件(6)——控件树及控件区域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。