~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/xwt/Xwt/Xwt/Table.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// Table.cs
 
3
//  
 
4
// Author:
 
5
//       Lluis Sanchez <lluis@xamarin.com>
 
6
// 
 
7
// Copyright (c) 2011 Xamarin Inc
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
 
 
27
using System;
 
28
using System.Linq;
 
29
using System.Collections.Generic;
 
30
using System.ComponentModel;
 
31
 
 
32
using Xwt.Backends;
 
33
using System.Windows.Markup;
 
34
using Xwt.Drawing;
 
35
 
 
36
namespace Xwt
 
37
{
 
38
        public class Table: Widget
 
39
        {
 
40
                ChildrenCollection<TablePlacement> children;
 
41
                double defaultRowSpacing = 6;
 
42
                double defaultColSpacing = 6;
 
43
                Dictionary<int,double> rowSpacing;
 
44
                Dictionary<int,double> colSpacing;
 
45
                
 
46
                protected new class WidgetBackendHost: Widget.WidgetBackendHost, ICollectionEventSink<TablePlacement>, IContainerEventSink<TablePlacement>
 
47
                {
 
48
                        public void AddedItem (TablePlacement item, int index)
 
49
                        {
 
50
                                ((Table)Parent).OnAdd (item.Child, item);
 
51
                        }
 
52
 
 
53
                        public void RemovedItem (TablePlacement item, int index)
 
54
                        {
 
55
                                ((Table)Parent).OnRemove (item.Child);
 
56
                        }
 
57
 
 
58
                        public void ChildChanged (TablePlacement child, string hint)
 
59
                        {
 
60
                                ((Table)Parent).OnChildChanged (child, hint);
 
61
                        }
 
62
 
 
63
                        public void ChildReplaced (TablePlacement child, Widget oldWidget, Widget newWidget)
 
64
                        {
 
65
                                ((Table)Parent).OnReplaceChild (child, oldWidget, newWidget);
 
66
                        }
 
67
                }
 
68
                
 
69
                protected override BackendHost CreateBackendHost ()
 
70
                {
 
71
                        return new WidgetBackendHost ();
 
72
                }
 
73
                
 
74
                IBoxBackend Backend {
 
75
                        get { return (IBoxBackend) BackendHost.Backend; }
 
76
                }
 
77
                
 
78
                public Table ()
 
79
                {
 
80
                        children = new ChildrenCollection<TablePlacement> ((WidgetBackendHost)BackendHost);
 
81
                        // For some reason the table has a black background by default in WPF. Lets work around that
 
82
                        BackgroundColor = Colors.White;
 
83
                }
 
84
                
 
85
                [DefaultValue(6)]
 
86
                public double DefaultRowSpacing {
 
87
                        get { return defaultRowSpacing; }
 
88
                        set { defaultRowSpacing = value; OnPreferredSizeChanged (); }
 
89
                }
 
90
                
 
91
                [DefaultValue(6)]
 
92
                public double DefaultColumnSpacing {
 
93
                        get { return defaultColSpacing; }
 
94
                        set { defaultColSpacing = value; OnPreferredSizeChanged (); }
 
95
                }
 
96
                
 
97
                public void SetRowSpacing (int row, double spacing)
 
98
                {
 
99
                        if (rowSpacing == null)
 
100
                                rowSpacing = new Dictionary<int, double> ();
 
101
                        rowSpacing [row] = spacing;
 
102
                        OnPreferredSizeChanged ();
 
103
                }
 
104
                
 
105
                public void SetColumnSpacing (int col, double spacing)
 
106
                {
 
107
                        if (colSpacing == null)
 
108
                                colSpacing = new Dictionary<int, double> ();
 
109
                        colSpacing [col] = spacing;
 
110
                        OnPreferredSizeChanged ();
 
111
                }
 
112
                
 
113
                public ChildrenCollection<TablePlacement> Placements {
 
114
                        get { return children; }
 
115
                }
 
116
                
 
117
                public IEnumerable<Widget> Children {
 
118
                        get { return children.Select (c => c.Child); }
 
119
                }
 
120
                
 
121
                public void Attach (Widget widget, int left, int top)
 
122
                {
 
123
                        Attach (widget, left, top, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Fill | AttachOptions.Expand);
 
124
                }
 
125
                
 
126
                public void Attach (Widget widget, int left, int top, AttachOptions xOptions, AttachOptions yOptions)
 
127
                {
 
128
                        Attach (widget, left, left + 1, top, top + 1, xOptions, yOptions);
 
129
                }
 
130
                
 
131
                public void Attach (Widget widget, int left, int right, int top, int bottom)
 
132
                {
 
133
                        Attach (widget, left, right, top, bottom, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Fill | AttachOptions.Expand);
 
134
                }
 
135
                
 
136
                public void Attach (Widget widget, int left, int right, int top, int bottom, AttachOptions xOptions, AttachOptions yOptions)
 
137
                {
 
138
                        var p = new TablePlacement ((WidgetBackendHost)BackendHost, widget) {
 
139
                                Left = left,
 
140
                                Right = right,
 
141
                                Top = top,
 
142
                                Bottom = bottom,
 
143
                                XOptions = xOptions,
 
144
                                YOptions = yOptions
 
145
                        };
 
146
                        children.Add (p);
 
147
                }
 
148
                
 
149
                public bool Remove (Widget widget)
 
150
                {
 
151
                        for (int n=0; n<children.Count; n++) {
 
152
                                if (children[n].Child == widget) {
 
153
                                        children.RemoveAt (n);
 
154
                                        return true;
 
155
                                }
 
156
                        }
 
157
                        return false;
 
158
                }
 
159
 
 
160
                public void InsertRow (int top, int bottom)
 
161
                {
 
162
                        var potentials = children.Where (c => c.Top >= top);
 
163
                        var shift = bottom - top;
 
164
                        foreach (var toShift in potentials) {
 
165
                                toShift.Top += shift;
 
166
                                toShift.Bottom += shift;
 
167
                        }
 
168
                }
 
169
                
 
170
                /// <summary>
 
171
                /// Removes all children
 
172
                /// </summary>
 
173
                public void Clear ()
 
174
                {
 
175
                        children.Clear ();
 
176
                }
 
177
                
 
178
                void OnAdd (Widget child, TablePlacement placement)
 
179
                {
 
180
                        RegisterChild (child);
 
181
                        Backend.Add ((IWidgetBackend)GetBackend (child));
 
182
                        OnPreferredSizeChanged ();
 
183
                }
 
184
                
 
185
                void OnRemove (Widget child)
 
186
                {
 
187
                        UnregisterChild (child);
 
188
                        Backend.Remove ((IWidgetBackend)GetBackend (child));
 
189
                        OnPreferredSizeChanged ();
 
190
                }
 
191
                
 
192
                void OnChildChanged (TablePlacement placement, object hint)
 
193
                {
 
194
                        OnPreferredSizeChanged ();
 
195
                }
 
196
                
 
197
                internal protected virtual void OnReplaceChild (TablePlacement placement, Widget oldWidget, Widget newWidget)
 
198
                {
 
199
                        if (oldWidget != null)
 
200
                                OnRemove (oldWidget);
 
201
                        OnAdd (newWidget, placement);
 
202
                }
 
203
                
 
204
                protected override void OnReallocate ()
 
205
                {
 
206
                        var size = Backend.Size;
 
207
                        var mode = Surface.SizeRequestMode;
 
208
                        if (mode == SizeRequestMode.HeightForWidth) {
 
209
                                CalcDefaultSizes (mode, size.Width, false, true);
 
210
                                CalcDefaultSizes (mode, size.Height, true, true);
 
211
                        } else {
 
212
                                CalcDefaultSizes (mode, size.Height, true, true);
 
213
                                CalcDefaultSizes (mode, size.Width, false, true);
 
214
                        }
 
215
                        
 
216
                        var visibleChildren = children.Where (c => c.Child.Visible).ToArray ();
 
217
                        IWidgetBackend[] widgets = new IWidgetBackend [visibleChildren.Length];
 
218
                        Rectangle[] rects = new Rectangle [visibleChildren.Length];
 
219
                        for (int n=0; n<visibleChildren.Length; n++) {
 
220
                                var bp = visibleChildren [n];
 
221
                                widgets [n] = (IWidgetBackend)GetBackend (bp.Child);
 
222
                                rects [n] = new Rectangle (bp.NextX, bp.NextY, bp.NextWidth, bp.NextHeight);
 
223
                        }
 
224
                        
 
225
                        Backend.SetAllocation (widgets, rects);
 
226
                        
 
227
                        if (!Application.EngineBackend.HandlesSizeNegotiation) {
 
228
                                foreach (var bp in visibleChildren)
 
229
                                        bp.Child.Surface.Reallocate ();
 
230
                        }
 
231
                }
 
232
                
 
233
                double GetSpacing (int cell, bool isRow)
 
234
                {
 
235
                        double sp;
 
236
                        if (isRow) {
 
237
                                if (rowSpacing != null && rowSpacing.TryGetValue (cell, out sp))
 
238
                                        return sp;
 
239
                                else
 
240
                                        return defaultRowSpacing;
 
241
                        } else {
 
242
                                if (colSpacing != null && colSpacing.TryGetValue (cell, out sp))
 
243
                                        return sp;
 
244
                                else
 
245
                                        return defaultColSpacing;
 
246
                        }
 
247
                }
 
248
                
 
249
                void CalcDefaultSizes (SizeRequestMode mode, bool calcHeights, out TablePlacement[] visibleChildren, out Dictionary<int,WidgetSize> fixedSizesByCell, out HashSet<int> cellsWithExpand, out WidgetSize[] sizes, out double spacing)
 
250
                {
 
251
                        bool useLengthConstraint = mode == SizeRequestMode.HeightForWidth && calcHeights || mode == SizeRequestMode.WidthForHeight && !calcHeights;
 
252
                        visibleChildren = children.Where (b => b.Child.Visible).ToArray ();
 
253
                        int lastCell = 0;
 
254
 
 
255
                        fixedSizesByCell = new Dictionary<int, WidgetSize> ();
 
256
                        cellsWithExpand = new HashSet<int> ();
 
257
                        HashSet<int> cellsWithWidget = new HashSet<int> ();
 
258
                        sizes = new WidgetSize [visibleChildren.Length];
 
259
                        
 
260
                        // Get the size of each widget and store the fixed sizes for widgets which don't span more than one cell
 
261
                        
 
262
                        for (int n=0; n<visibleChildren.Length; n++) {
 
263
                                var bp = visibleChildren[n];
 
264
                                int start = GetStartAttach (bp, calcHeights);
 
265
                                int end = GetEndAttach (bp, calcHeights);
 
266
                                
 
267
                                if (end > lastCell)
 
268
                                        lastCell = end;
 
269
                                
 
270
                                // Check if the cell is expandable and store the value
 
271
                                AttachOptions ops = calcHeights ? bp.YOptions : bp.XOptions;
 
272
                                for (int i=start; i < end; i++) {
 
273
                                        cellsWithWidget.Add (i);
 
274
                                        if ((ops & AttachOptions.Expand) != 0)
 
275
                                                cellsWithExpand.Add (i);
 
276
                                }
 
277
                                        
 
278
                                WidgetSize s;
 
279
                                if (useLengthConstraint)
 
280
                                        s = GetPreferredLengthForSize (mode, bp.Child, calcHeights ? bp.NextWidth : bp.NextHeight);
 
281
                                else
 
282
                                        s = GetPreferredSize (calcHeights, bp.Child);
 
283
                                sizes [n] = s;
 
284
                                
 
285
                                if (end == start + 1) {
 
286
                                        // The widget only takes one cell. Store its size if it is the biggest
 
287
                                        bool changed = false;
 
288
                                        WidgetSize fs;
 
289
                                        fixedSizesByCell.TryGetValue (start, out fs);
 
290
                                        if (s.MinSize > fs.MinSize) { 
 
291
                                                fs.MinSize = s.MinSize;
 
292
                                                changed = true;
 
293
                                        }
 
294
                                        if (s.NaturalSize > fs.NaturalSize) {
 
295
                                                fs.NaturalSize = s.NaturalSize;
 
296
                                                changed = true;
 
297
                                        }
 
298
                                        if (changed)
 
299
                                                fixedSizesByCell [start] = fs;
 
300
                                }
 
301
                        }
 
302
                        
 
303
                        // For widgets that span more than one cell, calculate the floating size, that is, the size
 
304
                        // which is not taken by other fixed size widgets
 
305
                        
 
306
                        List<TablePlacement> widgetsToAdjust = new List<TablePlacement> ();
 
307
                        Dictionary<TablePlacement,WidgetSize[]> growSizes = new Dictionary<TablePlacement, WidgetSize[]> ();
 
308
                        
 
309
                        for (int n=0; n<visibleChildren.Length; n++) {
 
310
                                var bp = visibleChildren[n];
 
311
                                int start = GetStartAttach (bp, calcHeights);
 
312
                                int end = GetEndAttach (bp, calcHeights);
 
313
                                if (end == start + 1)
 
314
                                        continue;
 
315
                                widgetsToAdjust.Add (bp);
 
316
                                
 
317
                                WidgetSize fixedSize = new WidgetSize (0);
 
318
                                
 
319
                                // We are going to calculate the spacing included in the widget's span of cells
 
320
                                // (there is spacing between each cell)
 
321
                                double spanSpacing = 0;
 
322
                                
 
323
                                for (int c = start; c < end; c++) {
 
324
                                        WidgetSize fs;
 
325
                                        fixedSizesByCell.TryGetValue (c, out fs);
 
326
                                        fixedSize += fs;
 
327
                                        if (c != start && c != end)
 
328
                                                spanSpacing += GetSpacing (c, calcHeights);
 
329
                                }
 
330
                                
 
331
                                // sizeToGrow is the size that the whole cell span has to grow in order to fit
 
332
                                // this widget. We substract the spacing between cells because that space will
 
333
                                // be used by the widget, so we don't need to allocate more size for it
 
334
                                
 
335
                                WidgetSize sizeToGrow = sizes [n] - fixedSize - new WidgetSize (spanSpacing);
 
336
                                
 
337
                                WidgetSize sizeToGrowPart = new WidgetSize (sizeToGrow.MinSize / (end - start), sizeToGrow.NaturalSize / (end - start));
 
338
 
 
339
                                // Split the size to grow between the cells of the widget. We need to know how much size the widget
 
340
                                // requires for each cell it covers.
 
341
                                
 
342
                                WidgetSize[] widgetGrowSizes = new WidgetSize [end - start];
 
343
                                for (int i=0; i<widgetGrowSizes.Length; i++)
 
344
                                        widgetGrowSizes [i] = sizeToGrowPart;
 
345
                                growSizes[bp] = widgetGrowSizes;
 
346
                        }
 
347
                        
 
348
                        // Now size-to-grow values have to be adjusted. For example, let's say widget A requires 100px for column 1 and 100px for column 2, and widget B requires
 
349
                        // 60px for column 2 and 60px for column 3. So the widgets are overlapping at column 2. Since A requires at least 100px in column 2, it means that B can assume
 
350
                        // that it will have 100px available in column 2, which means 40px more than it requested. Those extra 40px can then be substracted from the 60px that
 
351
                        // it required for column 3.
 
352
                        
 
353
                        foreach (var n in cellsWithWidget) {
 
354
                                // Get a list of all widgets that cover this cell
 
355
                                var colCells = widgetsToAdjust.Where (bp => GetStartAttach (bp, calcHeights) <= n && GetEndAttach (bp, calcHeights) > n).ToArray ();
 
356
                                WidgetSize maxv = new WidgetSize (0);
 
357
                                TablePlacement maxtMin = null;
 
358
                                TablePlacement maxtNatural = null;
 
359
                                
 
360
                                // Find the widget that requires the maximum size for this cell
 
361
                                foreach (var bp in colCells) {
 
362
                                        WidgetSize cv = growSizes[bp][n - GetStartAttach (bp, calcHeights)];
 
363
                                        if (cv.MinSize > maxv.MinSize) {
 
364
                                                maxv.MinSize = cv.MinSize;
 
365
                                                maxtMin = bp;
 
366
                                        }
 
367
                                        if (cv.NaturalSize > maxv.NaturalSize) {
 
368
                                                maxv.NaturalSize = cv.NaturalSize;
 
369
                                                maxtNatural = bp;
 
370
                                        }
 
371
                                }
 
372
                                
 
373
                                // Adjust the required size of all widgets of the cell (excluding the widget with the max size)
 
374
                                foreach (var bp in colCells) {
 
375
                                        WidgetSize[] widgetGrows = growSizes[bp];
 
376
                                        int cellIndex = n - GetStartAttach (bp, calcHeights);
 
377
                                        if (bp != maxtMin) {
 
378
                                                double cv = widgetGrows[cellIndex].MinSize;
 
379
                                                // splitExtraSpace is the additional space that the widget can take from this cell (because there is a widget
 
380
                                                // that is requiring more space), split among all other cells of the widget
 
381
                                                double splitExtraSpace = (maxv.MinSize - cv) / (widgetGrows.Length - 1);
 
382
                                                for (int i=0; i<widgetGrows.Length; i++)
 
383
                                                        widgetGrows[i].MinSize -= splitExtraSpace;
 
384
                                        }
 
385
                                        if (bp != maxtNatural) {
 
386
                                                double cv = widgetGrows[cellIndex].NaturalSize;
 
387
                                                double splitExtraSpace = (maxv.NaturalSize - cv) / (widgetGrows.Length - 1);
 
388
                                                for (int i=0; i<widgetGrows.Length; i++)
 
389
                                                        widgetGrows[i].NaturalSize -= splitExtraSpace;
 
390
                                        }
 
391
                                }
 
392
                        }
 
393
                        
 
394
                        // Find the maximum size-to-grow for each cell
 
395
                        
 
396
                        Dictionary<int,WidgetSize> finalGrowTable = new Dictionary<int, WidgetSize> ();
 
397
                        
 
398
                        foreach (var bp in widgetsToAdjust) {
 
399
                                int start = GetStartAttach (bp, calcHeights);
 
400
                                int end = GetEndAttach (bp, calcHeights);
 
401
                                WidgetSize[] widgetGrows = growSizes[bp];
 
402
                                for (int n=start; n<end; n++) {
 
403
                                        WidgetSize curGrow;
 
404
                                        finalGrowTable.TryGetValue (n, out curGrow);
 
405
                                        var val = widgetGrows [n - start];
 
406
                                        if (val.MinSize > curGrow.MinSize)
 
407
                                                curGrow.MinSize = val.MinSize;
 
408
                                        if (val.NaturalSize > curGrow.NaturalSize)
 
409
                                                curGrow.NaturalSize = val.NaturalSize;
 
410
                                        finalGrowTable [n] = curGrow;
 
411
                                }
 
412
                        }
 
413
                        
 
414
                        // Add the final size-to-grow to the fixed sizes calculated at the begining
 
415
                        
 
416
                        foreach (var it in finalGrowTable) {
 
417
                                WidgetSize ws;
 
418
                                fixedSizesByCell.TryGetValue (it.Key, out ws);
 
419
                                fixedSizesByCell [it.Key] = it.Value + ws;
 
420
                        }
 
421
                        
 
422
                        spacing = 0;
 
423
                        for (int n=1; n<lastCell; n++) {
 
424
                                if (cellsWithWidget.Contains (n))
 
425
                                        spacing += GetSpacing (n, calcHeights);
 
426
                        }
 
427
                        
 
428
                }
 
429
                
 
430
                void CalcDefaultSizes (SizeRequestMode mode, double totalSize, bool calcHeights, bool calcOffsets)
 
431
                {
 
432
                        TablePlacement[] visibleChildren;
 
433
                        Dictionary<int,WidgetSize> fixedSizesByCell;
 
434
                        HashSet<int> cellsWithExpand;
 
435
                        WidgetSize[] sizes;
 
436
                        double spacing;
 
437
                        
 
438
                        CalcDefaultSizes (mode, calcHeights, out visibleChildren, out fixedSizesByCell, out cellsWithExpand, out sizes, out spacing);
 
439
                        
 
440
                        double naturalSize = 0;
 
441
                        
 
442
                        // Get the total natural size
 
443
                        foreach (var ws in fixedSizesByCell.Values)
 
444
                                naturalSize += ws.NaturalSize;
 
445
                        
 
446
                        double remaining = totalSize - naturalSize - spacing;
 
447
                        
 
448
                        if (remaining < 0) {
 
449
                                // The box is not big enough to fit the widgets using its natural size.
 
450
                                // We have to shrink the cells
 
451
                                
 
452
                                // List of cell indexes that we have to shrink
 
453
                                var toShrink = new List<int> (fixedSizesByCell.Keys);
 
454
                                
 
455
                                // The total amount we have to shrink
 
456
                                double shrinkSize = -remaining;
 
457
                                
 
458
                                while (toShrink.Count > 0 && shrinkSize > 0) {
 
459
                                        SizeSplitter sizePart = new SizeSplitter (shrinkSize, toShrink.Count);
 
460
                                        shrinkSize = 0;
 
461
                                        for (int i=0; i < toShrink.Count; i++) {
 
462
                                                int n = toShrink[i];
 
463
                                                double reduction = sizePart.NextSizePart ();
 
464
                                                WidgetSize size;
 
465
                                                fixedSizesByCell.TryGetValue (n, out size);
 
466
                                                size.NaturalSize -= reduction;
 
467
                                                
 
468
                                                if (size.NaturalSize < size.MinSize) {
 
469
                                                        // If the widget can't be shrinked anymore, we remove it from the shrink list
 
470
                                                        // and increment the remaining shrink size. We'll loop again and this size will be
 
471
                                                        // substracted from the cells which can still be reduced
 
472
                                                        shrinkSize += (size.MinSize - size.NaturalSize);
 
473
                                                        size.NaturalSize = size.MinSize;
 
474
                                                        toShrink.RemoveAt (i);
 
475
                                                        i--;
 
476
                                                }
 
477
                                                fixedSizesByCell [n] = size;
 
478
                                        }
 
479
                                }
 
480
                        }
 
481
                        else {
 
482
                                int nexpands = cellsWithExpand.Count;
 
483
                                var expandRemaining = new SizeSplitter (remaining, nexpands);
 
484
                                foreach (var c in cellsWithExpand) {
 
485
                                        WidgetSize ws;
 
486
                                        fixedSizesByCell.TryGetValue (c, out ws);
 
487
                                        ws.NaturalSize += expandRemaining.NextSizePart ();
 
488
                                        fixedSizesByCell [c] = ws;
 
489
                                }
 
490
                        }
 
491
                        
 
492
                        for (int n=0; n<visibleChildren.Length; n++) {
 
493
                                var bp = visibleChildren[n];
 
494
                                double allocatedSize = 0;
 
495
                                double cellOffset = 0;
 
496
                                AttachOptions ops = calcHeights ? bp.YOptions : bp.XOptions;
 
497
                                
 
498
                                int start = GetStartAttach (bp, calcHeights);
 
499
                                int end = GetEndAttach (bp, calcHeights);
 
500
                                for (int i=start; i<end; i++) {
 
501
                                        WidgetSize ws;
 
502
                                        fixedSizesByCell.TryGetValue (i, out ws);
 
503
                                        allocatedSize += ws.NaturalSize;
 
504
                                        if (i != start)
 
505
                                                allocatedSize += GetSpacing (i, calcHeights);
 
506
                                }
 
507
                                
 
508
                                if ((ops & AttachOptions.Fill) == 0) {
 
509
                                        double s = sizes[n].NaturalSize;
 
510
                                        if (s < allocatedSize) {
 
511
                                                cellOffset = (allocatedSize - s) / 2;
 
512
                                                allocatedSize = s;
 
513
                                        }
 
514
                                }
 
515
                                
 
516
                                // cellOffset is the offset of the widget inside the cell. We store it in NextX/Y, and
 
517
                                // will be used below to calculate the total offset of the widget
 
518
                                
 
519
                                if (calcHeights) {
 
520
                                        bp.NextHeight = allocatedSize;
 
521
                                        bp.NextY = cellOffset;
 
522
                                }       
 
523
                                else {
 
524
                                        bp.NextWidth = allocatedSize;
 
525
                                        bp.NextX = cellOffset;
 
526
                                }
 
527
                        }
 
528
                        
 
529
                        if (calcOffsets) {
 
530
                                var sortedChildren = visibleChildren.OrderBy (c => GetStartAttach (c, calcHeights)).ToArray();
 
531
                                var cells = fixedSizesByCell.OrderBy (c => c.Key);
 
532
                                double offset = 0;
 
533
                                int n = 0;
 
534
                                foreach (var c in cells) {
 
535
                                        if (c.Key > 0)
 
536
                                                offset += GetSpacing (c.Key, calcHeights);
 
537
                                        while (n < sortedChildren.Length && GetStartAttach (sortedChildren[n], calcHeights) == c.Key) {
 
538
                                                // In the loop above we store the offset of the widget inside the cell in the NextX/Y field
 
539
                                                // so now we have to add (not just assign) the offset of the cell to NextX/Y
 
540
                                                if (calcHeights)
 
541
                                                        sortedChildren[n].NextY += offset;
 
542
                                                else
 
543
                                                        sortedChildren[n].NextX += offset;
 
544
                                                n++;
 
545
                                        }
 
546
                                        offset += c.Value.NaturalSize;
 
547
                                }
 
548
                        }
 
549
                }
 
550
                
 
551
                WidgetSize CalcSize (bool calcHeights)
 
552
                {
 
553
                        TablePlacement[] visibleChildren;
 
554
                        Dictionary<int,WidgetSize> fixedSizesByCell;
 
555
                        HashSet<int> cellsWithExpand;
 
556
                        WidgetSize[] sizes;
 
557
                        double spacing;
 
558
                        SizeRequestMode mode = calcHeights ? SizeRequestMode.WidthForHeight : SizeRequestMode.HeightForWidth;
 
559
                        
 
560
                        CalcDefaultSizes (mode, calcHeights, out visibleChildren, out fixedSizesByCell, out cellsWithExpand, out sizes, out spacing);
 
561
 
 
562
                        WidgetSize size = new WidgetSize (spacing);
 
563
                        foreach (var s in fixedSizesByCell.Values)
 
564
                                size += s;
 
565
                        return size;
 
566
                }
 
567
                
 
568
                protected override WidgetSize OnGetPreferredWidth ()
 
569
                {
 
570
                        return CalcSize (false);
 
571
                }
 
572
                
 
573
                protected override WidgetSize OnGetPreferredHeight ()
 
574
                {
 
575
                        return CalcSize (true);
 
576
                }
 
577
                
 
578
                protected override WidgetSize OnGetPreferredHeightForWidth (double width)
 
579
                {
 
580
                        CalcDefaultSizes (SizeRequestMode.HeightForWidth, width, false, false);
 
581
                        return CalcSize (true);
 
582
                }
 
583
                
 
584
                protected override WidgetSize OnGetPreferredWidthForHeight (double height)
 
585
                {
 
586
                        CalcDefaultSizes (SizeRequestMode.WidthForHeight, height, true, false);
 
587
                        return CalcSize (false);
 
588
                }
 
589
                
 
590
                int GetStartAttach (TablePlacement tp, bool calcHeight)
 
591
                {
 
592
                        if (calcHeight)
 
593
                                return tp.Top;
 
594
                        else
 
595
                                return tp.Left;
 
596
                }
 
597
                
 
598
                int GetEndAttach (TablePlacement tp, bool calcHeight)
 
599
                {
 
600
                        if (calcHeight)
 
601
                                return tp.Bottom;
 
602
                        else
 
603
                                return tp.Right;
 
604
                }
 
605
                
 
606
                WidgetSize GetPreferredSize (bool calcHeight, Widget w)
 
607
                {
 
608
                        if (calcHeight)
 
609
                                return w.Surface.GetPreferredHeight ();
 
610
                        else
 
611
                                return w.Surface.GetPreferredWidth ();
 
612
                }
 
613
                
 
614
                WidgetSize GetPreferredLengthForSize (SizeRequestMode mode, Widget w, double width)
 
615
                {
 
616
                        if (mode == SizeRequestMode.WidthForHeight)
 
617
                                return w.Surface.GetPreferredWidthForHeight (width);
 
618
                        else
 
619
                                return w.Surface.GetPreferredHeightForWidth (width);
 
620
                }
 
621
        }
 
622
        
 
623
        [ContentProperty("Child")]
 
624
        public class TablePlacement
 
625
        {
 
626
                IContainerEventSink<TablePlacement> parent;
 
627
                int left, right, top, bottom;
 
628
                AttachOptions xOptions, yOptions;
 
629
                Widget child;
 
630
                
 
631
                internal TablePlacement (IContainerEventSink<TablePlacement> parent, Widget child)
 
632
                {
 
633
                        this.parent = parent;
 
634
                        this.child = child;
 
635
                }
 
636
                
 
637
                internal double NextWidth;
 
638
                internal double NextHeight;
 
639
                internal double NextX;
 
640
                internal double NextY;
 
641
                
 
642
                public int Left {
 
643
                        get {
 
644
                                return left;
 
645
                        }
 
646
                        set {
 
647
                                left = value;
 
648
                                parent.ChildChanged (this, "Left");
 
649
                        }
 
650
                }
 
651
                
 
652
                public int Right {
 
653
                        get {
 
654
                                return right;
 
655
                        }
 
656
                        set {
 
657
                                right = value;
 
658
                                parent.ChildChanged (this, "Right");
 
659
                        }
 
660
                }
 
661
                
 
662
                public int Top {
 
663
                        get {
 
664
                                return top;
 
665
                        }
 
666
                        set {
 
667
                                top = value;
 
668
                                parent.ChildChanged (this, "Top");
 
669
                        }
 
670
                }
 
671
                
 
672
                public int Bottom {
 
673
                        get {
 
674
                                return bottom;
 
675
                        }
 
676
                        set {
 
677
                                bottom = value;
 
678
                                parent.ChildChanged (this, "Bottom");
 
679
                        }
 
680
                }
 
681
                
 
682
                public Widget Child {
 
683
                        get { return child; }
 
684
                        set {
 
685
                                var old = child;
 
686
                                child = value;
 
687
                                parent.ChildReplaced (this, old, value);
 
688
                        }
 
689
                }
 
690
                
 
691
                public AttachOptions XOptions {
 
692
                        get { return xOptions; }
 
693
                        set {
 
694
                                xOptions = value; 
 
695
                                parent.ChildChanged (this, "XOptions");
 
696
                        }
 
697
                }
 
698
                
 
699
                public AttachOptions YOptions {
 
700
                        get { return yOptions; }
 
701
                        set {
 
702
                                yOptions = value; 
 
703
                                parent.ChildChanged (this, "YOptions");
 
704
                        }
 
705
                }
 
706
        }
 
707
        
 
708
        [Flags]
 
709
        public enum AttachOptions
 
710
        {
 
711
                Expand = 1,
 
712
                Fill = 2,
 
713
                Shrink = 4
 
714
        }
 
715
}
 
716