MTK的控件和窗体绘制机制及其事件响应机制

一、entryfunction

在文档里面我们看到下面的条款:

         以下引自:write applications using pixtel MMI platform.pdf

Before display the new screen over previous screen the following must be executed:

1.          save the contents of previous screen.

保存前面那个窗体的内容;

2.          get the buffer to store the contents of screen to displayed.

获得足够的缓存空间来保存当前要显示的窗体的内容。

3.          get display attribute for the following screen, i.e. item to display as lists, circular menu etc.

获得下一个要显示的窗体的属性;

4.          retrieve number of submenu items to be displayed.

获取要显示的子菜单条目的个数;

5.          set the parent of new screen to be displayed.

设置要被显示的新窗体的父窗体;

6.          set the submenu item to be displayed highlighted, on next screen.

设置下一级子菜单要被高亮显示的条目;

7.          set the function to be executed on pressing right or left soft key.

设置按下左右软件的执行函数;

8.          set the function to be called on exiting the next screen.

设置退出下个窗体的的调用函数;

这些工作一般都在窗体的入口函数里面实现的内容,因此我们有必要先从窗体的入口函数开始。以一段代码来说明(这里省略了很多的东西):

void EntryScrIncomingOptions(void)

{      

         1、退出上一窗口,进入新的窗口

EntryNewScreen(ITEM_SCR_INCOMING_OPTIONS,NULL, EntryScrIncomingOptions, NULL);

2、获取当前窗口的GUI buffer

guiBuffer = GetCurrGuiBuffer(ITEM_SCR_INCOMING_OPTIONS);  

3、获取列表窗口的子菜单数目;

number_of_items = GetNumOfChild_Ext(MITEM_OPT_PSEUDO);

4、获取要显示的字符串序列;

GetSequenceStringIds_Ext(MITEM_OPT_PSEUDO, list_of_items);

5、设置当前窗口的父窗口的ID;

SetParentHandler(MITEM_OPT_PSEUDO);

6、注册highlight 函数

RegisterHighlightHandler(ExecuteCurrHiliteHandler);

7、在已经获取了以上信息后,绘制当前的窗口;

    ShowCategory1Screen(

        STR_SCR1002_CAPTION,

        0,

        STR_GLOBAL_OK,

        IMG_GLOBAL_OK,

        STR_GLOBAL_BACK,

        IMG_GLOBAL_BACK,

        number_of_items,

        list_of_items,

        0,

        guiBuffer);

         8、后面是注册按键函数;

    SetRightSoftkeyFunction(GoBackHistory, KEY_EVENT_UP);

    SetKeyHandler(KbCBackCallIncomingRejected, KEY_END, KEY_EVENT_DOWN);

    SetKeyHandler(KbCBackCallIncomingAccepted, KEY_SEND, KEY_EVENT_DOWN);

    SetSideVolumeKeysForCM();

    SetKeyHandler(GoBackHistory, KEY_LEFT_ARROW, KEY_EVENT_DOWN);

}

这是一个典型的窗口入口函数,窗口的进入都是通过类似的这种 entry××function() 来实现。这时引来另外一个问题,就是关于窗口切换是通过什么来实现的,怎么才能保证这些窗口在切换的过程中保持当时的状态。这就涉及到history 管理。

History 的管理是通过栈来实现的。在文档中,我们读到这样的内容:

History concept is implemented as a stack. Whenever a new history node is added to the list, it is added at the beginning and whenever a node is deleted it is deleted form the beginning.

         历史这个概念是用栈来实现的。当一个新的历史节点加入到这个列表时,它就被加到栈顶,但这个节点被删除时,它就被从栈顶删除。

一般来说历史的应用步骤是这样的:

1.        saving screen contents to history.

保存窗体的数据到历史中去。这个数据的内容将在下面提到。

2.        going back N levers in history and redrawing screen at that depth.

返回到N 级历史,并重画当时的窗体。

3.        deleting N nodes from top of stack from history.

从历史的栈顶删除N个节点。

还是这样一个函数:

[color=Blue]    void EntryFunc()

