今天要講的是GacUI裏面的三個基礎的排版功能。這些功能均可以在GacUI_Layout示例代碼裏面找到。html
大部分排版的概念都是成對出現的,譬如說git
GuiFlowComposition 和 GuiFlowItemCompositionsegmentfault
熟悉地球上最早進的GUI:WPF的朋友們可能會馬上反應過來,在WPF裏面,「右邊」的這三個類都是經過DependencyProperty來作的,因此實際上WPF只有「左邊」的這三個類。不過GacUI之因此不這麼作有,天然是由於C++的限制,不可能作出好的DependencyProperty。事實上GacUI已是我製做的第八個GUI類庫了,以前的七個都由於不一樣的緣由而失敗了,其中就有一個是用來模擬DependencyProperty的。ui
DependencyProperty的原理就是在類裏面放一張表,用來動態的查詢一個屬性的值,就跟Javascript等語言的作法同樣。可是這裏的key其實並非屬性的名字,而是用來定義DependencyProperty的那個類的名字。因此你在WPF裏面纔會用到什麼Grid.Row啊,Grid.Column這樣的名字。.net
爲何要使用這樣的名字呢?這一點就是WPF比Windows Forms先進的地方。咱們知道Windows Forms的Font屬性都是繼承的,可是這個所謂的繼承,其實是實如今了全部定義或者覆蓋了這個Font屬性的類型裏面,這是一種很是粗暴並且很容易出錯的實現方法。DependencyProperty就沒有這個問題,由於當你的屬性須要模擬這一行爲的時候,你只要看一眼發現對象裏面並無cache你的這個key和對應的值,那就直接往父對象裏面找。更改的時候,還能夠隨便廣播一個事件(固然我並無看WPF的代碼,因此我說的只是可能的一種方法)。因而全部跟這個屬性相關的邏輯就所有集中到了一塊兒。code
可是在C++裏面無法作,緣由只有一個,就是由於太慢了。不過如今GacUI其實並無什麼立場來講WPF這一點很差,由於我如今初始化窗口的時候仍是在用反射。不過我跟WPF有一點本質的區別,就是個人腳本都是靜態類型的,因此過不久我就能夠把腳本轉C++的功能上線了,到那個時候運行時就再也沒有什麼反射了。WPF其實也注意到了這樣的一個問題,因此他在新的UWP裏面(不要問我爲何WPF沒有,哈哈哈哈),實現了一個叫作x:Bind的綁定,作的事情跟GacUI其實一摸同樣:把data binding的邏輯所有轉換成代碼,而不是在運行時去hook這些對象。orm
英雄所見略同。htm
既然不能用DependencyProperty,那GacUI要怎麼辦呢?鑑於layout實際上是一個combinator,那我就原本在WPF寫成
<Button Grid.Row="0" Grid.Column="1">Click Me!</Button>
的東西,在GacUI改爲
<Cell Site="row:0 column:0"> <Button Text="Click Me!"/> </Cell>
就行了。Combinator就是這麼用的,哈哈哈哈哈。
上一篇文章 介紹了基礎的排版功能和 GuiSharedSizeRootComposition、GuiSharedSizeItemComposition 這一租排版對象。不過這裏介紹的這三組跟SharedSize不一樣的是,「右邊」的對象必須是「左邊」的對象的直接子對象。不過在運行的時候我並無檢查,由於只要你不這麼放,我實際上只會簡單的忽略這些屬性。下面介紹的全部的排版對象都有基礎的排版功能所須要的那些屬性,譬如AlignmentToParent、Margin、InternalMargin、PreferredMinSize、MinSizeLimitation等這些屬性,因此在這裏我只會講每一個對象獨有的功能。
Direction的意義很簡單。做爲一個Stack,天然會有生長的方向。所以分別提供從左到右、從右到左、從上到下、從下到上的生長方向也是很天然的,參考 GuiStackComposition::Direction 。
Padding指的是每個StackItem之間的間距。其實咱們老是可使用StackItem的InternalMargin,或者調節StackItem裏面的對象的Margin或者AlignmentToParent來模擬這一屬性。可是當你須要在每個StackItem之間都插入相同的空間的時候,這樣作無疑是很浪費時間的,所以Stack就提供了這樣的一個屬性。
ExtraMargin跟InternalMargin很接近,可是他只對StackItem起做用(也就是說,你能夠把不是StackItem的東西放進Stack,那麼這個時候,相對於這個對象,Stack就變成了一個普通的GuiBoundsComposition)。當肯定了全部的StackItem須要的空間以後,ExtraMargin會在全部StackItem的整體的周圍留下這麼大的空間,讓Stack自己變大。
在Stack並無要求要把本身變大到足夠放下全部子對象的前提下,StackItem是有可能會由於Stack不夠大而看不見的。所以IsStackItemClipped函數會告訴你這種狀況到底發生了沒有。並且就算髮生了也不要緊,由於EnsureVisible函數可讓全部的StackItem根據Direction的要求進行滑動,使得你喜好的其中一個StackItem被顯示出來。說到這裏可能不少人都不明白爲何要有這樣的功能 —— 其實很簡單,Tab控件就有這個要求。
StackItem也有一個ExtraMargin屬性。可是這個屬性跟Margin不同的地方在於,Stack並不會去理會StackItem的ExtraMargin屬性的值。Stack會先告訴每個StackItem他們應該被放到哪裏,而後最終處理StackItem的位置的時候,會根據ExtraMargin變大一點。固然當你的ExtraMargin比Stack的Padding還要大的時候,你的StackItem就會跟別的StackItem有交叉。配合 GuiGraphicsComposition::MoveChild函數,那麼Tab控件的需求就被徹底知足了 —— 點中的TabHeader不只會變大,並且還會老是在最上面,擋住旁邊的兩個TabHeader。
這個屬性的值是一個 GuiAxis 對象。雖然這個對象看起來很複雜,可是咱們在使用的時候只須要關心它的構造函數。Stack是一維的,可是Flow是二維的,所以生長方向天然就會有8個,因此GuiAxis構造函數就須要你填入一個 AxisDirection 枚舉結構的值。
RowPadding是虛擬行的行距。不過這裏的行是Axis屬性規定的Y軸方向的間距。根據設置的不一樣,因此這個虛擬的行也多是現實中的列。
ColumnPadding是虛擬列的列距。不過這裏的行是Axis屬性規定的X軸方向的間距。根據設置的不一樣,因此這個虛擬的列也多是現實中的行。
ExtraMargin跟Stack的ExtraMargin意思徹底一致,在此再也不贅述。
Alignment指的是當一個虛擬行已經放不下更多的FlowItem,可是他還有空間的時候,要怎麼處理。固然咱們會有(虛擬的)左對齊、居中和擴展這三種方法,因此我提供了 FlowAlignment 這一個枚舉類型。
ExtraMargin跟StackItem的ExtraMargin意思徹底一致,在此再也不贅述。
FlowOption指的是計算基線的方法。不一樣的FlowItem可使用不一樣的基線,從而使得內容在邏輯上被真正的對齊。舉個例子,你須要放不少按鈕,可是其中一個按鈕可能下面會有一點裝飾,那麼這個時候你就須要擡高一下相應的FlowItem的基線,使得對齊的是按鈕,從而裝飾就顯示在正一行的下面。
在這裏須要指出,當FlowOption::baseLine的值是Percentage的時候,基線是是從上往下計算的。所以你要底部對齊,percentage屬性就要寫1.0。
Table的Rows和Columns屬性都是 GuiCellOption 的值的列表。當你使用C++設置這個值的時候,你須要首先調用 GuiTableComposition::SetRowsAndColumns 函數告訴Table一共有多少行多少列,而後調用 GuiTableComposition::SetRowOption 和 GuiTableComposition::SetColumnOption 去指定具體的值。
GuiCellOption的composeType屬性能夠設置,這一行或者列要使用內容的最小值、固定的大小或者是佔用Table空間的百分比來構成。這裏須要注意的是,Table會首先排除composeType是MinSize和Absolute的那些行和列佔用的空間,剩下的部分才分配給composeType是Percentage的行和列。
所以若是你須要講一個按鈕居中在窗口的中間的話,你就能夠簡單地經過設置這些屬性來完成:
<Table AlignmentToParent="left:0 top:0 right:0 bottom:0"> <att.Rows> <CellOption>composeType:Percentage percentage:0.5</CellOption> <CellOption>composeType:MinSize</CellOption> <CellOption>composeType:Percentage percentage:0.5</CellOption> </att.Rows> <att.Columns> <CellOption>composeType:Percentage percentage:0.5</CellOption> <CellOption>composeType:MinSize</CellOption> <CellOption>composeType:Percentage percentage:0.5</CellOption> </att.Columns> <Cell Site="row:1 column:1> <Button Text="Click Me!"/> </Cell> </Table>
全部percentage加起來並無要求必定要是1,因此你就算寫是三個2,那也跟三個0.1是同樣的 —— 每個佔用1/3的空間。
CellPadding指的是Table的外邊框和內邊框的大小。也就是說除了行距和列距之外,Table自己還會放大CellPadding這麼大的地方,讓Cell和Table自己也有一個距離。
Site屬性分別有四個值:row、column、rowSpan和columnSpan,用來指定一個Cell在Table中到底佔用了哪些格子。不一樣的Cell之間不能重疊,可是一個Cell能夠佔用多個格子。舉個例子:若是你須要在一個窗口裏面放一個文本框,而後右下角有OK和Cancel兩個按鈕的話,咱們天然能夠想到須要使用2×3的表格來作。使用GacUI固然能夠簡單地作到:
<Table AlignmentToParent=left:0 top:0 right:0 bottom:0" CellPadding="5"> <att.Rows> <CellOption>composeType:Percentage percentage:0.5</CellOption> <CellOption>composeType:MinSize</CellOption> </att.Rows> <att.Columns> <CellOption>composeType:Percentage percentage:0.5</CellOption> <CellOption>composeType:MinSize</CellOption> <CellOption>composeType:MinSize</CellOption> </att.Columns> <Cell Site="row:0 column:0 columnSpan:3"> <MultilineTextBox> <att.BoundsComposition-set AlignmentToParent="left:0 top:0 right:0 bottom:0"/> </MultilineTextBox> </Cell> <Cell Site="row:2 column:1"> <Button Text="OK"> <!-- 當文字變得不少的時候,按鈕會自動變大,可是最小會保持30×100的大小 --> <att.BoundsComposition-set PreferredMinSize="x:100 y:30"/> </Button> </Cell> <Cell Site="row:2 column:1"> <Button Text="Cancel"> <att.BoundsComposition-set PreferredMinSize="x:100 y:30"/> </Button> </Cell> </Table>
GacUI其實還能夠經過把Control和Composition放進一個富文本文檔裏面來進行排版。不過因爲篇幅限制,這個內容我將在介紹富文本文檔的時候一併說明。