一、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() ; 這個是窗體的退出函數;