Merge branch 'feature/menu-dialogs'
authorIngo Ruhnke <grumbel@gmail.com>
Mon, 25 Aug 2014 07:54:29 +0000 (09:54 +0200)
committerIngo Ruhnke <grumbel@gmail.com>
Mon, 25 Aug 2014 07:54:29 +0000 (09:54 +0200)
12 files changed:
src/control/controller.cpp
src/control/controller.hpp
src/gui/dialog.cpp [new file with mode: 0644]
src/gui/dialog.hpp [new file with mode: 0644]
src/gui/menu.cpp
src/gui/menu.hpp
src/gui/menu_manager.cpp
src/gui/menu_manager.hpp
src/math/rectf.hpp
src/supertux/colorscheme.cpp
src/supertux/colorscheme.hpp [new file with mode: 0644]
src/supertux/menu/main_menu.cpp

index a25c39b..686ccb3 100644 (file)
@@ -59,19 +59,19 @@ Controller::set_control(Control control, bool value)
 }
 
 bool
-Controller::hold(Control control)
+Controller::hold(Control control) const
 {
   return controls[control];
 }
 
 bool
-Controller::pressed(Control control)
+Controller::pressed(Control control) const
 {
   return !oldControls[control] && controls[control];
 }
 
 bool
-Controller::released(Control control)
+Controller::released(Control control) const
 {
   return oldControls[control] && !controls[control];
 }
index 8d8543e..ab9decd 100644 (file)
@@ -51,11 +51,11 @@ public:
 
   void set_control(Control control, bool value);
   /** returns true if the control is pressed down */
-  bool hold(Control control);
+  bool hold(Control control) const;
   /** returns true if the control has just been pressed down this frame */
-  bool pressed(Control control);
+  bool pressed(Control control) const;
   /** returns true if the control has just been released this frame */
-  bool released(Control control);
+  bool released(Control control) const;
 
   virtual void reset();
   virtual void update();
