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

« back to all changes in this revision

Viewing changes to external/xwt/Xwt/Xwt/Box.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
// Box.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
 
 
35
namespace Xwt
 
36
{
 
37
        public class Box: Widget
 
38
        {
 
39
                ChildrenCollection<BoxPlacement> children;
 
40
                Orientation direction;
 
41
                double spacing = 6;
 
42
                
 
43
                protected new class WidgetBackendHost: Widget.WidgetBackendHost, ICollectionEventSink<BoxPlacement>, IContainerEventSink<BoxPlacement>
 
44
                {
 
45
                        public void AddedItem (BoxPlacement item, int index)
 
46
                        {
 
47
                                ((Box)Parent).OnAdd (item.Child, item);
 
48
                        }
 
49
 
 
50
                        public void RemovedItem (BoxPlacement item, int index)
 
51
                        {
 
52
                                ((Box)Parent).OnRemove (item.Child);
 
53
                        }
 
54
 
 
55
                        public void ChildChanged (BoxPlacement child, string hint)
 
56
                        {
 
57
                                ((Box)Parent).OnChildChanged (child, hint);
 
58
                        }
 
59
 
 
60
                        public void ChildReplaced (BoxPlacement child, Widget oldWidget, Widget newWidget)
 
61
                        {
 
62
                                ((Box)Parent).OnReplaceChild (child, oldWidget, newWidget);
 
63
                        }
 
64
                }
 
65
                
 
66
                protected override BackendHost CreateBackendHost ()
 
67
                {
 
68
                        return new WidgetBackendHost ();
 
69
                }
 
70
                
 
71
                IBoxBackend Backend {
 
72
                        get { return (IBoxBackend) BackendHost.Backend; }
 
73
                }
 
74
                
 
75
                internal Box (Orientation dir)
 
76
                {
 
77
                        children = new ChildrenCollection<BoxPlacement> ((WidgetBackendHost)BackendHost);
 
78
                        direction = dir;
 
79
                }
 
80
                
 
81
                public double Spacing {
 
82
                        get { return spacing; }
 
83
                        set { spacing = value; OnPreferredSizeChanged (); }
 
84
                }
 
85
                
 
86
                public ChildrenCollection<BoxPlacement> Placements {
 
87
                        get { return children; }
 
88
                }
 
89
                
 
90
                public IEnumerable<Widget> Children {
 
91
                        get { return children.Select (c => c.Child); }
 
92
                }
 
93
                
 
94
                public void PackStart (Widget widget)
 
95
                {
 
96
                        PackStart (widget, BoxMode.None, 0);
 
97
                }
 
98
                
 
99
                public void PackStart (Widget widget, BoxMode mode)
 
100
                {
 
101
                        PackStart (widget, mode, 0);
 
102
                }
 
103
                
 
104
                public void PackStart (Widget widget, BoxMode mode, int padding)
 
105
                {
 
106
                        Pack (widget, mode, padding, PackOrigin.Start);
 
107
                }
 
108
                
 
109
                public void PackEnd (Widget widget)
 
110
                {
 
111
                        PackEnd (widget, BoxMode.None, 0);
 
112
                }
 
113
                
 
114
                public void PackEnd (Widget widget, BoxMode mode)
 
115
                {
 
116
                        PackEnd (widget, mode, 0);
 
117
                }
 
118
                
 
119
                public void PackEnd (Widget widget, BoxMode mode, int padding)
 
120
                {
 
121
                        Pack (widget, mode, padding, PackOrigin.End);
 
122
                }
 
123
                
 
124
                void Pack (Widget widget, BoxMode mode, int padding, PackOrigin ptype)
 
125
                {
 
126
                        var p = new BoxPlacement ((WidgetBackendHost)BackendHost, widget);
 
127
                        p.BoxMode = mode;
 
128
                        p.Padding = padding;
 
129
                        p.PackOrigin = ptype;
 
130
                        children.Add (p);
 
131
                }
 
132
                
 
133
                public bool Remove (Widget widget)
 
134
                {
 
135
                        for (int n=0; n<children.Count; n++) {
 
136
                                if (children[n].Child == widget) {
 
137
                                        children.RemoveAt (n);
 
138
                                        return true;
 
139
                                }
 
140
                        }
 
141
                        return false;
 
142
                }
 
143
                
 
144
                /// <summary>
 
145
                /// Removes all children
 
146
                /// </summary>
 
147
                public void Clear ()
 
148
                {
 
149
                        children.Clear ();
 
150
                }
 
151
                
 
152
                void OnAdd (Widget child, BoxPlacement placement)
 
153
                {
 
154
                        RegisterChild (child);
 
155
                        Backend.Add ((IWidgetBackend)GetBackend (child));
 
156
                        OnPreferredSizeChanged ();
 
157
                }
 
158
                
 
159
                void OnRemove (Widget child)
 
160
                {
 
161
                        UnregisterChild (child);
 
162
                        Backend.Remove ((IWidgetBackend)GetBackend (child));
 
163
                        OnPreferredSizeChanged ();
 
164
                }
 
165
                
 
166
                void OnChildChanged (BoxPlacement placement, object hint)
 
167
                {
 
168
                        OnPreferredSizeChanged ();
 
169
                }
 
170
                
 
171
                internal protected virtual void OnReplaceChild (BoxPlacement placement, Widget oldWidget, Widget newWidget)
 
172
                {
 
173
                        if (oldWidget != null)
 
174
                                OnRemove (oldWidget);
 
175
                        OnAdd (newWidget, placement);
 
176
                }
 
177
                
 
178
                protected override void OnReallocate ()
 
179
                {
 
180
                        var size = Backend.Size;
 
181
                        if (size.Width == 0 || size.Height == 0)
 
182
                                return;
 
183
                        
 
184
                        var visibleChildren = children.Where (c => c.Child.Visible).ToArray ();
 
185
                        IWidgetBackend[] widgets = new IWidgetBackend [visibleChildren.Length];
 
186
                        Rectangle[] rects = new Rectangle [visibleChildren.Length];
 
187
                        
 
188
                        if (direction == Orientation.Horizontal) {
 
189
                                CalcDefaultSizes (Surface.SizeRequestMode, size.Width, size.Height);
 
190
                                double xs = 0;
 
191
                                double xe = size.Width + spacing;
 
192
                                for (int n=0; n<visibleChildren.Length; n++) {
 
193
                                        var bp = visibleChildren [n];
 
194
                                        if (bp.PackOrigin == PackOrigin.End)
 
195
                                                xe -= bp.NextSize + spacing;
 
196
                                        double x = bp.PackOrigin == PackOrigin.Start ? xs : xe;
 
197
                                        widgets[n] = (IWidgetBackend)GetBackend (bp.Child);
 
198
                                        rects[n] = new Rectangle (x, 0, bp.NextSize >= 0 ? bp.NextSize : 0, size.Height >= 0 ? size.Height : 0);
 
199
                                        if (bp.PackOrigin == PackOrigin.Start)
 
200
                                                xs += bp.NextSize + spacing;
 
201
                                }
 
202
                        } else {
 
203
                                CalcDefaultSizes (Surface.SizeRequestMode, size.Height, size.Width);
 
204
                                double ys = 0;
 
205
                                double ye = size.Height + spacing;
 
206
                                for (int n=0; n<visibleChildren.Length; n++) {
 
207
                                        var bp = visibleChildren [n];
 
208
                                        if (bp.PackOrigin == PackOrigin.End)
 
209
                                                ye -= bp.NextSize + spacing;
 
210
                                        double y = bp.PackOrigin == PackOrigin.Start ? ys : ye;
 
211
                                        widgets[n] = (IWidgetBackend)GetBackend (bp.Child);
 
212
                                        rects[n] = new Rectangle (0, y, size.Width >= 0 ? size.Width : 0, bp.NextSize >= 0 ? bp.NextSize : 0);
 
213
                                        if (bp.PackOrigin == PackOrigin.Start)
 
214
                                                ys += bp.NextSize + spacing;
 
215
                                }
 
216
                        }
 
217
                        Backend.SetAllocation (widgets, rects);
 
218
                        
 
219
                        if (!Application.EngineBackend.HandlesSizeNegotiation) {
 
220
                                foreach (var bp in visibleChildren)
 
221
                                        bp.Child.Surface.Reallocate ();
 
222
                        }
 
223
                }
 
224
                
 
225
                void CalcDefaultSizes (SizeRequestMode mode, double totalSize, double lengthConstraint)
 
226
                {
 
227
                        bool calcHeights = direction == Orientation.Vertical;
 
228
                        bool useLengthConstraint = mode == SizeRequestMode.HeightForWidth && calcHeights || mode == SizeRequestMode.WidthForHeight && !calcHeights;
 
229
                        int nexpands = 0;
 
230
                        double naturalSize = 0;
 
231
                        
 
232
                        var visibleChildren = children.Where (b => b.Child.Visible).ToArray ();
 
233
                        var sizes = new Dictionary<BoxPlacement,WidgetSize> ();
 
234
                        
 
235
                        // Get the natural size of each child
 
236
                        foreach (var bp in visibleChildren) {
 
237
                                WidgetSize s;
 
238
                                if (useLengthConstraint)
 
239
                                        s = GetPreferredLengthForSize (mode, bp.Child, lengthConstraint);
 
240
                                else
 
241
                                        s = GetPreferredSize (calcHeights, bp.Child);
 
242
                                sizes [bp] = s;
 
243
                                naturalSize += s.NaturalSize;
 
244
                                bp.NextSize = s.NaturalSize;
 
245
                                if ((bp.BoxMode & BoxMode.Expand) != 0)
 
246
                                        nexpands++;
 
247
                        }
 
248
                        
 
249
                        double remaining = totalSize - naturalSize - (spacing * (double)(visibleChildren.Length - 1));
 
250
                        if (remaining < 0) {
 
251
                                // The box is not big enough to fit the widgets using its natural size.
 
252
                                // We have to shrink the widgets.
 
253
                                
 
254
                                // List of widgets that we have to shrink
 
255
                                var toShrink = new List<BoxPlacement> (visibleChildren);
 
256
                                
 
257
                                // The total amount we have to shrink
 
258
                                double shrinkSize = -remaining;
 
259
                                
 
260
                                while (toShrink.Count > 0 && shrinkSize > 0) {
 
261
                                        SizeSplitter sizePart = new SizeSplitter (shrinkSize, toShrink.Count);
 
262
                                        shrinkSize = 0;
 
263
                                        for (int i=0; i < toShrink.Count; i++) {
 
264
                                                var bp = toShrink[i];
 
265
                                                bp.NextSize -= sizePart.NextSizePart ();
 
266
                                                
 
267
                                                WidgetSize size = sizes [bp];
 
268
                                                
 
269
                                                if (bp.NextSize < size.MinSize) {
 
270
                                                        // If the widget can't be shrinked anymore, we remove it from the shrink list
 
271
                                                        // and increment the remaining shrink size. We'll loop again and this size will be
 
272
                                                        // substracted from the cells which can still be reduced
 
273
                                                        shrinkSize += (size.MinSize - bp.NextSize);
 
274
                                                        bp.NextSize = size.MinSize;
 
275
                                                        toShrink.RemoveAt (i);
 
276
                                                        i--;
 
277
                                                }
 
278
                                        }
 
279
                                }
 
280
                        }
 
281
                        else {
 
282
                                var expandRemaining = new SizeSplitter (remaining, nexpands);
 
283
                                foreach (var bp in visibleChildren) {
 
284
                                        if ((bp.BoxMode & BoxMode.Expand) != 0)
 
285
                                                bp.NextSize += expandRemaining.NextSizePart ();
 
286
                                }
 
287
                        }
 
288
                }
 
289
                
 
290
                protected override WidgetSize OnGetPreferredWidth ()
 
291
                {
 
292
                        WidgetSize s = new WidgetSize ();
 
293
                        
 
294
                        if (direction == Orientation.Horizontal) {
 
295
                                int count = 0;
 
296
                                foreach (var cw in Children.Where (b => b.Visible)) {
 
297
                                        s += cw.Surface.GetPreferredWidth ();
 
298
                                        count++;
 
299
                                }
 
300
                                if (count > 0)
 
301
                                        s += spacing * (double)(count - 1);
 
302
                        } else {
 
303
                                foreach (var cw in Children.Where (b => b.Visible))
 
304
                                        s = s.UnionWith (cw.Surface.GetPreferredWidth ());
 
305
                        }
 
306
                        return s;
 
307
                }
 
308
                
 
309
                protected override WidgetSize OnGetPreferredHeight ()
 
310
                {
 
311
                        WidgetSize s = new WidgetSize ();
 
312
                        
 
313
                        if (direction == Orientation.Vertical) {
 
314
                                int count = 0;
 
315
                                foreach (var cw in Children.Where (b => b.Visible)) {
 
316
                                        s += cw.Surface.GetPreferredHeight ();
 
317
                                        count++;
 
318
                                }
 
319
                                if (count > 0)
 
320
                                        s += spacing * (double)(count - 1);
 
321
                        } else {
 
322
                                foreach (var cw in Children.Where (b => b.Visible))
 
323
                                        s = s.UnionWith (cw.Surface.GetPreferredHeight ());
 
324
                        }
 
325
                        return s;
 
326
                }
 
327
                
 
328
                protected override WidgetSize OnGetPreferredHeightForWidth (double width)
 
329
                {
 
330
                        return GetPreferredLengthForSize (SizeRequestMode.HeightForWidth, width);
 
331
                }
 
332
                
 
333
                protected override WidgetSize OnGetPreferredWidthForHeight (double height)
 
334
                {
 
335
                        return GetPreferredLengthForSize (SizeRequestMode.WidthForHeight, height);
 
336
                }
 
337
                
 
338
                WidgetSize GetPreferredLengthForSize (SizeRequestMode mode, double width)
 
339
                {
 
340
                        WidgetSize s = new WidgetSize ();
 
341
                        
 
342
                        if ((direction == Orientation.Horizontal && mode == SizeRequestMode.HeightForWidth) || (direction == Orientation.Vertical && mode == SizeRequestMode.WidthForHeight)) {
 
343
                                CalcDefaultSizes (mode, width, -1);
 
344
                                foreach (var bp in children.Where (b => b.Child.Visible)) {
 
345
                                        s = s.UnionWith (GetPreferredLengthForSize (mode, bp.Child, bp.NextSize));
 
346
                                }
 
347
                        }
 
348
                        else {
 
349
                                int count = 0;
 
350
                                foreach (var bp in children.Where (b => b.Child.Visible)) {
 
351
                                        s += GetPreferredLengthForSize (mode, bp.Child, width);
 
352
                                        count++;
 
353
                                }
 
354
                                if (count > 0)
 
355
                                        s += spacing * (double)(count - 1);
 
356
                        }
 
357
                        return s;
 
358
                }
 
359
                
 
360
                WidgetSize GetPreferredSize (bool calcHeight, Widget w)
 
361
                {
 
362
                        if (calcHeight)
 
363
                                return w.Surface.GetPreferredHeight ();
 
364
                        else
 
365
                                return w.Surface.GetPreferredWidth ();
 
366
                }
 
367
                
 
368
                WidgetSize GetPreferredLengthForSize (SizeRequestMode mode, Widget w, double width)
 
369
                {
 
370
                        if (mode == SizeRequestMode.WidthForHeight)
 
371
                                return w.Surface.GetPreferredWidthForHeight (width);
 
372
                        else
 
373
                                return w.Surface.GetPreferredHeightForWidth (width);
 
374
                }
 
375
        }
 
376
        
 
377
        [Flags]
 
378
        public enum BoxMode
 
379
        {
 
380
                None = 0,
 
381
                Fill = 1,
 
382
                Expand = 2,
 
383
                FillAndExpand = 3
 
384
        }
 
385
        
 
386
        [ContentProperty("Child")]
 
387
        public class BoxPlacement
 
388
        {
 
389
                IContainerEventSink<BoxPlacement> parent;
 
390
                int position;
 
391
                BoxMode boxMode = BoxMode.None;
 
392
                int padding;
 
393
                PackOrigin packType = PackOrigin.Start;
 
394
                Widget child;
 
395
                
 
396
                internal BoxPlacement (IContainerEventSink<BoxPlacement> parent, Widget child)
 
397
                {
 
398
                        this.parent = parent;
 
399
                        this.child = child;
 
400
                }
 
401
                
 
402
                internal double NextSize;
 
403
                
 
404
                public int Position {
 
405
                        get {
 
406
                                return this.position;
 
407
                        }
 
408
                        set {
 
409
                                position = value;
 
410
                                parent.ChildChanged (this, "Position");
 
411
                        }
 
412
                }
 
413
                
 
414
                [DefaultValue (BoxMode.None)]
 
415
                public BoxMode BoxMode {
 
416
                        get {
 
417
                                return this.boxMode;
 
418
                        }
 
419
                        set {
 
420
                                boxMode = value;
 
421
                                parent.ChildChanged (this, "BoxMode");
 
422
                        }
 
423
                }
 
424
 
 
425
                [DefaultValue (0)]
 
426
                public int Padding {
 
427
                        get {
 
428
                                return this.padding;
 
429
                        }
 
430
                        set {
 
431
                                padding = value;
 
432
                                parent.ChildChanged (this, "Padding");
 
433
                        }
 
434
                }
 
435
 
 
436
                [DefaultValue (PackOrigin.Start)]
 
437
                public PackOrigin PackOrigin {
 
438
                        get {
 
439
                                return this.packType;
 
440
                        }
 
441
                        set {
 
442
                                packType = value;
 
443
                                parent.ChildChanged (this, "PackType");
 
444
                        }
 
445
                }
 
446
                
 
447
                public Widget Child {
 
448
                        get { return child; }
 
449
                        set {
 
450
                                var old = child;
 
451
                                child = value;
 
452
                                parent.ChildReplaced (this, old, value);
 
453
                        }
 
454
                }
 
455
        }
 
456
        
 
457
        public enum PackOrigin
 
458
        {
 
459
                Start,
 
460
                End
 
461
        }
 
462
        
 
463
        class SizeSplitter
 
464
        {
 
465
                int rem;
 
466
                int part;
 
467
                
 
468
                public SizeSplitter (double total, int numParts)
 
469
                {
 
470
                        if (numParts > 0) {
 
471
                                part = ((int)total) / numParts;
 
472
                                rem = ((int)total) % numParts;
 
473
                        }
 
474
                }
 
475
                
 
476
                public double NextSizePart ()
 
477
                {
 
478
                        if (rem > 0) {
 
479
                                rem--;
 
480
                                return part + 1;
 
481
                        }
 
482
                        else
 
483
                                return part;
 
484
                }
 
485
        }
 
486
}
 
487