It's Got Layers...Cascading Menus In The Notes ClientSo the audience has spoken (all three of you :-) and the consensus was that I should talk a bit about how I made cascading menus with layers in the Notes client. It's an interesting exercise and one which might be useful, depending on your circumstances. First, I'll explain what I did to get this working and then discuss some of the limitations and drawbacks. So let's get on with it, shall we?
As a quick refresher, most of you probably know about layers and their availability in the Notes client. In practice, I don't know many developers that use them, but they can be extremely valuable in designing your user interface when you are concerned with pixel perfect layout and organization. The really cool thing about layers is that they allow you to place overlapping box elements anywhere on your Notes form or page. What rocks even more is that any elements that you can put on a form or a page can be placed into a layer. Thus, if you need your input field to be exactly 45px down and 210 px over from the edge of the screen, you can do that with a layer.
Before we get too deep, there are a few things you need to keep in mind about layers. First, a layer is positioned relative to its containing block. In the case of one layer within another, for example, the position of the internal layer will be measured from the top and left edges of the parent layer. This is very similar to relative positioning on the web. Second, layers have a z-index property (also analogous to the web). This property controls how overlapping layers interact. A child with a z-index of 10 will appear above it's parent with a z-index of 0. The higher the z-index, the "closer" that layer is to the user. Finally, I want to review the somewhat tricky way to implement hide-when formulas with layers. In order to hide a layer, you must put your hide-when formula on the paragraph that contains the layer anchor, not the layer itself. Whenever you create a layer, a layer anchor is generated as well (at the position your cursor is at the time you create it). Although you can move the layer anywhere you want, the layer anchor stays in place, unless you choose to move it by cutting and pasting it somewhere else. When you first start using layers this is a little weird, but you get used to it pretty quickly.
When I blogged awhile back about creating a cascading menu in Notes using Action buttons, I got a good response from people. I like the simplicity of the technique and have used it in some of my databases at work. However, several people asked me how to control the formatting of the menu. Unfortunately, Notes provides no interface to do this, so you are stuck with the menu rendering based on your operating system properties. This made me start exploring other avenues for implementing the same kind of functionality and in the end I found that layers can give you a pretty slick presentation.
The first step is to determine what kind of menu you want to create, a horizontal cascade or vertical cascade. As you are designing your UI, this will probably be pretty apparent based on other choices you have made. If you can't decide, just start with one. As you'll see, the technique is the same for either and changing from one to another is very simple. For our example, let's create a horizontal cascade. That is, our menu items will cascade out from the right as the user expands them.
In the screen shot below, I've created a fictional menu that might be something like you would see on a company intranet. To keep it simple, we just have three menu choices, "Personal", "Human Resources" and "Finance". The idea is that when you click on one of these options, the menu will expand and you will see additional choices under that category. In this example, we'll only expand the menu two levels deep, but you could extend this technique to more if you need it. (I'd caution against going any more that three levels deep...that gets tedious for the user).
If you look at this form in design mode (in the sample database), you'll see that I used a table to separate the top level elements. This is not really necessary, but it makes it easy to align all of the labels. Once the top level elements are created, it's time to start adding the layers. Click anywhere on your form and choose 'Create - Layer'. This will generate an transparent layer that is one third the width and height of your screen (or containing element). Notice the little icon layer icon in the top left corner of the new layer. This is the layer anchor. If you grab on one of the sides of the layer, you will see that you can drag it anywhere on the page. As you do, the layer anchor stays in place. To format the layer, select it and then pull up the layer properties dialog. You can modify the height, width, position, color, etc.
To create the menus in this example, I visually manipulated the size of the layer until it fit what I was looking for. Since we can place any kind of element inside a layer, I opted to construct the menu options with a table, again purely for formatting purposes. Each row of the table was designed to hold one menu item. I then formatted the table with borders, colors, etc., getting the exact layout I wanted.
Once you create the layers, you need to position them on the form as you want them to appear. This part can get tedious, because you just have to use trial and error to see if everything lines up as you like. The thing I really like about this, though, is that you have complete control over the final product, which is something you can't always say about Notes.
Once your layers are arranged in the order that will simulate your cascade, it's time to work on hiding them until they are needed. To do this, remember that the hide-when must be placed on each individual layer anchor. Notice in this sample database that I arranged them all on different lines. This is necessary because each layer will have its own hide-when formula. The approach that I decided on to control the visibility was to create two hidden fields on the form, one entitled "MenuLevel1" and one named "MenuLevel2". The initial value of these fields is blank, but when a hotspot on the menu is clicked, it will set the value of the appropriate field. It will be one of these field values that the hide-when formulas compare themselves against.
"MenuLevel1" controls the first level cascade layers, whereas "MenuLevel2" corresponds to the second level of the cascade. If you want to extend this further, just add the necessary number of fields. The hide-when formulas on the layer anchors themselves are very simple. They just check if the applicable hidden field has a value equal to the value I (arbitrarily) picked and if not, they are hidden. For example, the hide-when formula on the first-level layer for HR reads "MenuLevel1 != "HR". So far, so good.
After the hide-when formulas are defined, the next step is to create the links that do all the work. In my first example, all of the menu labels are just text, so I created an action hotspot on each. In general, clicking on a link should reveal the next level of the cascade corresponding to that link. If a completely different menu is selected, the previous entry needs to be hidden and the new one displayed. Here's a sample from the hotspot on the 'Personal' link:
@If(MenuLevel1 != "Personal";
In this formula the first couple lines determine if the level 1 cascade corresponding to the link should be shown or hidden. If it's not already visible (field != "Personal") then we turn it on, otherwise we turn it off. In order to avoid the situation where the user is in a second level cascaded menu for another link before clicking this one, the "MenuLevel2" field gets cleared (hiding the second level cascade). Finally, the hide-when formulas are triggered by the last line.
When a user executes an action on the menu, we want the menu to "de-cascade" (I'm making up words now folks). In addition to the code that runs whatever the action is supposed to do, we just need to make sure that "MenuLevel1" and "MenuLevel2" get set to blank so that the menus all disappear.
This is the essence of this technique. It takes a little patience to get things lined up the way you like, but the basic concept is quite easy. Of course, why stop there? There are a lot of improvements that could be had.
Getting Jiggy With It
First off all, what if the user opens a menu and then decides not to choose an action? On the web or in other software, the user can just click onto a blank area of the screen (or at least an area outside the menu) and it will disappear. I couldn't think offhand how to accomplish this within Notes, so I added a small icon in the bottom of each menu that will effectively close it by setting the "MenuLevel1" and/or "MenuLevel2" fields to blank. If you are in second-level cascade, clicking this icon will close just that menu, leaving its parent open. If you click it in a first-level menu, all will close.
After I tested the rough framework of the cascading menu concept, I decided to try different formatting options. One thing I tried was to create the links as graphic image wells so that you could have a rollover effect. This entails breaking out your favorite image editing software and creating the link to the appropriate size, with both the normal and hover state, as seen in the example below. All of these images should be placed in the Image Resource section of your application and then you can add these resources to your menu tables. When viewed in the Notes client, you get a nice rollover on the menus.
After playing with the images, I thought of an even easier way to get a nice rollover effect. Since you can put any element in a layer, you should be able to use an embedded outline right? Turns out, you can! I created the outlines to represent each menu, then created an embedded outline within each layer and played with the formatting until it matched what I wanted. Then a quick preview in Notes and...perfect!
The beauty of this technique is that the menus can be placed anywhere in your application where it makes sense. In fact, it just look a couple of minutes to reconfigure my initial cascading menu to turn it into a vertical one. You could have menus popping up from the bottom, the right size, wherever!
So...What's The Catch?
Surprisingly, there aren't many drawbacks to this technique. One thing to be aware of is that the layers cannot cascade across frames, so using your typical frameset with a navigation panel on the left is out. If you are rethinking your design though, you might not need frames. In the example, I created another frame to serve as the container for an embedded view. Using layers, you could easily create your entire UI in a single page.
One thing I found the hard way is that trying to contain the layers within tables is not an easy task. At first, I thought that this would make the design easier to manage, but I kept getting results like the screenshot below.
It turns out that the layer would not display correctly unless the table cell was as high as the height of the layer. This got unruly and difficult to work with very quickly. Hmmm...functionality buggy with tables? Never! ;-) In the end, I think positioning the menus by hand is a more elegant solution anyway.
After penning this post, I figured I shouldn't show you all this stuff without leaving you with something to take home. So...you can download a sample database, which is exactly what I took these screenshots from. Please note that this isn't anything polished, just my playground for the day. As always, if you have any questions, please feel free to contact me...email@example.com.
I imagine many more variations on this theme and I will be exploring more ideas for this in the future. I also look forward to hearing from others on how they implemented functionality with layers in their applications. Until next time..."This is Walter Cronkite. Good night..."
Technorati tags: Show-n-Tell Thursday, Lotus Notes, Lotus Domino