The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Showing a basic native GUI message box

From Allegro Wiki
Jump to: navigation, search

One of the native addons functions is al_show_native_message_box(). This function is more important than it may seem, since it allows you to show an error message without a console display, and this apart of being more user friendly it also allows you to do other important things very fast.

The easiest example to show a native message would be something like this:

#include "allegro5/allegro.h"
#include <allegro5/allegro_native_dialog.h>

int main(int argc, char **argv){

   if(al_init()) {
      al_show_native_message_box(al_get_current_display(), 
                                 "Window Title", 
                                 "Content Title", 
                                 "The error message here",
                                 NULL, ALLEGRO_MESSAGEBOX_ERROR);
      return 0;
   }

   return 0;
}


With that code you should only get this message box, obviously if you're making a console application your console surely is over there too.

ND1.PNG

As you can see before be able to use the al_show_native_message_box() function, you need to initialize Allegro, and we call the function al_get_current_display() to get the current display, since the message function needs it. al_get_current_display returns NULL if there is no display, and since al_show_native_message_box() can accepts NULLs argument there's no problem. We're going to always use al_get_current_display() because in case there is a display we need to send the pointer of such display to the al_show_native_message_box() function, otherwise you will get some problems, like the message box showing behind the main display, hiding it completely to the user.

Now, if you're like me, you surely have noticed that not only the parameters but the function name itself are too long, so before we move on we're going to wrap that function up.

Sometime I might be a really lazy person, so I came up with this:

#include "allegro5/allegro.h"
#include <allegro5/allegro_native_dialog.h>
     
short m_box(const char* message       = "No message given",
            const char* content_title = "Error",
            const char* title         = "Error"){

   switch(al_show_native_message_box(al_get_current_display(), 
                                     title, 
                                     content_title,
                                     message, NULL,
                                     ALLEGRO_MESSAGEBOX_ERROR)){

      case 0: return 0; // User pressed the Cancel button
      case 1: return 1; //  "     "      "    OK     "
      case 2: return 2; // I don't understand what does this

   }
}

int main(){

   if(!al_init()) {

      switch(m_box()){

         case 0: /*Somthing*/; break;
         case 1: /*Somthing*/; break;
         case 2: /*Somthing*/; break;

      }

      return 0;
   }

   return 0;
}

So as you can see, this way you can actually call the function with no parameters, I know, why to call an error message box if the only thing you'll get is: "No message given"?. Well, I told you that I could be really lazy. Now, for those who doesn't know what the <censored> is going on, here we're telling the function to initialize the parameter by default to those messages if no parameter is sent by the function in the calling process, it's a functionality of the programming language we're using.

They are ordered in a laziness progressive way, what does that mean you say?, well suppose that today you're not lazy at all, you could do something like this:

m_box("I'm not a lazy person, this is a good message", "Take the content title", 
      "And yes I want to change the window title");

This way you would be customizing the entire text box, which is very nice. Now suppose you're a little bit tired:

m_box("I'm not so lazy so take the message", "Take the content title too");

Ok this time, you just gave two arguments to the function so the third one will use the default paramater which is just "Error", very explanatory isn't?.

Now, <censored> you're really tired, you have just got home from a party but your programming instincts still there, so you would just do this:

m_box("I'm too tired ok? I just wanted to tell you there is an error, now leave me alone");

That's pretty fine, the two last parameters are going to be initialized with their corresponding default parameters which are very explanatory: "Error Error", everyone would get it isn't? there is nothing more important than a message box with the "Error" word written twice.

And here is where people like me would come in doing something like this:

m_box();

So the user will get a message box telling him twice that there is an error, and then: "No message given"... Well I guess that is better than when you get those "Error 8X00UOYKCUF" from Windows. To be honest I only use this type of message on little experiments when I know that there are just two or three things that could be possible failing.

I think the returning part of the wrap function is very simple it doesn't need to be explained. So apart of being an user friendly way to show an error message, what else can we do with this?. Well, I don't know if you have noticed that sometimes suppose that you have a 800MB game and if only and just only one little image is missed the entire application won't run?, well, I have noticed and let me tell you it's really annoying, I mean, I know if the image is not there then what the <censored> could the program do for you?, well, I would like to have the option to run it anyway, as long as the program notify me that there is a missed image, that's what we're going to do here.

So first of all, if our application doesn't find the image what it can do?, well replace it obviously, al_draw_bitmap() doesn't work with an NULL argument, but even if it could accept NULL pointers, we would like to show to the user that there is a missed image, so we need to replace the image our program couldn't find with something else, so what are we going to do? try to load an "error" image?, and what if that image is missed too?, so unfortunately we're going to need to create an image on the fly with Allegro itself and the primitive addons.

We need then to create a function that'll return us an ALLEGRO_BITMAP pointer, pointing to an image created on the fly, the function could be something like this:

ALLEGRO_BITMAP *make_err_ima(ALLEGRO_DISPLAY *T_disp){

   ALLEGRO_BITMAP *T_img = al_create_bitmap(32,32);
   if(!T_img)return 0;
   al_set_target_bitmap(T_img);
   al_clear_to_color(al_map_rgb(255, 0, 0));
   al_draw_line( 0,  0, 32, 32, al_map_rgb(255, 255, 255), 1.5);
   al_draw_line(32,  0,  0, 32, al_map_rgb(255, 255, 255), 1.5);
   al_set_target_bitmap(al_get_backbuffer(T_disp));
   return T_img;

}


ALLEGRO_BITMAP *T_img = al_create_bitmap(32,32);

This is our temporal "error" image pointer, which is going to be 32 X 32 pixels.

if(!T_img)return 0;

If there is some problem when creating the temporal "error" image then return 0, this will avoid the subsequent code to prevent a complete crash. If something like this should happen the best thing to do would be to notify the user that there must be something really wrong at his/her system, not been able to load an image and not be able to create it, makes it clear that there is a big problem.

al_set_target_bitmap(T_img);

If you have already read the Basic tutorial on loading and showing images, you surly know that the target bitmap is always a buffer bitmap which is hidden to the user until you call al_flip_display(). So we need to tell Allegro to change that to allow us to perform drawing operations in another bitmap, in this case, the T_img bitmap. With al_set_target_bitmap() we can do exactly that.

al_clear_to_color(al_map_rgb(255, 0, 0));

Here we paint our 32X32 image in red.

   al_draw_line( 0,  0, 32, 32, al_map_rgb(255, 255, 255), 1.5);
   al_draw_line(32,  0,  0, 32, al_map_rgb(255, 255, 255), 1.5);

Now we draw some lines to make an "X".

al_set_target_bitmap(al_get_backbuffer(T_disp));
return T_img;

And finally we set back our target bitmap to the buffer bitmap. And return the pointer pointing to a bitmap created on the fly!.

Now, let's see a complete code showing an example

#include "allegro5/allegro.h"
#include "allegro5/allegro_image.h"
#include <allegro5/allegro_primitives.h>
#include "allegro5/allegro_native_dialog.h"

short m_box(const char* message       = "No message given",
            const char* content_title = "Error",
            const char* title         = "Error"){

   switch(al_show_native_message_box(al_get_current_display(),
                                     title,
                                     content_title,
                                     message, NULL,
                                     ALLEGRO_MESSAGEBOX_YES_NO)){

      case 0: return 0; // User pressed the Cancel button
      case 1: return 1; //  "     "      "    OK     "
      case 2: return 2; // I don't understand what does this

   }
}

ALLEGRO_BITMAP *make_err_ima(){

   ALLEGRO_BITMAP *T_img = al_create_bitmap(32,32);
   if(!T_img)return 0;
   al_set_target_bitmap(T_img);
   al_clear_to_color(al_map_rgb(255, 0, 0));
   al_draw_line( 0,  0, 32, 32, al_map_rgb(255, 255, 255), 1.5);
   al_draw_line(32,  0,  0, 32, al_map_rgb(255, 255, 255), 1.5);
   al_set_target_bitmap(al_get_backbuffer(al_get_current_display()));
   return T_img;

}

