Difference between revisions of "Libwiigui/tutorial"

From WiiBrew
Jump to navigation Jump to search
Line 427: Line 427:
 
but it can be whatever you named your menu function.
 
but it can be whatever you named your menu function.
  
Now, open menu.cpp. Find the MainMenu function. To load an image, you declare an instance of the GuiImage class. Unless, in the main window, you will want to declare it this way:
+
Now, open menu.cpp. Find the MainMenu function. To load an image, you declare an instance of the GuiImage class. Unless you are in the MainMenu function, you will want to declare it this way:
 
<source lang="cpp">
 
<source lang="cpp">
 
GuiImage image(&ImageData);
 
GuiImage image(&ImageData);
Line 448: Line 448:
 
Then your image will be put into the image_name_png buffer when it is compiled. Just place your image in the images folder, and the compiler will take care of the rest for you.  
 
Then your image will be put into the image_name_png buffer when it is compiled. Just place your image in the images folder, and the compiler will take care of the rest for you.  
  
Last, you must append your new image to the GuiWindow you are dealing with. Before doing do, you need to suspend the gui thread, so that you are not making changes to variables while the thread is reading them, and resume the thread after appending:
+
Then, you must append your new image to the GuiWindow you are dealing with. Before doing do, you need to suspend the gui thread, so that you are not making changes to variables while the thread is reading them, and resume the thread after appending:
 
<source lang = "cpp">
 
<source lang = "cpp">
 
HaltGui();
 
HaltGui();
Line 455: Line 455:
 
</source>
 
</source>
 
Unless the image is created as a pointer, you must have the & before it to pass the address of your GuiImage to the Append function.
 
Unless the image is created as a pointer, you must have the & before it to pass the address of your GuiImage to the Append function.
 +
 +
Now, unless you want your menu to display for only a frame or two, you are going to want to keep the menu going somehow. Do this by adding a loop in it, usually a while loop that checks to see if your menu variable has changed:
 +
 +
<source lang = "cpp">
 +
while(menu == MENU_NONE){
 +
    VIDEO_WaitVSync();
 +
    //Other code (you will use this mostly with GuiButtons)
 +
}
 +
</source>
 +
 +
Last, you ''must'' remove any GuiElements you have Append()ed in your menu (unless it was declared globally, but you should still remove it) before returning to the MainMenu function, or else the GUI will keep trying to display an image that doesn't exist any more. Use the Delete() function, which takes in a GuiElement:
 +
 +
<source lang = "cpp">
 +
mainWindow->Delete(&image);
 +
</source>
  
 
So that's all it takes to load an image. Easy, right? Here's an example of the complete proccess:
 
So that's all it takes to load an image. Easy, right? Here's an example of the complete proccess:

Revision as of 00:44, 7 July 2009

Foreward

LibwiiGui is written in C++ and uses classes. Classes are a collection of functions and variables related to a single thing. When using these classes, their collected functions and variables (if they are declared public) can be accessed by a dot (.) in some cases, and an arrow (->) in other cases. This will become more understandable later on in the tutorial, but for now, know that when the class instance is created with an asterisk (*) before it, you will be using the arrow (->). When the class is created without the asterisk, you will use the dot (.).

This tutorial uses the same design as the demo included with libwiigui. Every menu is created inside a function. The GUI updating routine is created as a function, and is in a thread. When wanting to change to a knew menu, you set the menu variable to a value that denotes the menu you wish to change to. Again, this will become more apparent later on.

Lastly, before attempting to read this tutorial (or try to use libwiigui for that matter) you should already have at least a basic understanding of C++, if not more. You can easily find some online tutorials (cprogramming.com, cplusplus.com) or a book from your local library.


Setting Up

You first must have devkitPro. DevkitPro is what allows programmer's to compile their programs for other systems, such as the Wii, PSP, and DS. This should be pretty straight-foreward for Windows users. Just download the latest Windows installer, and go through the installer. When asked what to install, you must install devkitPPC. That allows you to compile for the PowerPC architecture, which the Wii uses. You also will need libOGC, which is the library that lets you program for the Wii. Windows users may skip to the horizontal line below.

