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.LengthStyleValueType, |
| 14 inherits: true, |
| 15 initialValue: { value: 8, unit: 'px' }, |
| 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) { |
| 55 childHeight = child.layoutManager.getIntrinsicHeight(); |
| 56 if (childHeight.value < height) |
| 57 childHeight = childHeight.value; |
| 58 else |
| 59 childHeight = height; |
| 60 dims = child.layoutManager.layout(width, 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 return { |
| 128 width: width, |
| 129 height: height, |
| 130 } |
| 131 } |
| 132 function getIntrinsicWidth() { |
| 133 let width = this.node.getProperty('width'); |
| 134 if (typeof height != 'number') { |
| 135 let spacing = this.node.getProperty('toolbar-spacing'); |
| 136 if (typeof spacing != 'number') |
| 137 spacing = 0; |
| 138 width = 0; |
| 139 let children = this.walkChildren(); |
| 140 let loop = children.next(); |
| 141 // we exclude the last child because at our ideal width we wouldn't need
it |
| 142 let last1 = null; // last one |
| 143 let last2 = null; // one before the last one |
| 144 while (!loop.done) { |
| 145 if (last1) |
| 146 width += last1.layoutManager.getIntrinsicWidth().value; |
| 147 if (last2) |
| 148 width += spacing; |
| 149 last2 = last1; |
| 150 last1 = loop.value; |
| 151 loop = children.next(); |
| 152 } |
| 153 } |
| 154 return super(width); // applies and provides our own min-width/max-width ru
les |
| 155 } |
| 156 function getIntrinsicHeight() { |
| 157 // we grow our minimum height to be no smaller than the children's |
| 158 let result = super(); |
| 159 let determineHeight = false; |
| 160 let heightProperty = this.node.getProperty('height'); |
| 161 if (typeof heightProperty != 'number') |
| 162 determineHeight = true; |
| 163 let children = this.walkChildren(); |
| 164 let loop = children.next(); |
| 165 // here we include the last child so that if it pops in our height doesn't
change |
| 166 while (!loop.done) { |
| 167 let child = loop.value; |
| 168 let childHeight = child.layoutManager.getIntrinsicHeight(); |
| 169 if (determineHeight) { |
| 170 if (result.value < childHeight.value) |
| 171 result.value = childHeight.value; |
| 172 } |
| 173 if (result.minimum < childHeight.minimum) |
| 174 result.minimum = childHeight.minimum; |
| 175 loop = children.next(); |
| 176 } |
| 177 if (result.minimum > result.maximum) |
| 178 result.maximum = result.minimum; |
| 179 if (result.value > result.maximum) |
| 180 result.value = result.maximum; |
| 181 if (result.value < result.minimum) |
| 182 result.value = result.minimum; |
| 183 return result; |
| 184 } |
| 185 function paintChildren(canvas) { |
| 186 let width = this.node.width; |
| 187 let loop = children.next(); |
| 188 while ((!loop.done) && (loop.value != this.firstSkippedChild)) |
| 189 this.paintChild(loop.value, canvas); |
| 190 if (this.showingOverflow) |
| 191 this.paintChild(this.overflowChild, canvas); |
| 192 } |
| 193 function paintChild(child, canvas) { |
| 194 if (child.needsPaint) { |
| 195 canvas.save(); |
| 196 try { |
| 197 canvas.beginPath(); |
| 198 canvas.rect(child.x, child.y, child.width, child.height); |
| 199 canvas.clip(); |
| 200 child.paint(canvas); |
| 201 } finally { |
| 202 canvas.restore(); |
| 203 } |
| 204 } |
| 205 } |
| 206 } |
| 207 sky.registerLayoutManager('toolbar', module.exports.ToolbarLayoutManager); |
| 208 </script> |
OLD | NEW |