vegUI.org home of the javascript based window manager and AJAX framework

vegUI Tutorial 11 - The Popup Menu

written by vegu on 20 Dec, 2006 10:05:31
A live example of this tutorial can be found here.

All image files used in this tutorial can be downloaded here.

Pop-up menus are everywhere these days, so what would vegUI be if it didn't have it's own pop-up menu element.

What exactly is a pop-up menu? A pop-up or context menu is a list of items that 'pops up' or shows when the user does some action. For instance there is a 'File' button on the top of your browser application. If you click the File button a menu opens, or if you right click on a page, a menu opens. Those are context / pop-up menus.

For this tutorial we will start from scratch (that is for those people that are continuing from the previous tutorials). Our manager element will basically be the same, just with a lighter background. So let's start by creating and building the vegUI manager.

Code: Javascript
Manager = new VegUIManager('Manager');
Manager.set(800,600,0,0);
Manager.T.Css.backgroundColor = '#b2b2b2';
Manager.build(document.body);


All this should be familiar to you by now, so i am not going to explain it. If you still have questions about the code snippet above i suggest you take a look at the previous tutorials :).

While we will take a look at multiple menu and nested menu behavior later on, for now we will concentrate on creating a single menu that opens when a button is clicked. First we need to create a new vegUIMenu element.

Code: Javascript
myMenu = Manager.get_new(VUI_MENU);


Then we set-up our menu by calling its set() or set_menu() method

Code: Javascript
myMenu.set(30,55,150,1500,'menu','menu_arrow.gif');

Definition: VegUIMenu.set_menu

The first two arguments are position x and y of the menu, the third parameter is the width of the menu. We do not need to specify height as this will be done automatically depending on the amount of items the menu holds. The fourth argument is the close-time. The time that needs to pass before the menu closes automatically after the mouse cursor has left it (ms). Argument 5 is the css class to use for the menu itself and the last argument is the path to the icon graphic to use for the arrow icon that is displayed beside an item that links to a nested menu.

The menu is two predefined childs, the Skin and the TRow child. We will take a look at the Skin child later when were working on the appearance of our menu, but right now the TRow child is where it's at ;)

TRow is an abbreviation of TemplateRow. It is a permanent template that never gets built and will be used by the menu when an item is added to it. Every item that is added to the menu will be cloned from the TRow element. So in order to set up the appearance of our items we need to initially set it up on the menu's TRow child.

Code: Javascript
myMenu.TRow.set(0,0,0,0,20,16,16,'menu_item_normal','menu_item_hover');

Definition: VegUIMenuItem.set_mnuitem

We can skip the first four arguments as we're working with a template element, but for sake of completeness i will go over what they are. The first argument would be the caption of the item - in other words the text the user sees. The second argument would be the function that is executed when the item is clicked, the third argument would be the path to the icon that is displayed on the left side of the item and the fourth argument would be the icon to be displayed on the right side of the item.

Now the arguments that are of interest to us. The fifth argument is the item height, argument six is the space (width) to reserve for the left icon and argument seven is the space to reserve for the right icon - each will take up 16 pixels in our case.

The last two arguments are the names of the css classes to use when the item is in normal mode and when the mouse cursor is hovering above the item.

We have not defined the css classes yet so lets do this next.

Code: CSS
.menu {
background-color: #c8c8c8;
border: 1px #838383 solid;
}

.menu_item_hover {
background-color: #838383;
color: #fff;
font: bold 10px Verdana, Arial, Helvetica;
}

.menu_item_normal {
background-color: transparent;
color: #707070;
font: bold 10px Verdana, Arial, Helvetica;
}


We do not want to have an empty menu. Right now if you add items to the menu while it is a template (in other words: while it is not built) it can cause some buggy behavior (version 2.0) so we will build our menu first and then add some items to it. We add items by calling it's add_item method.

Code: Javascript
Manager.build_element(myMenu);
myMenu.add_item('Item 1',null,'menu_icon.gif');
myMenu.add_item('Item 2');

Definition: VegUIMenu.add_item

add_item takes as the first argument the label of the item. The second argument is optional and can be a function that should be executed when the user clicks on the item. The last argument is optional as well and is a path to the image file that should be displayed on the left side of the item.

Ok menu is done, build the page. Oh we're looking at an empty page, that's because menu's are hidden by default and its popup method needs to be called in order for it to appear. We could do so by adding myMenu.popup() to the end of our script but we want to have more control than that so let's add the button i was talking about earlier :)

First, the two css classes we will use for our button.

Code: CSS
.btn_openmenu_2 { background-image: url('btn_openmenu_2.gif'); }
.btn_openmenu_1 { background-image: url('btn_openmenu_1.gif'); }


We will position the button itself to be above where our menu is positioned right now. So when the menu pops up it will come up below the button.

Code: Javascript
BtnOpenMenu = Manager.get_new(VUI_BUTTON);
BtnOpenMenu.set(30,30,75,18,'btn_openmenu_1','btn_openmenu_2');
BtnOpenMenu.States[VUI_MOUSE_DOWN].Scripts.add(
function() { myMenu.popup(); }, 'call_menu'
);
Manager.build_element(BtnOpenMenu);


Notice the second argument we give to the add() function of the Scripts property. It gives the function we add a unique id so we can find it later. We dont need to you, but figured id explain since this is something we did not have a chance to go over yet.

Load the page now and click the button and our menu should open :).

Skinning the menu


Any skin nodes you want to add to the menu to build its skin should be children of it's Skin child. However in our example we will not need to add any additional nodes as it will be sufficient to give the existing skin node a border and change its margins a bit. The Skin child of the menu has its rmarg and bmarg template properties set to 0 by default, meaning it will span the whole menu. It is also positioned on the z-axis to be the lower than the items of the menu so that those wont be blocked by it.

As i said all we will be doing is adding a border to our skin. My goal was to make the menu look similar to the button graphic we used for the button that opens the menu. In order to do that we will set a white 1pixel border on the skin and alter its bmarg and rmarg properties to 2 pixels instead of 0, which will result in a white border that seems to be inside of the menu.

First the menu_skin css class

Code: CSS
.menu_skin {
border: 1px #fff solid;
}


Then we set up the Skin children of the menu to use the class and so the margins are 2 pixels intead of 0.

Code: Javascript
myMenu.Skin.T.className = 'menu_skin';
myMenu.Skin.set_marg(2,2);


Another thing i did in the live example was editing the menu_item_hover css class and giving it a transparency of 60%, this goes well with the new skin.

Code: CSS
.menu_item_hover {
background-color: #838383;
color: #fff;
font: bold 10px Verdana, Arial, Helvetica;
opacity: 0.6;
moz-opacity: 0.6; /*older mozilla browsers*/
filter: alpha(opacity=60); /* internet explorer */
}


That is pretty much it. If you reload the page, our menu should look a bit more stylish with a black outer border which is provided by the menu itself and a white inner border which is provided by the Skin element.

Nested menus


With vegUI menus it is possible to have nested menu. A nested menu is a menu that is opened by hovering over an item in another menu. Nested menus are moved to be positioned beside the item that they are linked to automatically. The following code snippet will be added before Manager.build_element(myMenu) is called as we will use myMenu as a template.

Code: Javascript
subMenu  = Manager.get_clone(myMenu);


Now we build both our main menu and the nested menu, then add some items to our sub menu and finally we link it to the main menu.

Code: Javascript
subMenu.add_item('Sub-Item 1');
subMenu.add_item('Sub-Item 2');
subMenu.add_item('Sub-Item 3', function() { alert('Hi!'); });

myMenu.link_menu(1,subMenu);



Note (18th December 2007): Firefox 2.0.0.11 crashes when sub-item 3 is clicked. I havent figured out why this happens yet, it has to do with the alert() call that is linked to it. Simply replace it with something else (window.document.title = 'Hi!' for example) while i figure out why this is happening.


