Duilib源碼解析——控件尋找

鼠標(biāo)點擊了一個按鈕之后,是怎樣觸發(fā)到對應(yīng)的按鈕的事件的够吩?
首先比然,鼠標(biāo)點擊觸發(fā)一系列Windows消息,這里以WM_LBUTTONUP舉例說明消息處理過程:
首先周循,windows消息最終會到CPaintManagerUI::MessageHandler當(dāng)中强法,中間的過程已經(jīng)有很多文章講述過,此處忽略不寫湾笛;

case WM_LBUTTONUP:
    {
        POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
        m_ptLastMousePos = pt;
        //if( m_pEventClick == NULL ) break;
        if (m_pEventClick == NULL) {
          m_pEventClick = FindControl(pt);
          if (m_pEventClick == NULL)
            break;
        }
        ReleaseCapture();
        TEventUI event = { 0 };
        event.Type = UIEVENT_BUTTONUP;
        event.pSender = m_pEventClick;
        event.wParam = wParam;
        event.lParam = lParam;
        event.ptMouse = pt;
        event.wKeyState = (WORD)wParam;
        event.dwTimestamp = ::GetTickCount();
        m_pEventClick->Event(event);
        m_pEventClick = NULL;
    }
    break;

這里是CPaintManagerUI:MessageHandler中對WM_LBUTTONUP消息的處理饮怯,其實基本上所有鼠標(biāo)消息的處理都是這個形式,先取出坐標(biāo)值嚎研,然后找到對應(yīng)的控件蓖墅,最后封裝成Duilib中的事件。那么顯然關(guān)鍵就在于FindControl的實現(xiàn):

CControlUI* CPaintManagerUI::FindControl(POINT pt) const
{
    ASSERT(m_pRoot);
    return m_pRoot->FindControl(__FindControlFromPoint, &pt, 
        UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST);
}

CControlUI* CALLBACK CPaintManagerUI::__FindControlFromPoint(CControlUI* pThis, LPVOID pData)
{
    LPPOINT pPoint = static_cast<LPPOINT>(pData);
    return ::PtInRect(&pThis->GetPos(), *pPoint) ? pThis : NULL;
}

CControlUI* CControlUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags)
{
    if( (uFlags & UIFIND_VISIBLE) != 0 && !IsVisible() ) return NULL;
    if( (uFlags & UIFIND_ENABLED) != 0 && !IsEnabled() ) return NULL;
    if( (uFlags & UIFIND_HITTEST) != 0 &&
         (!m_bMouseEnabled || !::PtInRect(&m_rcItem, * static_cast<LPPOINT>(pData))) ) 
    return NULL;
    return Proc(this, pData);
}

顯然CControlUI::FindControl當(dāng)中需要傳入一個函數(shù)嘉赎,這樣就可以根據(jù)不同的尋找策略傳入不同的函數(shù)置媳,這里傳入的是__FindControlFromPoint,顯然是根據(jù)鼠標(biāo)位置來尋找控件公条,此外Duilib中還有FindControlFromNameHash拇囊、FindControlFromTab、FindControlFromeShortCut等等靶橱,這里就不說了寥袭;
這段代碼是不是好像很淺顯路捧,判斷是否只查找visible跟enable的并判斷自身狀態(tài),判斷是否允許鼠標(biāo)事件传黄,判斷傳入的點是否屬于自己的區(qū)域范圍內(nèi)杰扫,等等;
很明顯膘掰,就這樣是沒有辦法從層層嵌套的控件中找到最終響應(yīng)事件的那一個的章姓,真正的核心邏輯在Duilib中所有控件容器的基類,CContainerUI::FindControl中:
首先:

        if( (uFlags & UIFIND_VISIBLE) != 0 && !IsVisible() ) return NULL;
        if( (uFlags & UIFIND_ENABLED) != 0 && !IsEnabled() ) return NULL;
        if( (uFlags & UIFIND_HITTEST) != 0 ) {
            if( !::PtInRect(&m_rcItem, *(static_cast<LPPOINT>(pData))) ) return NULL;
            if( !m_bMouseChildEnabled ) {
                CControlUI* pResult = NULL;
                if( m_pVerticalScrollBar != NULL ) pResult = m_pVerticalScrollBar->FindControl(Proc, pData, uFlags);
                if( pResult == NULL && m_pHorizontalScrollBar != NULL ) pResult = m_pHorizontalScrollBar->FindControl(Proc, pData, uFlags);
                if( pResult == NULL ) pResult = CControlUI::FindControl(Proc, pData, uFlags);
                return pResult;
            }
        }