{

            EntryNewScreen( Screen_ID , Exit_Func , Entry_Func , NULL );

           guiBuffer = GetCurrGuiBuffer( SCR_ID_WORDMAIN_LIST );

              ShowCategroyXXScreen( Title_ID , … , guiBuffer);

    }[/color]

       这里无论是EntryNewScreen的调用,还是guiBuffer的传入,我们都很少考虑过对这些指针和函数在GUI的管理起到了什么样的作用。下面我们就要了解,以上的代码与History管理之间存在的关系。

    在MTK环境中,每当我们进入一个窗口,系统将先提取前一个窗口需保留的数据。这些数据包括:

1.    窗口ID ;

2.    进入窗口时调用的函数和退出调用的函数 -- Exit_Func 和 Entry_Func ;

3.    组成窗体的控件的属性(如,列表控件当前高亮显示的条目、当前屏的首末条目等)。

通过跟踪调试程序可以看到 这些动作是在每次调用 EntryNewScreen函数的时候实现的。具体的实现过程就是 :EntryNewScreen函数先将上次执行EntryNewScreen时所记录的currExitScrnID, currEntryFuncPtr以history结构为载体记录入栈;然后执行了记录中的currExitFuncPtr;最后将本窗口的 scrnID、exitFuncPtr、entryFuncPtr分别记录入全局变量currExitScrnID、currExitFuncPtr和 currEntryFuncPtr,留待下次调用EntryNewScreen时使用。在退出本窗口的时候通过GoBackHistory 出栈,返回到本窗口的父窗口。

举例说明这些数据在实际中是如何被使用的。

假设存在AB两个窗口,A窗口需要保留的数据为data_A。我们先从A窗口进入到B窗口。data_A将在B窗口调用EntryNewScreen() 的时候,被压入一个结构类似于栈的数据存储区域;当从B调用GoBackHistory()返回A时,data_A从栈顶被弹出,然后A利用data_A 将自身还原到其进入B之前的状态。

这就是History管理的作用。简言之,就是要保持窗口的外观状态。到这里,我们就回答了前面所提出的问题。

 

二、窗口的绘制


绘制窗体实际上可以看作是绘制轮廓和绘制组件两个部分。

我们先看这么一个文件 CustCoordinates.c

这个文件主要是定义了这么一个宏 g_categories_controls_map:

或者类似于这样的定义,这个数组就具体的将窗体的轮廓以及窗体的各个组件的位置作出了定义

下面我们以一个具体的例子作为说明:

const U8 category5[] =

{

    5,                                         // 这个代表组件的个数;

    DM_BASE_LAYER_START,                                     // 开始层;

    DM_SCR_BG,                                                             // 背景;

    DM_BASE_CONTROL_SET1,              //表示窗体的基本组成——状态栏、标题和软按键

    DM_MULTILINE_INPUTBOX1,                   //多行输入框

    DM_CATEGORY_CONTROLLED_AREA    // 输入法的部分;

};

这些组件被定义在枚举结构mmi_dm_control_ids_enum中。


const S16 coordinate_set5[] =

{

    DM_FULL_SCREEN_COORDINATE_FLAG,      

    DM_CONTENT_COORDINATE_FLAG,

    DM_FULL_SCREEN_COORDINATE_FLAG

};

这个数组是这些组件的属性;这个属性主要是指各个组件的座标,高度,宽度;

这些数组里面定义的内容通过dm_get_coordinates() 这个函数映射到 具体的绘制过程中;

在dm_get_coordinates 这个函数中我们可以看到,这些 DM_FULL_SCREEN_COORDINATE_FLAG常量实际上都被转化为座标。


现在我们回到刚才的那个函数 ShowCategory..Screen() 中来。下图是个典型的窗口图:


以下为例子:


void ShowCategory353Screen(

        U8 *title,

        U16 title_icon,

        U16 left_softkey,

        U16 left_softkey_icon,

        U16 right_softkey,

        U16 right_softkey_icon,

        S32 number_of_items,

        U8 **list_of_items,

        U16 *list_of_icons,

        U8 **list_of_descriptions,

        S32 flags,

        S32 highlighted_item,

        U8 *history_buffer)

