Container Sides - GimmickNG/AIRDock GitHub Wiki

There are five sides in the ContainerSide enumeration class, which are:

  • ContainerSide.FILL - Adds a panel to the same container
  • ContainerSide.LEFT - Adds a panel to the left subcontainer of the current container
  • ContainerSide.RIGHT - Adds a panel to the right subcontainer of the current container
  • ContainerSide.TOP - Adds a panel to the top subcontainer of the current container
  • ContainerSide.BOTTOM - Adds a panel to the bottom subcontainer of the current container

If there is no subcontainer, then a new subcontainer is created; this process occurs with two possible conditions:

Adding a subcontainer to an empty container

This happens when there are no panels attached to the container; recall that no IContainer instance can have both subcontainers and panels (without undefined behavior occurring). Suppose a container A is empty, and a container needs to be added to the RIGHT side; the tree view and the physical rendering is shown below:

              ┌───────────────────┐
              │                   │
    A         │                   │
    |         │         A         │
    *         │                   │
              │                   │
              └───────────────────┘

Then, a new container is created and added as a subcontainer to A (first as a weak link, and then committed; the intermediate step is left out, but it is worth noting). For the DefaultContainer implementation, another container is created, which is complementary to the current side (LEFT in this case):

              ┌─────────┬─────────┐
    A         │         │         │
   / \        │         │         │
  B   C       │    C    │    B    │
  |   |       │         │         │
  *   *       │         │         │
              └─────────┴─────────┘

That's pretty much it. Calling A.getSide(PanelContainerSide.RIGHT) returns B, and calling A.getSide(PanelContainerSide.LEFT) returns C.

Adding a subcontainer to a non-empty container

Since no IContainer can have both panels and subcontainers at the same time without undefined behavior occurring, the default behavior which occurs when a container needs to be added to a non-empty container, is to create a new container which is complementary to the container that is being created as a soft link, merge all the container's contents into this container, add the other container as a soft link, and commit right after.

Suppose a container A has 2 panels, b and c, and a new container B has to be added to the RIGHT side:

              ┌───────────────────┐
              │                   │
    A         │                   │
    |         │         A         │
  [b,c]       │                   │
              ╞═══╤═══╤═══════════╡
              │ b │ c │           │
              └───┴───┴───────────┘

Before adding B to the RIGHT side, a new container C is created as a soft link, and the contents are merged into it:

              ┌───────────────────┐
              │                   │
    A         │                   │
    : \       │         C         │
    C  B      │                   │
    |         │                   │
  [b,c]       ╞═══╤═══╤═══════════╡
              │ b │ c │           │
              └───┴───┴───────────┘

Which finally results in the following layout:

              ┌─────────┬─────────┐
              │         │         │
    A         │         │         │
   / \        │         │         │
  C   B       │    C    │    B    │
  |           │         │         │
[b,c]         ╞═══╤═══╤═╡         │
              │ b │ c │ │         │
              └───┴───┴─┴─────────┘

Because of the merging that occurs, a few points should be noted:

  1. No priority is given to any side.
  2. Because no priority is given to any side, the order in which containers are added matters.
  3. Serializing containers becomes a little trickier because the exact order in which panels are added affects the final layout.
  4. Non-complementary (aka anti-complementary) sides always result in new containers being created, and the existing contents merged down into the complementary container for the new side.

For an example of #1, #2, and #4, consider the following case:

A container A has to have two containers, B and C, added to LEFT and TOP respectively. What is the final layout of the container?

  1. Add container B to the LEFT side. A new container Z is created for the RIGHT side, and if A had any contents, they would have been merged into Z. So far, so good:
              ┌─────────┬─────────┐
    A         │         │         │
   / \        │         │         │
  B   Z       │    B    │    Z    │
  |   |       │         │         │
  *   *       │         │         │
              └─────────┴─────────┘
  1. Add container C to A. Here, though, the current "side" of A is LEFT. C is being added to the TOP side, but TOP is anti-complementary to LEFT; as a result, A's entire subtree will be merged into a new container, Y, and C will be added to the TOP side:
              ┌───────────────────┐
     A        │                   │
   /   \      │         C         │
  C     Y     │                   │
  |    / \    ├─────────┬─────────┤
  *   B   Z   │         │         │
      |   |   │    B    │    Z    │
      *   *   │         │         │
              └─────────┴─────────┘

On the other hand, if C were added first and then B, i.e. TOP followed by LEFT, then the following steps would occur instead:

  1. Add container C to the TOP side. A new container Z is created for the BOTTOM side, and if A had any contents, they would have been merged into Z:
              ┌───────────────────┐
              │                   │
    A         │         C         │
   / \        │                   │
  C   Z       ├───────────────────┤
  |   |       │                   │
  *   *       │         Z         │
              │                   │
              └───────────────────┘
  1. Add container B to A. Here, though, the current "side" of A is TOP. B is being added to the LEFT side, but LEFT is anti-complementary to TOP; as a result, A's entire subtree will be merged into a new container, Y, and B will be added to the LEFT side:
              ┌─────────┬─────────┐
     A        │         │         │
   /   \      │         │    C    │
  B     Y     │         │         │
  |    / \    │    B    ├─────────┤
  *   C   Z   │         │         │
      |   |   │         │    Z    │
      *   *   │         │         │
              └─────────┴─────────┘

Remember: Don't mix up the order!

Pros:

  • No priority over sides - because of only two sides, or none at all - means that there's no situation like the following occurring here (in this example, there are four sides in a container; which side should be longer? A is blocking off B, which is blocking off C; D is blocked off on both sides by A and C)
    ┌───┬─────────────────┐
    │   │      B          │
    │   ├─────────────┬───┤
    │   │             │   │
    │   │             │   │
    │   │             │   │
    │ A │             │ C │
    │   │             │   │
    │   │             │   │
    │   ├─────────────┤   │
    │   │      D      │   │
    └───┴─────────────┴───┘
  • Works seamlessly with parked containers (no merging means that parked containers won't be removed from their stages)

Cons:

  • Order of insertion matters
  • Serialization is more tricky because the order of insertion has to be kept track of as well, requiring a linear instead of an associative data structure
  • Really unbalanced trees can be produced in extreme cases:

Assume containers [B, C, D, E, F, G, H ...] are to be added to A in alternating TOP-LEFT sides (where + indicates a container is on the TOP side and * indicates a container is on the LEFT side):

                                                       A:
   A+                    ┌──────────────────────────────┬───────────────────────────────┐
 /   \                   │                              │                               │
H+    T*                 │                              │                               │
|   /   \                │                              │                               │
*  G*    U+              │                              │               C               │
   |   /   \             │                              │                               │
   *  F+    V*           │                              │                               │
      |    /  \          │                              │                               │
      *  E*    W+        │               H              ├───────────────┬───────────────┤
         |   /   \       │                              │               │               │
         *  D+   X*      │                              │               │       E       │
            |   /  \     │                              │               │               │
            *  C*   Y+   │                              │       D       ├───────┬───────┤
               |   / \   │                              │               │       │   G   │
               *  B+  Z  │                              │               │   F   ├───┬───┤
                  |   |  │                              │               │       │ H │...│
                  *   *  └──────────────────────────────┴───────────────┴───────┴───┴───┘

(Extra truncated due to size constraints.)

Notes about older versions

The newer version contains breaking changes over the older versions, which used the PanelContainerSide class to refer to the same concept (instead of ContainerSide), and an int datatype instead of a String. This has knock-on effects regarding resolvers - namely, newer versions allow for multiple side codes to be scanned in a single function call, something that is not possible in older versions.