From BlenderWiki

Jump to: navigation, search
Adapted Window Header


Adding a Pulldown Menu

As you have finished the first tutorial ("Adding A New Space Window") successfully you are ready to add some stuff to the window header. This tutorial shall explain how to create a Pull-down menu and how to bind the selection events to your code. We will start reusing the source code we created for the new window space (especially header_demo.c).


Adding the Menu Title

Edit demo_buttons() in file src/header_demo.c as follows:

	xco += XIC + 14;
 
	/* draw pulldown menu */
	xmax= GetButStringLength("Pulldown");
	uiDefPulldownBut(block, draw_pulldownmenu, NULL, "Pulldown", 
						xco, -2, xmax-3, 24, "");
	xco+= xmax;

What we want to do is to create a Pull-down menu with the title "Pulldown". In case we want to create addition header items we need to keep track of the distance of the next possible header item. This is done by requesting the string length of the menu title and adding the result to xco. The menu is created by the call uiDefPulldownBut(). UI elements are contained in uiBlocks. Each element in the uiBlock may contain new UI elements which are defined in new uiBlocks. In case of the Pull-down menu a list of buttons will be drawn if the menu is selected. Therefore a callback function draw_pulldownmenu() has to be added to src/header_demo.c which creates the lists of buttons.


Adding the Item List

Add the following code to src/header_demo.c, before the definition of demo_buttons():

static uiBlock *draw_pulldownmenu(void *arg_unused)
{
	uiBlock *block;
	short yco= 0, menuwidth=120;
 
	block= uiNewBlock(&curarea->uiblocks, "draw_pulldownmenu", 
						UI_EMBOSSP, UI_HELV, curarea->headwin);
 
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 1",
						0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 0, "");
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 2", 
						0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
 
	/* draw spacing between menu items */
	uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); 
 
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 3", 
						0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 4", 
						0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
 
	uiTextBoundsBlock(block, 50);
 
	return block;
}

The code above creates a new uiBlock containing five buttons (one defined as separator). If you compile and test the result you will see that the menu is overlapping with the bottom window. This can be avoided by appending following lines at the end of the button definitions.

	...
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 4|NumPad .", 
				0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
 
	if(curarea->headertype==HEADERTOP) {
		uiBlockSetDirection(block, UI_DOWN);
	}
	else {
		uiBlockSetDirection(block, UI_TOP);
		uiBlockFlipOrder(block);
	}
 
	uiTextBoundsBlock(block, 50);
	...

Note: I have no idea what uiTextBoundsBlock() is used for. I have played with the parameters and the menu width, but couldn't get it. If there is a wise guy who knows, please edit this part.

One Call to bind them all

Now we have a menu which looks nice and does nothing. This might get boring after some minutes so let's bind the menu items to a callback function named do_draw_pulldownmenu().

Add the following function to your code:

#include <stdio.h> /* sprintf */
 
void do_draw_pulldownmenu(void *arg, int event);
 
...
 
void do_draw_pulldownmenu(void *arg, int event)
{
	switch(event) {
	case 0:
		printf("Item 1 selected\n");
		break;
	case 1:
		printf("Item 2 selected\n");
		break;
	case 2:
		printf("Item 3 selected\n");
		break;
	case 3:
		printf("Item 4 selected\n");
		break;
	}
}


Note: After analysing different code it seems to be some kind of standard to derive the name of the event based callbacks from the drawing callbacks. So I am trying to conform, even if I believe it's confusing.

In draw_pulldownmenu() we need to add the following call in front of the first button definition:

	...
	uiBlockSetButmFunc(block, do_draw_pulldownmenu, NULL);
 
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 1|Shift S",
	...

All following buttons are now bound to this event callback.

You should recompile and execute blender from the command line to see any effect.

Maximize/Minimize the Menus

If you have a look at one of the other window types (e.g. 3D View) you will see that it is possible to minimize and maximize Pull-down menus by clicking the small triangle next to the window type selection menu. This can be done by adding the following code in demo_buttons():

void demo_buttons(ScrArea *sa)
{
 
	...
 
	xco += XIC + 14;
 
	// minimize/maximize pulldown menu
	uiBlockSetEmboss(block, UI_EMBOSSN);
	if (curarea->flag & HEADER_NO_PULLDOWN) {
		uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, 
					 ICON_DISCLOSURE_TRI_RIGHT,
					 xco,2,XIC,YIC-2,
					 &(curarea->flag), 0, 0, 0, 0, 
					 "Show pulldown menus");
	}
	else {
		uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, 
					 ICON_DISCLOSURE_TRI_DOWN,
					 xco,2,XIC,YIC-2,
					 &(curarea->flag), 0, 0, 0, 0, 
					 "Hide pulldown menus");
	}
	uiBlockSetEmboss(block, UI_EMBOSS);
	xco+=XIC;
 
	...