{

    /*----------------------------------------------------------------*/

    /* Local Variables                                                */

    /*----------------------------------------------------------------*/

    dm_data_struct dm_data;

    S32 i;

    U8 h_flag;


    /*----------------------------------------------------------------*/

    /* Code Body                                                      */

    /*----------------------------------------------------------------*/

gdi_layer_lock_frame_buffer(); // 锁定当前层的buffer;


// 创建一个列表式的窗口

    create_fixed_icontext_menuitems();

    associate_fixed_icontext_list();

         // 并将窗口下的内容,包括标题栏,左右软件,以及各个子菜单的图标 显示出来;

    ShowListCategoryScreen(

        (UI_string_type) title,

        get_image(title_icon),

        get_string(left_softkey),

        get_image(left_softkey_icon),

        get_string(right_softkey),

        get_image(right_softkey_icon),

        number_of_items);


    if (list_of_descriptions == NULL)

{

        for (i = 0; i < number_of_items; i++)

        {

            add_fixed_icontext_item((UI_string_type) list_of_items[i], wgui_get_list_menu_icon(i, list_of_icons[i]));

            wgui_pop_up_description_strings[i].text_strings[0] = NULL;

       }

    }

    else

    {

        for (i = 0; i < number_of_items; i++)

        {

            add_fixed_icontext_item((UI_string_type) list_of_items[i], wgui_get_list_menu_icon(i, list_of_icons[i]));

            wgui_pop_up_description_strings[i].text_strings[0] = (UI_string_type) list_of_descriptions[i];

        }

}


    h_flag = set_list_menu_category_history(MMI_CATEGORY52_ID, history_buffer);


         // 高亮当前选中的ITEM

    if (h_flag)

    {

        fixed_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item);

    }

    else

    {

        fixed_list_goto_item_no_redraw(highlighted_item);

    }

    MMI_title_string = (UI_string_type) title;

    MMI_title_icon = (PU8) get_image(title_icon);

    set_pop_up_descriptions(1, number_of_items, MMI_fixed_list_menu.highlighted_item);

    gdi_layer_unlock_frame_buffer();

// 前面这整个一段是用来绘制一个列表窗口,这部分的内容基本上根据自己的选择有什么画什么;


// 而下面的内容是每个窗体都共有的部分;

    ExitCategoryFunction = ExitListCategoryScreen;

    dm_setup_category_functions(dm_redraw_category_screen,dm_get_category_history, dm_get_category_history_size);

    dm_data.s32ScrId = (S32) GetActiveScreenId();

    dm_data.s32CatId = MMI_CATEGORY52_ID;

    dm_data.s32flags = 0;

    dm_setup_data(&dm_data);

   dm_redraw_category_screen();

}   /* end of ShowCategory353Screen */


因为MTK后面的代码的gui 部分是用 draw_manager 这个来控制的,因此所有的窗口的控件的实际绘制过程都是通过 dm_redraw_category_screen(); 这个函数来实现的;这个函数可以帮助我们绘制一些比较特殊的自己需要的组件,当然如果我们使用的组件已经包含在这个函数里面,那么直接使用。

可以分析一下这个函数的流程:

获取窗体模版的ID; // 这个在showcategory里面实现,dm_data.s32CatId = MMI_CATEGORY52_ID;

                                                                      ↓

根据模版的ID得到组件的集合和个数;

// control_set_ptr = dm_search_control_set((U16) g_dm_data.s32CatId, &DeafultCoordinateSet_p);

                                                                           ↓

根据模版ID得到组件属性标识的集合;

// UICtrlAccessPtr_p = dm_search_coordinate_set(g_dm_data.s32ScrId);

                                                                           ↓

锁定当前的frame,各个组件绘制前的任务准备就绪;// gdi_layer_lock_frame_buffer();

                                                                           ↓

绘制窗体之前,还可以改变窗体的大小;

// UICtrlAccessPtr_p = dm_get_cat_scr_coordinates(UICtrlAccessPtr_p, &dm_cat_scr_info);

                                                                           ↓

获取不同组件的不同属性,并根据不同的属性绘制出自己需要的窗口;

// for (u8CtrlCt = 1; u8CtrlCt <= u8NoOfUICtrls; u8CtrlCt++) 这个语句包含的内容

                                                                           ↓

全部绘制完毕,整合GDI layer,将当前frame 解锁;


上面已经说明了一个窗体的绘制过程。另外与窗体相关的函数还有:

Redrawcategory**screen(); 这个是窗体的刷新函数;

Exitcategory**screen() ; 这个是窗体的退出函数;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章