diff --git a/src/gui/dialog.cpp b/src/gui/dialog.cpp
new file mode 100644 (file)
index 0000000..260c7bf
--- /dev/null
@@ -0,0 +1,228 @@
+//  SuperTux
+//  Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "gui/dialog.hpp"
+
+#include "control/controller.hpp"
+#include "gui/menu_manager.hpp"
+#include "gui/menu.hpp"
+#include "gui/mousecursor.hpp"
+#include "supertux/resources.hpp"
+#include "supertux/colorscheme.hpp"
+#include "video/drawing_context.hpp"
+#include "video/renderer.hpp"
+#include "video/video_system.hpp"
+
+Dialog::Dialog() :
+  m_text(),
+  m_buttons(),
+  m_selected_button(),
+  m_text_size()
+{
+}
+
+Dialog::~Dialog()
+{
+}
+
+void
+Dialog::set_text(const std::string& text)
+{
+  m_text = text;
+
+  m_text_size = Sizef(Resources::normal_font->get_text_width(m_text),
+                      Resources::normal_font->get_text_height(m_text));
+
+}
+
+void
+Dialog::add_button(const std::string& text, const std::function<void ()>& callback, bool focus)
+{
+  m_buttons.push_back({text, callback});
+
+  if (focus)
+  {
+    m_selected_button = m_buttons.size() - 1;
+  }
+}
+
+int
+Dialog::get_button_at(const Vector& mouse_pos) const
+{
+  Rectf bg_rect(Vector(SCREEN_WIDTH/2 - m_text_size.width/2,
+                       SCREEN_HEIGHT/2 - m_text_size.height/2),
+                Sizef(m_text_size.width,
+                      m_text_size.height + 44));
+
+  for(int i = 0; i < static_cast<int>(m_buttons.size()); ++i)
+  {
+    float segment_width = bg_rect.get_width() / m_buttons.size();
+    float button_width = segment_width;
+    float button_height = 24.0f;
+    Vector pos(bg_rect.p1.x + segment_width/2.0f + i * segment_width,
+               bg_rect.p2.y - 12);
+    Rectf button_rect(Vector(pos.x - button_width/2, pos.y - button_height/2),
+                      Vector(pos.x + button_width/2, pos.y + button_height/2));
+    if (button_rect.contains(mouse_pos))
+    {
+      return i;
+    }
+  }
+  return -1;
+}
+
+void
+Dialog::event(const SDL_Event& ev)
+{
+  switch(ev.type) {
+    case SDL_MOUSEBUTTONDOWN:
+    if(ev.button.button == SDL_BUTTON_LEFT)
+    {
+      Vector mouse_pos = VideoSystem::current()->get_renderer().to_logical(ev.motion.x, ev.motion.y);
+      int new_button = get_button_at(mouse_pos);
+      if (new_button != -1)
+      {
+        m_selected_button = new_button;
+        on_button_click(m_selected_button);
+
+        // warning: this will "delete this"
+        MenuManager::instance().set_dialog({});
+      }
+    }
+    break;
+
+    case SDL_MOUSEMOTION:
+    {
+      Vector mouse_pos = VideoSystem::current()->get_renderer().to_logical(ev.motion.x, ev.motion.y);
+      int new_button = get_button_at(mouse_pos);
+      if (new_button != -1)
+      {
+        m_selected_button = new_button;
+        if(MouseCursor::current())
+          MouseCursor::current()->set_state(MC_LINK);
+      }
+      else
+      {
+        if(MouseCursor::current())
+          MouseCursor::current()->set_state(MC_NORMAL);
+      }
+    }
+    break;
+
+    default:
+      break;
+  }
+}
+
+void
+Dialog::process_input(const Controller& controller)
+{
+  if (controller.pressed(Controller::LEFT))
+  {
+    m_selected_button -= 1;
+    m_selected_button = std::max(m_selected_button, 0);
+  }
+
+  if (controller.pressed(Controller::RIGHT))
+  {
+    m_selected_button += 1;
+    m_selected_button = std::min(m_selected_button, static_cast<int>(m_buttons.size()) - 1);
+  }
+
+  if (controller.pressed(Controller::ACTION) ||
+      controller.pressed(Controller::MENU_SELECT))
+  {
+    on_button_click(m_selected_button);
+
+    // warning: this will "delete this"
+    MenuManager::instance().set_dialog({});
+  }
+}
+
+void
+Dialog::draw(DrawingContext& ctx)
+{
+  Rectf bg_rect(Vector(SCREEN_WIDTH/2 - m_text_size.width/2,
+                       SCREEN_HEIGHT/2 - m_text_size.height/2),
+                Sizef(m_text_size.width,
+                      m_text_size.height + 44));
+
+  // draw background rect
+  ctx.draw_filled_rect(bg_rect.grown(12.0f),
+                       Color(0.2f, 0.3f, 0.4f, 0.8f),
+                       16.0f,
+                       LAYER_GUI-10);
+
+  ctx.draw_filled_rect(bg_rect.grown(8.0f),
+                       Color(0.6f, 0.7f, 0.8f, 0.5f),
+                       16.0f,
+                       LAYER_GUI-10);
+
+  // draw text
+  ctx.draw_text(Resources::normal_font, m_text,
+                Vector(bg_rect.p1.x + bg_rect.get_width()/2.0f,
+                       bg_rect.p1.y),
+                ALIGN_CENTER, LAYER_GUI);
+
+  // draw HL line
+  ctx.draw_filled_rect(Vector(bg_rect.p1.x, bg_rect.p2.y - 35),
+                       Vector(bg_rect.get_width(), 4),
+                       Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
+  ctx.draw_filled_rect(Vector(bg_rect.p1.x, bg_rect.p2.y - 35),
+                       Vector(bg_rect.get_width(), 2),
+                       Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
+
+  // draw buttons
+  for(int i = 0; i < static_cast<int>(m_buttons.size()); ++i)
+  {
+    float segment_width = bg_rect.get_width() / m_buttons.size();
+    float button_width = segment_width;
+    float button_height = 24.0f;
+    Vector pos(bg_rect.p1.x + segment_width/2.0f + i * segment_width,
+               bg_rect.p2.y - 12);
+
+    if (i == m_selected_button)
+    {
+      float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
+      ctx.draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2),
+                                 Vector(pos.x + button_width/2, pos.y + button_height/2)).grown(2.0f),
+                           Color(1.0f, 1.0f, 1.0f, blink),
+                           14.0f,
+                           LAYER_GUI-10);
+      ctx.draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2),
+                                 Vector(pos.x + button_width/2, pos.y + button_height/2)),
+                           Color(1.0f, 1.0f, 1.0f, 0.5f),
+                           12.0f,
+                           LAYER_GUI-10);
+    }
+
+    ctx.draw_text(Resources::normal_font, m_buttons[i].text,
+                  Vector(pos.x, pos.y - int(Resources::normal_font->get_height()/2)),
+                  ALIGN_CENTER, LAYER_GUI,
+                  i == m_selected_button ? ColorScheme::Menu::active_color : ColorScheme::Menu::default_color);
+  }
+}
+
+void
+Dialog::on_button_click(int button) const
+{
+  if (m_buttons[button].callback)
+  {
+    m_buttons[button].callback();
+  }
+}
+
+/* EOF */
diff --git a/src/gui/dialog.hpp b/src/gui/dialog.hpp
new file mode 100644 (file)
index 0000000..7366141
--- /dev/null
@@ -0,0 +1,68 @@
+//  SuperTux
+//  Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HEADER_SUPERTUX_GUI_DIALOG_HPP
+#define HEADER_SUPERTUX_GUI_DIALOG_HPP
+
+#include <SDL.h>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "math/sizef.hpp"
+
+class Controller;
+class DrawingContext;
+
+class Dialog
+{
+private:
+  struct Button
+  {
+    std::string text;
+    std::function<void ()> callback;
+  };
+
+  std::string m_text;
+  std::vector<Button> m_buttons;
+  int m_selected_button;
+
+  Sizef m_text_size;
+
+public:
+  Dialog();
+  virtual ~Dialog();
+
+  void set_text(const std::string& text);
+  void add_button(const std::string& text, const std::function<void ()>& callback = {},
+                  bool focus = false);
+
+  void event(const SDL_Event& event);
+  void process_input(const Controller& controller);
+  void draw(DrawingContext& context);
+
+private:
+  void on_button_click(int button) const;
+  int get_button_at(const Vector& pos) const;
+
+private:
+  Dialog(const Dialog&) = delete;
+  Dialog& operator=(const Dialog&) = delete;
+};
+
+#endif
+
+/* EOF */
index a329e25..e17764d 100644 (file)
@@ -23,6 +23,7 @@
 #include "gui/menu_item.hpp"
 #include "gui/menu_manager.hpp"
 #include "gui/mousecursor.hpp"