This adds the small button next to the window type selection menu to the header. To evaluate the state of this button we wrap the definition of the Pull-down as follows:

	...
	if((curarea->flag & HEADER_NO_PULLDOWN)==0) {
		/* draw pulldown menu */
		xmax= GetButStringLength("Pulldown");
		uiDefPulldownBut(block, draw_pulldownmenu, NULL, "Pulldown", 
							xco, -2, xmax-3, 24, "");
		xco+= xmax;
	}
 
	uiBlockSetEmboss(block, UI_EMBOSSN);
 
	/* always as last */
	curarea->headbutlen= xco+2*XIC;
	...

Now you should be able to maximize and minimize the uiBlock containing the Pull-down menu.

Resulting Code

The resulting code should now look as follows:

#include "DNA_screen_types.h" /* ScrArea */
#include "BIF_interface.h" /* uiNewBlock, uiBlockSetCol */
#include "BIF_screen.h" /* curarea pointer */
#include "BIF_resources.h" /* themes, icons, etc. (-> TH_HEADER) */
#include "BSE_headerbuttons.h" /* GetButStringLength */
#include "DNA_space_types.h" /* SPACE_DEMO */
#include "blendef.h"	
 
#include <stdio.h> /* sprintf */
 
 
void do_draw_pulldownmenu(void *arg, int event);
 
static uiBlock *draw_pulldownmenu(void *arg_unused)
{
	uiBlock *block;
	short yco= 0, menuwidth=120;
 
	block= uiNewBlock(&curarea->uiblocks, "draw_pulldownmenu", 
						UI_EMBOSSP, UI_HELV, curarea->headwin);
 
	uiBlockSetButmFunc(block, do_draw_pulldownmenu, NULL);
 
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 1",
					0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 0, "");
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 2", 
					0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
 
	/* draw spacing between menu items */
	uiDefBut(block, SEPR, 0, "",0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); 
 
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 3", 
				0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
	uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Item 4", 
				0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
 
	if(curarea->headertype==HEADERTOP) {
		uiBlockSetDirection(block, UI_DOWN);
	}
	else {
		uiBlockSetDirection(block, UI_TOP);
		uiBlockFlipOrder(block);
	}
 
	uiTextBoundsBlock(block, 50);
 
	return block;
}
 
 
void demo_buttons(ScrArea *sa)
{
	/* draw buttons */
	uiBlock *block;
	short xco, xmax;
	char name[256];
 
	sprintf(name, "header %d", curarea->headwin);
 
	/* begin of a new UI block */
	block= uiNewBlock(&curarea->uiblocks, name, 
				UI_EMBOSS, UI_HELV, curarea->headwin);
 
	/* change color if mouse over header */
	if(area_is_active_area(curarea)) 
		uiBlockSetCol(block, TH_HEADER);
	else 
		uiBlockSetCol(block, TH_HEADERDESEL);
 
	curarea->butspacetype= SPACE_DEMO;
 
	xco = 8;
 
	/* draw window type selection menu */
	uiDefIconTextButC(block, ICONTEXTROW,B_NEWSPACE, ICON_VIEW3D, 
				windowtype_pup(), xco, 0, XIC+10, YIC, 
				&(curarea->butspacetype), 1.0, SPACEICONMAX, 0, 0, 
				"Displays Current Window Type. "
				"Click for menu of available types.");
 
	xco += XIC + 14;
 
	// minimize/maximize pulldown menu
	uiBlockSetEmboss(block, UI_EMBOSSN);
	if (curarea->flag & HEADER_NO_PULLDOWN) {
		uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, 
				ICON_DISCLOSURE_TRI_RIGHT,
				xco,2,XIC,YIC-2,
				&(curarea->flag), 0, 0, 0, 0, 
				"Show pulldown menus");
	}
	else {
		uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, 
				ICON_DISCLOSURE_TRI_DOWN,
				xco,2,XIC,YIC-2,
				&(curarea->flag), 0, 0, 0, 0, 
				"Hide pulldown menus");
	}
	uiBlockSetEmboss(block, UI_EMBOSS);
	xco+=XIC;
 
	if((curarea->flag & HEADER_NO_PULLDOWN)==0) {
		/* draw pulldown menu */
		xmax= GetButStringLength("Pulldown");
		uiDefPulldownBut(block, draw_pulldownmenu, NULL, "Pulldown", 
							xco, -2, xmax-3, 24, "");
		xco+= xmax;
	}
 
	uiBlockSetEmboss(block, UI_EMBOSSN);
 
	/* always as last */
	curarea->headbutlen= xco+2*XIC;
 
	uiDrawBlock(block);	
}
 
 
 
void do_draw_pulldownmenu(void *arg, int event)
{
	switch(event) {
	case 0:
		printf("Item 1 selected\n");
		break;
	case 1:
		printf("Item 2 selected\n");
		break;
	case 2:
		printf("Item 3 selected\n");
		break;
	case 3:
		printf("Item 4 selected\n");
		break;
	}
}