ALLEGRO_BITMAP *smart_ima_loader(const char *image_name){

   ALLEGRO_BITMAP *T_img = NULL;
   T_img = al_load_bitmap(image_name);

   if(!T_img)
      switch(m_box("There is a proble when loading an image,"
             "would you like to continue anyway?", image_name)){

         case 0: return NULL; break;
         case 1: return make_err_ima(); break;

      }

   else
      return T_img;

}

int main(){

   ALLEGRO_DISPLAY *display = NULL;
   ALLEGRO_BITMAP  *image   = NULL;

   if(!al_init()){
      m_box("Problems when initilizing Allegro");
   }

   if(!al_init_image_addon()){
      m_box("Problems when initilizing the image addon");
   }

   if(!al_init_primitives_addon()){
      m_box("Problems when initilizing the primitives addon");
   }

   display = al_create_display(800,600);

   if(!display){
      m_box("Problems when creating the display");
   }
   //Since all this initializations are very necessary, there is no much we can do in case they fail

   image = smart_ima_loader("image.png");

   if(!image){
      return 0;
   }

   al_draw_bitmap(image, 200, 200, 0);

   al_flip_display();

   al_rest(2);

   al_destroy_bitmap(image);

   return 0;
}

Ok take it easy it is not so complicate!. We're going to cover all the points.

You surely have noticed that there is a new function: smart_ima_loader() this is a smart function compared to al_load_bitmap(), but I bet you could come up with something much better. This function automatically will show an error message if the image doesn't exist, and if the user wants to run the application anyways it will create a very simple image on the fly to replace it.

ALLEGRO_BITMAP *smart_ima_loader(const char *image_name){

   ALLEGRO_BITMAP *T_img = NULL;
   T_img = al_load_bitmap(image_name);

   if(!T_img)
      
      switch(m_box("There is a proble when loading an image,"
             "would you like to continue anyway?", image_name)){

         case 0: return NULL; break;
         case 1: return make_err_ima(); break;

      }

   else
      return T_img;

}

So how it works?.

   ALLEGRO_BITMAP *T_img = NULL;
   T_img = al_load_bitmap(image_name);

Well, we first create an ALLEGRO_BITMAP and try to initialize it with al_load_bitmap().

   if(!T_img)
      
      switch(m_box("There is a proble when loading an image,"
             "would you like to continue anyway?", image_name)){

         case 0: return NULL; break;
         case 1: return make_err_ima(); break;

      }

   else
      return T_img;

If everything goes well, the else statement will be called, returning the correct image. But if something goes wrong the adventure begins ;).

      switch(m_box("There is a proble when loading an image,"
             "would you like to continue anyway?", image_name)){

         case 0: return NULL; break;
         case 1: return make_err_ima(); break;

      }

Ok, something went wrong when loading the image, we notify it to the user with the m_box() function, but at the same time, that function will return a number telling us which button was pressed, returns 0 if the "No" button was pressed or 1 if the "Yes" button was pressed, and we need to process that information correctly. But why am I using a switch statement instead of a normal if()?, well it's because supposedly the al_show_native_message_box() could also return a third argument which would be the number 2, the manual says that will return "0 if the dialog window was closed without activating a button." and "2 if the Cancel or No button was pressed." but as you can see, that is not the case, actually it returns 0 if Cancel or No button was pressed, and 1 if the OK or Yes button was pressed, but I wanted to use the switch statement in case in the future this is fixed or I manage to understand if I'm doing something wrong.

case 0: return NULL; break;
case 1: return make_err_ima(); break;

So if the m_box() function returns 0, that means that the user doesn't want to continue, so we're going to return NULL. But in case m_box() returns 1, that means that the user wants to continue, so we call make_err_ima() to create an "error" image on the fly to replace the image that the program wasn't able to load.

image = smart_ima_loader("image.png");
 
   if(!image){
      return 0;
   }

So finally after understanding how everything works, we call our "smart" function to load an image. Remember that in this case if our "smart" function returns a NULL pointers that means that the user doesn't want to continue, so you need to free all memory used by other bitmaps and practically kill the application right away, but I'm not going to cover that in this tutorial, this is just a demonstration to give you an idea about how fast you can interact with the user with the native dialog addon.

This would me an example of our message box.

ND2.PNG

And this is the image we create on the fly in case the user wants to continue.

ND3.PNG