OLD | NEW |
| (Empty) |
1 SKY MODULE | |
2 <import src="sky:core" as="sky"/> | |
3 <script> | |
4 // display: toolbar; | |
5 // toolbar-spacing: <length> | |
6 // display: spring; // remaining space is split equally amongst the springs | |
7 // children are vertically centered, layout out left-to-right with toolbar-spac
ing space between them | |
8 // last child is hidden by default unless there's not enough room for the other
s, then it's shown last, right-aligned | |
9 module.exports.SpringLayoutManager = class SpringLayoutManager extends sky.Layo
utManager { } | |
10 sky.registerLayoutManager('spring', module.exports.SpringLayoutManager); | |
11 sky.registerProperty({ | |
12 name: 'toolbar-spacing', | |
13 type: sky.PositiveLengthStyleGrammar, | |
14 inherits: true, | |
15 initialValue: 8, | |
16 needsLayout: true, | |
17 }); | |
18 module.exports.ToolbarLayoutManager = class ToolbarLayoutManager extends sky.La
youtManager { | |
19 constructor (styleNode) { | |
20 super(styleNode); | |
21 this.showingOverflow = false; | |
22 this.firstSkippedChild = null; | |
23 this.overflowChild = null; | |
24 } | |
25 function layout(width, height) { | |
26 let children = null; | |
27 let loop = null; | |
28 if (height == null) | |
29 height = this.getIntrinsicHeight().value; | |
30 if (width == null) | |
31 this.assumeDimensions(0, height); | |
32 else | |
33 this.assumeDimensions(width, height); | |
34 let spacing = this.node.getProperty('toolbar-spacing'); | |
35 if (typeof spacing != 'number') | |
36 spacing = 0; | |
37 this.overflowChild = null; | |
38 this.firstSkippedChild = null; | |
39 | |
40 // layout children and figure out whether we need to truncate the child lis
t and show the overflow child | |
41 let springCount = 0; | |
42 let minX = 0; | |
43 let overflowChildWidth = 0; | |
44 let pendingSpacing = 0; | |
45 children = this.walkChildren(); | |
46 loop = children.next(); | |
47 while (!loop.done) { | |
48 let child = loop.value; | |
49 let dims = null; | |
50 if (child.layoutManager instanceof module.exports.SpringLayoutManager) { | |
51 springCount += 1; | |
52 pendingSpacing = spacing; // not +=, because we only have one extra spa
cing per batch of springs | |
53 } else { | |
54 if (child.needsLayout || child.descendantNeedsLayout) { | |
55 childHeight = child.layoutManager.getIntrinsicHeight(); | |
56 if (childHeight.value < height) | |
57 childHeight = childHeight.value; | |
58 else | |
59 childHeight = height; | |
60 dims = child.layoutManager.layout(null, height); | |
61 this.setChildSize(child, dims.width, dims.height); | |
62 } else { | |
63 dims = { | |
64 width: child.width, | |
65 height: child.height, | |
66 }; | |
67 } | |
68 loop = children.next(); | |
69 if (!loop.done) { | |
70 if (minX > 0) | |
71 minX += spacing + pendingSpacing; | |
72 minX += dims.width; | |
73 pendingSpacing = 0; | |
74 } else { | |
75 overflowChildWidth = spacing + dims.width; | |
76 this.overflowChild = child; | |
77 } | |
78 } | |
79 } | |
80 | |
81 // figure out the spacing | |
82 this.showingOverflow = false; | |
83 let springSize = 0; | |
84 if (width != null) { | |
85 if (minX <= width) { | |
86 if (springCount > 0) | |
87 springSize = (width - minX) / sprintCount; | |
88 } else { | |
89 this.showingOverflow = true; | |
90 } | |
91 } else { | |
92 width = minX; | |
93 } | |
94 | |
95 // position the children | |
96 // TODO(ianh): support rtl toolbars | |
97 let x = 0; | |
98 let lastWasNonSpring = false; | |
99 children = this.walkChildren(); | |
100 loop = children.next(); | |
101 while (!loop.done) { | |
102 let child = loop.value; | |
103 if (child.layoutManager instanceof module.exports.SpringLayoutManager) { | |
104 x += springSize; | |
105 if (lastWasNonSpring) | |
106 x += spacing; | |
107 lastWasNonSpring = false; | |
108 } else { | |
109 if (!loop.done) { | |
110 if (x + child.width + overflowChildWidth > width) { | |
111 this.firstSkippedChild = child; | |
112 break; // don't display any more children | |
113 } | |
114 this.setChildPosition(child, x, (height - child.height)/2); | |
115 x += child.width + spacing; | |
116 lastWasNonSpring = true; | |
117 } else { | |
118 // assert: this.showingOverflow == false | |
119 } | |
120 } | |
121 } | |
122 if (this.showingOverflow) | |
123 this.setChildPosition(this.overflowChild, width-this.overflowChild.width,
(height - this.overflowChild.height)/2); | |
124 else | |
125 this.firstSkippedChild = this.overflowChild; | |
126 | |
127 this.markAsLaidOut(); | |
128 return { | |
129 width: width, | |
130 height: height, | |
131 } | |
132 } | |
133 function layoutDescendants() { | |
134 this.layout(node.width, node.height); | |
135 } | |
136 function getIntrinsicWidth() { | |
137 let width = this.node.getProperty('width'); | |
138 if (typeof width != 'number') { | |
139 let spacing = this.node.getProperty('toolbar-spacing'); | |
140 if (typeof spacing != 'number') | |
141 spacing = 0; | |
142 width = 0; | |
143 let children = this.walkChildren(); | |
144 let loop = children.next(); | |
145 // we exclude the last child because at our ideal width we wouldn't need
it | |
146 let last1 = null; // last one | |
147 let last2 = null; // one before the last one | |
148 while (!loop.done) { | |
149 if (last1) | |
150 width += last1.layoutManager.getIntrinsicWidth().value; | |
151 if (last2) | |
152 width += spacing; | |
153 last2 = last1; | |
154 last1 = loop.value; | |
155 loop = children.next(); | |
156 } | |
157 } | |
158 return super(width); // applies and provides our own min-width/max-width ru
les | |
159 } | |
160 function getIntrinsicHeight() { | |
161 // we grow our minimum height to be no smaller than the children's | |
162 let result = super(); | |
163 let determineHeight = false; | |
164 let heightProperty = this.node.getProperty('height'); | |
165 if (typeof heightProperty != 'number') | |
166 determineHeight = true; | |
167 let children = this.walkChildren(); | |
168 let loop = children.next(); | |
169 // here we include the last child so that if it pops in our height doesn't
change | |
170 while (!loop.done) { | |
171 let child = loop.value; | |
172 let childHeight = child.layoutManager.getIntrinsicHeight(); | |
173 if (determineHeight) { | |
174 if (result.value < childHeight.value) | |
175 result.value = childHeight.value; | |
176 } | |
177 if (result.minimum < childHeight.minimum) | |
178 result.minimum = childHeight.minimum; | |
179 loop = children.next(); | |
180 } | |
181 if (result.minimum > result.maximum) | |
182 result.maximum = result.minimum; | |
183 if (result.value > result.maximum) | |
184 result.value = result.maximum; | |
185 if (result.value < result.minimum) | |
186 result.value = result.minimum; | |
187 return result; | |
188 } | |
189 function paintChildren(canvas) { | |
190 let width = this.node.width; | |
191 let children = this.walkChildren(); | |
192 let loop = children.next(); | |
193 while ((!loop.done) && (loop.value != this.firstSkippedChild)) | |
194 canvas.paintChild(loop.value); | |
195 if (this.showingOverflow) | |
196 canvas.paintChild(this.overflowChild); | |
197 } | |
198 function inChild(child, x, y) { | |
199 return (x >= child.x) && (y >= child.y) && (x < child.x+child.width) && (y
< child.y+child.height); | |
200 } | |
201 function hitTest(x, y) { | |
202 let children = this.walkChildrenBackwards(); | |
203 let loop = children.next(); | |
204 while ((!loop.done) && (loop.value != this.firstSkippedChild)) | |
205 if (this.inChild(loop.value, x, y)) | |
206 return loop.value; | |
207 if (this.showingOverflow) | |
208 if (this.inChild(this.overflowChild, x, y)) | |
209 return this.overflowChild; | |
210 return this.node; | |
211 } | |
212 } | |
213 sky.registerLayoutManager('toolbar', module.exports.ToolbarLayoutManager); | |
214 </script> | |
OLD | NEW |