Return pointer to MenuItem when creating one
[supertux.git] / src / gui / menu.cpp
index 1fb5ae6..e468543 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <iostream>
 #include <sstream>
+#include <math.h>
 #include <cstdlib>
 #include <cstdio>
 #include <string>
 #include "timer.hpp"
 #include "control/joystickkeyboardcontroller.hpp"
 
-static const float MENU_REPEAT_INITIAL = 0.4;
-static const float MENU_REPEAT_RATE = 0.2;
-static const float FLICK_CURSOR_TIME = 0.5;
+static const float MENU_REPEAT_INITIAL = 0.4f;
+static const float MENU_REPEAT_RATE = 0.2f;
+static const float FLICK_CURSOR_TIME = 0.5f;
 
 extern SDL_Surface* screen;
 
 std::vector<Menu*> Menu::last_menus;
 Menu* Menu::current_ = 0;
+Menu* Menu::previous = 0;
 Font* Menu::default_font;
 Font* Menu::active_font;
 Font* Menu::deactive_font;
@@ -81,7 +83,7 @@ bool confirm_dialog(Surface *background, std::string text)
       }
 
       if(background == NULL)
-        context.draw_gradient(Color(0.8, 0.95, 0.85), Color(0.8, 0.8, 0.8),
+        context.draw_gradient(Color(0.8f, 0.95f, 0.85f), Color(0.8f, 0.8f, 0.8f),
                               LAYER_BACKGROUND0);
       else
         context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
@@ -114,23 +116,29 @@ bool confirm_dialog(Surface *background, std::string text)
 
   return false;
 }
