r2 - 05 May 2010 - 22:25:30 - MingmingKillerYou are here: TWiki >  Columns Web > MingmingKillerArticle9

MiniGUI修改心得2

作者:胡明明

邮箱:mingming_killer@126.com

在使用MiniGUI的时候总有有些功能MiniGUI没有的,或者是MiniGUI的某些功能不太适用的。这就需要我们开发人员动手稍微改一下MiniGUI了。这次我遇到的情况就是非模式对话框要实现一些模式对话框的功能。希望能对大家以后遇到同样问题的时候会有所帮助。我使用的MiniGUI版本是2.0.4,thread模式。

一、问题来源

模式对话框(model dialog)和非实模式对话框(modeless dialog),我相信大家对它们的概念应该是比较清楚了的。MiniGUI中也提供这2种对话框的支持。不过前段时间客户有要求,说我们的产品的某个界面要能像window的那样用键盘能操作:

1:tab、left、right、up、down遍历控件;

2:按回车可以点击按钮;

3:按esc可以发送IDCANCEL;

不过这边这个界面是用非模式对话框做的,如果改成模式对话框当然就可以了。但是不能这个干,因为一个地方同时需要操作2个窗口,所以这些对话框必须是非模的。这下问题就来了,既不能用模式对话框,但又要有模式对话框的部分功能。

二、解决思路

其实呢,这个问题是还是比较好解决的。首先让我们来研究下为什么模式对话框能有上面所说的那些功能呢。大家有没有发现,在MiniGUI中不管是什么样的窗口,都会有一个MiniGUI默认的窗口过程处理函数:

// 普通窗口、非模式对话框:
int DefaultMainWinProc (hDlg, message, wParam, lParam);

// 模式对话框:
int DefaultDialogProc (hDlg, message, wParam, lParam);

// 控件:
int DefaultControlProc (hCtrl, message, wParam, lParam);

一般来说窗口过程的处理流程如下图所示:

Figure 1
图 1 窗口过程流程

MiniGUI是基于消息响应机制的GUI,我们在使用模式对话框的时候,就算我们没有响应任何按键相关的消息,模式对话框都能具有上述的那些功能,这就说明了是这些默认的窗口过程处理函数帮我们实现模式对话框的这些功能。同时也说明了非模式对话框的默认窗口过程处理函数没有帮我们实现这些功能。这下我们就有解决思路了:在非模式对话框的窗口过程处理函数中加上类似模式对话框中的相应处理代码。

三、实现方法

其实上述功能都是相应按键消息(MSG_KEYDOWN)的相关功能。功能(需求)既然已经清楚了,当然可以自己写相应的代码。不过我觉得这样比较麻烦,因为MiniGUI的模式对话框中已经有这些功能的实现代码了,而且是飞漫经过测试发放出来的,一般来说比自己写的要好些。所以我决定只是简单把模式对话框处理函数中的相关代码复制到非模式对话框的处理函数中去。

OK,先来让我们看看这段代码的庐山真面目吧。首先我们的线索是DefaultDialogProc() 这个函数,因为这个我们供我们外面应该程序调用的模式对话框的默认处理函数。然后在MiniGUI的源代码中顺藤摸瓜可以发现,这个函数在内部的实现是:libminigui/src/gui/Dialog.c 中的:

int GUIAPI PreDefDialogProc? (HWND hWnd, int message, WPARAM wParam, LPARAM lParam);

这个函数里面就是我们熟悉的 switch-case 的消息处理过程了,然后找到 case MSG_KEYDOWN 这一段,好好看看,就会为什么模式对话框能有以上的那些功能了。然后让我们找到非模式对话框的默认过程处理函数。有了上面的经验,找到这个应该很轻松了。这个的实现是在:libminigui/src/gui/window.c 中的:

int PreDefMainWinProc? (HWND hWnd, int message, WPARAM wParam, LPARAM lParam);

这个函数虽然风格和一般的 switch-case 不太一样,但是原理是一样的。我们看完后,发现 MSG_KEYDOWN 中没有类似模式对话框那些代码,所以当然也就没有模式对话框的那些功能啦。 这里我们只要简单的把模式话框处理函数中MSG_KEYDOWN 那一段中相应的代码复制到非模式对话框中的 MSG_kEYDOWN 即可。但是我不推荐这么做。第一,这样需要重新编译MiniGUI的lib库,麻烦。第二,这样外面应该程序所有的非模式对话框和普通窗口都有这些功能了(说不定有些不一定想要这些功能呢)。

我的做法是在外面修改,而不是改MiniGUI内部的源代码。这样可以同时避免以上2个问题。还记得我们写的模式对话框过程处理函数么,最后是由我们应该程序来调用 DefaultMainWinProc? () 的,这样的话,我们可以调用我们自己写的非模式对话框窗口过程处理函数。在需要模式对话框的功能的时候,我们就调用自己写的那个;在不需要这些功能的时候,就调用MiniGUI原来的那个。 下面就是我自己照搬MiniGUI模式对话框的非模式对话框处理函数代码:

int AppDefaultWindowProc (HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
        switch (message)
        {
                case MSG_KEYDOWN:
                {	
                        // 获取当前处于焦点状态的子控件
                        HWND hCurFocus = GetFocusChild(hDlg);
                        
                        // 如果当前处于焦点状态的子控件要求处理
                        // 所有的按键响应消息则不退出处理
                        if ( (hCurFocus) && (SendMessage(hCurFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_WANTALLKEYS) )
                        break;
                        
                        
                        switch (wParam) 
                        {
                                // Enter 键响应
                                case SCANCODE_KEYPADENTER:
                                case SCANCODE_ENTER:
                                { 
                                        HWND hDef;
                                        
                                        // 如果当前焦点控件要求处理Enter 键则不处理
                                        if ( (hCurFocus) && (SendMessage(hCurFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_WANTENTER) )
                                        break;
                                        
                                        // 如果当前处于焦点的不是默认按钮则不处理
                                        if ( SendMessage(hCurFocus, MSG_GETDLGCODE, 0L, 0L) & DLGC_PUSHBUTTON )
                                        break;
                                        
                                        // 向默认按钮发送被按下消息
                                        hDef = GetDlgDefPushButton (hDlg);
                                        if (hDef) 
                                        {
                                                SendMessage (hDlg, MSG_COMMAND, 
                                                GetDlgCtrlID(hDef), 0L);
                                                return 0;
                                        }
                                        
                                        break;
                                }
                                
                                
                                // Esc 键响应
                                case SCANCODE_ESCAPE:
                                {
                                        SendMessage (hDlg, MSG_COMMAND, IDCANCEL, 0L);
                                        return 0;
                                }
                                
                                
                                // Tab 键响应
                                case SCANCODE_TAB:
                                {
                                        HWND hNewFocus;
                                        
                                        // 如果当前焦点控件要求处理tab 键则不处理
                                        if ( (hCurFocus) && (SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_WANTTAB) )
                                        break;
                                        
                                        // shift+tab: 向前遍历
                                        if (lParam & KS_SHIFT)
                                        hNewFocus = GetNextDlgTabItem (hDlg, hCurFocus, TRUE);
                                        // tab: 向后遍历
                                        else
                                        hNewFocus = GetNextDlgTabItem (hDlg, hCurFocus, FALSE);
                                        
                                        if (hNewFocus != hCurFocus) 
                                        {
                                                SetNullFocus (hCurFocus);
                                                SetFocus (hNewFocus);
                                        }
                                        
                                        return 0;
                                }
                                
                                
                                // 上, 下, 左, 右键响应
                                case SCANCODE_CURSORBLOCKUP:
                                case SCANCODE_CURSORBLOCKLEFT:
                                case SCANCODE_CURSORBLOCKDOWN:
                                case SCANCODE_CURSORBLOCKRIGHT:
                                {
                                        HWND hNewFocus;
                                        
                                        // 如果当前焦点控件要求处理箭头按键则不处理
                                        if ( (hCurFocus) && (SendMessage(hCurFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_WANTARROWS) )
                                        break;
                                        
                                        // 右, 下: 向后遍历
                                        if ( (SCANCODE_CURSORBLOCKDOWN == LOWORD(wParam)) || 
                                        (SCANCODE_CURSORBLOCKRIGHT == LOWORD(wParam)) )
                                        hNewFocus = GetNextDlgGroupItem (hDlg, hCurFocus, FALSE);
                                        // 左, 上: 向前遍历
                                        else
                                        hNewFocus = GetNextDlgGroupItem (hDlg, hCurFocus, TRUE);
                                        
                                        if ( hNewFocus != hCurFocus ) 
                                        {
                                                // 如果当前焦点控件是static 时则不处理
                                                if ( SendMessage(hCurFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_STATIC)
                                                return 0;
                                                
                                                SetFocus (hNewFocus);
                                                
                                                // Radio 风格Button 控件的特殊处理
                                                if ( SendMessage(hNewFocus, MSG_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON ) 
                                                {
                                                        SendMessage (hNewFocus, BM_CLICK, 0, 0L);
                                                        ExcludeWindowStyle (hCurFocus, WS_TABSTOP);
                                                        IncludeWindowStyle (hNewFocus, WS_TABSTOP);
                                                }
                                        }
                                        
                                        return 0;
                                }
                                
                                default:
                                break;
                                
                        }  // end swtich(wParam)
                        
                        break;
                }
                
                default:
                break;
                
        }  // end swtich(message)
        
        return DefaultMainWinProc (hDlg, message, wParam, lParam);
        
}  // end AppDefaultWindowProc()

然后在应用程序中,如果有非模式对话框要实现上述功能的,就用上面这个函数替换带掉DefaultMainWinProc() 即可,像下面这样:

// XX对话框过程处理函数
static int SomeDlgProc (HWND hDlg,int message,WPARAM wParam,LPARAM lParam)
{
        switch (message)
        {
                case XX:
                break;
                
                ... ...
                
                default:
                break;
        }
        
        // return DefaultWindowProc (hDlg, message, wParam, lParam);
        return AppDefaultWindowProc (hDlg, message, wParam, lParam);
}

注:DefaultWindowProc() 对于非模式对话框来说就是调用 DefaultMainWinProc? () 的。

参考资料:飞漫MiniGUI编程指南 2.0.4

-- MingmingKiller - 14 Mar 2010

Topic attachments
I Attachment Action Size Date Who Comment
jpgjpg diy2_1.jpg manage 30.3 K 14 Mar 2010 - 17:39 MingmingKiller Figure 1
Edit | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r2 < r1 | More topic actions
 
Powered by TWiki
This site is powered by the TWiki collaboration platformCopyright © by the Feynman Software and/or the contributing authors. All material on this collaboration platform is the property of Feynman Software and/or the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback