Create ImageButtons
If you write a GUI for a game, at some time comes the point where you want to create the most exciting kind of buttons: ImageButtons ! But care, I'm not talking about a default button frame, that has a picture instead of a text, I'm talking about completely self-drawn buttons. I've found two ways to accomplish that task, both are nice.
Contents
Preparations
There are a few tasks we have to accomplish before being able to create an ImageButton.
Draw it !
The most important thing is, of course, draw the pictures. If you don't want to accomplish this, ask your graphics artist :) You have to draw 4 pictures/button, wich represents the four states:
- Normal: That is how the button looks like most of the time.
- Hover: This is how the button should look when the mouse comes over it.
- Pushed: When the user clicks the mouse button (and holds it down a bit) over the button, it looks like this.
- Disabled: You can imagine what this is.
This is the file we'll use for this HOWTO and as you see, i didn't ask my graphics artist to do one :p You can find all other files (source code, data files) there too. [[1]] Edit: shit the link is currently dead ... I hope I find the files so I can upload them again, but atm everything that is written here must be enough. On questions/problems just ask in the forum.
The Imageset
When I talk about an Imageset, I mean two files: The picture file (.tga, .png, or whatever) and the description (.imageset) file. The picture file can contain a lot of images, but it's size has to be a power of two. The description file is an xml file wich gives a name to a (rectangular) location in the picture. So you can call the location (150,150,32,32) "UnitIcon" for example.
An example for a .imageset file:
<?xml version="1.0" encoding="UTF-8"?> <Imageset Name="THEButton" Imagefile="THEButton.png" > <Image Name="btnNormal" XPos="0" YPos="0" Width="64" Height="20" /> <Image Name="btnHover" XPos="0" YPos="20" Width="64" Height="20" /> <Image Name="btnPushed" XPos="0" YPos="40" Width="64" Height="20" /> <Image Name="btnDisabl" XPos="0" YPos="60" Width="64" Height="20" /> </Imageset>
We will use this imageset in our examples. It defines four named images: btnNormal, btnHover, btnPushed and btnDisabl (saved in the file "THEButton.png"). The whole imageset is called "THEButton" and saved in a file named "THEButton.imageset".
How to use this imageset now ? This is very simple, you just have to do this in your initialisation code:
// Load the THEButton Imageset that has the pictures for our button. CEGUI::ImagesetManager::getSingleton().createImageset( "THEButton.imageset" );
That's all ! Of course, you should do this in a try/catch block.
If, for some wicked reason, you want to do something in code with this imageset, you can either save the pointer that the function above returns, or you just get that pointer if you know the name of the imageset (what you usually do), using this function:
CEGUI::Imageset *m_pImageSet = CEGUI::ImagesetManager::getSingleton().getImageset( "THEButton" );
Enough about imagesets.
The programmer's job
Now we can create the button itself (in two different ways):
The first way: mainly in code
One way is to create the button in code, you usually do this if you only know wich button to display at runtime. Maybe a strategy game is the best to illustrate this. Imagine you have the buttonspanel, a place on screen with an array of buttons. The buttons that are present there depend on something (like wich unit is currently selected or so). The first thing you have to do, is not in code. Create one layout file for every button. The file should look somewhat like this:
Content of button1.layout version 0.1
<?xml version="1.0" encoding="UTF-8" ?> <GUILayout> <Window Type="TaharezLook/ImageButton" Name="btnNewGame"> <Property Name="UnifiedPosition" Value="{{0,0},{0,0}}" /> <Property Name="UnifiedSize" Value="{{0,64},{0,20}}" /> <!-- Here we choose what images to take. set:THEButton means they are stored --> <!-- in the imageset named THEButton and image:btnNormal specifies wich image it is. --> <Property Name="NormalImage" Value="set:THEButton image:btnNormal" /> <Property Name="HoverImage" Value="set:THEButton image:btnHover" /> <Property Name="PushedImage" Value="set:THEButton image:btnPushed" /> <Property Name="DisabledImage" Value="set:THEButton image:btnDisabl" /> <!-- Now the button would be ready, but without caption ... So we add a caption. --> </Window> </GUILayout>
If you have an imagebutton but you want it to have a caption, you could write this kind of layout file:
Content of button1.layout version 0.2
<?xml version="1.0" encoding="UTF-8" ?> <GUILayout> <Window Type="TaharezLook/ImageButton" Name="btnNewGame"> <Property Name="UnifiedPosition" Value="{{0,0},{0,0}}" /> <Property Name="UnifiedSize" Value="{{0,64},{0,20}}" /> <!-- Here we choose what images to take. set:THEButton means they are stored --> <!-- in the imageset named THEButton and image:btnNormal specifies wich image it is. --> <Property Name="NormalImage" Value="set:THEButton image:btnNormal" /> <Property Name="HoverImage" Value="set:THEButton image:btnHover" /> <Property Name="PushedImage" Value="set:THEButton image:btnPushed" /> <Property Name="DisabledImage" Value="set:THEButton image:btnDisabl" /> <!-- Now the button would be ready, but without caption ... So we add a caption. --> <Window Type="TaharezLook/StaticText" Name="btnNewGame_text__"> <!-- We make the text take all the space of the button to center the text. --> <!-- You should adapt these values to your pictures, just play a bit with em ;) --> <Property Name="UnifiedPosition" Value="{{0,0},{0,0}}" /> <Property Name="UnifiedSize" Value="{{1,0},{1,0}}" /> <!-- Disable the frame and background, so we got only the text and not a StaticText widget. --> <Property Name="FrameEnabled" Value="False" /> <Property Name="BackgroundEnabled" Value="False" /> <!-- Here we center the text into the button --> <Property Name="HorzFormatting" Value="WordWrapCentred" /> <Property Name="VertFormatting" Value="Middle" /> <!-- We MUST disable the text so that it is the button that gets our mouse events, --> <!-- and not the static text ! If you forget that line, the buttons graphics will correspond, --> <!-- but the clicks on the button won't work ! because they are "eaten" by the staticText. --> <Property Name="Disabled" Value="True" /> <!-- finally, this is the caption we want the button to have. --> <Property Name="Text">New game</Property> </Window> </Window> </GUILayout>
This adds a child window of type StaticText, with no border and background, that takes all the place in the button, so we can center it. If your caption has to be not centered, play with the values.
A problem that comes up now, is that if the user clicks on the button, the click will be absorbed by the StaticText, and won't reach the button. That's bad. I searched around and found the following two propertys:
<Property Name="MousePassThroughEnabled" Value="True" /> <Property Name="DistributeCapturedInputs" Value="True" />
But both didn't seem to work as expected, I asked the forums and lindquist told me that "If all you want is a simple label added to the ImageButton then adding a TextComponent in the looknfeel is a much cleaner solution.". He is probably right, but I didn't struggle with looknfeels yet. If you achieved this, you can tell me or add a chapter to this wiki :)
The solution he suggested (and the only one wich worked, if i remember correctly, is disabling the staticText (like in the complete code written above)
<Property Name="Disabled" Value="True" />
Now all you need to do to load and show the button are the following four lines of code.
CEGUI::Window *w = CEGUI::WindowManager::getSingleton().loadWindowLayout( "button1.layout" ); rootWin->addChildWindow( w ); w->setPosition( CEGUI::UVector2( CEGUI::UDim(0.5f,-32.0f), CEGUI::UDim(0.5f,-10.0f) ) ); w->setVisible( true );
Supposing rootWin is your current root window, or any other window you want to put the button in. We set the position to be the center (0.5f) minus the half the height/width of the button, so the button is really centered. Of course, you can do everything else you want before (and while) showing the button.
That's it ! now you can work with the button however you want :)
The second way: Let the power of layout guide you
I suppose you read the first way, because they are very similar and I won't rewrite the whole code here. The main difference is that if you like layouts, you'll already have declared all your windows in layout files. The the "Content of button1.layout version 0.2" won't be alone in a layout file of it's own, but it will be in your already existing layout files. You still have to load the Imageset. I Provided a sample, named imgButtonSample.layout
That's it ! If you got suggestions, corrections or whatever feedback, just tell me.
--Pompei2 11:18, 11 January 2007 (PST)
--fezztah
To properly centre the label on an image button vertically as well as horizontally I had to change the line: <Property Name="VertFormatting" Value="Middle" /> such that Value="VertCentred" instead. I'm not sure if the original is a mistake so I won't alter it in case it has some other purpose.