Linux and Mac users will need to download devkitPPC and libOGC. Select the appropriate version to download, and put them both in a directory (I would suggest making a devkitpro directory in your home folder and putting both in this folder). In Linux, you will want to edit your bash configuration file to define the DEVKITPRO and DEVKITPPC variables. The following code should do the trick for most linux users:

sudo gedit /usr/bash.bashrc

Then input your password, and add the following to the top of the file:

export DEVKITPPC="<path to devkitpro directory>"
eport DEVKITPPC="<path to devkitPPC directory>"

You also may want to download the examples from devkitpro. Put these in your devkitpro directory in a folder named examples.


If you haven't already done so, download libwiigui. You can extract this anywhere, as this is where your source will be for your entire project. You also will need to download the required devkitpro libraries. Extract these to your libogc folder (should be devkitPro/libogc) overwriting any existing files.


Class Creation

There are two ways a class instance can be created. LibwiiGui uses both methods. When you want a class to be able to be accessed by any other function, you will want to create it with an asterisk, which declares a pointer to your class. You will want to use this for your main GuiWindow, which should contain the background, and every other window (these other windows will be your menus). You also will want to initially set it to NULL for safety's sake.

GuiWindow * mainWindow = NULL;

These declarations should be at the top of the file, underneath the includes. Then, when using it, you will use ->

mainWindow->Append(image);

If you have a class that is only relavent to the function you are in (e.g. a menu) you will want to declare the instance inside the function:

int menu_function(){
     GuiWindow menuwindow;
     GuiImage menuimage;
}

and then use the dot:

int menu_function(){
     GuiWindow menuwindow;
     GuiImage menuimage;
     menuwindow.Append(&menuimage);
}


The GuiElement