The link_menu call needs to happen after the items are added to myMenu. In the earlier stages of the tutorial we added 2 items to myMenu, 'Item 1' and 'Item 2'. We can link our sub menu to one of them by passing their index as second argument to the link_menu method. The first item in the menu will have the index 0 the second item will have the index 1 and so on.

The first argument is the menu to be linked.

If you load the page now and hover your mouse over item 1 in our main menu then our sub menu should pop up on the right side :). The nesting of menus is not restricted to the root menu, sub menus can be linked to other sub menus as well.

Handling Multiple Menus


When you have several menus then you probably do not want it to be possible to have more than one of them open at the same time (if they belong to the same group of menus or something). In order to take care of that problem the vegUI menu provides the befriend method. Befriend allows you to befriend multiple menus with each other and when a menu pops up all it's 'friends' will be closed automatically.

In order to test this we need to create a third menu. Again we will use myMenu as a template so make sure you add the code below before myMenu is built by the manager.

Code: Javascript
otherMenu = Manager.get_clone(myMenu);
otherMenu.set(120);


We also called the set() method on our new menu and moved it a bit to the right (x position 120) so it wont overlap positions with myMenu.

Now we call the befriend method on myMenu (you could call it on otherMenu as well, it just needs to be called on one of the menus that you want to befriend with eachother). The argument submitted to it is an array that holds all of the menus you want to befriend, even the menu that you're calling the function on.

Code: Javascript
myMenu.befriend([myMenu,otherMenu]);


Okay, again, lets build our new menu and add some items :)

Code: Javascript
Manager.build_element(otherMenu);

otherMenu.add_item('Other-Item 1');
otherMenu.add_item('Other-Item 2');
otherMenu.add_item('Other-Item 3');


Oh yea, we also need a second button so we can open the second Menu. We will use our first button as a template so add the code below before it is built.

Code: Javascript
BtnOpenMenu2 = Manager.get_clone(BtnOpenMenu);
BtnOpenMenu2.set(120,30);
BtnOpenMenu2.States[VUI_MOUSE_DOWN].Scripts.add(
function() { otherMenu.popup(); }, 'call_menu'
);


Cloning an element will also clone its mouse state properties. The function we added to be executed by button 1 was to open myMenu. When cloning Button 2 from Button 1 that function was copied over as well, but we dont want our second button to open the first menu. Thats the reason why we gave the function the id 'call_menu' as now we can overwrite the function by simply adding another function with the same id to our cloned button.

Load the page and test it, you should not be able to open both menus at the same time as either will close automatically if the other is opened.

Built in fade-in effect


The vegui menu also is the first element that lets use use the fx engine of vegUI. If you initialize the FX engine on the manager then the menu will fade in slowly instead of appearing instantly. The menu also has properties to control the fade in effect or to turn it off even if the fx engine is initialized, you can read more about those properties and other menu specific properties that i did not mention in this tutorial in API documentation.

To initialize the common FX engine simply call init_fx on the manager element.

Code: Javascript
Manager.init_fx(30)


The argument we pass to the function is the interval of the FX timer. In my case i set it to 30 ms, i dont recommend going much lower than that as it can be a hit on performance, an interval of 1ms would attempt to be executed 1000 times a second which can hurt weaker computers and it isnt beneficial to us anyways.

Opening menus now should happen in a smooth fade in effect :)

Thats the end of the menu tutorial, we will tackle the selector widget next!

Related Posts


Your Comment

name:*
email-address:

Are you human?



Comments to this post: (2)

RSS Feed for comments RSS Feed for comments
Spellbreaker
on 16 Dec, 2007 - 20:22:23

Firefox 2.0.0.11 crashes on this tutorial when I click the menu and the "Hi" Messagebox comes up.

Report this comment
vegu
on 17 Dec, 2007 - 01:46:12

You're right i will have to look into that.

Report this comment