Opened 9 years ago

Closed 8 years ago

Last modified 8 years ago

#11030 closed enhancement (fixed)

BorderContainer: ability to hold N vertical/horizontal containers

Reported by: rcarver Owned by: bill
Priority: high Milestone: 1.6
Component: Dijit Version: 1.4.0
Keywords: BorderContainer SplitContainer Cc:
Blocked By: Blocking:

Description (last modified by bill)

SplitContainer has been deprecated, but there is no way to get N vertical/horizontal containers in a region with BorderContainer like there is with SplitContainer.

Suggestion was made to nest BorderContainers, but that's not acceptable. Border and split handlers do not show as they would with a SplitContainer.

Change History (16)

comment:1 Changed 9 years ago by rcarver

Sorry, should have noted N where N > 3.

comment:2 Changed 9 years ago by bill

Description: modified (diff)
Milestone: tbdfuture
Owner: set to cb1kenobi
Summary: dijit.BorderContainer cannot hold N vertical/horizontal containersBorderContainer: ability to hold N vertical/horizontal containers
Type: defectenhancement

I talked to Chris about a month ago about rewriting BorderContainer to handle arbitrary layouts, so assigning this ticket to him.

comment:3 Changed 9 years ago by bill

Description: modified (diff)

Here are some notes I made after the discussion mentioned above:

Chris implemented a version of BorderContainer that could handle 7 children, and in doing so eliminated the branching on design=headline or design.sidebar. The seven panes were:

+-----------------------+
|         header        |
+------+---------+------+
|      |   top   |      |
| left |  center | right|
|      |  bottom |      |
+------+---------+------+
|        footer         |
+-----------------------+

However, I think (and Chris agreed) that BorderContainer could follow a generic algorithm like the current dijit.layout.LayoutContainer does... or from another angle, the current LayoutContainer class could be enhanced to support draggable splitters and collapsible panes. The goal would be to reduce the code size, and allow for more complicated layouts, like having 2 headers (for example: a menubar and a toolbar).

The current dijit.layout.LayoutContainer's algorithm is implemented here: http://bugs.dojotoolkit.org/browser/dijit/trunk/layout/_LayoutWidget.js#L219. Basically, it lays panes down from the outside in, according to a specified order of panes, so the free space in the center keeps getting smaller and smaller.

If a user wanted to achieve a 7 pane layout using the current dijit.layout.LayoutContainer, they would simply specify 7 children in the following precendence order:

  1. header (layoutAlign=top)
  2. footer (layoutAlign=bottom)
  3. left (layoutAlign=left)
  4. right (layoutAlign=right)
  5. top (layoutAlign=top)
  6. bottom (layoutAlign=bottom)
  7. client (layoutAlign=client)

The order is what determines which pane takes "precedence"... in the above example "header" comes before "left" so the LayoutContainer's upper-left corner is filled by "header" rather than "left"... and so on recursively.

Alternately the splitters could also be treated as children:

  1. header (layoutAlign=top)
  2. header splitter (layoutAlign=top)
  3. footer (layoutAlign=bottom)
  4. footer (layoutAlign=bottom)
  5. left (layoutAlign=left)
  6. left splitter (layoutAlign=left)
  7. right (layoutAlign=right)
  8. right splitter (layoutAlign=right)
  9. top (layoutAlign=top)
  10. top splitter (layoutAlign=top)
  11. bottom (layoutAlign=bottom)
  12. bottom splitter (layoutAlign=bottom)
  13. client (layoutAlign=client)

I see two issues with using that technique for a new BorderContainer, but I think they are fairly easy to overcome:

  1. calculating the maximum amount that the user can drag a splitter

When dragging the splitter on (for example) the header of the 7 pane example above, the max size the header pane can become is:

LayoutContainerHeight - (footerHeight + topHeight + bottomHeight)

To calculate that generically I think it's just a question of adding up the heights of all the elements with layoutAlign==top or layoutAlign=bottom:

LayoutContainerHeight - (sum of heights of other horizontal panes)

And similarly to calculate the max size that a vertical pane can be expanded to

LayoutContainerWidth - (sum of widths of other vertical panes)
  1. avoiding calling resize() on panes that haven't moved

The efficiency issue is both with unnecessary marginBox() (as a getter) calls, and more importantly, unnecessary resize() calls on complicated child widgets like Grid.