Every class instance you create, save for a GuiSound, GuiTrigger, or a GuiImageData, will be inherited from the GuiElement class. This means that you can use any GuiElement function in any GuiElement you use, allowing you to move, get the width of, and get the state of images, buttons, keyboards, option browsers, etc. The GuiElement (from libwiigui/gui.h) class defines the following public functions which you can use with any GuiElement:

		//!Constructor

		GuiElement();

		//!Destructor

		~GuiElement();

		//!Set the element's parent

		//!\param e Pointer to parent element

		void SetParent(GuiElement * e);

		//!Gets the element's parent

		//!\return Pointer to parent element

		GuiElement * GetParent();

		//!Gets the current leftmost coordinate of the element

		//!Considers horizontal alignment, x offset, width, and parent element's GetLeft() / GetWidth() values

		//!\return left coordinate

		int GetLeft();

		//!Gets the current topmost coordinate of the element

		//!Considers vertical alignment, y offset, height, and parent element's GetTop() / GetHeight() values

		//!\return top coordinate

		int GetTop();

		//!Sets the minimum y offset of the element

		//!\param y Y offset

		void SetMinY(int y);

		//!Gets the minimum y offset of the element

		//!\return Minimum Y offset

		int GetMinY();

		//!Sets the maximum y offset of the element

		//!\param y Y offset

		void SetMaxY(int y);

		//!Gets the maximum y offset of the element

		//!\return Maximum Y offset

		int GetMaxY();

		//!Sets the minimum x offset of the element

		//!\param x X offset

		void SetMinX(int x);

		//!Gets the minimum x offset of the element

		//!\return Minimum X offset

		int GetMinX();

		//!Sets the maximum x offset of the element

		//!\param x X offset

		void SetMaxX(int x);

		//!Gets the maximum x offset of the element

		//!\return Maximum X offset

		int GetMaxX();

		//!Gets the current width of the element. Does not currently consider the scale

		//!\return width

		int GetWidth();

		//!Gets the height of the element. Does not currently consider the scale

		//!\return height

		int GetHeight();

		//!Sets the size (width/height) of the element

		//!\param w Width of element

		//!\param h Height of element

		void SetSize(int w, int h);

		//!Checks whether or not the element is visible

		//!\return true if visible, false otherwise

		bool IsVisible();

		//!Checks whether or not the element is selectable

		//!\return true if selectable, false otherwise

		bool IsSelectable();

		//!Checks whether or not the element is clickable

		//!\return true if clickable, false otherwise

		bool IsClickable();

		//!Checks whether or not the element is holdable

		//!\return true if holdable, false otherwise

		bool IsHoldable();

		//!Sets whether or not the element is selectable

		//!\param s Selectable

		void SetSelectable(bool s);

		//!Sets whether or not the element is clickable

		//!\param c Clickable

		void SetClickable(bool c);

		//!Sets whether or not the element is holdable

		//!\param c Holdable

		void SetHoldable(bool d);

		//!Gets the element's current state

		//!\return state

		int GetState();

		//!Gets the controller channel that last changed the element's state

		//!\return Channel number (0-3, -1 = no channel)

		int GetStateChan();

		//!Sets the element's alpha value

		//!\param a alpha value

		void SetAlpha(int a);

		//!Gets the element's alpha value

		//!Considers alpha, alphaDyn, and the parent element's GetAlpha() value

		//!\return alpha

		int GetAlpha();

		//!Sets the element's scale

		//!\param s scale (1 is 100%)

		void SetScale(float s);

		//!Gets the element's current scale

		//!Considers scale, scaleDyn, and the parent element's GetScale() value

		float GetScale();

		//!Set a new GuiTrigger for the element

		//!\param t Pointer to GuiTrigger

		void SetTrigger(GuiTrigger * t);

		//!\overload

		//!\param i Index of trigger array to set

		//!\param t Pointer to GuiTrigger

		void SetTrigger(u8 i, GuiTrigger * t);

		//!Checks whether rumble was requested by the element

		//!\return true is rumble was requested, false otherwise

		bool Rumble();

		//!Sets whether or not the element is requesting a rumble event

		//!\param r true if requesting rumble, false if not

		void SetRumble(bool r);

		//!Set an effect for the element

		//!\param e Effect to enable

		//!\param a Amount of the effect (usage varies on effect)

		//!\param t Target amount of the effect (usage varies on effect)

		void SetEffect(int e, int a, int t=0);

		//!Sets an effect to be enabled on wiimote cursor over

		//!\param e Effect to enable

		//!\param a Amount of the effect (usage varies on effect)

		//!\param t Target amount of the effect (usage varies on effect)

		void SetEffectOnOver(int e, int a, int t=0);

		//!Shortcut to SetEffectOnOver(EFFECT_SCALE, 4, 110)

		void SetEffectGrow();

		//!Gets the current element effects

		//!\return element effects

		int GetEffect();

		//!Checks whether the specified coordinates are within the element's boundaries

		//!\param x X coordinate

		//!\param y Y coordinate

		//!\return true if contained within, false otherwise

		bool IsInside(int x, int y);

		//!Sets the element's position

		//!\param x X coordinate

		//!\param y Y coordinate

		void SetPosition(int x, int y);

		//!Updates the element's effects (dynamic values)

		//!Called by Draw(), used for animation purposes

		void UpdateEffects();

		//!Sets a function to called after after Update()

		//!Callback function can be used to response to changes in the state of the element, and/or update the element's attributes

		void SetUpdateCallback(UpdateCallback u);

		//!Checks whether the element is in focus

		//!\return true if element is in focus, false otherwise

		int IsFocused();

		//!Sets the element's visibility

		//!\param v Visibility (true = visible)

		virtual void SetVisible(bool v);

		//!Sets the element's focus

		//!\param f Focus (true = in focus)

		virtual void SetFocus(int f);

		//!Sets the element's state

		//!\param s State (STATE_DEFAULT, STATE_SELECTED, STATE_CLICKED, STATE_DISABLED)

		//!\param c Controller channel (0-3, -1 = none)

		virtual void SetState(int s, int c = -1);

		//!Resets the element's state to STATE_DEFAULT

		virtual void ResetState();

		//!Gets whether or not the element is in STATE_SELECTED

		//!\return true if selected, false otherwise

		virtual int GetSelected();

		//!Sets the element's alignment respective to its parent element

		//!\param hor Horizontal alignment (ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTRE)

		//!\param vert Vertical alignment (ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE)

		virtual void SetAlignment(int hor, int vert);

		//!Called constantly to allow the element to respond to the current input data

		//!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD

		virtual void Update(GuiTrigger * t);

		//!Called constantly to redraw the element

		virtual void Draw();

