Adding a floating panel takes several steps, based on how these are handled in
Blender. A summary of how it works:
- each window type in Blender can be assigned a 'Panel handler', for example
with a hotkey or pulldown menu event.
- multiple handlers are possible for 1 window
- when a window is drawn, it checks as last step the handlers and draws the
Panels
- event handling for Panel buttons can be done using the main window event
queue
In this tutorial we will add a Paint floating panel and will use the 'Properties' panel
from the Image window as our reference.
In include/BIF_space.h we need to define our new handler. Go down to where the
previous handler was defined
<ccode>#define IMAGE_HANDLER_PROPERTIES 30 </ccode>
and add the line
<ccode> #define IMAGE_HANDLER_PAINT 31</ccode>
after it. Now we can reference this event code by name instead of number. Please
note that the handler number doesn't really matter, apart from that it's saved in
a file when a Panel is visible, so when you load a .blend project again the Panel
will show up still.
Open drawimage.c and locate void image_blockhandlers(ScrArea *sa).
You can see that the blockhandlers of the current screenarea are gone over to
see if they are set and if they are, the function to draw the panel is called.
<ccode> for(a=0; a<SPACE_MAXHANDLER; a+=2) {
switch(sima->blockhandler[a]) {
case IMAGE_HANDLER_PROPERTIES:
image_panel_properties(sima->blockhandler[a+1]);
break;
}
/* clear action value for event */
sima->blockhandler[a+1]= 0;
}</ccode>
so we need to add a case for our new handler
<ccode> for(a=0; a<SPACE_MAXHANDLER; a+=2) {
switch(sima->blockhandler[a]) {
case IMAGE_HANDLER_PROPERTIES:
image_panel_properties(sima->blockhandler[a+1]);
break;
case IMAGE_HANDLER_PAINT:
image_panel_paint(sima->blockhandler[a+1]);
break;
}
/* clear action value for event */
sima->blockhandler[a+1]= 0;
}</ccode>
As you can see, we are calling a function called image_panel_paint that we
have not yet defined yet. We will do that next.
n drawimage.c we will look at the
image_panel_properties function. We will disect it...
<ccode>static void image_panel_properties(short cntrl) //
IMAGE_HANDLER_PROPERTIES
{
extern VPaint
Gvp; /* from vpaint -
this was copied from the paint panel*/
uiBlock *block;</ccode>
Here we can see that we create a uiBlock* called
block. this will contain our panel.
<ccode>block= uiNewBlock(&curarea->uiblocks,
"image_panel_properties", UI_EMBOSS, UI_HELV,
curarea->win);
</ccode>
Here we initialize our uiblock by passing it the pointer to the current area
uiblocks, give it the name of the function, and set some drawing flags
and tell it where to draw.
<ccode>uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE |
cntrl);</ccode>
Here we add a control bar to the block passing it a
control number that was passed in.
<ccode>uiSetPanelHandler(IMAGE_HANDLER_PROPERTIES); //
for close and esc</ccode>
Here we tell the block what handler it belongs to so
that we can close it.
<ccode>if(uiNewPanel(curarea, block, "Properties", "Image", 10,
230, 318, 204)==0)
return;</ccode>
Now we create a panel inside the block container, if
it is not visible (tabbed, minimized), it returns, otherwise, we
continue.
The rest of the code in the function defines the
contents of the floating panel using standard blender ui components.
So we will define our simple paint panel this
way
<ccode>static void image_panel_paint(short cntrl) /*
IMAGE_HANDLER_PAINT */
{
uiBlock *block;
block= uiNewBlock(&curarea->uiblocks,
"image_panel_paint", UI_EMBOSS,
UI_HELV, curarea->win);
uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
uiSetPanelHandler(IMAGE_HANDLER_PAINT); // for close and esc
if(uiNewPanel(curarea, block, "Paint", "Image", 10, 230, 318, 04)==0)
return;
/* Here we will define our stuff
*- this was copied from the paint
* panel*/
uiBlockBeginAlign(block);
uiDefButF(block, NUMSLI, 0, "R ", 979,160,194,19, &Gvp.r,
0.0, 1.0, B_VPCOLSLI, 0, "The amount of red used for painting");
uiDefButF(block, NUMSLI, 0, "G ", 979,140,194,19, &Gvp.g,
0.0, 1.0, B_VPCOLSLI, 0, "The amount of green used for painting");
uiDefButF(block, NUMSLI, 0, "B ", 979,120,194,19, &Gvp.b,
0.0, 1.0, B_VPCOLSLI, 0, "The amount of blue used for painting");
uiBlockEndAlign(block);
uiDefButF(block, NUMSLI, 0, "Opacity
", 979,100,194,19, &Gvp.a, 0.0, 1.0, 0, 0, "The amount of
pressure on the brush");
uiDefButF(block, NUMSLI, 0, "Size ",
979,80,194,19, &Gvp.size, 2.0, 64.0, 0, 0, "The size of the
brush");
uiDefButF(block, COL, B_VPCOLSLI, "",
1176,100,28,80, &(Gvp.r), 0, 0, 0, 0, "");
}
</ccode>
A simple way to activate a piece of code is with a hotkey. Currently the
properties panel is activated by the Nkey. So we will look at the code that this
calls in space.c it is in the function
<ccode>void winqreadimagespace(ScrArea *sa, void *spacedata,
BWinEvent *evt)</ccode>
looking down to NKEY we find
<ccode> case NKEY:
if(G.qual==0) {
add_blockhandler(curarea, IMAGE_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
scrarea_queue_winredraw(curarea);
}</ccode>
The first call is made to add_blockhandler which passes the current
area(curarea), the handler to add(IMAGE_HANDLER_PROPERTIES) and an
optional flag (UI_PNL_TO_MOUSE) which causes the panel to open at the
location of the mouse. If this last option is left out, the window will open in the
lowest left area it can. After that, the call to scrarea_queue_winredraw queues a
refresh for the current area(curarea).
For this example we will add our paint panel with the Vkey for no particular
reason.
<ccode> case VKEY:
if(G.qual==0) {
add_blockhandler(curarea, IMAGE_HANDLER_PAINT, 0);
scrarea_queue_winredraw(curarea);
}</ccode>
Now you could add whatever activation code you want at this point. Either a
hotkey or a menu option or whatever makes you (and Ton and the gang)
happy. This particular panel will not be activated with the V key, but instead be
placed in a menu. But we will leave it at this for the purposes of this tutorial.
At this point, the code should compile and run. Please keep in mind that each
window type ( image, 3d, oops, etc) can currently support only 4 floating panels.
Have fun and Happy Coding!