• Duilib教程-自动布局2


    在上一节中,我简单介绍了控件随父LAYOUT自由移动的设置。在这一节,我将介绍一种常见的情况:嵌入窗口。

    在项目中,我们很少会100%的编写一个软件,特别是界面相关的,我们会使用以前已经编写好的窗口,或网上的开源模块。举一个简单的例子来说,如果你要编写一个视频播放器,关于视频的播放窗口,就用不着用DUI来实现,我们完全可以使用网上的开源库,嵌入一个播放的WND即可(当然有的库也支持回调的方式,用户可以在自己的窗口中将回调出来的图片进行自由绘制)。

    我们需要在窗口大小改变时,即时地改变播放窗口的大小。也许你会说这非常简单,直接重载OnSize,然后获取占位控件(使用占位控件才是最正确的选择,如果在程序中判断左边距、右边距,就做不到UI、CODE分离了)的大小,然后设置即可。但是当你真正使用的时候,发现并没有那么简单。来看代码:

    UIManager.cpp 第750行:

        case WM_SIZE:
    
            {
    
                if( m_pFocus != NULL ) {
    
                    TEventUI event = { 0 };
    
                    event.Type = UIEVENT_WINDOWSIZE;
    
                    event.pSender = m_pFocus;
    
                    event.dwTimestamp = ::GetTickCount();
    
                    m_pFocus->Event(event);
    
                }
    
                if( m_pRoot != NULL ) m_pRoot->NeedUpdate();
    
            }
    
            return true;

    我们看到,窗口大小改变,ROOT只是简单的 NeedUpdate,重绘而已,它的大小并没有设置为与窗口一样的大小。

    在WinImplBase.cpp 第214 行:

    LRESULT WindowImplBase::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    
    {
    
             SIZE szRoundCorner = m_PaintManager.GetRoundCorner();
    
    #if defined(WIN32) && !defined(UNDER_CE)
    
             if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) {
    
                       CDuiRect rcWnd;
    
                       ::GetWindowRect(*this, &rcWnd);
    
                       rcWnd.Offset(-rcWnd.left, -rcWnd.top);
    
                       rcWnd.right++; rcWnd.bottom++;
    
                       HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy);
    
                       ::SetWindowRgn(*this, hRgn, TRUE);
    
                       ::DeleteObject(hRgn);
    
             }
    
    #endif
    
             bHandled = FALSE;
    
             return 0;
    
    }

    也是啥也没做。

    所以在OnSize里面设置窗口位置,并不会达到效果。

    那么DUILIB是在哪里设置ROOT的大小呢?UIManager.cpp 第 615行,即在WM_PAINT中进行设置。

    m_pRoot->SetPos(rcClient);

    1.SetPos

    当你看到这里时,我想你已经知道第一种方法了。即在 OnSize中,

    RECT rc;
    
    GetClientRect (m_hWnd, &rc);
    
    m_PaintManager.GetRoot()->SetPos (rc);
    
    const RECT& rc_pos = targer_ui_->GetPos ();
    
    ::MoveWindow (move_wnd, rc_pos.left, rc_pos.top, rc_pos.right – rc_pos.left, rc_pos.bottom – rc_pos.top, TRUE);

    即我们主动设置大小,ROOT设置了POS后,会将它的子控件也设置POS,详情请看源码。所以,我们就能够得到正确的位置信息了。

    但是这并不是最好的方式,原因很简单,OnSize会被频繁的调用,特别是在程序初始化的时候,OnSize被调用N次,而且在最小化的时候也会被调用。而且当你看1.SetPos时,你也猜到了会有第二种方式了。

    2.委托 OnSize

    假设我们的占位控件为 target_ui_,它有一个委托成员变量:OnSize。直接看代码吧:

    target_ui_->OnSize += MakeDelegate (this, &CYourWnd::OnTargetSizeChanged);
    
     
    
    bool CYourWnd:: OnTargetSizeChanged (void* param)
    
    {
    
    const RECT& rc_pos = targer_ui_->GetPos ();
    
    ::MoveWindow (move_wnd, rc_pos.left, rc_pos.top, rc_pos.right – rc_pos.left, rc_pos.bottom – rc_pos.top, TRUE);
    
    }

    如此简单,又如此优美的代码。

    注意使用的是 +=

    在这里,我们也看到了作者自己实现了委托的编写(我不清楚是不是使用了开源库),可见作者的C++功底是相当深厚的。

    看CControlUI的源码,你会发现如下委托对象:

    public:
    
        CEventSource OnInit;
    
        CEventSource OnDestroy;
    
        CEventSource OnSize;
    
        CEventSource OnEvent;
    
        CEventSource OnNotify;

    顾名思义,无需赘述。

    这里说一下Event和Notify的区别。

    Event是控件自己收到的消息,比如鼠标左键按下、弹起、双击等,DUILIB先向控件自己发一个事件。

    Notify通知,是向WND发送的通知消息,类似MFC中对话框收到控件的NOTIFY(包括按钮的单击),它默认情况下是由窗口接收的,在窗口的Notify函数中进行响应。

    DUILIB的处理流程是,先向CONTROL发送事件,然后向WND发送通知。

    OnNotify相当有用,因为你可以定制每个控件的响应,而不需要在WND的Notify中进行一大堆的if..else..了。

    OnEvent用处也很大,看情况使用了。

  • 相关阅读:
    二分练习题4 查找最接近的元素 题解
    二分练习题5 二分法求函数的零点 题解
    二分练习题3 查找小于x的最大元素 题解
    二分练习题2 查找大于等于x的最小元素 题解
    二分练习题1 查找元素 题解
    code forces 1176 D. Recover it!
    code forces 1173 B. Nauuo and Chess
    code forces 1173 C. Nauuo and Cards
    吴恩达深度学习课程笔记-15
    吴恩达深度学习课程笔记-14
  • 原文地址:https://www.cnblogs.com/lin1270/p/4209597.html
Copyright © 2020-2023  润新知