今天要讲的是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放进一个富文本文档里面来进行排版。不过因为篇幅限制,这个内容我将在介绍富文本文档的时候一并说明。