So the goals for efficiency should be:

  • don't call resize() on a pane if it's the same size as before
  • don't call marginBox() (as a getter) unnecessarily. apart from initial setup (when we query the height of horizontal panes and the width of vertical panes), we never need to query the width of the vertical panes, and we only need to query the height of the horizontal panes if the width has changed. (the latter case might be true when a width change causes the toolbar's icons overflow to two rows).

What if LayoutContainer remembered the size of each pane, and then whenever there was a resize() it would go through each pane, getting ready to resize it, but if it turned out the same size as before it would skip it? Plus, it would only call marginBox() when it needed to, as listed above.

For example, in a 7-pane layout say that the user increased the height of the header pane. The code would process the panes like this (in this order):

  1. header pane. it used to be 500x200, and now I'm told that it's size has been changed to 500x250. It's size has changed, so call resize() on it, to reflect that change.
  2. footer pane. It used to be 500x200, and neither it's height nor width has changed, so we don't call resize() on it.
  3. left pane. It used to be 100x500, but now it's 100x450, so call resize(). (the height changed but the width didn't, and we don't need to do a marginBox() to get the width since we already know it from last time.)
  4. right pane: same
  5. top pane: It used to be 300x100, and it still is, no resizing
  6. bottom pane: same 7 center pane: resize

Resizing a vertical pane is a bit different:

  1. header pane: no size change, skip
  2. footer pane: no size change, skip
  3. left pane: resized from 100x500 --> 150x500, resize
  4. right pane: no change
  5. top pane: width decreased by 50px, so maybe the height has changed? change width, then query height (same as current dijit.layout.LayoutContainer does)
  6. bottom pane: basically same as top pane
  7. center pane: resize

comment:4 Changed 8 years ago by bill

Chris, any progress on this?

comment:5 Changed 8 years ago by bill

(In [23340]) Convert BorderContainer to use dijit.layout.layoutChildren() rather than custom layout code. Refs #11030, fixes #12078 !strict.

comment:6 Changed 8 years ago by bill

(In [23379]) Simplify code computing max size a pane can be expanded to, in order to support future where there are multiple panes with the same region (ex: two top panes). As before, a problem remains with the slider disappearing when the center pane has a scrollbar and is shrunk less than about 50px tall, since the browser demands that much space just to draw the scrollbar.

Refs #11030 !strict.

comment:7 Changed 8 years ago by bill

(In [23380]) Fix [23340] regression where you can't see the splitters being dragged when liveSplitters=false. Refs #11030, fixes #12078 !strict.

comment:8 Changed 8 years ago by bill

(In [23440]) Fix keyboard control broken in [23379], plus miscalculation of max size when pane has maxSize parameter, refs #11030 !strict.

comment:9 Changed 8 years ago by bill

(In [23441]) Now that #11430 is fixed, remove workaround code from test, refs #11430, #11030.

comment:10 Changed 8 years ago by bill

(In [23442]) Remove attributes which assume only one widget in each region: _left, _top, _bottom, etc. Refs #11030 !strict.

comment:11 Changed 8 years ago by bill

Milestone: future1.6
Owner: changed from cb1kenobi to bill
Status: newassigned

comment:12 Changed 8 years ago by bill

Resolution: fixed
Status: assignedclosed

(In [23443]) 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.

comment:13 Changed 8 years ago by bill

(In [23444]) More tests, refs #11030 !strict.

comment:14 Changed 8 years ago by bill

(In [23446]) Test changes needed after removing _top and _left attributes, plus fixing filename, refs #11030.

comment:15 Changed 8 years ago by bill

(In [23650]) Fix destroy of splitters on BorderContainer destroy, and add tests for proper cleanup on destroyRecursive() and removeChild().

Also moved non-robot tests to regular DOH BorderContainer.html test file, and replaced button clicks with direct function calls.

Refs #11030 ([23442]), fixes #12191 !strict.

comment:16 Changed 8 years ago by bill

(In [23900]) Don't create gutter for center pane. It was being created accidentally. It's an old bug, but causes problems in 1.6 where the gutter overlaps the real BorderContainer panes, if app has changed z-index of gutters.

Refs #11030, #12351 !strict.

Note: See TracTickets for help on using tickets.