Most functions are pretty self-explanatory. Some you will not use often, while others you will be using all the time (like SetPosition). Remember, you will want to use these functions with the upper-most class you can. For example, if you set GuiImageData to a GuiImage, and then set the GuiImage to a GuiButton, you will want to set the position of the GuiButton, not the GuiImage.


Your First Image

Images are created using the GuiImage class. Images can either be loaded from the sd card, or from a buffer. There are quite a few things you will have to do, though, before you can display your image. Open demo.cpp from the libwiigui template.

First, we must initialize the video hardware, by using the function from video.cpp. This must be done in any wii application you make, even if you are not going to be displaying any images:

InitVideo();

If you are going to want sound, initilaize the sound hardware also:

InitAudio();

If you are going to be loading images from the sd card (or using the sd card for any other reason), you will need to initialize it:

fatInitDefault();

Next, you will need to start the Gui updating thread:

InitGUIThreads();

This is what writes any changes you made to the screen. Last, you would call the function that starts your main menu. In the demo, it is:

MainMenu(MENU_SETTINGS);

but it can be whatever you named your menu function.

Now, open menu.cpp. Find the MainMenu function. To load an image, you declare an instance of the GuiImage class. Unless you are in the MainMenu function, you will want to declare it this way:

GuiImage image(&ImageData);

What is the ImageData inside the initializer function? Well before actually creating the image, you must load your image. This can be done by creating an instance of the GuiImageData class:

GuiImageData imagedata(image_source);

The image source is your image buffer, where the contents of the image are kept. The image must be in the png format, and have dimensions that are a multiple of 4. To load an image from an sd card to an image buffer, do the following:

FILE *fp = fopen("Path to image on sd card","r");
''todo''

You may be wondering how an image gets loaded into a buffer without the sd card. Whenever you want to add a new image, you must copy the image to the source/images folder, and add it to filelist.h:

extern const u8		image_name_png[];

extern const u32	image_name_png_size;

Then your image will be put into the image_name_png buffer when it is compiled. Just place your image in the images folder, and the compiler will take care of the rest for you.

Then, you must append your new image to the GuiWindow you are dealing with. Before doing do, you need to suspend the gui thread, so that you are not making changes to variables while the thread is reading them, and resume the thread after appending:

HaltGui();
GuiWindow.Append(&image);
ResumeGui();

Unless the image is created as a pointer, you must have the & before it to pass the address of your GuiImage to the Append function.

Now, unless you want your menu to display for only a frame or two, you are going to want to keep the menu going somehow. Do this by adding a loop in it, usually a while loop that checks to see if your menu variable has changed:

while(menu == MENU_NONE){
    VIDEO_WaitVSync();
    //Other code (you will use this mostly with GuiButtons)
}

Last, you must remove any GuiElements you have Append()ed in your menu (unless it was declared globally, but you should still remove it) before returning to the MainMenu function, or else the GUI will keep trying to display an image that doesn't exist any more. Use the Delete() function, which takes in a GuiElement:

mainWindow->Delete(&image);

So that's all it takes to load an image. Easy, right? Here's an example of the complete proccess:

main.cpp:

#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <ogcsys.h>
#include <unistd.h>
#include "FreeTypeGX.h"
#include "video.h"
#include "audio.h"
#include "menu.h"
#include "input.h"
#include "filelist.h"
#include "demo.h"

int ExitRequested = 0;

void initall(){
	InitVideo(); // Initialise video
        InitAudio()
	InitGUIThreads();
	fatInitDefault();
}

void ExitApp()
{
	ShutoffRumble();
	StopGX();
	exit(0);
}

//---------------------------------------------------------------------------------

int main(int argc, char **argv) {
	initall();
	MainMenu(MAIN_SCREEN);
}

