Permalink
Browse files

Support assigning multiple panes to the same !BorderContainer region,…

… so !BorderContainer can be used in place of !SplitContainer or !LayoutContainer without requiring nested !BorderContainer widgets.

Panes can specify a "layoutPriority" attribute that controls which panes are closer to the edges of the !BorderContainer whenever two panes have the same region, or top and left / bottom and right panes are competing.

Also includes a bug fix for display of dragged bottom/right splitter when it's moved beyond its maximum allowed value.

Fixes #11030 !strict.
  • Loading branch information...
wkeese committed Dec 26, 2010
1 parent accef4e commit efe42d1ba357e76141f79c2c2aa7762bd9988977
View
@@ -15,13 +15,15 @@ dojo.declare(
// include optional splitters (splitter="true") to make them resizable by the user. The remaining
// space is designated for the center region.
//
- // NOTE: Splitters must not be more than 50 pixels in width.
- //
// The outer size must be specified on the BorderContainer node. Width must be specified for the sides
// and height for the top and bottom, respectively. No dimensions should be specified on the center;
// it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
// "left" and "right" except that they will be reversed in right-to-left environments.
//
+ // For complex layouts, multiple children can be specified for a single region. In this case, the
+ // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
+ // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
+ // instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes.
// example:
// | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
// | style="width: 400px; height: 300px;">
@@ -57,11 +59,6 @@ dojo.declare(
// Optional hook to override the default Splitter widget used by BorderContainer
_splitterClass: "dijit.layout._Splitter",
- constructor: function(){
- this._splitters = {};
- this._splitterThickness = {};
- },
-
postMixInProperties: function(){
// change class name to indicate that BorderContainer is being used purely for
// layout (like LayoutContainer) rather than for pretty formatting.
@@ -213,21 +210,28 @@ dojo.declare(
// Generate list of wrappers of my children in the order that I want layoutChildren()
// to process them (i.e. from the outside to the inside)
- var wrappers = dojo.map(this.getChildren(), function(child){
+ var wrappers = dojo.map(this.getChildren(), function(child, idx){
return {
pane: child,
- firstPriority: child.region == "center" ? Infinity : 0,
- secondPriority: (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1)
+ weight: [
+ child.region == "center" ? Infinity : 0,
+ child.layoutPriority,
+ (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
+ idx
+ ]
};
}, this);
wrappers.sort(function(a, b){
- var res = a.firstPriority != b.firstPriority ?
- a.firstPriority - b.firstPriority :
- a.secondPriority - b.secondPriority;
- return res;
+ var aw = a.weight, bw = b.weight;
+ for(var i=0; i<aw.length; i++){
+ if(aw[i] != bw[i]){
+ return aw[i] - bw[i];
+ }
+ }
+ return 0;
});
- // Make new list, combining the external children with splitters and gutters
+ // Make new list, combining the externally specified children with splitters and gutters
var childrenAndSplitters = [];
dojo.forEach(wrappers, function(wrapper){
var pane = wrapper.pane;
@@ -272,6 +276,12 @@ dojo.extend(dijit._Widget, {
// See the `dijit.layout.BorderContainer` description for details.
region: '',
+ // layoutPriority: [const] Number
+ // Parameter for children of `dijit.layout.BorderContainer`.
+ // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
+ // between children with a lower layoutPriority.
+ layoutPriority: 0,
+
// splitter: [const] Boolean
// Parameter for child of `dijit.layout.BorderContainer` where region != "center".
// If true, enables user to resize the widget by putting a draggable splitter between
@@ -403,7 +413,7 @@ dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
layoutFunc(boundChildSize);
}
// TODO: setting style directly (usually) sets content box size, need to set margin box size
- splitterStyle[splitterAttr] = delta + splitterStart + (boundChildSize - childSize) + "px";
+ splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
}),
dojo.connect(de, "ondragstart", dojo.stopEvent),
dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
@@ -487,7 +497,6 @@ dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
templateString: '<div class="dijitGutter" role="presentation"></div>',
- // TODO: unneeded? why set this.horizontal?
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
@@ -22,33 +22,36 @@
dojo.addOnLoad(function(){
doh.robot.initRobot('../test_BorderContainer_nested.html');
- doh.register("API", [
- function initialConditions(){
- checkBCpanes(dijit.byId("bc1"));
- doh.t(isVisible(dijit.byId("bc1")));
- checkInside(dijit.byId("bc1"), dijit.byId("tc"));
- }
- ]);
-
- doh.register("testTab2", [
- {
- name: "tab2",
- timeout: 10000,
- runTest: function(t){
- var d = new doh.Deferred();
-
- doh.robot.mouseMoveAt("tc_tablist_bc2", 500);
- doh.robot.mouseClick({left:true}, 500);
-
- doh.robot.sequence(d.getTestCallback(function(){
- checkBCpanes(dijit.byId("bc2"));
- doh.t(isVisible(dijit.byId("bc2")));
- checkInside(dijit.byId("bc2"), dijit.byId("tc"));
- }), 1000);
-
- return d;
- }
- }
+ doh.register("initial layout", [
+ function nestedBC(){
+ checkBCpanes(dijit.byId("nbc"));
+ doh.t(isVisible(dijit.byId("nbc")));
+ checkInside("nbc", "tc");
+ },
+ function layoutPriorityBC(){
+ dijit.byId("tc").selectChild(dijit.byId("pbc"));
+ doh.t(isVisible(dijit.byId("pbc")), "second pane visible");
+
+ // Check layout of BorderContainer panes relative to each other
+ checkAbove("top bar above second top bar", "top1", "top2");
+ checkAbove("second top bar above inner top bar", "top2", "top3");
+ checkLeft("outer left vs. inner left", "left1", "left2");
+ checkLeft("inner left vs. inner top", "left2", "top3");
+ checkLeft("inner left vs. inner bottom", "left2", "bottom3");
+ checkAbove("inner top vs. center", "top3", "center");
+ checkAbove("center vs. inner bottom", "center", "bottom3");
+ checkLeft("inner right vs. inner top", "top3", "right2");
+ checkLeft("inner right vs. center", "center", "right2");
+ checkAbove("inner bottom bar above second bottom bar", "bottom3", "bottom2");
+ checkAbove("second bottom bar above bottom bar", "bottom2", "bottom1");
+
+ // Check that all panes within BC borders
+ var children = dijit.byId("pbc").getChildren();
+ doh.is(11, children.length, "# of children");
+ dojo.forEach(children, function(child){
+ checkInside(child, dijit.byId("pbc"));
+ });
+ }
]);
doh.run();
@@ -1,19 +1,26 @@
- function checkInside(/*Widget*/ child, /*Widget*/ parent){
+ function checkInside(/*Widget*/ child, /*Widget*/ parent, /*String?*/ comment){
// summary:
// Test that child is fully inside of parent
+
+ child = dijit.byId(child);
+ parent = dijit.byId(parent);
+
var cp = dojo.position(child.domNode, true),
pp = dojo.position(parent.domNode, true);
doh.t(
cp.y >= pp.y && cp.y+cp.h <= pp.y+pp.h &&
cp.x >= pp.x && cp.x+cp.w <= pp.x+pp.w,
- child.region + " inside " + parent.id + dojo.toJson(cp) + dojo.toJson(pp)
+ (comment ? comment + ": " : "") + child.region + " inside " + parent.id + dojo.toJson(cp) + dojo.toJson(pp)
);
}
function checkAbove(/*String*/ comment, /*Widget*/ above, /*Widget*/ below){
// summary:
// Test that child is fully above parent
+ above = dijit.byId(above);
+ below = dijit.byId(below);
+
var ap = dojo.position(above.domNode, true),
bp = dojo.position(below.domNode, true);
@@ -25,6 +32,9 @@
// summary:
// Test that child is fully left of parent
+ left = dijit.byId(left);
+ right = dijit.byId(right);
+
var lp = dojo.position(left.domNode, true),
rp = dojo.position(right.domNode, true);
@@ -36,7 +46,8 @@
function checkBCpanes(/*BorderContainer*/ bc){
// summary:
// Check that all the panes in this BorderContainer are in sane
- // positions relative to each other
+ // positions relative to each other. Assumes at most one pane
+ // in each region.
var children = bc.getChildren(),
regions = {};
@@ -30,11 +30,17 @@
<body class="claro">
<h2 class="testTitle">dijit.layout.BorderContainer tests</h2>
- <p>Basic layout</p>
+
+ <p>
+ The second tab holds a single BorderContainer but specifying layoutPriority on it's children to have multiple children
+ for each region.
+ </p>
+ <p>
+ The first tab holds a BorderContainer which nests another BorderContainer, simulating multiple children in a single region.
+ </p>
<div id="tc" data-dojo-type="dijit.layout.TabContainer" data-dojo-props='style:"width:800px;height:600px;"'>
- <div id="bc1" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='title:"Tab 1",
- style:"border: 2px solid black; width: 90%; height: 500px; padding: 10px;"'>
+ <div id="nbc" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='title:"Nested BC"'>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"left", style:"background-color: #acb386; width: 100px;"'>
left
</div>
@@ -48,19 +54,18 @@ <h2 class="testTitle">dijit.layout.BorderContainer tests</h2>
bottom bar
</div>
- <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='region:"center", design:"sidebar",
- style:"border: 2px solid black; padding: 0px;"'>
+ <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='region:"center", design:"sidebar"'>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"left", style:"background-color: #acb386; width: 100px;"'>
- left
+ left inner
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"right", style:"background-color: #acb386; width: 100px;"'>
- right
+ right inner
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"top", style:"background-color: #b39b86; height: 100px;"'>
- top bar
+ inner top bar
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"bottom", style:"background-color: #b39b86; height: 100px;"'>
- bottom bar
+ inner bottom bar
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"center", style:"background-color: #f5ffbf; padding: 10px;"'>
main panel with <a href="http://www.dojotoolkit.org/">a link</a>.<br />
@@ -74,44 +79,47 @@ <h2 class="testTitle">dijit.layout.BorderContainer tests</h2>
</div>
</div>
</div>
- <div id="bc2" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='title:"Tab 2"'>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"left", splitter:true, style:"background-color: #770; width: 100px;"'>
- left
+
+ <div id="pbc" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='title:"layoutPriority BC"'>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"left1", region:"left", splitter:true, layoutPriority: 3, style:"background-color: #770; width: 100px;"'>
+ left (layoutPriority == 3)
</div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"right", splitter:true, style:"background-color: #077; width: 100px;"'>
- right
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"right1", region:"right", splitter:true, layoutPriority: 3, style:"background-color: #770; width: 100px;"'>
+ right (layoutPriority == 3)
</div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"top", splitter:true, style:"background-color: #700; height: 100px;"'>
- top bar
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"top1", region:"top", splitter:true, layoutPriority: 1, style:"background-color: #077; color: white; height: 50px;"'>
+ top bar (layoutPriority == 1)
</div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"bottom", splitter:true, style:"background-color: #007; height: 100px;"'>
- bottom bar
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id: "bottom1", region:"bottom", splitter:true, layoutPriority: 1, style:"background-color: #077; color: white; height: 50px;"'>
+ bottom bar (layoutPriority == 1)
</div>
-
- <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props='region:"center", design:"sidebar",
- style:"border: 2px solid black; padding: 0px;"'>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"left", style:"background-color: #acb386; width: 100px;"'>
- left
- </div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"right", style:"background-color: #acb386; width: 100px;"'>
- right
- </div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"top", style:"background-color: #b39b86; height: 100px;"'>
- top bar
- </div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"bottom", style:"background-color: #b39b86; height: 100px;"'>
- bottom bar
- </div>
- <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='region:"center", style:"background-color: #f5ffbf; padding: 10px;"'>
- main panel with <a href="http://www.dojotoolkit.org/">a link</a>.<br />
- (to check we're copying children around properly).<br />
- <select data-dojo-type="dijit.form.FilteringSelect">
- <option value="1">foo</option>
- <option value="2">bar</option>
- <option value="3">baz</option>
- </select>
- Here's some text that comes AFTER the combo box.
- </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"top2", region:"top", splitter:true, layoutPriority: 2, style:"background-color: #f5ffbf; height: 50px;"'>
+ second top bar (layoutPriority == 2)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"bottom2", region:"bottom", splitter:true, layoutPriority: 2, style:"background-color: #f5ffbf; height: 50px;"'>
+ second bottom bar (layoutPriority == 2)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"left2", region:"left", splitter:true, layoutPriority: 4, style:"background-color: #acb386; width: 100px;"'>
+ inner left (layoutPriority == 4)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"right2", region:"right", splitter:true, layoutPriority: 4, style:"background-color: #acb386; width: 100px;"'>
+ inner right (layoutPriority == 4)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"top3", region:"top", splitter:true, layoutPriority: 5, style:"background-color: #b39b86; height: 50px;"'>
+ inner top bar (layoutPriority == 5)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"bottom3", region:"bottom", splitter:true, layoutPriority: 5, style:"background-color: #b39b86; height: 50px;"'>
+ inner bottom bar (layoutPriority == 5)
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props='id:"center", region:"center", style:"background-color: #f5ffbf; padding: 10px;"'>
+ main panel with <a href="http://www.dojotoolkit.org/">a link</a>.<br />
+ (to check we're copying children around properly).<br />
+ <select data-dojo-type="dijit.form.FilteringSelect">
+ <option value="1">foo</option>
+ <option value="2">bar</option>
+ <option value="3">baz</option>
+ </select>
+ Here's some text that comes AFTER the combo box.
</div>
</div>
</div>

0 comments on commit efe42d1

Please sign in to comment.