+#include "supertux/colorscheme.hpp"
 #include "supertux/globals.hpp"
 #include "supertux/resources.hpp"
 #include "supertux/screen_manager.hpp"
@@ -381,7 +382,7 @@ Menu::draw_item(DrawingContext& context, int index)
 
   MenuItem& pitem = *(items[index]);
 
-  Color text_color = default_color;
+  Color text_color = ColorScheme::Menu::default_color;
   float x_pos       = pos.x;
   float y_pos       = pos.y + 24*index - menu_height/2 + 12;
   int text_width  = int(Resources::normal_font->get_text_width(pitem.text));
@@ -400,7 +401,7 @@ Menu::draw_item(DrawingContext& context, int index)
 
   if(index == active_item)
   {
-    text_color = active_color;
+    text_color = ColorScheme::Menu::active_color;
   }
 
   if(active_item == index)
@@ -424,7 +425,7 @@ Menu::draw_item(DrawingContext& context, int index)
     {
       context.draw_text(Resources::normal_font, pitem.text,
                         Vector(pos.x, y_pos - int(Resources::normal_font->get_height()/2)),
-                        ALIGN_CENTER, LAYER_GUI, inactive_color);
+                        ALIGN_CENTER, LAYER_GUI, ColorScheme::Menu::inactive_color);
       break;
     }
 
@@ -446,7 +447,7 @@ Menu::draw_item(DrawingContext& context, int index)
     {
       context.draw_text(Resources::big_font, pitem.text,
                         Vector(pos.x, y_pos - int(Resources::big_font->get_height()/2)),
-                        ALIGN_CENTER, LAYER_GUI, label_color);
+                        ALIGN_CENTER, LAYER_GUI, ColorScheme::Menu::label_color);
       break;
     }
     case MN_TEXTFIELD:
@@ -459,17 +460,17 @@ Menu::draw_item(DrawingContext& context, int index)
           context.draw_text(Resources::normal_font,
                             pitem.get_input_with_symbol(true),
                             Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
-                            ALIGN_RIGHT, LAYER_GUI, field_color);
+                            ALIGN_RIGHT, LAYER_GUI, ColorScheme::Menu::field_color);
         else
           context.draw_text(Resources::normal_font,
                             pitem.get_input_with_symbol(false),
                             Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
-                            ALIGN_RIGHT, LAYER_GUI, field_color);
+                            ALIGN_RIGHT, LAYER_GUI, ColorScheme::Menu::field_color);
       }
       else
         context.draw_text(Resources::normal_font, pitem.input,
                           Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
-                          ALIGN_RIGHT, LAYER_GUI, field_color);
+                          ALIGN_RIGHT, LAYER_GUI, ColorScheme::Menu::field_color);
 
       context.draw_text(Resources::normal_font, pitem.text,
                         Vector(left, y_pos - int(Resources::normal_font->get_height()/2)),
index 5ecd3f8..8fc4ad5 100644 (file)
@@ -30,12 +30,6 @@ class MenuItem;
 
 class Menu
 {
-  static Color default_color;
-  static Color active_color;
-  static Color inactive_color;
-  static Color label_color;
-  static Color field_color;
-
 private:
   /* Action done on the menu */
   enum MenuAction {
index 9a34c98..1bd6330 100644 (file)
@@ -19,6 +19,7 @@
 #include <assert.h>
 
 #include "control/input_manager.hpp"
+#include "gui/dialog.hpp"
 #include "gui/menu.hpp"
 #include "gui/mousecursor.hpp"
 #include "math/sizef.hpp"
@@ -134,6 +135,7 @@ public:
 };
 
 MenuManager::MenuManager() :
+  m_dialog(),
   m_menu_stack(),
   m_transition(new MenuTransition)
 {
@@ -157,20 +159,31 @@ MenuManager::refresh()
 void
 MenuManager::process_input()
 {
-  if (current())
+  if (m_dialog)
   {
-    current()->process_input();
+    m_dialog->process_input(*InputManager::current()->get_controller());
+  }
+  else if (current_menu())
+  {
+    current_menu()->process_input();
   }
 }
 
 void
-MenuManager::event(const SDL_Event& event_)
+MenuManager::event(const SDL_Event& ev)
 {
-  if (current() && !m_transition->is_active())
+  if (!m_transition->is_active())
   {
-    // only pass events when the menu is fully visible and not in a
-    // transition animation
-    current()->event(event_);
+    if (m_dialog)
+    {
+      m_dialog->event(ev);
+    }
+    else if (current_menu())
+    {
+      // only pass events when the menu is fully visible and not in a
+      // transition animation
+      current_menu()->event(ev);
+    }
   }
 }
 
@@ -184,24 +197,34 @@ MenuManager::draw(DrawingContext& context)
   }
   else
   {
-    if (current())
+    if (m_dialog)
+    {
+      m_dialog->draw(context);
+    }
+    else if (current_menu())
     {
       // brute force the transition into the right shape in case the
       // menu has changed sizes
-      m_transition->set(menu2rect(*current()));
+      m_transition->set(menu2rect(*current_menu()));
       m_transition->draw(context);
 
-      current()->draw(context);
+      current_menu()->draw(context);
     }
   }
 
-  if (current() && MouseCursor::current())
+  if (current_menu() && MouseCursor::current())
   {
     MouseCursor::current()->draw(context);
   }
 }
 
 void
+MenuManager::set_dialog(std::unique_ptr<Dialog> dialog)
+{
+  m_dialog = std::move(dialog);
+}
+
+void
 MenuManager::push_menu(int id)
 {
   push_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
@@ -279,7 +302,7 @@ MenuManager::on_window_resize()
 }
 
 Menu*
-MenuManager::current() const
+MenuManager::current_menu() const
 {
   if (m_menu_stack.empty())
   {
index 643841b..e05a083 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "SDL.h"
 
+class Dialog;
 class DrawingContext;
 class Menu;
 class MenuTransition;
@@ -34,12 +35,11 @@ private:
 public:
   static MenuManager& instance();
 
-public:
+private:
+  std::unique_ptr<Dialog> m_dialog;
   std::vector<std::unique_ptr<Menu> > m_menu_stack;
   std::unique_ptr<MenuTransition> m_transition;
 
-  friend class Menu;
-
 public:
   MenuManager();
   ~MenuManager();
@@ -50,6 +50,8 @@ public:
 
   void draw(DrawingContext& context);
 
+  void set_dialog(std::unique_ptr<Dialog> dialog);
+
   void set_menu(int id);
   void set_menu(std::unique_ptr<Menu> menu);
   void push_menu(int id);
@@ -64,7 +66,7 @@ public:
   }
 
 private:
-  Menu* current() const;
+  Menu* current_menu() const;
   void transition(Menu* from, Menu* to);
 
 private:
index 80bc478..cdff094 100644 (file)
@@ -129,6 +129,12 @@ public:
     return ((v1 - v2).norm ());
   }
 
+  Rectf grown(float border) const
+  {
+    return Rectf(p1.x - border, p1.y - border,
+                 p2.x + border, p2.y + border);
+  }
+
   // leave these two public to save the headaches of set/get functions for such
   // simple things :)
 
index 462cfe2..b32b111 100644 (file)
@@ -14,6 +14,8 @@
 //  You should have received a copy of the GNU General Public License
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+#include "supertux/colorscheme.hpp"
+
 #include "gui/menu.hpp"
 #include "object/floating_text.hpp"
 #include "object/level_time.hpp"
@@ -35,11 +37,11 @@ Color LevelIntro::stat_color(1.0,1.0,1.0);
 Color Statistics::header_color(1.0,1.0,1.0);
 Color Statistics::text_color(1.0,1.0,0.6);
 
-Color Menu::default_color(1.0,1.0,1.0);
-Color Menu::active_color(0.2,0.5,1.0);
-Color Menu::inactive_color(0.5,0.5,0.5);
-Color Menu::label_color(0.0,1.0,1.0);
-Color Menu::field_color(1.0,1.0,0.6);
+Color ColorScheme::Menu::default_color(1.0,1.0,1.0);
+Color ColorScheme::Menu::active_color(0.2,0.5,1.0);
+Color ColorScheme::Menu::inactive_color(0.5,0.5,0.5);
+Color ColorScheme::Menu::label_color(0.0,1.0,1.0);
+Color ColorScheme::Menu::field_color(1.0,1.0,0.6);
 
 Color PlayerStatus::text_color(1.0,1.0,0.6);
 
diff --git a/src/supertux/colorscheme.hpp b/src/supertux/colorscheme.hpp
new file mode 100644 (file)
index 0000000..8708436
--- /dev/null
@@ -0,0 +1,38 @@
+//  SuperTux
+//  Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HEADER_SUPERTUX_SUPERTUX_COLORSCHEME_HPP
+#define HEADER_SUPERTUX_SUPERTUX_COLORSCHEME_HPP
+
+#include "video/color.hpp"
+
+class ColorScheme
+{
+public:
+  class Menu
+  {
+  public:
+    static Color default_color;
+    static Color active_color;
+    static Color inactive_color;
+    static Color label_color;
+    static Color field_color;
+  };
+};
+
+#endif
+
+/* EOF */
index 8eb4d7a..615c0aa 100644 (file)
@@ -17,6 +17,7 @@
 #include "supertux/menu/main_menu.hpp"
 
 #include "audio/sound_manager.hpp"
+#include "gui/dialog.hpp"
 #include "gui/menu_item.hpp"
 #include "gui/menu_manager.hpp"
 #include "supertux/fadeout.hpp"
@@ -80,8 +81,15 @@ MainMenu::menu_action(MenuItem* item)
       break;
 
     case MNID_QUITMAINMENU:
-      ScreenManager::current()->quit(std::unique_ptr<ScreenFade>(new FadeOut(0.25)));
-      SoundManager::current()->stop_music(0.25);
+      std::unique_ptr<Dialog> dialog(new Dialog);
+      dialog->set_text(_("Do you really want to quit SuperTux?"));
+      dialog->add_button(_("Cancel"));
+      dialog->add_button(_("Quit SuperTux"), [] {
+          MenuManager::instance().clear_menu_stack();
+          ScreenManager::current()->quit(std::unique_ptr<ScreenFade>(new FadeOut(0.25)));
+          SoundManager::current()->stop_music(0.25);
+        }, true);
+      MenuManager::instance().set_dialog(std::move(dialog));
       break;
   }
 }