zoukankan      html  css  js  c++  java
  • 游戏UI设计(2.1)窗口之父CXWnd的封装

    窗口之父CXWnd的封装

    [http://blog.csdn.net/mythma]

    在一般的程序中的对话框、按钮、编辑框等都属于窗口。在MFC中,类CWnd作为窗口类的基类,抽象出了它们的共同特征,并由此派生出若干的子类。在普通的windows程序中,我们可以方便的使用MFC为我们封装好的这些类(CDialog, CButton ,CEdit等),而在游戏程序中,由于需要与dx/opengl结合以及效率等原因,因此不直接使用它们,这就需要我们自己来封装。如此之多的控件如何封装能?我们不妨也为所有的窗口、控件抽象一个基类,就叫她CXWnd。那就让我们从 CXWnd开始,一步一步来实现我们真正的UI吧。

     

    我们先撇开DX具体绘制窗口的过程,看看窗口、控件有什么共同的特征,以及如何在CXWnd中来实现。

     

    1、  窗口的大小与位置

    窗口是一个矩形的区域,因此用两个变量就可以表示其大小(宽:m_Wdith, 高:m_Height)。

    窗口的位置也可以用两个变量表示( X坐标:       m_XPos , Y坐标:m_YPos)。但这两个变量表示窗口的绝对坐标(相对于屏幕左上角)还是相对坐标(相对于父窗口的坐上角)呢?

    从用户的角度(即创建窗口)看,使用相对坐标方便一点;

    从程序的角度(即绘制窗口)看,使用绝对坐标方便一点。

    我们封装CXWnd的目的在于使用,因此要从用户的角度出发,即m_XPosm_YPos表示的是窗口的相对坐标。当绘制窗口的时候,我们需要获得窗口的绝对坐标,因此需要提供一个成员函数来转化——GetAbsolutePos(float& x, float& y)

    (图就不用画了吧,:))

     

    2、  窗口之间的联系

    为了更具代表性,想一下(就不画了哦)一个带属性页的对话框的窗口层次,与它相关的窗口有一个父窗口(不包括祖父哦),若干的子窗口(各种控件),以及若干个同级窗口,因此就需要有一个指向其父窗口的指针(CXWnd* m_wndParent),一个指向其子窗口链表的指针(CXWnd* m_wndChildm_wndChild指向窗口链表的第一个窗口,该窗口z序在子窗口中最小,既显示在最前面),再加上同级窗口间的指针(CXWnd* m_wndNextSibling指向下一个同级窗口;CXWnd* m_wndPreviousSibling指向前一个同级窗口)。这么多窗口之间用这四种指针联系起来,画出来就是一棵窗口关系树。为什么同级窗口需要两个指针呢?在消息循环中就可以找到答案了。

    对这些指针,需要哪些操作呢?

    除了基本的Get***Wnd() Set***Wnd()外,还需要:

    添加一个子窗口AddChildWnd(wndChild : CXWnd*)

    删除一个子窗口DeleteChildWnd(wndChild : CXWnd*)    

    删除所有的子窗口DeleteAllChildWnd(),用来释放内存。

    获取子窗口个数 GetChildCount()

    以上几个都是对单向链表的基本操作。

     

    3、  消息循环

    从经典的消息函数WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)那,得到消息后,主窗口需要传递下去,于是CXWnd有了一个相似的函数——PostMessage(UINT msg, WPARAM wParam, LPARAM lParam, void* Data);,其中对消息进行分类处理。

    1) 鼠标消息

    鼠标消息处理,当然是如下三个(不过在CXWnd中不需要实现它们,只是声明为纯虚函数,因为各个控件的鼠标处理函数都不尽相同)

    virtual void OnMouseDown(int Button, int X, int Y) = NULL;

    virtual void OnMouseMove(int X, int Y) = NULL;

    virtual void OnMouseUp(int Button, int X, int Y) = NULL;

     

    如何得知是哪个窗口得到了鼠标消息呢?那就需要挨个搜索各个窗口,看鼠标当前的坐标(注意是绝对坐标)在哪个窗口上。因此加入函数CursorIntersect(FLOAT X, FLOAT Y)来完成此功能。

           鼠标消息是如何在窗口树中传递的呢?范围肯定是从大到小扫描,即先看是否在某窗口上,若在则看是否在该窗口的子窗口上……,终于找到最上的最底层的窗口,于是子窗口先处理鼠标消息,即最上的子窗口具有处理鼠标消息的优先权。因此消息的传递是从上而下,就称之为正序消息传递吧——PostMsgToChildren(……)负责把消息正序传给每一个子窗口。

     

    2) 键盘消息

    同鼠标消息一样,键盘消息的处理有如下两个纯虚成员函数:

    virtual void OnKeyDown(WPARAM Key, LPARAM Extended) = NULL;

    virtual void OnKeyUp(WPARAM Key, LPARAM Extended) = NULL;

     

    键盘消息的处理的特殊之处在于处理它的窗口(控件)需要获得焦点(原因很简单),因此每个窗体需要一个存放其获取焦点的子窗体的指针成员变量(CXWnd* m_wndFocus)和一个判断其是否获得焦点的bool成员变量(bool m_bFocus),从而就有了SetFocusWnd(……)和GetFocus()成员函数。

    键盘消息的传递与鼠标消息的传递一样。

     

    3) 窗口绘制消息

    窗口的绘制是与具体的窗口对象相关的,因此在CXWnd中不予实现,但窗口绘制的消息是要传递的,那该消息是如何传递的呢?

    每个窗口都有自己的z序,最后面的要先绘制,最前面的要后绘制(要不就被当住了),对应与窗口树就是,父窗口要先绘制,然后最下子窗口后绘制,最后是最上的子窗口。因此最下的窗口具有处理窗口绘制消息的优先权。而最上的子窗口排在子窗口链表的最前面,因此该消息需要逆序从后往前传递。因此需要一个专门的传递该消息的成员函数——PostMsgToChildrenReverse(……)。

    窗口的z序不是固定的,因此需要一个函数来改变它们的z ——MoveToFront(……)。

     

     

           至此,CXWnd的主体功能已经完成了,看看它有那些成员吧:

  • 相关阅读:
    HDU 1075 What Are You Talking About(字典树)
    HDU 1075 What Are You Talking About (stl之map映射)
    HDU 1247 Hat’s Words(字典树活用)
    字典树HihoCoder
    HDU 1277全文检索(字典树)
    HDU 3294 Girls' research(manachar模板题)
    HDU 3294 Girls' research(manachar模板题)
    HDU 4763 Theme Section(KMP灵活应用)
    Ordering Tasks UVA
    Abbott's Revenge UVA
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333297.html
Copyright © 2011-2022 走看看