millennium-dev team mailing list archive
-
millennium-dev team
-
Mailing list archive
-
Message #00136
[Merge] lp:~rafalcieslak256/millenniumduel/cached-redrawing into lp:millenniumduel
Rafał Cieślak has proposed merging lp:~rafalcieslak256/millenniumduel/cached-redrawing into lp:millenniumduel.
Requested reviews:
Adam Malinowski (adayah)
Related bugs:
Bug #1204559 in Millennium Duel: "Drawing widgets is time-consuming"
https://bugs.launchpad.net/millenniumduel/+bug/1204559
For more details, see:
https://code.launchpad.net/~rafalcieslak256/millenniumduel/cached-redrawing/+merge/176962
The super-awesome drawing efficiency improvement.
THE PROBLEM:
Currently our drawing code is insanely slow. It redraws everything over and over, even when the application is idling. Redrawing takes loads of time, because every single time the display is redrawn, all buttons, labels need to re-render their text (which takes relatively long time). This, together with redrawing the same parts of image several times in a row, results in visibly slow behavior.
Not only does the running application hog CPU pointlessly, but it also is not responsive. Clicking "button1" - "button2" several times quickly and observing the stdout causes a visible lag, the text messages appear a significant amount of time too late. Clicling "quit" and "now" several times quickly causes a visible lag in the display, the interface does not change instantly after a click, but a few hundred milliseconds later. YMMV, but I expect that you should experience such lag too, since I can observe it on a quite a fast machine with a quick CPU.
While such lag is affordable, it should be avoided. It leads to less pleasant UX, and if this effect accumulates (and most certainly, it would), soon we'll end up with serious performance problems.
THE APPROACH:
The point in avoiding relentless redraws is noticing that re-rendering has to be done very rarely. Most of the time widget's contents do not change at all, and most of time the whole display's contents do not change.
The key to improve effectiveness consists of two parts:
* Using cached bitmaps for all drawables, that store widget's appearance and are used to draw the widget (drawing bitmaps over each other is super-fast, since allegro uses GPU for that [wow!]).
* Re-rendering drawables only if it's really needed, that is when it's contents have changed (for example, a button needs to be re-rendered if it's text has changed, a HBox needs to be redrawn if a child has been added, or if one of it's children needs to be re-rendered).
Combining these two tricks led me to astonishing results, I am now completely unable to notice any extensive CPU usage caused by the application, and there is no interface lag at all.
While working on this branch, I have used valgrind's callgrind tool to monitor where most of execution time is spent, and the results confirm that this approach is close to perfect (if not the perfect one), since rendering functions now take immeasurable amounts of time [this branch seems to work at least 500x faster, but this may vary with the CPU and GPU properties].
THE SOLUTION:
This branch introduces a cache bitmap for every single drawable. Also, each drawable has now a flag needs_redrawing, that represents the need to re-render it's contents.
If a drawable does not need redrawing, then draw(...) will simply use that cached bitmap. Otherwise, it will call redraw(...) asking it to render the drawable as it wishes onto that cache bitmap.
That means any custom widget should no longer overwrite draw(...), which is implemented in the Drawable class.
Instead, redraw(int width, int height) should be overwritten with widget-specific graphics.
The caller of redraw(...) ensures that the proper target bitmap an translation is set, so all that the widget has to do within its redraw(...) is to fill it with accurate contents (given the requested width and height).
Note that redraw(...) will be called rarely.
Also, all drawables have now a useful convenience function SetNeedsRedrawing(), which sets this widget's flag, as well as its parent's and parent's parent's, so that a change in an embedded widget will propagate redrawing all widgets that contain it, so that their contents get updated.
Therefore whenever widget's appearance gets changed (for example, button's text is changed), the widget should call SetNeedsRedrawing().
Also, the main loop redraws the main widget if and only if either it needs redrawing or the main widget is now an another drawable. That greatly reduces the need to keep redrawing the main display.
All the changes I did in main() are because now Display::init() has to be called BEFORE any drawables are constructed, and therefore I had to create them dynamically.
--
https://code.launchpad.net/~rafalcieslak256/millenniumduel/cached-redrawing/+merge/176962
Your team Millennium Developers is subscribed to branch lp:millenniumduel.
=== modified file 'src/Box.hpp'
--- src/Box.hpp 2013-07-24 20:30:41 +0000
+++ src/Box.hpp 2013-07-25 15:05:03 +0000
@@ -13,7 +13,7 @@
void SetMargin(int margin);
//Drawable Methods:
- virtual void draw(SDrawArea a) = 0;
+ virtual void redraw(int w, int h) = 0;
virtual int GetMinimalHeight() = 0;
virtual int GetMinimalWidth () = 0;
virtual void on_clicked(SCoordinates p) = 0;
=== modified file 'src/Fixed.cpp'
--- src/Fixed.cpp 2013-07-24 20:14:31 +0000
+++ src/Fixed.cpp 2013-07-25 15:05:03 +0000
@@ -1,12 +1,12 @@
#include "Fixed.hpp"
#include "allegro5/allegro_primitives.h"
+#include <iostream>
+
Fixed::Fixed(int w, int h) : width(w), height(h){
}
-void Fixed::draw(SDrawArea a){
- use_new_transform_to(a.x,a.y);
- al_draw_filled_rectangle(a.x, a.y, a.x+a.w, a.y+a.h, _background_color);
+void Fixed::redraw(int w, int h){
for(auto q : items){
Drawable* x = q.first;
@@ -15,16 +15,24 @@
int width = x->GetMinimalWidth ();
int height = x->GetMinimalHeight();
- x->draw(SDrawArea(a.x + c.x, a.y + c.y, width, height));
+ x->draw(SDrawArea(c.x, c.y, width, height));
}
}
void Fixed::add_item(Drawable& d, SCoordinates c){
+ if(d.parent != nullptr){
+ std::cerr << "Unable to add item to Fixed: item already has a parent" << std::endl;
+ return;
+ }
items[&d] = c;
+ d.parent = this;
+
+ SetNeedsRedrawing();
}
void Fixed::remove_item(Drawable& d){
items.erase(&d);
+ SetNeedsRedrawing();
}
void Fixed::on_clicked(SCoordinates p){
=== modified file 'src/Fixed.hpp'
--- src/Fixed.hpp 2013-07-21 12:29:15 +0000
+++ src/Fixed.hpp 2013-07-25 15:05:03 +0000
@@ -7,7 +7,7 @@
class Fixed : public Drawable{
public:
Fixed(int w = 0, int h = 0);
- void draw(SDrawArea a);
+ void redraw(int w, int h);
void add_item(Drawable& d, SCoordinates c);
void remove_item(Drawable& d);
void on_clicked(SCoordinates y);
=== modified file 'src/HBox.cpp'
--- src/HBox.cpp 2013-07-24 20:24:51 +0000
+++ src/HBox.cpp 2013-07-25 15:05:03 +0000
@@ -1,20 +1,17 @@
#include "HBox.hpp"
#include <allegro5/allegro_primitives.h>
-void HBox::draw(SDrawArea a){
+void HBox::redraw(int w, int h){
- _RecalculateSizes(a.h, &Drawable::GetMinimalWidth);
-
- use_new_transform_to(a.x,a.y);
- al_draw_filled_rectangle(a.x, a.y, a.x+a.w, a.y+a.h, _background_color);
-
- int x = a.x + margin, y = a.y + margin;
+ _RecalculateSizes(h, &Drawable::GetMinimalWidth);
+
+ int x = margin, y = margin;
for(auto i : children){
if(!i.is_visible) continue; //an unvisible widget. skip it...
int width = i.size;
- int height = a.h - 2*margin;
+ int height = h - 2*margin;
i.item.draw(SDrawArea(x,y,width,height));
@@ -24,8 +21,8 @@
}
// Remember my size (for click recognition)
- current_height = a.h;
- current_width = a.w;
+ current_height = h;
+ current_width = w;
}
=== modified file 'src/HBox.hpp'
--- src/HBox.hpp 2013-07-24 20:30:41 +0000
+++ src/HBox.hpp 2013-07-25 15:05:03 +0000
@@ -5,7 +5,7 @@
class HBox : public Box{
public:
- virtual void draw(SDrawArea a) override;
+ virtual void redraw(int w, int h) override;
virtual int GetMinimalHeight() override;
virtual int GetMinimalWidth () override;
=== modified file 'src/Image.cpp'
--- src/Image.cpp 2013-07-24 14:07:57 +0000
+++ src/Image.cpp 2013-07-25 15:05:03 +0000
@@ -18,11 +18,10 @@
int Image::GetMinimalHeight(){return _height;}
int Image::GetMinimalWidth (){return _width; }
-void Image::SetMinimalHeight(int h) {_height = h;}
-void Image::SetMinimalWidth (int w) {_width = w;}
+void Image::SetMinimalHeight(int h) {_height = h; SetNeedsRedrawing();}
+void Image::SetMinimalWidth (int w) {_width = w; SetNeedsRedrawing();}
-void Image::draw(SDrawArea a){
- use_new_transform_to(a.x,a.y);
- al_draw_scaled_bitmap(_bitmap,0,0,_bitmap_width,_bitmap_height, 0,0, a.w, a.h, 0);
+void Image::redraw(int w, int h){
+ al_draw_scaled_bitmap(_bitmap,0,0,_bitmap_width,_bitmap_height, 0,0, w, h, 0);
//al_draw_bitmap(_bitmap,0,0,0);
}
=== modified file 'src/Image.hpp'
--- src/Image.hpp 2013-07-24 20:30:41 +0000
+++ src/Image.hpp 2013-07-25 15:05:03 +0000
@@ -12,7 +12,7 @@
Image(std::string filepath);
virtual ~Image();
- virtual void draw(SDrawArea a) override;
+ virtual void redraw(int w, int h) override;
virtual int GetMinimalHeight() override;
virtual int GetMinimalWidth () override;
=== modified file 'src/LayeredView.cpp'
--- src/LayeredView.cpp 2013-07-24 15:57:31 +0000
+++ src/LayeredView.cpp 2013-07-25 15:05:03 +0000
@@ -14,11 +14,13 @@
children.insert(i);
break;
}
+ SetNeedsRedrawing();
}
void LayeredView::HideLayer(Drawable& layer)
{
SetVisibility(layer,false);
+ SetNeedsRedrawing();
}
void LayeredView::SetOnlyVisibleLayer(Drawable& layer)
@@ -28,13 +30,14 @@
i.is_visible = true;
else
i.is_visible = false;
+ SetNeedsRedrawing();
}
-void LayeredView::draw(SDrawArea area)
+void LayeredView::redraw(int w, int h)
{
for(auto i = children.rbegin() ; i != children.rend() ; i++)
if(i->is_visible)
- i->item.draw(area);
+ i->item.draw(SDrawArea(0,0,w,h));
}
void LayeredView::on_clicked(SCoordinates p)
=== modified file 'src/LayeredView.hpp'
--- src/LayeredView.hpp 2013-07-24 20:30:41 +0000
+++ src/LayeredView.hpp 2013-07-25 15:05:03 +0000
@@ -13,7 +13,7 @@
void SetOnlyVisibleLayer(Drawable& layer);
//Drawable Methods:
- virtual void draw(SDrawArea area);
+ virtual void redraw(int w, int h);
virtual void on_clicked(SCoordinates p);
virtual void on_clicked();
virtual int GetMinimalWidth ();
=== modified file 'src/OrderedContainer.cpp'
--- src/OrderedContainer.cpp 2013-07-24 14:40:03 +0000
+++ src/OrderedContainer.cpp 2013-07-25 15:05:03 +0000
@@ -1,13 +1,21 @@
#include "OrderedContainer.hpp"
+#include <iostream>
OrderedContainer::OrderedContainer() {};
void OrderedContainer::Add(Drawable& item, int position, BoxPackingMode mode, bool is_visible){
+ if(item.parent != nullptr){
+ std::cerr << "Unable to add item to OrderedContainer: item already has a parent" << std::endl;
+ return;
+ }
ContainerEntry ce(item);
ce.order = position;
ce.mode = mode;
ce.is_visible = is_visible;
children.insert(ce);
+ item.parent = this;
+
+ SetNeedsRedrawing();
}
void OrderedContainer::SetVisibility(Drawable& item, bool is_visible)
@@ -18,4 +26,6 @@
i.is_visible = is_visible;
break;
}
+
+ SetNeedsRedrawing();
}
=== modified file 'src/OrderedContainer.hpp'
--- src/OrderedContainer.hpp 2013-07-24 20:30:41 +0000
+++ src/OrderedContainer.hpp 2013-07-25 15:05:03 +0000
@@ -18,7 +18,7 @@
void SetVisibility(Drawable& item, bool is_visible);
//Drawable Methods:
- virtual void draw(SDrawArea a) = 0;
+ virtual void redraw(int w, int h) = 0;
virtual int GetMinimalHeight() = 0;
virtual int GetMinimalWidth () = 0;
virtual void on_clicked(SCoordinates p) = 0;
=== modified file 'src/VBox.cpp'
--- src/VBox.cpp 2013-07-24 20:24:51 +0000
+++ src/VBox.cpp 2013-07-25 15:05:03 +0000
@@ -1,19 +1,16 @@
#include "VBox.hpp"
#include <allegro5/allegro_primitives.h>
-void VBox::draw(SDrawArea a){
-
- _RecalculateSizes(a.h, &Drawable::GetMinimalHeight);
-
- use_new_transform_to(a.x,a.y);
- al_draw_filled_rectangle(a.x, a.y, a.x+a.w, a.y+a.h, _background_color);
-
- int x = a.x + margin, y = a.y + margin;
+void VBox::redraw(int w, int h){
+
+ _RecalculateSizes(h, &Drawable::GetMinimalHeight);
+
+ int x = margin, y = margin;
for(auto i : children){
if(!i.is_visible) continue; //an unvisible widget. skip it...
- int width = a.w - 2*margin;
+ int width = w - 2*margin;
int height = i.size;
@@ -24,8 +21,8 @@
}
// Remember my size (for click recognition)
- current_height = a.h;
- current_width = a.w;
+ current_height = h;
+ current_width = w;
}
=== modified file 'src/VBox.hpp'
--- src/VBox.hpp 2013-07-24 20:30:41 +0000
+++ src/VBox.hpp 2013-07-25 15:05:03 +0000
@@ -5,7 +5,7 @@
class VBox : public Box{
public:
- virtual void draw(SDrawArea a) override;
+ virtual void redraw(int w, int h) override;
virtual int GetMinimalHeight() override;
virtual int GetMinimalWidth () override;
=== modified file 'src/button.cpp'
--- src/button.cpp 2013-07-20 16:32:23 +0000
+++ src/button.cpp 2013-07-25 15:05:03 +0000
@@ -13,11 +13,10 @@
click_reaction = f;
}
-void Button::draw(SDrawArea a){
- use_new_transform_to(a.x,a.y);
+void Button::redraw(int w, int h){
ALLEGRO_COLOR color = al_map_rgb(120, 140, 200);
- al_draw_filled_rectangle(0,0, a.w, a.h, color);
- al_draw_text(Display::main_font, al_map_rgb(255,255,255), a.w/2, a.h/2 - 8, ALLEGRO_ALIGN_CENTRE, text.c_str());
+ al_draw_filled_rectangle(0,0, w, h, color);
+ al_draw_text(Display::main_font, al_map_rgb(255,255,255), w/2, h/2 - 8, ALLEGRO_ALIGN_CENTRE, text.c_str());
}
void Button::on_clicked(){
=== modified file 'src/button.hpp'
--- src/button.hpp 2013-07-24 20:30:41 +0000
+++ src/button.hpp 2013-07-25 15:05:03 +0000
@@ -12,7 +12,7 @@
void (*click_reaction)() = nullptr;
std::string text;
- virtual void draw(SDrawArea a) override;
+ virtual void redraw(int w, int h) override;
virtual void on_clicked() override;
virtual int GetMinimalHeight() override;
=== modified file 'src/display.cpp'
--- src/display.cpp 2013-07-24 14:07:57 +0000
+++ src/display.cpp 2013-07-25 15:05:03 +0000
@@ -12,6 +12,8 @@
ALLEGRO_FONT* Display::main_font = nullptr;
Drawable* Display::main_widget = nullptr;
+bool Display::main_widget_changed = false;
+
void Display::init(){
al_init();
al_install_mouse();
@@ -29,7 +31,7 @@
}
void Display::redraw(){
- al_clear_to_color(al_map_rgb(0,0,0));
+ //al_clear_to_color(al_map_rgb(0,0,0));
if(main_widget != nullptr){
main_widget->draw( SDrawArea(0,0,al_get_display_width(display),al_get_display_height(display)) );
}
@@ -63,7 +65,12 @@
}
}
- redraw();
+ if(main_widget_changed){
+ redraw();
+ main_widget_changed = false;
+ }else if(main_widget->_needs_redrawing){
+ redraw();
+ }
}
@@ -76,4 +83,5 @@
void Display::SetMainWidget(Drawable& v){
main_widget = &v;
+ main_widget_changed = true;
}
=== modified file 'src/display.hpp'
--- src/display.hpp 2013-07-20 19:17:30 +0000
+++ src/display.hpp 2013-07-25 15:05:03 +0000
@@ -23,6 +23,8 @@
static Drawable* main_widget;
static ALLEGRO_DISPLAY* display;
+
+ static bool main_widget_changed;
};
#endif //__DISPLAY_HPP__
=== modified file 'src/drawable.cpp'
--- src/drawable.cpp 2013-07-24 20:14:31 +0000
+++ src/drawable.cpp 2013-07-25 15:05:03 +0000
@@ -11,10 +11,23 @@
}
Drawable::Drawable(){
+ parent = nullptr;
+ _previous_height = -1;
+ _previous_width = -1;
+ _needs_redrawing = true;
+ _cache_bitmap = al_create_bitmap(0,0);
+
_background_color = al_map_rgba(0,0,0,0); //fully transparent background by default
}
Drawable::~Drawable(){
+ al_destroy_bitmap(_cache_bitmap);
+}
+
+
+void Drawable::_ResizeCacheBitmap(int w, int h){
+ al_destroy_bitmap(_cache_bitmap);
+ _cache_bitmap = al_create_bitmap(w,h);
}
void Drawable::SetBackgroundColor(ALLEGRO_COLOR bg){
@@ -24,11 +37,34 @@
_background_color = al_map_rgba(0,0,0,0);
}
-void Drawable::draw(SDrawArea a){
- use_new_transform_to(a.x,a.y);
- ALLEGRO_COLOR red = al_map_rgb(255, 0, 0);
- al_draw_filled_rectangle(0,0, a.w, a.h, red);
+void Drawable::draw(SDrawArea area){
+ if(_needs_redrawing || area.w != _previous_width || area.h != _previous_height){
+ // Somethings changed, this drawable's buffer needs to be redrawn
+ ALLEGRO_BITMAP* last_bitmap = al_get_target_bitmap();
+
+ if(area.w != _previous_width || area.h != _previous_height) _ResizeCacheBitmap(area.w, area.h);
+
+ al_set_target_bitmap(_cache_bitmap);
+ use_new_transform_to(0,0);
+ al_clear_to_color(_background_color);
+
+ redraw(area.w, area.h);
+
+ al_set_target_bitmap(last_bitmap);
+
+ _needs_redrawing = false;
+ _previous_height = area.h;
+ _previous_width = area.w;
+ }
+
+ // Draw this widget's cached version
+ al_draw_bitmap(_cache_bitmap, area.x, area.y, 0);
+
+}
+void Drawable::SetNeedsRedrawing(){
+ _needs_redrawing = true;
+ if(parent != nullptr) parent->SetNeedsRedrawing();
}
void Drawable::on_clicked(SCoordinates p){
=== modified file 'src/drawable.hpp'
--- src/drawable.hpp 2013-07-24 20:14:31 +0000
+++ src/drawable.hpp 2013-07-25 15:05:03 +0000
@@ -32,7 +32,9 @@
/* Used to restore the default, transparent background */
void RemoveBackgroundColor();
- virtual void draw(SDrawArea area);
+ void draw(SDrawArea area);
+ virtual void redraw(int w, int h) = 0;
+
/* Click coordinates are relative to widget position */
virtual void on_clicked(SCoordinates p);
/* This version ignores position */
@@ -43,8 +45,24 @@
/* A helpful macro, used by probably every drawable in its draw() func */
static void use_new_transform_to(int x, int y);
-protected:
- ALLEGRO_COLOR _background_color;
+
+ void SetNeedsRedrawing();
+
+ /* The widget that contains this one.
+ * nullptr, if this widget has no parent */
+ Drawable* parent;
+
+ bool _needs_redrawing;
+private:
+ ALLEGRO_COLOR _background_color;
+
+
+ ALLEGRO_BITMAP* _cache_bitmap;
+ void _ResizeCacheBitmap(int w, int h);
+
+ int _previous_width;
+ int _previous_height;
+
};
#endif //__DRAWABLE_HPP__
=== modified file 'src/label.cpp'
--- src/label.cpp 2013-07-20 16:32:23 +0000
+++ src/label.cpp 2013-07-25 15:05:03 +0000
@@ -12,10 +12,9 @@
}
-void Label::draw(SDrawArea a){
- use_new_transform_to(a.x,a.y);
+void Label::redraw(int w, int h){
ALLEGRO_COLOR white = al_map_rgb(255, 255, 255);
- al_draw_text(Display::main_font, white, a.w/2, a.h/2 - 8 ,ALLEGRO_ALIGN_CENTRE, text.c_str());
+ al_draw_text(Display::main_font, white, w/2, h/2 - 8 ,ALLEGRO_ALIGN_CENTRE, text.c_str());
}
int Label::GetMinimalHeight() {return 20;}
=== modified file 'src/label.hpp'
--- src/label.hpp 2013-07-24 20:30:41 +0000
+++ src/label.hpp 2013-07-25 15:05:03 +0000
@@ -9,7 +9,7 @@
Label(std::string);
std::string text;
- virtual void draw(SDrawArea a) override;
+ virtual void redraw(int w, int h) override;
virtual int GetMinimalHeight() override;
virtual int GetMinimalWidth () override;
=== modified file 'src/main.cpp'
--- src/main.cpp 2013-07-24 20:14:31 +0000
+++ src/main.cpp 2013-07-25 15:05:03 +0000
@@ -8,11 +8,10 @@
#include <iostream>
#include <cstdlib>
-Fixed additional_menu;
-
-LayeredView main_menu;
-VBox main_menu_vbox;
-VBox dialog_box;
+LayeredView* main_menu;
+Fixed* additional_menu;
+VBox* main_menu_vbox;
+VBox* dialog_box;
void quit(){
std::cout << "Quitting..." << std::endl;
@@ -23,6 +22,12 @@
int main(){
Display::init();
+ // Any Drawable can be constructed only AFTER init() is called
+ additional_menu = new Fixed();
+ main_menu = new LayeredView();
+ main_menu_vbox = new VBox();
+ dialog_box = new VBox();
+
Label label_main_menu("Main menu");
Button button1("Button1");
@@ -30,16 +35,16 @@
Button button2("Button2");
button2.set_click_reaction( [](){std::cout << "Button2 clicked" << std::endl;} );
Button button_to_2("To menu 2");
- button_to_2.set_click_reaction( [](){Display::SetMainWidget(additional_menu);} );
+ button_to_2.set_click_reaction( [](){Display::SetMainWidget(*additional_menu);} );
Button button_quit("QUIT");
- button_quit.set_click_reaction( [](){main_menu.ActivateLayer(dialog_box);} );
+ button_quit.set_click_reaction( [](){main_menu->ActivateLayer(*dialog_box);} );
Label label_additional_menu("Menu 2");
Button button3("Button3");
button3.set_click_reaction( [](){std::cout << "Button3 (menu 2) clicked" << std::endl;} );
Button button_to_main("To main menu");
- button_to_main.set_click_reaction( [](){Display::SetMainWidget(main_menu);} );
+ button_to_main.set_click_reaction( [](){Display::SetMainWidget(*main_menu);} );
Image image_smile("../data/smile.png");
@@ -48,37 +53,43 @@
Button button_yes("Yes");
button_yes.set_click_reaction(quit);
Button button_no("No");
- button_no.set_click_reaction( [](){main_menu.HideLayer(dialog_box);} );
-
- main_menu_vbox.SetSpacing(3); // gaps between buttons
- main_menu_vbox.SetMargin(5); // empty frame around the box
- main_menu_vbox.Add(label_main_menu, 0);
- main_menu_vbox.Add(button1, 1);
- main_menu_vbox.Add(button2, 2);
- main_menu_vbox.Add(button_to_2, 3, BOX_EXPAND);
- main_menu_vbox.Add(button_quit, 4);
-
- additional_menu.add_item(label_additional_menu,SCoordinates(42,27));
- additional_menu.add_item(button3, SCoordinates(42,52));
- additional_menu.add_item(button_to_main, SCoordinates(42,77));
- additional_menu.add_item(image_smile,SCoordinates(42,100));
-
- main_menu_vbox.SetBackgroundColor(al_map_rgb(0,0,70));
- additional_menu.SetBackgroundColor(al_map_rgb(0,0,70));
-
- dialog_box.SetSpacing(3);
- dialog_box.SetMargin(20);
- dialog_box.Add(label_dialog_box,0);
- dialog_box.Add(button_yes,1);
- dialog_box.Add(button_no,2);
- dialog_box.SetBackgroundColor(al_map_rgba(0,0,0,210));
-
- main_menu.Add(main_menu_vbox,0);
- main_menu.Add(dialog_box,1,BOX_SHRINK,false);
-
- Display::SetMainWidget(main_menu);
+ button_no.set_click_reaction( [](){main_menu->HideLayer(*dialog_box);} );
+
+ main_menu_vbox->SetSpacing(3); // gaps between buttons
+ main_menu_vbox->SetMargin(5); // empty frame around the box
+ main_menu_vbox->Add(label_main_menu, 0);
+ main_menu_vbox->Add(button1, 1);
+ main_menu_vbox->Add(button2, 2);
+ main_menu_vbox->Add(button_to_2, 3, BOX_EXPAND);
+ main_menu_vbox->Add(button_quit, 4);
+
+ additional_menu->add_item(label_additional_menu,SCoordinates(42,27));
+ additional_menu->add_item(button3, SCoordinates(42,52));
+ additional_menu->add_item(button_to_main, SCoordinates(42,77));
+ additional_menu->add_item(image_smile,SCoordinates(42,100));
+
+ main_menu_vbox->SetBackgroundColor(al_map_rgb(0,0,70));
+ additional_menu->SetBackgroundColor(al_map_rgb(0,0,70));
+
+ dialog_box->SetSpacing(3);
+ dialog_box->SetMargin(20);
+ dialog_box->Add(label_dialog_box,0);
+ dialog_box->Add(button_yes,1);
+ dialog_box->Add(button_no,2);
+ dialog_box->SetBackgroundColor(al_map_rgba(0,0,0,210));
+
+ main_menu->Add(*main_menu_vbox,0);
+ main_menu->Add(*dialog_box,1,BOX_SHRINK,false);
+
+ Display::SetMainWidget(*main_menu);
Display::main_loop();
+
+ delete main_menu;
+ delete additional_menu;
+ delete main_menu_vbox;
+ delete dialog_box;
+
Display::cleanup();
return 0;
}
Follow ups