一個簡單的UI框架思想

嵌牛導(dǎo)讀:寫了這么久,終于接觸到UI了。在我的印象中UI的因果邏輯我完全不懂。在《跟我一起學(xué)C++第三季》中接觸了一個非常簡陋的UI框架,然后結(jié)合最近學(xué)習(xí)wxWidgets的經(jīng)驗(yàn),所以想純靠偽代碼寫一下UI框架干的事情。

嵌牛鼻子:UI框架

嵌牛提問:UI框架window之間的通信如何進(jìn)行的

嵌牛正文:

事件驅(qū)動

本UI是事件驅(qū)動模型。什么是事件驅(qū)動模型呢?實(shí)際上是比較抽象的。其實(shí)就和Reactor模式一樣。當(dāng)我們移動鼠標(biāo),或者點(diǎn)擊鼠標(biāo),鍵盤輸入。就會產(chǎn)生一個事件。我們針對這些事件寫好對應(yīng)的eventHandler。當(dāng)事件一個一個發(fā)生的時候,eventHandler就一個一個調(diào)用。所以是沒有涉及多線程,因此如果某個eventHandler比較耗時,就會導(dǎo)致我們感覺起來很卡。實(shí)際上就是因?yàn)槠渌僮鬟€在排隊(duì),并沒有被調(diào)用。

Application 代表運(yùn)行的程序,其邏輯很簡單。

while(1)

{

? ? // 當(dāng)有事件到來的時候被喚醒,

? ? eventID = getEventID();

? ? switch(eventID)

? ? {

? ? case ButtonClicked:

? ? ? ? eventHandler();

? ? break;

? ? case ...

? ? }

}

Application的實(shí)現(xiàn)一般為單例模式,因?yàn)檫\(yùn)行的程序只有一個。

App單例的實(shí)現(xiàn)

WindowBase

我們需要將圖形繪制到屏幕上。而屏幕是由一個一個像素點(diǎn)構(gòu)成的。我們繪圖的過程實(shí)際上就是向這些像素點(diǎn)填充的一個過程。有點(diǎn)小孩子在方格上填充色彩的感覺。

ScreenBuffer

因此我們就需要抽象出一個二維數(shù)組。用來代表圖形上的像素。ScreenBuffer,其是WindowBase的嵌套類。

圖形的表示

我們在坐標(biāo)軸上確定一個矩形需要倆個東西。

圖形的起始坐標(biāo)?!狿osition

圖形的寬度和高度?!猄ize

這樣我們就知道了我們的圖形是要畫在哪個位置。

舉個栗子:

比如說我們當(dāng)前的畫布是高25。寬80。由25 * 80個像素點(diǎn)構(gòu)成我們的畫布。

當(dāng)我們想要畫一條線的時候。我們給定起始點(diǎn)(0,4)。

for(int i = 0; i < 80; ++i)

? ? buffer[4][i] = '-'

這樣我們就在buffer中畫出了一條線。而buffer常常也被我們稱為邏輯屏幕。如果需要其呈現(xiàn)到實(shí)際屏幕上,只需要調(diào)用相關(guān)的接口(暫定為refresh)將buffer中的數(shù)據(jù)刷新到屏幕上即完成繪圖。

便利的接口

我們就可以提供一些便利的接口給派生類。

DrawHLine(startX, startY, length);

DrawVLine(startX, startY, length);

FillReact(startX, startY, width, length);

Write(char)

Write(string)

refresh()

Window

Window類用來抽象窗口。我們看到的一個Button,一個Label,一個子窗口都是窗口。Window是繼承自WindowBase的。因?yàn)槊總€Window都需要自己的圖形。所有都會有自己的Draw方法。

可能的派生類

Button

Label

繼承Window的子類有很多。它們之間的最大區(qū)別在于繪制自己的圖形方式不同。從而呈現(xiàn)出不同的樣子。

Window標(biāo)識

對于不同的Window或者說控件來說。我們都需要一個標(biāo)識。標(biāo)識主要是用來關(guān)聯(lián)eventHandler的。

比如說。

在Exit和About這倆個Button上。我們發(fā)生ButtonClicked事件的eventHandler肯定是不同的。但事件類型時一樣的。

所以我們的回調(diào)函數(shù)不僅僅需要確定發(fā)生事件的類型,同時還需要確定發(fā)生事件的Window。

Window之間的關(guān)系

以一個一般的窗口為例。我們總是有一個root窗口。這個窗口代表著這個程序,關(guān)閉這個窗口的同時也會導(dǎo)致程序的退出。

root窗口下有很多子窗口。子窗口下又有很多子窗口。關(guān)閉一個窗口會導(dǎo)致所有子窗口的關(guān)閉。不難明白窗口之間的關(guān)系是呈現(xiàn)樹狀關(guān)系。

Window之間的通信

這里直接舉了wxWidget上的例子。

可以看到我們總共有5個Window。

TotalWindow

LeftWindow

PlusWindow(實(shí)際上是PlusButton,Button類繼承自Window)

MinusWindow

RightWindow

當(dāng)我們點(diǎn)擊+號的時候,我們希望右側(cè)的窗口的數(shù)值+1。這該怎么通信呢?

我的第一反應(yīng)是去做一個eventfd。當(dāng)+點(diǎn)擊的時候,就向eventfd寫一個char。然后RightPanel注冊了一個事件。但是想想就覺的麻煩。

實(shí)際上我們可以直接利用樹裝關(guān)系。通過Parent來獲得RightPanel。

大致的偽代碼思路如下。

LeftPanel::OnPlus()

{

? ? count++;

? ? p = getParent();

? ? p->rPanel->text = count;

? ? p->rPanel->refresh();

}

Event

事件類。我覺的是最好寫的。

eventID用來標(biāo)識事件的類型。

eventHandler事件的回調(diào)函數(shù)要準(zhǔn)備好。

因?yàn)槭录|發(fā)的時候,大部分時候會重繪Window,所以我們需要讓事件保留Window的指針。

總體關(guān)系如下:

一個RootWindow可以有無數(shù)的childWindow。每個Window可以綁定無數(shù)個事件。每個事件只能關(guān)聯(lián)到一個Window。

————————————————

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容