跟之前一樣的判斷识埋,是否可見凡伊,可響應(yīng)事件;對于控件容器來說多了個m_bMouseChildEnabled的標(biāo)志位窒舟,作用從名字就可以看出來系忙,假如禁止了子控件響應(yīng)鼠標(biāo)事件,這里還需要對滾動條做特殊處理惠豺,判斷鼠標(biāo)是否落在滾動條上银还;

    CControlUI* pResult = NULL;
    if( m_pVerticalScrollBar != NULL ) pResult = m_pVerticalScrollBar->FindControl(Proc, pData, uFlags);
    if( pResult == NULL && m_pHorizontalScrollBar != NULL ) pResult = m_pHorizontalScrollBar->FindControl(Proc, pData, uFlags);
    if( pResult != NULL ) return pResult;

    if( (uFlags & UIFIND_ME_FIRST) != 0 ) {
        CControlUI* pControl = CControlUI::FindControl(Proc, pData, uFlags);
        if( pControl != NULL ) return pControl;
    }

常規(guī)的處理,判斷是否在滾動條上洁墙,判斷是否需要直接返回最外層容器蛹疯,這樣在部分場合就省去了對子元素的尋找;

    RECT rc = m_rcItem;
    rc.left += m_rcInset.left;
    rc.top += m_rcInset.top;
    rc.right -= m_rcInset.right;
    rc.bottom -= m_rcInset.bottom;
    if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();
    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
    if( (uFlags & UIFIND_TOP_FIRST) != 0 ) {
        for( int it = m_items.GetSize() - 1; it >= 0; it-- ) {
            CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
            if( pControl != NULL ) {
                if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
                    continue;
                else 
                    return pControl;
            }            
        }
    }
    else {
        for( int it = 0; it < m_items.GetSize(); it++ ) {
            CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
            if( pControl != NULL ) {
                if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
                    continue;
                else 
                    return pControl;
            } 
        }
    }

    if( pResult == NULL && (uFlags & UIFIND_ME_FIRST) == 0 ) pResult = CControlUI::FindControl(Proc, pData, uFlags);
    return pResult;

最后热监,確定了不在滾動條區(qū)域內(nèi)苍苞,將該容器去掉滾動條區(qū)域與內(nèi)邊距,由for循環(huán)匹配子控件狼纬,兩段循環(huán)其實邏輯一樣羹呵,只是尋找順序有區(qū)別而已。首先會進(jìn)到子控件的FindControl中疗琉,匹配鼠標(biāo)的點是否落在該控件冈欢,之后的判斷是為了檢查該點是否屬于容器的內(nèi)邊距,因為控件布局后有可能有部分落在容器外面盈简;假如最終沒有符合條件的子控件凑耻,會走容器自己的CControlUI::FindControl,這時返回容器本身柠贤。
經(jīng)過這一系列邏輯香浩,即完成從一個點得到控件的操作【拭悖可以看出其實也沒什么難的邻吭,就是拿子控件的矩形區(qū)域一個個去匹配鼠標(biāo)的點而已,匹配不到則再判斷是否為根節(jié)點宴霸,比較細(xì)節(jié)的地方就是滾動條與內(nèi)邊距的處理而已囱晴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膏蚓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畸写,更是在濱河造成了極大的恐慌驮瞧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枯芬,死亡現(xiàn)場離奇詭異论笔,居然都是意外死亡,警方通過查閱死者的電腦和手機千所,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門翅楼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人真慢,你說我怎么就攤上這事±砭ィ” “怎么了黑界?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長皂林。 經(jīng)常有香客問我朗鸠,道長,這世上最難降的妖魔是什么础倍? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任烛占,我火速辦了婚禮,結(jié)果婚禮上沟启,老公的妹妹穿的比我還像新娘忆家。我一直安慰自己,他們只是感情好德迹,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布芽卿。 她就那樣靜靜地躺著,像睡著了一般胳搞。 火紅的嫁衣襯著肌膚如雪卸例。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天肌毅,我揣著相機與錄音筷转,去河邊找鬼。 笑死悬而,一個胖子當(dāng)著我的面吹牛呜舒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笨奠,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼阴绢,長吁一口氣:“原來是場噩夢啊……” “哼店乐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呻袭,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤眨八,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后左电,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廉侧,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年篓足,在試婚紗的時候發(fā)現(xiàn)自己被綠了段誊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡栈拖,死狀恐怖连舍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涩哟,我是刑警寧澤索赏,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贴彼,受9級特大地震影響潜腻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜器仗,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一融涣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧精钮,春花似錦威鹿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弹沽,卻和暖如春檀夹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背策橘。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工炸渡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丽已。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓蚌堵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吼畏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的泻蚊。 ??事件躲舌,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,495評論 1 11
  • 1、窗體 1性雄、常用屬性 (1)Name屬性:用來獲取或設(shè)置窗體的名稱没卸,在應(yīng)用程序中可通過Name屬性來引用窗體。 ...
    Moment__格調(diào)閱讀 4,549評論 0 11
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,104評論 1 32
  • ¥開啟¥ 【iAPP實現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程秒旋,因...
    小菜c閱讀 6,426評論 0 17
  • 在廈門學(xué)習(xí)的最后一天迁筛,我參加了一場演講比賽煤蚌。如果把這場演講比賽比作“華山論劍”,那演講中一招一式的鋪排設(shè)計都是滿滿...
    鄭腔宛調(diào)閱讀 483評論 0 1