menu.cpp:

#include <gccore.h>
#include <ogcsys.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <wiiuse/wpad.h>

#include "libwiigui/gui.h"
#include "menu.h"
#include "demo.h"
#include "input.h"
#include "filelist.h"

static GuiImageData * pointer[4];
static GuiImage * bgImg = NULL;
static GuiSound * bgMusic = NULL;
static GuiWindow * mainWindow = NULL;
static lwp_t guithread = LWP_THREAD_NULL;
static bool guiHalt = true;



static void
ResumeGui()
{
	guiHalt = false;
	LWP_ResumeThread (guithread);
}




static void
HaltGui()
{
	guiHalt = true;

	// wait for thread to finish
	while(!LWP_ThreadIsSuspended(guithread))
		usleep(50);
}



static void *
UpdateGUI (void *arg)
{
	while(1)
	{
		if(guiHalt)
		{
			LWP_SuspendThread(guithread);
		}
		else
		{
			mainWindow->Draw();

			#ifdef HW_RVL
			for(int i=3; i >= 0; i--) // so that player 1's cursor appears on top!
			{
				if(userInput[i].wpad.ir.valid)
					Menu_DrawImg(userInput[i].wpad.ir.x-48, userInput[i].wpad.ir.y-48,
						96, 96, pointer[i]->GetImage(), userInput[i].wpad.ir.angle, 1, 1, 255);
				DoRumble(i);
			}
			#endif

			Menu_Render();

			for(int i=0; i < 4; i++)
				mainWindow->Update(&userInput[i]);

			if(ExitRequested)
			{
				for(int a = 0; a < 255; a += 15)
				{
					mainWindow->Draw();
					Menu_DrawRectangle(0,0,screenwidth,screenheight,(GXColor){0, 0, 0, a},1);
					Menu_Render();
					
				}
				ExitApp();
			}
		}
	}
	return NULL;
}




void
InitGUIThreads()
{
	LWP_CreateThread (&guithread, UpdateGUI, NULL, NULL, 0, 70);
}





int MenuDefault(){

     int menu=MENU_DEFAULT;
     GuiImageData image_data(image_png);
     GuiImage image(&image_data);
     HaltGui();
     mainWindow->Append(&image);
     ResumeGui();
     while(menu==MENU_DEFAULT){
          VIDEO_WaitVSync();
     }

     mainWindow->Delete(&image);
     return menu;
}

MainMenu(){
void MainMenu(int menu)
{
	int currentMenu = menu;

	#ifdef HW_RVL
	pointer[0] = new GuiImageData(player1_point_png);
	pointer[1] = new GuiImageData(player2_point_png);
	pointer[2] = new GuiImageData(player3_point_png);
	pointer[3] = new GuiImageData(player4_point_png);
	#endif

	mainWindow = new GuiWindow(screenwidth, screenheight);

	bgImg = new GuiImage(screenwidth, screenheight, (GXColor){50, 50, 50, 255});
	bgImg->ColorStripe(30);
	mainWindow->Append(bgImg);

	GuiTrigger trigA;
	trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A);

	ResumeGui();

	bgMusic = new GuiSound(bg_music_ogg, bg_music_ogg_size, SOUND_OGG);
	bgMusic->SetVolume(50);
	bgMusic->Play(); // startup music

	while(currentMenu != MENU_EXIT)
	{
		switch (currentMenu)
		{
			case MENU_DEFAULT:
				currentMenu = MenuDefault();
				break;
			default: // unrecognized menu
				currentMenu = MenuDefault();
				break;
		}
	}

	ResumeGui();
	ExitRequested = 1;
	while(1) usleep(50);

	HaltGui();

	bgMusic->Stop();
	delete bgMusic;
	delete bgImg;
	delete mainWindow;

	delete pointer[0];
	delete pointer[1];
	delete pointer[2];
	delete pointer[3];

	mainWindow = NULL;
}
}

If you want any other menu names (MENU_DEFAULT), you can define them in menu.h:

menu.h

#ifndef _MENU_H_
#define _MENU_H_

#include <ogcsys.h>

