• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            cexer

            cexer
            posts - 12, comments - 334, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            轉(zhuǎn)帖請注明出處 http://www.shnenglu.com/cexer/archive/2008/08/18/59285.html

              拋棄了上一個消息機(jī)制,因為它的實現(xiàn)不得不多用了幾個模板函數(shù),在使用的時候有代碼膨脹的現(xiàn)象。雖然其程度不如 win32gui,SmartWin,不過因為本人有點極端,所以相當(dāng)?shù)夭粷M意。于是又開始寫一個新的消息機(jī)制,它的外表看起來像是 SmartWin++ 和 AWT 的混血兒。 

              SmartWin++ 的想法極富創(chuàng)意,但是其實現(xiàn)卻不怎么漂亮,就像魯迅先生說的:離的越近,傷痕和不足越容易看見?;蛘吒鼘I(yè)一點,借用一個朋友的 QQ 簽名說的:每個整潔的接口后面,都有一個齷齪的實現(xiàn)。仔細(xì)看過它的的齷齪實現(xiàn)之后,我想大概是永遠(yuǎn)不會用它寫程序了,不過同樣要對作者的靈感表示敬佩和感謝。自己寫這個框架到后來,竟然也有向 SmartWin++ 靠攏的趨勢。

              AWT 的接口和實現(xiàn)很漂亮,不過看它在 C++ 上的這個實現(xiàn),因為 C++ 與 java 一些語言特性上的差別,導(dǎo)致這個照搬過來的實現(xiàn)并不適用于 C++,一是性能很低,二是使用起來會比較麻煩。大概是因為作者要靠慮跨平臺的因素,所以縛手縛腳的沒有能放開寫。其實在這個基礎(chǔ)上好好地優(yōu)化改進(jìn)一下,會是個很不錯的框架。

              自己的這個框架的消息機(jī)制是介于 SmartWin++ 的 Aspects 與 AWT 的 Listener 這間的一種機(jī)制。底層實現(xiàn)很簡單,在上面再包裝出一些 Listener 。這些 Listener 的結(jié)構(gòu)看起來像 AWT 的 Listener,完成的功能卻是 SmartWin++ 的 Aspects 的功能??梢詫Ρ纫幌驴纯?/p>


            AWT ( C++的一個移植版)

              比如說在 AWT 當(dāng)中鼠標(biāo)相關(guān)的 Listener 大概是這樣的:

               1: class MouseListener : public EventListener 
               2: {
               3:  
               4: public:
               5:  
               6:     virtual void mouseClicked( MouseEvent& ) = 0;
               7:     virtual void mousePressed( MouseEvent& ) = 0;
               8:     virtual void mouseReleased( MouseEvent& ) = 0;
               9:     virtual void mouseEntered( MouseEvent& ) = 0;
              10:     virtual void mouseExited( MouseEvent& ) = 0;
              11:  
              12: };

             
              如果一個類想處理自己的鼠標(biāo)消息,則必須在這個類上派生,然后調(diào)用addListener 將自己添加進(jìn) Listeners 當(dāng)中即可。比如說要寫一個按鈕類 Button* button,它要處理自己的鼠標(biāo)按下的消息,代碼大概會是這個樣子:

               1: class Button:public Component,
               2:             ,public MouseListener  // 從 MouseListener 接口派生
               3:             ,public SomethingElse
               4: {
               5:     Button()
               6:     {
               7:         addMouseListener( this );
               8:  
               9:         // do something else
              10:     }
              11:  
              12:     virtual void mousePressed( MouseEvent& me )
              13:     {
              14:         // button is pressed
              15:         // do something
              16:     }
              17: };


              如果還有外部的類想要處理這個按鈕的鼠標(biāo)按下消息,那么這個外部的類也必須從 MouseListener 派生,然后將自己添加進(jìn)按鈕的 Listeners 當(dāng)中。代碼大概像這個樣子:

               1: // 從 MouseListener 接口派生
               2: class MouseEventTester:public MouseListener,public SomethingElse
               3: {
               4:     // 實現(xiàn) mousePreseed 函數(shù)
               5:     virtual void mousePressed( MouseEvent& )
               6:     {
               7:         // mouse is pressed
               8:         // do something;
               9:     }
              10: };
              11:  
              12: // 添加到按鈕的鼠標(biāo) Listener 列表當(dāng)中
              13:  
              14: Button* button = new Button();
              15: button->addMouseListener( new MouseEventTester() );


              AWT 都是使用虛函數(shù)實現(xiàn)的,其性能上會有一些問題,誰叫是從 java 移植的呢。而且消息處理函數(shù)的名字被限定死了,至少那些有特定名字習(xí)慣(比如很多人寫的函數(shù)非得用大寫字母開始)的程序會一定不會滿意的。另外,消息的種類也被接口限制死了,除了接口提供的 mousePressed,mouseClicked 等消息的函數(shù),要添加其它消息的處理能力就比較麻煩。


            SmartWin++

              再看看 SmartWin++ Aspect 方式。SmartWin++ 關(guān)于鼠標(biāo)消息的 Aspect 像這個樣子:

               1:  
               2: template</*一大堆模板參數(shù)*/>
               3: class AspectMouseClicks
               4: {
               5:     void onLeftMouseUp( Handler eventHandler );
               6:     void onLeftMouseUp( Handler eventHandler);
               7:  
               8:     void onLeftMouseDown( Handler eventHandler);
               9:     void onLeftMouseDown( Handler eventHandler);
              10:  
              11:     void onMouseMove( Handler eventHandler);
              12:     void onMouseMove( Handler eventHandler);
              13:  
              14:     // 其它一些鼠標(biāo)消息
              15:     // .....
              16:  
              17: protected:
              18:     virtual ~AspectMouseClicks()
              19:     {}
              20: };


              還是寫一個按鈕,它想處理自己的鼠標(biāo)按下消息,代碼大概像這樣:

               1: class Button:public AspectMouseClicks</*一大堆參數(shù)*/>
               2: {
               3:     Button()
               4:     {
               5:         onLeftMouseUp( &Button::mousePressed );
               6:     }
               7:     
               8:     void mousePressed( MouseEvent& me )
               9:     {
              10:         // mouse is pressed
              11:         // do something
              12:     }
              13: };


              而因為 SmartWin++ 的設(shè)計上的問題,除了按鈕本身之外,僅有按鈕的祖先窗口(包括父窗口)對象也能夠處理它的鼠標(biāo)消息。比如一個窗口 TestWindow,在它之上創(chuàng)建了按鈕 Button,則這個按鈕的消息要么被 TestWindow 處理,要么被自己處理,不能被其它的外部類或函數(shù)處理。TestWndow 當(dāng)中處理這個按鈕的鼠標(biāo)按下的消息的代碼大概像這樣:

               1: class TestWindow:public Something
               2: {
               3:  
               4: public:
               5:  
               6:     TestWindow()
               7:     {
               8:         Button* button = createButton();
               9:         button->onLeftMouseUp( &TestWindow::mousePressed );
              10:     }
              11:  
              12:     void mousePressed( MouseEvent& me )
              13:     {
              14:         // mouse is pressed
              15:         // do something
              16:     }
              17: };


              SmartWin++ 表面上看起來是使用的函數(shù)指針,效率應(yīng)該會比 AWT 的虛函數(shù)高一些,其它不然??纯瓷厦娴拇a,指定消息處理函數(shù)的時候,并沒有把消息處理函數(shù)的擁有者的指針傳進(jìn)去。因此在用函數(shù)指針調(diào)用函數(shù)的時候,SmartWin++ 框架為了尋找到函數(shù)的擁有者指針,用了很齷齪低效的的一個方法,也正是這樣的方法,使得父窗口類之外的其它類或函數(shù)不可能參與到消息的處理當(dāng)中來。

            自己的機(jī)制:
              
              像上面說的,自己新寫的這個消息機(jī)制介于 AWT 的 Listeners 與 SmartWin 的 Aspects 之間。這個框架當(dāng)中的MouseListener 的定義看起來像這樣:

               1: <typename TImpl>
               2: class MouseListener
               3: {
               4:  
               5: public:
               6:  
               7:     void onMouseClicked( TOwner* owner,MemberHandler handler );
               8:     void onMousePressed( TOwner* owner,MemberHandler handler );
               9:     void onMouseReleased( TOwner* owner,MemberHandler handler );
              10:     void onMouseDblclk( TOwner* owner,MemberHandler handler );
              11:     void onMouseEntered( TOwner* owner,MemberHandler handler );
              12:     void onMouseExited( TOwner* owner,MemberHandler handler );
              13:     void onMouseMoved( TOwner* owner,MemberHandler handler );
              14:  
              15: };


              有鼠標(biāo)消息的 GUI 類都已經(jīng)從這個 Listener 繼承,比如說如果要寫一個按鈕類,想要它有響應(yīng)鼠標(biāo)按下消息的能力,則代碼大概是這樣:

               1:  
               2: // 從 MouseListener 繼承
               3: class Button:public MouseListener<Button>
               4:             ,public SomethingElse
               5: {
               6:  
               7: public:
               8:  
               9:     Button()
              10:     {
              11:         onMousePressed( this,&Button::mousePressed );
              12:     }
              13:     
              14:     void mousePressed( UINT keys,POINT cursor )
              15:     {
              16:         // mouse is pressed
              17:         // do something
              18:     }
              19: };


              因為 Button 已經(jīng)從 MouseListener 派生,所以所有它的祖先窗口和父窗口都能用 MouseListener 的函數(shù) onMousePressed 來注冊自己的處理函數(shù):

               1: class TestWindow
               2: {
               3:  
               4: public:
               5:  
               6:     TestWindow()
               7:     {
               8:         Button* button = new Button();
               9:         button->onMousePressed( this,&TestWindow::mousePressed );
              10:     }
              11:  
              12:     void mousePressed( Widget* source,UINT keys,POINT cursor )
              13:     {
              14:         // mouse is pressed
              15:         // do something
              16:     }
              17: };


              需要注意的,這個 mousePressed 函數(shù)多了一個 Widget* 類型的 source 參數(shù),以便在一個函數(shù)處理多個 GUI 對象的鼠標(biāo)消息的時候,用來區(qū)別鼠標(biāo)消息是來自哪一個 GUI 對象。因為在按鈕類當(dāng)中,知道是處理的自己的消息,所以就不需要這樣一個額外的參數(shù)。

              到目前為止看起來好像都跟 SmartWin++ 沒有多大的區(qū)別。不過這個框架允許任何類甚至全局函數(shù)也能處理任何 GUI 對象對外公開的消息。比如說有一個額外的類想處理上面那個按鈕的鼠標(biāo)按下消息,其代碼大概像這樣:

               1: // 從 MouseListener 接口派生
               2: class MouseEventTester:public MessageListener
               3: {
               4:  
               5: public:
               6:  
               7:     void mousePressed( Widget* source,UINT keys,POINT cursor )
               8:     {
               9:         // mouse is on source,and it is pressed
              10:         // do something;
              11:     }
              12: };
              13:  
              14:  
              15: MouseEventTester* tester = new MouseEventTester();
              16:  
              17: // 添加到按鈕的鼠標(biāo) Listener 列表當(dāng)中
              18: Button* button = new Button();
              19: button->addListener( &tester );


              這個類不用派生自 MouseListener,而是直接派生自了底層的 MessageListener(其實 MessageListener 對外只提供了一個函數(shù) handleMessage)

              除了上面的方式,也可以這樣干,更簡單一些,甚至不用任何派生。

               1: // 從 MouseListener 接口派生
               2: class MouseEventTester
               3: {
               4:  
               5: public:
               6:  
               7:     void mousePressed( Widget* source,UINT keys,POINT cursor )
               8:     {
               9:         // mouse is on source,and it is pressed
              10:         // do something;
              11:     }
              12: };
              13:  
              14:  
              15: MouseEventTester* tester = new MouseEventTester();
              16:  
              17:  
              18:  
              19:  
              20: // 直接添加消息處理函數(shù)
              21: Button* button = new Button();
              22: button->onMousePressed( tester,&MouseEventTester::mousePressed );


              也提供對全局函數(shù)的支持比如:

               1:  
               2: // 全局函數(shù)
               3: void mousePressed( Widget* source,UINT keys,POINT cursor )
               4: {
               5:     // mouse is on source,and it is pressed
               6:     // do something
               7: }
               8:  
               9:  
              10: // 直接添加為消息處理函數(shù)
              11: Button* button = new Button();
              12: button->onMousePressed( &::mousePressed );


              這個版本的消息機(jī)制雖然不如上一個消息機(jī)制方便,需要手動地消息映射,不過極大地增加了靈活性,消除了代碼膨脹的問題。

              下面是一個完整的測試程序代碼:

               1: // cexer
               2: #include "../../cexer/include/GUI/panel.h"
               3: #include "../../cexer/include/GUI/window.h"
               4: #include "../../cexer/include/GUI/button.h"
               5: #include "../../cexer/include/GUI/checkbox.h"
               6: #include "../../cexer/include/GUI/radiobox.h"
               7: #include "../../cexer/include/GUI/GUI.h"
               8:  
               9: using namespace cexer;
              10: using namespace cexer::gui;
              11: using namespace cexer::gdi;
              12:  
              13: // c++ std
              14: #include <iostream>
              15: using namespace std;
              16:  
              17:  
              18: class TestWindow:public Window
              19: {
              20:  
              21: public:
              22:  
              23:     TestWindow( ):Window( NULL,_T("test window") )
              24:     {
              25:         onCreated( this,&TestWindow::windowCreated );
              26:         onClosing( this,&TestWindow::windowClosing );
              27:         onDestroy( this,&TestWindow::windowDestroy );
              28:         onResized( this,&TestWindow::windowResized );
              29:         onErasing( this,&TestWindow::windowErasing );
              30:  
              31:         onMouseClicked( this,&TestWindow::mouseClicked );
              32:         onMouseEntered( this,&TestWindow::mouseEntered );
              33:         onMouseExited(  this,&TestWindow::mouseExited );
              34:         onMousePressed( this,&TestWindow::mousePressed );
              35:         onMouseReleased( this,&TestWindow::mouseReleased );
              36:     }
              37:  
              38: public:
              39:  
              40:     LRESULT windowCreated( Widget*,CREATESTRUCT& )
              41:     {
              42:         wcout<<L"創(chuàng)建成功!"<<endl;
              43:  
              44:         Panel* panel = new Panel( this,_T("panel") );
              45:         panel->create( _T(""),10,10 );
              46:         panel->onResized( this,&TestWindow::windowResized );
              47:  
              48:         Button* button = new Button( panel,_T("button") );
              49:         button->create( _T("測試按鈕"),10,10 );
              50:         button->onClicked( this,&TestWindow::buttonClicked );
              51:  
              52:         Checkbox* checkbox = new Checkbox( panel,_T("checkbox") );
              53:         checkbox->create( _T("測試多選框"),10,40 );
              54:         checkbox->onClicked( this,&TestWindow::buttonClicked );
              55:  
              56:         
              57:         Radiobox* radiobox = new Radiobox( panel,_T("radiobox") );
              58:         radiobox->create( _T("測試單選框"),10,70 );
              59:         radiobox->onClicked( this,&TestWindow::buttonClicked );
              60:  
              61:         return 0;
              62:     }
              63:  
              64:     LRESULT windowDestroy( Widget* )
              65:     {
              66:         wcout<<L"銷毀成功!"<<endl;
              67:         ::PostQuitMessage(0);
              68:  
              69:         return 0;
              70:     }
              71:  
              72:     LRESULT windowClosing( Widget* )
              73:     {
              74:         if ( IDNO == confirmBox(_T("確定關(guān)閉窗口?")) )
              75:         {
              76:             return 0;
              77:         }
              78:  
              79:         destroy();
              80:  
              81:         return 0;
              82:     }
              83:  
              84:     LRESULT windowErasing( Widget*,Canvas& canvas )
              85:     {
              86:         canvas.fillRect( clientBounds() );
              87:         canvas.drawText( 100,100,_T("測試窗口") );
              88:  
              89:         return 0;
              90:     }
              91:  
              92:     LRESULT windowResized( Widget* source,UINT,SIZE size )
              93:     {
              94:         if ( source->name() == m_name )
              95:         {
              96:             long panelWidth  = ( size.cx-20 );
              97:             long panelHeight = ( size.cy-20 );
              98:  
              99:             resizeChild( _T("panel"),panelWidth,panelHeight );
             100:         }
             101:         else if ( source->name() == _T("panel") )
             102:         {
             103:             long childWidth  = ( size.cx-20 );
             104:             long childHeight = 20;
             105:  
             106:             resizeChild( _T("button")  ,childWidth,childHeight );
             107:             resizeChild( _T("checkbox"),childWidth,childHeight );
             108:             resizeChild( _T("radiobox"),childWidth,childHeight );
             109:         }
             110:         
             111:         return 0;
             112:     }
             113:  
             114:     LRESULT buttonClicked( Button* button )
             115:     {
             116:         messageBox( button->text() + _T("被點擊了!") );
             117:         return 0;
             118:     }
             119:  
             120:  
             121:     LRESULT mouseClicked( Widget*,UINT,POINT cursor)
             122:     {
             123:         wcout<<L"鼠標(biāo)點擊\t"<<L"位置";
             124:         wcout<<L"("<<cursor.x<<L","<<cursor.y<<L")"<<endl;
             125:         return 0;
             126:     }
             127:  
             128:     LRESULT mouseEntered( Widget*,UINT,POINT cursor )
             129:     {
             130:         wcout<<L"鼠標(biāo)進(jìn)入\t"<<L"位置";
             131:         wcout<<L"("<<cursor.x<<L","<<cursor.y<<L")"<<endl;
             132:         return 0;
             133:     }
             134:  
             135:     LRESULT mouseExited( Widget*,UINT,POINT cursor )
             136:     {
             137:         wcout<<L"鼠標(biāo)離開\t"<<L"位置";
             138:         wcout<<L"("<<cursor.x<<L","<<cursor.y<<L")"<<endl;
             139:         return 0;
             140:     }
             141:  
             142:     LRESULT mousePressed( Widget*,UINT,POINT cursor )
             143:     {
             144:         wcout<<L"鼠標(biāo)按下\t"<<L"位置";
             145:         wcout<<L"("<<cursor.x<<L","<<cursor.y<<L")"<<endl;
             146:         return 0;
             147:     }
             148:  
             149:     LRESULT mouseReleased( Widget*,UINT,POINT cursor )
             150:     {
             151:         wcout<<L"鼠標(biāo)釋放\t"<<L"位置";
             152:         wcout<<L"("<<cursor.x<<L","<<cursor.y<<L")"<<endl;
             153:         return 0;
             154:     }
             155:  
             156: };
             157:  
             158:  
             159:  
             160: int _tmain( int argc,TCHAR** argv )
             161: {
             162:     CEXER_GUI_THREAD();
             163:  
             164:     wcout.imbue( std::locale("") );
             165:  
             166:     TestWindow* window = new TestWindow();
             167:     window->create();
             168:  
             169:     return runGUIthread();
             170: }

            Feedback

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-08-19 12:37 by Touchsoft
            只看了開頭的鼠標(biāo)消息定義
            如果一個類想處理自己的鼠標(biāo)消息,則必須在這個類上派生,然后調(diào)用addListener
            像Decorator Pattern。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-08-19 14:03 by 空明流轉(zhuǎn)
            函數(shù)指針比虛方法更慢。。。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-08-19 16:11 by cexer
            @空明流轉(zhuǎn)
            的確是半斤八兩,不過虛函數(shù)還得查一下虛表,生成的匯編代碼會有多幾個寄存器操作,而靜態(tài)的函數(shù)指針是直接call地址的。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-08-20 01:24 by 陳梓瀚(vczh)
            很好,已經(jīng)快接近我那個了……

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-08-24 16:06 by dell
            每個整潔的接口后面,都有一個齷齪的實現(xiàn)。真是深刻。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2008-12-22 01:45 by null
            to cexer

            你都是怎么看源代碼的 我也下了個超小型的GUI庫 不知道從哪看起

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2009-10-12 00:56 by XML
            請教一下,你的MemberHandler 是怎么定義的?是如何讓onXXEvent接收任意類的成員函數(shù)的?

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2009-11-16 00:57 by 暗涌
            消息處理函數(shù)有點像C#中的EventHandler,總是把消息的發(fā)送者傳給處理函數(shù)。把發(fā)送者類型放到模板參數(shù)里是個不錯的辦法,有點像Singleton的模板。。。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2009-11-17 17:13 by cexer
            @null
            讀書千遍其義自現(xiàn)啊,最主要是悶著看代碼。
            從示例代碼,最高層開始往底層追溯

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2009-11-17 17:13 by cexer
            @XML
            那個代碼找不到了,不過實現(xiàn)還記得,以后會寫一下實現(xiàn)。

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2009-11-17 17:14 by cexer
            @暗涌
            嗯呵呵

            # re: 拋棄了上一個 GUI 消息機(jī)制,重寫了一個更靈活高效的  回復(fù)  更多評論   

            2010-02-22 21:01 by jom
            能寫自己的GUI框架,不錯啊,呵呵。
            亚洲色欲久久久综合网东京热| 青青草原综合久久大伊人导航| 久久久久人妻精品一区 | 久久久久女教师免费一区| 国产精品伊人久久伊人电影| 久久精品无码一区二区app| 国产精品久久久久久久久久影院| 久久人做人爽一区二区三区| 97久久超碰国产精品2021| 久久无码一区二区三区少妇| 亚洲精品乱码久久久久66| 久久夜色精品国产欧美乱| 精品欧美一区二区三区久久久| 中文字幕无码久久久| 精品国产乱码久久久久久1区2区 | 无码专区久久综合久中文字幕 | 国产精品美女久久久久AV福利| 三级韩国一区久久二区综合| 久久国产欧美日韩精品| 久久免费99精品国产自在现线| 久久无码人妻一区二区三区| 久久久久黑人强伦姧人妻| 久久精品www人人爽人人| 武侠古典久久婷婷狼人伊人| aaa级精品久久久国产片| 一本一道久久a久久精品综合 | 国产 亚洲 欧美 另类 久久| 亚洲国产精品无码久久| 日本高清无卡码一区二区久久| 99久久免费国产精品热| 99久久香蕉国产线看观香| 国产真实乱对白精彩久久| 91久久婷婷国产综合精品青草| 伊人久久大香线蕉综合网站| 2020最新久久久视精品爱| 色欲综合久久躁天天躁蜜桃| 中文字幕无码久久精品青草| 久久成人国产精品一区二区| 青青青国产精品国产精品久久久久| 一本久久a久久精品亚洲| 无码精品久久一区二区三区|