本教程將經過一個簡單的仿瀏覽器界面的程序,向你介紹關於構建圖形界面程序的基礎知識,掌握這些知識後,你將會對圖形界面開發有更加深入的理解。css
你能夠提早預覽咱們要寫的程序的最終效果,它的源代碼已經上傳到了 GitHub 和碼雲上,你能夠試着下載、編譯和運行它。若是你看不懂其中的代碼,或不知道它是被如何設計出來的,別擔憂!接下來的教程會一步一步幫助你理解圖形界面程序的開發方式。node
相較於其它開發庫或框架的示例程序而言,咱們開發的程序更加複雜一些,所以,咱們有必要在在開發前花點時間思考這個程序具體須要什麼功能,又該怎麼實現這些功能。git
以 Chrome 瀏覽器爲參考對象,咱們須要實現如下功能:github
示例頁面:因爲咱們開發的程序側重點在圖形界面開發上,所以須要添加幾個示例頁面做爲瀏覽對象,來模擬實現 Chrome 瀏覽器的頁面瀏覽功能。示例頁面有:npm
咱們的程序的圖形界面須要包含如下元素:編程
因爲咱們開發的程序的大部分源代碼都是用於實現圖形界面的,所以,這裏主要講述圖形界面上的設計與實現方法。瀏覽器
在研究過 Chrome 瀏覽器的界面後,咱們能夠知道它的界面結構以下:sass
主界面bash
考慮到每一個標籤頁都有本身的選項卡、導航欄和內容區,咱們應該將導航欄和內容區域移入到標籤頁中,結果以下圖所示:網絡
那麼,咱們須要開發的組件有以下幾個:
按照常規作法,把標籤頁拆分紅導航欄和內容區彷佛會更好一些,但考慮到組件之間的通訊成本和開發成本,組件化到標籤頁已經足夠,由於標籤頁除了導航欄外已經沒有值得組件化的東西了。選項卡欄亦是如此。
接下來咱們再試着找出界面元素的排列規則:
如上圖所示,紅色邊框標記的元素是橫向佈局,而綠色邊框標記的元素則是縱向佈局。從中可知,主界面佈局方向爲縱向,選項卡欄和標籤頁從上到下排列,選項卡欄高度固定,標籤頁面高度自動佔滿剩餘空間。標籤頁內的導航欄和內容區也是如此。導航欄中的元素佈局方向爲橫向,按鈕寬度固定,地址欄輸入框的寬度自動佔滿剩餘空間。
如何存儲多個標籤頁的數據?
正常狀況下,標籤頁數量是比較少的,對讀寫性能要求很低,所以能夠選擇用鏈表來存放。
標籤頁的數據應該包含什麼?
爲了便於管理標籤頁數據、展現頁面標題和狀態、實現組件間的通訊,標籤頁的數據應該包含編號、標題、狀態、標籤頁和主界面的 UI 元素對象的引用。僞代碼以下:
struct Page { number id; string title; boolean loading; UIElement tab; UIElement frame; UIElement browser; };
在準備開發前,咱們能夠花一些時間調查和研究開源社區上是否有其餘開發者開發的庫可供使用,選擇合適的開發庫有助於提高開發效率和下降開發成本。
圖形界面開發庫:
LCUI,C 語言編寫的圖形界面開發庫,支持 CSS 樣式和 Flex 佈局。以它現有的功能足以實現咱們的程序的圖形界面。
路由管理器:
LCUI Router,LCUI 的官方路由管理器,用於解決 LCUI 應用內多視圖的切換和狀態管理問題。咱們能夠用它實現瀏覽器的路由導航功能。
組件庫:
LC Design,專爲 LCUI 設計的組件庫,提供基礎排版樣式、佈局系統和實用工具 CSS 類,以及圖標、按鈕、下拉菜單等組件。
開發工具:
注:這裏假設你使用的是 Windows 系統,若是不是,請自行處理環境搭建和編譯上的問題
在開發前,你須要在你的計算機上安裝如下軟件:
以後,打開命令行窗口,輸入如下命令安裝 lcui-cli 和 lcpkg:
npm install -g @lcui/cli lcpkg
設置 lcpkg,讓它安裝相關依賴工具:
lcpkg setup
使用 lcui 命令建立一個名爲 lcui-router-app 的項目:
lcui create lcui-router-app
這個命令會替你完成如下工做:
注意,受限於國內的網絡環境,部分資源下載比較慢,建議先使用如下命令添加國內源:
npm config set registry https://registry.npm.taobao.org set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/
進入項目目錄,安裝 lcui-router 和 lc-design 依賴包:
cd lcui-router-app lcpkg install github.com/lc-soft/lcui-router github.com/lc-ui/lc-design --arch x64
修改 CMakeLists.txt.in
文件,添加依賴項:
... if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") target_link_libraries( app AppUI AppUIViews AppUIComponents AppLib optimized LCUI optimized LCUIMain + optimized LCUIRouter + optimized LCDesign debug "${LCPKG_DIR}/debug/lib/LCUI.lib" debug "${LCPKG_DIR}/debug/lib/LCUIMain.lib" + debug "${LCPKG_DIR}/debug/lib/LCUIRouter.lib" + debug "${LCPKG_DIR}/debug/lib/LCDesign.lib" ) else() target_link_libraries( app AppUI AppUIViews AppUIComponents AppLib debug "${LCPKG_DIR}/debug/lib/LCUI" + debug "${LCPKG_DIR}/debug/lib/LCUIRouter" + debug "${LCPKG_DIR}/debug/lib/LCDesign" optimized LCUI + optimized LCUIRouter + optimized LCDesign ) endif(WIN32) ...
修改 app/assets/views/app.xml
文件,引入 LC Design 組件庫的 CSS 樣式文件:
<?xml version="1.0" encoding="UTF-8" ?> <lcui-app> + <resource type="text/css" src="assets/stylesheets/lc-design.css"/> <resource type="text/css" src="assets/stylesheets/app.css"/> <ui> <resource type="text/xml" src="assets/views/home.xml"/> </ui> </lcui-app>
編輯 src/ui.c
文件,添加 LC Design 組件庫的初始化代碼:
#include <LCUI.h> + #include <LCDesign.h> #include <LCUI/gui/widget.h> #include <LCUI/gui/builder.h> #include "ui/views/browser.h" #include "version.h" #include "ui.h" int UI_Init(void) { LCUI_Widget root; LCUI_Widget wrapper; LCUI_Widget browser; LCUI_Init(); + LCDesign_Init(); UI_InitComponents(); UI_InitViews(); wrapper = LCUIBuilder_LoadFile("assets/views/app.xml"); if (!wrapper) { return -1; } ...
更新構建配置:
npm run configure
運行這個項目,看看效果:
npm start
新建 config 目錄,在裏面新建一個 LCUI Router 的路由配置文件,命名爲 router.js
,內容以下:
module.exports = [ { name: 'home', path: '/', component: 'home' }, { name: 'welcome', path: '/welcome', component: 'welcome' }, { name: 'help', path: '/help', component: 'help' }, { path: '/file', component: 'file' }, { name: 'file', path: '/file/*', component: 'file' }, { path: '*', component: 'notfound' } ]
配置文件採用 JavaScript 語言來描述是爲了便於編寫和維護,免去用 C 代碼來配置 LCUI Router 的麻煩。該文件內容描述了路徑 (Path) 與組件 (Component) 的映射關係,LCUI Router 會在路由變化時渲染與路徑匹配的組件。要讓它可以在咱們的程序中生效,還須要使用如下命令將它轉譯爲 C 代碼:
lcui compile router
該命令會在 src/lib
目錄中生成 router.h
和 router.h
文件,並在 src/ui/components.c
文件中追加 router-link
和 router-view
組件的註冊代碼。
lcui-cli 提供了視圖 (View) 和組件 (Widget) 兩種生成器,視圖與組件的區別主要在於複雜度和可複用性,你能夠根據實際狀況來選擇。
首先,咱們使用如下命令添加視圖:
# 瀏覽器主界面 lcui generate view browser # 標籤頁 lcui generate view frame # 新標籤頁的引導頁面 lcui generate view home # 歡迎頁面 lcui generate view welcome # 文件頁面 lcui generate view file # 關於頁面 lcui generate view help # 404 頁面 lcui generate view notfound
而後,添加組件:
# 選項卡 lcui generate widget frame-tab
在開始前,咱們須要先刪除 app/assets/views/browser.xml
文件,由於主界面的內容都是在運行時動態生成的,並不須要藉助 xml 文件來描述。
而後,編輯 app/assets/views/app.xml
文件,添加瀏覽器視圖:
<?xml version="1.0" encoding="UTF-8" ?> <lcui-app> <resource type="text/css" src="assets/stylesheets/lc-design.css"/> <resource type="text/css" src="assets/stylesheets/app.css"/> <ui> - <resource type="text/xml" src="assets/views/home.xml"/> + <browser id="browser" /> </ui> </lcui-app>
編輯 src/ui.c
文件,添加主界面的操做代碼,讓咱們的程序在啓動時打開一個新標籤頁。
... int UI_Init(void) { LCUI_Widget root; LCUI_Widget wrapper; + LCUI_Widget browser; LCUI_Init(); LCDesign_Init(); UI_InitComponents(); UI_InitViews(); wrapper = LCUIBuilder_LoadFile("assets/views/app.xml"); if (!wrapper) { return -1; } root = LCUIWidget_GetRoot(); + browser = LCUIWidget_GetById("browser"); + BrowserView_Active(browser, BrowserView_Load(browser, "/")); + Widget_SetTitleW(root, L"Browser demo"); Widget_Append(root, wrapper); Widget_Unwrap(wrapper); return 0; }
考慮到大量的代碼片斷會影響文章的閱讀體驗,從這裏開始,咱們將盡可能只關注功能的大體實現原理,具體實現代碼請查看相關文件。
主界面有如下功能:
爲了實現與用戶的交互,咱們須要添加如下事件處理器:
主界面的功能實現後,咱們再設計它的佈局和樣式:
相關文件:
選項卡組件由圖標、文字和關閉按鈕組成,在標籤頁處於加載中狀態的時候,咱們能夠用 LC Design 組件庫提供的 Spinner 組件來表達。
該組件提供如下方法:
佈局與樣式:
相關文件:
LCUI Router 提供了 router-view
組件用於渲染與當前路由匹配的組件,咱們能夠將它做爲標籤頁的內容區域,這樣咱們只須要爲導航欄添加相應的事件處理器,而後調用 LCUI Router 的接口來實現路由導航功能。
標籤頁面的功能有:
事件處理器:
樣式與佈局:
相關文件:
新標籤頁面提供歡迎頁、關於頁、文件瀏覽頁的快捷入口,效果與 Chrome 瀏覽器一致:
編輯 app/assets/views/home.xml
文件,添加如下內容:
<?xml version="1.0" encoding="UTF-8" ?> <lcui-app> <ui> <w class=" container"> <w class="v-home__cards d-flex justify-content-center align-items-center"> <router-link class="v-home__card" to="/welcome"> <icon class="v-home__card-icon text-pink" name="emoticon-cool-outline" /> <text class="v-home__card-title">Welcome!</text> </router-link> <router-link class="v-home__card" to="/file"> <icon class="v-home__card-icon text-orange" name="folder-search" /> <text class="v-home__card-title">File</text> </router-link> <router-link class="v-home__card" to="/help"> <icon class="v-home__card-icon text-blue" name="help-box" /> <text class="v-home__card-title">About</text> </router-link> </w> </w> </ui> </lcui-app>
編輯 src/ui/views/home.c
文件,用如下內容覆蓋:
#include <LCUI.h> #include <LCUI/gui/widget.h> #include <LCUI/gui/builder.h> #include <LCUI/timer.h> #include "home.h" static LCUI_WidgetPrototype home_proto; static void HomeView_OnTimer(void *arg) { LCUI_WidgetEventRec e; LCUI_InitWidgetEvent(&e, "PageLoaded"); e.cancel_bubble = FALSE; Widget_TriggerEvent(arg, &e, NULL); } static void HomeView_OnInit(LCUI_Widget w) { LCUI_Widget wrapper; wrapper = LCUIBuilder_LoadFile("assets/views/home.xml"); if (wrapper) { Widget_Append(w, wrapper); Widget_Unwrap(wrapper); } Widget_AddData(w, home_proto, 0); Widget_AddClass(w, "v-home"); Widget_SetTitleW(w, L"New tab"); LCUI_SetTimeout(0, HomeView_OnTimer, w); } void UI_InitHomeView(void) { home_proto = LCUIWidget_NewPrototype("home", NULL); home_proto->init = HomeView_OnInit; }
注:該頁面的代碼一樣適用於關於頁面、歡迎頁面、404 頁面,在下面的教程中將再也不重複說明,你只須要複製粘貼這段代碼到對應的文件而後作點修改便可。
新標籤頁面由 home
組件呈現,與該組件綁定的路徑是 /
,當導航到該路徑時 LCUI Router 會將新標籤頁面掛載到 router-view
組件內。
爲了通知主界面當前頁面已經加載完成,咱們在 home
組件初始化時建立一個定時器,等下一幀更新開始時觸發 PageLoaded 事件。若是你想讓選項卡中的 Spinner 組件的轉圈動畫多轉一段時間,能夠將定時器的超時時間設長一點。
樣式與佈局:
相關文件:
其它示例頁面的內容都是靜態的,若是咱們開發的程序的功能只是在這些頁面之間切換的話,未免有些過於簡單,爲了進一步展現圖形界面編程的例子,並充分利用 LCUI Router 的特性,咱們有必要開發一個更爲複雜的頁面,而文件瀏覽頁面剛好是最爲合適的選擇。
以 Chrome 瀏覽器的文件瀏覽頁面做爲參考對象:
頁面標題包含了當前目錄的路徑,文件列表項由圖標、文件名、大小和日期組成,在點擊文件名後會跳轉到該文件路徑,若是是目錄,則會渲染該目錄的文件列表。
對於文件列表的呈現,咱們能夠在視圖初始化後遍歷當前路徑下的文件列表,而後獲取它們的名稱、大小和修改日期。因爲文件操做比較耗時且容易阻塞 UI 線程而致使界面卡頓,咱們應該將這些操做放在工做線程上執行,等文件列表生成後再轉到主線程上渲染。
文件名點擊跳轉功能能夠用 LCUI Router 提供的 router-link
組件來實現。在構造文件列表項時,構造 router-link
組件來呈現文件名,而後將它的 to
屬性設爲目錄路徑,這樣在點擊文件名後 router-link
組件會自動導航到該路徑,從而使得位於標籤頁內容區內的 router-view
組件響應路由更新並從新渲染文件瀏覽頁面。
文件瀏覽頁面的功能有:
佈局與樣式:
相關文件:
Chrome 瀏覽器的關於頁面展現了它的名稱、版本號、幫助連接、版權聲明:
lcui-cli 爲咱們建立的項目已經預置了用於展現程序信息的 about 組件,咱們只須要在 help.xml 文件中引入它便可,代碼以下:
<?xml version="1.0" encoding="UTF-8" ?> <lcui-app> <ui> <w class="container"> <text class="v-help__title">About this app</text> <about /> </w> </ui> </lcui-app>
相關文件:
歡迎頁面和 404 頁面是純信息展現頁面,與其它頁面相似,內容可由你自由發揮,這裏就再也不詳細說明了。
至此,你已經完成了第一個基於 LCUI 的圖形界面程序!你已經瞭解了 LCUI 的基礎知識:組件、SCSS 和 XML 語法。你還學習了多個視圖如何切換,以及組件如何相互通訊。
接下來咱們運行如下命令來更新構建配置:
npm run configure
構建並運行程序,體驗一下最終效果。
npm start
若是一切正常的話,程序的運行效果會是這樣:
若是你發現這篇文章有錯別字、病句,或者某些內容使人費解,能夠在 GitHub 上改進此文章。