void InitGUIThreads();
void MainMenu (int menuitem);

enum
{
	MENU_EXIT = -1,
	MENU_NONE,
	MENU_SETTINGS,
	MENU_SETTINGS_FILE,
        MENU_DEFAULT
};

#endif

Buttons

Buttons are very useful, as they allow an image (or any other GuiElement) to be "clicked." GuiButtons add the following functions to your initial GuiElement functions:

		//!Constructor

		//!\param w Width

		//!\param h Height

		GuiButton(int w, int h);

		//!Destructor

		~GuiButton();

		//!Sets the button's image

		//!\param i Pointer to GuiImage object

		void SetImage(GuiImage* i);

		//!Sets the button's image on over

		//!\param i Pointer to GuiImage object

		void SetImageOver(GuiImage* i);

		//!Sets the button's image on hold

		//!\param i Pointer to GuiImage object

		void SetImageHold(GuiImage* i);

		//!Sets the button's image on click

		//!\param i Pointer to GuiImage object

		void SetImageClick(GuiImage* i);

		//!Sets the button's icon

		//!\param i Pointer to GuiImage object

		void SetIcon(GuiImage* i);

		//!Sets the button's icon on over

		//!\param i Pointer to GuiImage object

		void SetIconOver(GuiImage* i);

		//!Sets the button's icon on hold

		//!\param i Pointer to GuiImage object

		void SetIconHold(GuiImage* i);

		//!Sets the button's icon on click

		//!\param i Pointer to GuiImage object

		void SetIconClick(GuiImage* i);

		//!Sets the button's label

		//!\param t Pointer to GuiText object

		//!\param n Index of label to set (optional, default is 0)

		void SetLabel(GuiText* t, int n = 0);

		//!Sets the button's label on over (eg: different colored text)

		//!\param t Pointer to GuiText object

		//!\param n Index of label to set (optional, default is 0)

		void SetLabelOver(GuiText* t, int n = 0);

		//!Sets the button's label on hold

		//!\param t Pointer to GuiText object

		//!\param n Index of label to set (optional, default is 0)

		void SetLabelHold(GuiText* t, int n = 0);

		//!Sets the button's label on click

		//!\param t Pointer to GuiText object

		//!\param n Index of label to set (optional, default is 0)

		void SetLabelClick(GuiText* t, int n = 0);

		//!Sets the sound to play on over

		//!\param s Pointer to GuiSound object

		void SetSoundOver(GuiSound * s);

		//!Sets the sound to play on hold

		//!\param s Pointer to GuiSound object

		void SetSoundHold(GuiSound * s);

		//!Sets the sound to play on click

		//!\param s Pointer to GuiSound object

		void SetSoundClick(GuiSound * s);

		//!Constantly called to draw the GuiButton

		void Draw();

		//!Constantly called to allow the GuiButton to respond to updated input data

		//!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD

		void Update(GuiTrigger * t);

Once again, it's pretty self-explanatory. Buttons are simple to create, just remember, you must Append() the GuiButton to your GuiWindow, not the GuiImage. To create a button, declare an instance of the GuiButton class, giving the address your GuiImage (or another GuiElement) as the argument:

GuiImage image;
GuiButton button(&image);

Then you can use the button as if it were an image, except you can use SetTrigger, which sets when the button will be considered "clicked." To do this, you must first define a GuiTrigger. A GuiTrigger can be made by declaring an instance, the first argument being the Wiimote channel (use -1 for all), the second being the Wiimote buttons, and the third being the Gamecube buttons. For the buttons, you can do a bitwise-or ( | ) to "add" the buttons together, allowing the trigger to use more than one button.

GuiTrigger trigA;
trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A);

Here I am setting a GuiTrigger to trigger when the wiimote button A, classic controller button A, or gamecube button A is clicked.

Then, you must set your button's trigger using SetTrigger, giving the address of your GuiTrigger as the only argument:

button.SetTrigger(&trigA);

Now, if your button is clicked, the GetState function will return STATE_CLICKED:

if(button.GetState() == STATE_CLICKED){
   //Do something because button was clicked
}