-
+\f
 void
 Menu::push_current(Menu* pmenu)
 {
+  previous = current_;
+
   if (current_)
     last_menus.push_back(current_);
 
   current_ = pmenu;
-  current_->effect_time = real_time;
+  current_->effect_start_time = real_time;
+  current_->effect_progress   = 0.0f;
 }
 
 void
 Menu::pop_current()
 {
+  previous = current_;
+
   if (last_menus.size() >= 1) {
     current_ = last_menus.back();
-    current_->effect_time = real_time;
+    current_->effect_start_time = real_time;
+    current_->effect_progress   = 0.0f;
     last_menus.pop_back();
   } else {
     current_ = 0;
@@ -140,16 +148,20 @@ Menu::pop_current()
 void
 Menu::set_current(Menu* menu)
 {
+  previous = current_;
+
   last_menus.clear();
 
   if (menu)
-    menu->effect_time = real_time;
-
+    {
+      menu->effect_start_time = real_time;
+      menu->effect_progress = 0.0f;
+    }
   current_ = menu;
   // just to be sure...
   main_controller->reset();
 }
-
+\f
 MenuItem::MenuItem(MenuItemKind _kind, int _id)
   : kind(_kind) , id(_id)
 {
@@ -170,6 +182,12 @@ MenuItem::change_input(const  std::string& text_)
   input = text_;
 }
 
+void
+MenuItem::set_help(const std::string& help_text)
+{
+  help = help_text;
+}
+
 std::string MenuItem::get_input_with_symbol(bool active_item)
 {
   if(!active_item) {
@@ -188,14 +206,18 @@ std::string MenuItem::get_input_with_symbol(bool active_item)
 
   return string;
 }
-
+\f
 Menu::~Menu()
 {
   for(std::vector<MenuItem*>::iterator i = items.begin();
       i != items.end(); ++i)
     delete *i;
+
   if(current_ == this)
     current_ = NULL;
+
+  if (previous == this)
+    previous = NULL;
 }
 
 Menu::Menu()
@@ -210,6 +232,9 @@ Menu::Menu()
   arrange_left = 0;
   active_item  = -1;
 
+  effect_progress   = 0.0f;
+  effect_start_time = 0.0f;
+
   checkbox.reset(new Surface("images/engine/menu/checkbox-unchecked.png"));
   checkbox_checked.reset(new Surface("images/engine/menu/checkbox-checked.png"));
   back.reset(new Surface("images/engine/menu/arrow-back.png"));
@@ -217,7 +242,8 @@ Menu::Menu()
   arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
 }
 
-void Menu::set_pos(float x, float y, float rw, float rh)
+void
+Menu::set_pos(float x, float y, float rw, float rh)
 {
   pos_x = x + get_width() * rw;
   pos_y = y + get_height() * rh;
@@ -241,21 +267,24 @@ Menu::additem(MenuItem* item)
   }
 }
 
-void
+MenuItem*
 Menu::add_hl()
 {
-  additem(new MenuItem(MN_HL));
+  MenuItem* item = new MenuItem(MN_HL);
+  additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_label(const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_LABEL);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_controlfield(int id, const std::string& text,
                const std::string& mapping)
 {
@@ -263,48 +292,54 @@ Menu::add_controlfield(int id, const std::string& text,
   item->change_text(text);
        item->change_input(mapping);
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_entry(int id, const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_ACTION, id);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_deactive(int id, const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_DEACTIVE, id);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_toggle(int id, const std::string& text, bool toogled)
 {
   MenuItem* item = new MenuItem(MN_TOGGLE, id);
   item->text = text;
   item->toggled = toogled;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_back(const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_BACK);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_submenu(const std::string& text, Menu* submenu, int id)
 {
   MenuItem* item = new MenuItem(MN_GOTO, id);
   item->text = text;
   item->target_menu = submenu;
   additem(item);
+  return item;
 }
 
 void
@@ -322,6 +357,21 @@ Menu::clear()
 void
 Menu::update()
 {
+  int menu_height = get_height();
+  if (menu_height > SCREEN_HEIGHT)
+    { // Scrolling
+      int scroll_offset = (menu_height - SCREEN_HEIGHT) / 2 + 32;
+      pos_y = SCREEN_HEIGHT/2 - scroll_offset * ((float(active_item) / (items.size()-1)) - 0.5f) * 2.0f;
+    }
+
+  effect_progress = (real_time - effect_start_time) * 6.0f;
+
+  if(effect_progress >= 1.0f) {
+    effect_progress = 1.0f;
+  } else if (effect_progress <= 0.0f) {
+    effect_progress = 0.0f;
+  }
+
   /** check main input controller... */
   if(main_controller->pressed(Controller::UP)) {
     menuaction = MENU_ACTION_UP;
@@ -341,8 +391,7 @@ Menu::update()
     menuaction = MENU_ACTION_DOWN;
     menu_repeat_time = real_time + MENU_REPEAT_RATE;
   }
-  if(main_controller->pressed(Controller::JUMP)
-     || main_controller->pressed(Controller::ACTION)
+  if(main_controller->pressed(Controller::ACTION)
      || main_controller->pressed(Controller::MENU_SELECT)) {
     menuaction = MENU_ACTION_HIT;
   }
@@ -491,23 +540,13 @@ void
 Menu::draw_item(DrawingContext& context, int index)
 {
   float menu_height = get_height();
-  float menu_width = get_width();
+  float menu_width  = get_width();
 
   MenuItem& pitem = *(items[index]);
 
-  int effect_offset = 0;
-  if(effect_time != 0) {
-    if(real_time - effect_time > 0.5) {
-      effect_time = 0;
-    } else {
-      float effect_delta = (0.5 - (real_time - effect_time)) * 250;
-      effect_offset = (int) ((index % 2) ? effect_delta : -effect_delta);
-    }
-  }
-
   Font* text_font = default_font;
   float x_pos       = pos_x;
-  float y_pos       = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
+  float y_pos       = pos_y + 24*index - menu_height/2 + 12;
   int shadow_size = 2;
   int text_width  = int(text_font->get_text_width(pitem.text));
   int input_width = int(text_font->get_text_width(pitem.input) + 10);
@@ -525,13 +564,28 @@ Menu::draw_item(DrawingContext& context, int index)
       text_font = active_font;
     }
 
+  if(active_item == index)
+    {
+      float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
+      context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10 - 2, y_pos - 12 - 2),
+                                    Vector(pos_x + menu_width/2 - 10 + 2, y_pos + 12 + 2)),
+                               Color(1.0f, 1.0f, 1.0f, blink),
+                               14.0f,
+                               LAYER_GUI-10);
+      context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10, y_pos - 12),
+                                    Vector(pos_x + menu_width/2 - 10, y_pos + 12)),
+                               Color(1.0f, 1.0f, 1.0f, 0.5f),
+                               12.0f,
+                               LAYER_GUI-10);
+    }
+
   switch (pitem.kind)
     {
     case MN_DEACTIVE:
       {
         context.draw_text(deactive_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(deactive_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
+                          Vector(pos_x, y_pos - int(deactive_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
         break;
       }
 
@@ -539,7 +593,7 @@ Menu::draw_item(DrawingContext& context, int index)
       {
         // TODO
         float x = pos_x - menu_width/2;
-        float y = y_pos - 12 - effect_offset;
+        float y = y_pos - 12;
         /* Draw a horizontal line with a little 3d effect */
         context.draw_filled_rect(Vector(x, y + 6),
                                  Vector(menu_width, 4),
@@ -552,48 +606,38 @@ Menu::draw_item(DrawingContext& context, int index)
     case MN_LABEL:
       {
         context.draw_text(label_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(label_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
+                          Vector(pos_x, y_pos - int(label_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
         break;
       }
     case MN_TEXTFIELD:
     case MN_NUMFIELD:
     case MN_CONTROLFIELD:
       {
-        float width = text_width + input_width + 5;
-        float text_pos = SCREEN_WIDTH/2 - width/2;
-        float input_pos = text_pos + text_width + 10;
-
-        context.draw_filled_rect(
-          Vector(input_pos - 5, y_pos - 10),
-          Vector(input_width + 10, 20),
-          Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI-5);
-        context.draw_filled_rect(
-          Vector(input_pos - 4, y_pos - 9),
-          Vector(input_width + 8, 18),
-          Color(0, 0, 0, 0.5f), LAYER_GUI-4);
+        float left  = pos_x - menu_width/2 + 16;
+        float right = pos_x + menu_width/2 - 16;
 
         if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
           {
             if(active_item == index)
               context.draw_text(field_font,
                                 pitem.get_input_with_symbol(true),
-                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                                LEFT_ALLIGN, LAYER_GUI);
+                                Vector(right, y_pos - int(field_font->get_height()/2)),
+                                ALIGN_RIGHT, LAYER_GUI);
             else
               context.draw_text(field_font,
                                 pitem.get_input_with_symbol(false),
-                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                                LEFT_ALLIGN, LAYER_GUI);
+                                Vector(right, y_pos - int(field_font->get_height()/2)),
+                                ALIGN_RIGHT, LAYER_GUI);
           }
         else
           context.draw_text(field_font, pitem.input,
-                            Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                            LEFT_ALLIGN, LAYER_GUI);
+                            Vector(right, y_pos - int(field_font->get_height()/2)),
+                            ALIGN_RIGHT, LAYER_GUI);
 
         context.draw_text(text_font, pitem.text,
-                          Vector(text_pos, y_pos - int(text_font->get_height()/2)),
-                          LEFT_ALLIGN, LAYER_GUI);
+                          Vector(left, y_pos - int(text_font->get_height()/2)),
+                          ALIGN_LEFT, LAYER_GUI);
         break;
       }
     case MN_STRINGSELECT:
@@ -621,18 +665,18 @@ Menu::draw_item(DrawingContext& context, int index)
           Color(0, 0, 0, 0.5f), LAYER_GUI - 5);
 
         context.draw_text(text_font, pitem.list[pitem.selected],
-                                 Vector(SCREEN_WIDTH/2 + text_pos, y_pos - int(text_font->get_height()/2)),
-                                 CENTER_ALLIGN, LAYER_GUI);
+                                 Vector(pos_x + text_pos, y_pos - int(text_font->get_height()/2)),
+                                 ALIGN_CENTER, LAYER_GUI);
         context.draw_text(text_font, pitem.text,
-                                 Vector(SCREEN_WIDTH/2  + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
-                                 CENTER_ALLIGN, LAYER_GUI);
+                                 Vector(pos_x  + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
+                                 ALIGN_CENTER, LAYER_GUI);
         break;
       }
     case MN_BACK:
       {
         context.draw_text(text_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
+                          Vector(pos_x, y_pos - int(text_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
         context.draw_surface(back.get(),
                              Vector(x_pos + text_width/2  + 16, y_pos - 8),
                              LAYER_GUI);
@@ -642,34 +686,35 @@ Menu::draw_item(DrawingContext& context, int index)
     case MN_TOGGLE:
       {
         context.draw_text(text_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - (text_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
+                          Vector(pos_x - menu_width/2 + 16, y_pos - (text_font->get_height()/2)),
+                          ALIGN_LEFT, LAYER_GUI);
 
         if(pitem.toggled)
           context.draw_surface(checkbox_checked.get(),
-                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
+                               Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
                                LAYER_GUI + 1);
         else
           context.draw_surface(checkbox.get(),
-                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
+                               Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
                                LAYER_GUI + 1);
         break;
       }
     case MN_ACTION:
       context.draw_text(text_font, pitem.text,
-                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                        CENTER_ALLIGN, LAYER_GUI);
+                        Vector(pos_x, y_pos - int(text_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI);
       break;
 
     case MN_GOTO:
       context.draw_text(text_font, pitem.text,
-                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                        CENTER_ALLIGN, LAYER_GUI);
+                        Vector(pos_x, y_pos - int(text_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI);
       break;
     }
 }
 
-float Menu::get_width() const
+float
+Menu::get_width() const
 {
   /* The width of the menu has to be more than the width of the text
      with the most characters */
@@ -692,7 +737,8 @@ float Menu::get_width() const
   return menu_width + 24;
 }
 
-float Menu::get_height() const
+float
+Menu::get_height() const
 {
   return items.size() * 24;
 }
@@ -705,19 +751,42 @@ Menu::draw(DrawingContext& context)
     MouseCursor::current()->draw(context);
   }
 
+  float menu_width  = get_width();
   float menu_height = get_height();
-  float menu_width = get_width();
-
-  /* Draw a transparent background */
-  context.draw_filled_rect(
-    Vector(pos_x - menu_width/2, pos_y - 24*items.size()/2 - 10),
-    Vector(menu_width,menu_height + 20),
-    Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-10);
 
-  for(unsigned int i = 0; i < items.size(); ++i)
+  if (effect_progress != 1.0f)
     {
-      draw_item(context, i);
+    if (Menu::previous)
+      {
+        menu_width  = (menu_width  * effect_progress) + (Menu::previous->get_width()  * (1.0f - effect_progress));
+        menu_height = (menu_height * effect_progress) + (Menu::previous->get_height() * (1.0f - effect_progress));
+        //std::cout << effect_progress << " " << this << " " << last_menus.back() << std::endl;
+      }
+    else
+      {
+        menu_width  *= effect_progress;
+        menu_height *= effect_progress;
+      }
     }
+
+  /* Draw a transparent background */
+  context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2-4, pos_y - menu_height/2 - 10-4),
+                                Vector(pos_x + menu_width/2+4, pos_y - menu_height/2 + 10 + menu_height+4)),
+                           Color(0.2f, 0.3f, 0.4f, 0.8f), 
+                           20.0f,
+                           LAYER_GUI-10);
+
+  context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2, pos_y - menu_height/2 - 10),
+                                Vector(pos_x + menu_width/2, pos_y - menu_height/2 + 10 + menu_height)),
+                           Color(0.6f, 0.7f, 0.8f, 0.5f), 
+                           16.0f,
+                           LAYER_GUI-10);
+
+  if (effect_progress == 1.0f)
+    for(unsigned int i = 0; i < items.size(); ++i)
+      {
+        draw_item(context, i);
+      }
 }
 
 MenuItem&
@@ -759,11 +828,20 @@ Menu::is_toggled(int id) const
   return get_item_by_id(id).toggled;
 }
 
+Menu*
+Menu::get_parent() const
+{
+  if (last_menus.empty())
+    return 0;
+  else
+    return last_menus.back();
+}
+
 /* Check for menu event */
 void
 Menu::event(const SDL_Event& event)
 {
-  if(effect_time != 0)
+  if(effect_progress != 1.0f)
     return;
 
   switch(event.type) {