In a previous post, I looked at one option for positioning icon sub-shapes within a group using a grid system. This works nicely, but it’s based on equally sized grid rows and columns and therefore, equally sized icons too. So in this post I'm going to have a go at trying to managed variable sized icons, while still retaining the ability of collapsing individual items so that the remaining visible icons concertina up next to each other.
As before, the goal is to create the behaviour (shifting icons) but also, to allow as much flexibility so that additions, removals and reordering can happen as simply as possible. Additionally I want to keep an eye on maintainability and that means not just flexibility in the design of the shape, but also how easy it is for others to read and understand the logic. I’ll let you be the judge of that.
So let's have a look at the shape…
This is a group shape (in a master called ‘Host’) that contains four named sub-shapes. The white square is ‘MainShp’ and the other three representing three icons of varying widths.
Who knows who?
One of the challenges of mixed sizes is that one shape’s position is based on another. This is in contrast to the abstract positioning using a uniform grid where size is assumed to be same across the grid.
In accommodating mixed sizes you need either a central place to hold all of the icon sizes (probably the group shape), or some way of referring from one sub-shape to another.
Central is good and ordinarily, when designing shapes, I like to try and maintain a situation where the children all look to their parent for their positioning (and other values too). Where possible, I keep state and logic in the group shape, with sub-shapes only knowing where they should be positioned or whether they should be visible or not, but not why. Equally, the group shape shouldn’t contain references down to its sub-shapes, for example, to find out the height of a sub-shape. The reason for this is that it makes things less modular and becomes difficult to maintain. What happens, for example, if at some later stage you want to delete a sub-shape from a group? How do you know that you won’t be breaking some dependent logic up in the group or a sibling? If you’re just looking at the ShapeSheet of the sub-shape you want to delete, it’s not clear where those dependencies lie.
The opposite of this would be to hard code the chaining of one icon sub-shape to another. The down side, in addition to the hidden dependencies, is that any changes to the order or number of icons means that you’ve got to rewrite formulas in sub-shapes both up and down the chain.
What I'm looking for is a method of keeping sub-shapes agnostic of one another to the point where I can introduce and remove sub-shapes with the minimum of changes required in adjoining shapes.
So maybe this is a reasonable case for breaking my ‘don’t talk to siblings’ rule, but you still need a system that allows you to refer to neighbouring sub-shapes without the drawbacks of hard coding.
The system I’m going to adopt includes using named sub-shapes as a means of identifying and referring to siblings. Positioning occurs based being able to dynamically change the name of the previous sub-shape rather than using a hard coded reference. It also uses a naming convention with expectations of cells in sibling sub-shapes which I’ll talk about in a second, but to start with, here’s a screenshot of the group’s User section:
The two key cells are IconNames, which defines a list of sub-shape names (and, since it’s a list, an order), and IconsVisibility, which allows you to toggle the associated icon’s visibility.
The remaining cells just define and origin for placement of the first icon, the direction in which the icons are laid out and a margin between the icons.
If you move over to one of the icon sub-shapes you can see how the above names list is being used:
The sub-shape (Icon_1 in this case) gets to know which order it is by looking up its own (local) name within the list.
It’s worth noting here that you can use named cell references for peers, ie shapes in the same container, but not parent or child shape. So a group shape cannot contain a named reference to one of its sub-shapes and a sub-shape cannot contain a named reference to its parent group.
This is really how this shape works – by naming your sub-shapes and keeping those names in a list, you provide the means to access any other sub-shape in that list and if, as a sub-shape, you know your own position then you have easy access to the shape behind you.
Moving on down the cells, the name of the preceding sub-shape is stored in User.PreviousName by subtracting 1 from the current sub-shape’s index.
User.PreviousPush is then in charge of setting a reference (User.PreviousPos) to the preceding shape by taking the sibling sub-shape’s name creating a formula that references the User.NextPos cell in the sibling shape. The full formula is:
Once you know where the previous sub-shape’s position is you can set your own ‘next position’ by adding your own width (see the blue dot above) and margin, and this is then ready for the following sub-shape to reference.
I mentioned using a naming convention and this is it – each sub-shape has an expectation of finding a cell name User.NextPos and as long as that exists, then everything works correctly.
Finally, the icons size and position are set as follows:
Adding additional icons
In terms of future changes, if you want to make alterations to the shape by adding (or removing) icons you only need to make three main changes:
- Add your sub-shape to the group and name it.
- Append or insert the new sub-shape’s name in the User.IconsNames list, plus adjust the User.IconsVisibility cell accordingly
- Add the User, size and Pin cell formulas to the new sub-shape. (Aside from the Width and Height cells these are generic formulas that can be pasted straight over with no further changes.)
Changing the icons order
Changing the order of the icons is also straight forward and only involves rearranging the names list cell and visibility cell to match.
In general I would normally try and steer away from using static strings to create references, but given that the intended use for this is within a group shape (and master) that, as the shape designer, you have reasonable control and can have some expectation that a user will not break open the shape and start renaming sub-shapes then this can be an exception.
If you want to have a look at the working shape you can download the file here: