// $Id$
//
// SuperTux
-// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
#include <config.h>
#include <sys/types.h>
#include <cassert>
#include <stdexcept>
-#include "app/globals.h"
-#include "menu.h"
-#include "video/screen.h"
-#include "video/drawing_context.h"
-#include "app/setup.h"
-#include "app/gettext.h"
-#include "math/vector.h"
-#include "main.h"
-#include "control/joystickkeyboardcontroller.h"
+#include "menu.hpp"
+#include "mainloop.hpp"
+#include "video/drawing_context.hpp"
+#include "gettext.hpp"
+#include "math/vector.hpp"
+#include "main.hpp"
+#include "resources.hpp"
+#include "timer.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
-static const int MENU_REPEAT_INITIAL = 400;
-static const int MENU_REPEAT_RATE = 200;
-static const int FLICK_CURSOR_TIME=500;
+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;
-using namespace SuperTux;
-
-Surface* checkbox;
-Surface* checkbox_checked;
-Surface* back;
-Surface* arrow_left;
-Surface* arrow_right;
-
std::vector<Menu*> Menu::last_menus;
Menu* Menu::current_ = 0;
Font* Menu::default_font;
dialog->add_entry(true, _("Yes"));
dialog->add_entry(false, _("No"));
dialog->add_hl();
-
+
Menu::set_current(dialog);
DrawingContext context;
+ // TODO make this a screen and not another mainloop...
while(true)
{
SDL_Event event;
-
- if(event.type == SDL_QUIT)
- throw std::runtime_error("received window close event");
-
while (SDL_PollEvent(&event)) {
+ if(event.type == SDL_QUIT)
+ main_loop->quit();
+ main_controller->process_event(event);
dialog->event(event);
}
if(background == NULL)
- context.draw_gradient(Color(200,240,220), Color(200,200,220), LAYER_BACKGROUND0);
+ 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);
dialog->draw(context);
- dialog->action();
+ dialog->update();
switch (dialog->check())
{
last_menus.push_back(current_);
current_ = pmenu;
- current_->effect.start(500);
+ current_->effect_time = real_time;
}
void
{
if (last_menus.size() >= 1) {
current_ = last_menus.back();
- current_->effect.start(500);
-
+ current_->effect_time = real_time;
last_menus.pop_back();
+ } else {
+ current_ = 0;
}
}
last_menus.clear();
if (menu)
- menu->effect.start(500);
+ menu->effect_time = real_time;
current_ = menu;
// just to be sure...
toggled = false;
selected = false;
target_menu = 0;
- input_flickering = false;
- input_flickering_timer.init(true);
- input_flickering_timer.start(FLICK_CURSOR_TIME);
}
void
std::string MenuItem::get_input_with_symbol(bool active_item)
{
- if(!active_item)
+ if(!active_item) {
input_flickering = true;
- else
- {
- if(input_flickering_timer.get_left() < 0)
- {
- if(input_flickering)
- input_flickering = false;
- else
- input_flickering = true;
- input_flickering_timer.start(FLICK_CURSOR_TIME);
- }
- }
+ } else {
+ input_flickering = ((int) (real_time / FLICK_CURSOR_TIME)) % 2;
+ }
char str[1024];
if(input_flickering)
- sprintf(str,"%s ",input.c_str());
+ snprintf(str, sizeof(str), "%s ",input.c_str());
else
- sprintf(str,"%s_",input.c_str());
+ snprintf(str, sizeof(str), "%s_",input.c_str());
std::string string = str;
for(std::vector<MenuItem*>::iterator i = items.begin();
i != items.end(); ++i)
delete *i;
+ if(current_ == this)
+ current_ = NULL;
}
Menu::Menu()
pos_y = SCREEN_HEIGHT/2;
arrange_left = 0;
active_item = -1;
- effect.init(false);
- repeat_timer.init(true);
+ 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"));
+ arrow_left.reset(new Surface("images/engine/menu/arrow-left.png"));
+ arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
}
-void Menu::set_pos(int x, int y, float rw, float rh)
+void Menu::set_pos(float x, float y, float rw, float rh)
{
- pos_x = x + (int)((float)get_width() * rw);
- pos_y = y + (int)((float)get_height() * rh);
+ pos_x = x + get_width() * rw;
+ pos_y = y + get_height() * rh;
}
/* Add an item to a menu */
* selectable item added
*/
if (active_item == -1
- && item->kind != MN_HL
+ && item->kind != MN_HL
&& item->kind != MN_LABEL
&& item->kind != MN_DEACTIVE) {
active_item = items.size() - 1;
/* Process actions done on the menu */
void
-Menu::action()
+Menu::update()
{
/** check main input controller... */
if(main_controller->pressed(Controller::UP)) {
menuaction = MENU_ACTION_UP;
- repeat_timer.start(MENU_REPEAT_INITIAL);
+ menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
}
- if(main_controller->hold(Controller::UP) && !repeat_timer.check()) {
+ if(main_controller->hold(Controller::UP) &&
+ menu_repeat_time != 0 && real_time > menu_repeat_time) {
menuaction = MENU_ACTION_UP;
- repeat_timer.start(MENU_REPEAT_RATE);
- }
+ menu_repeat_time = real_time + MENU_REPEAT_RATE;
+ }
if(main_controller->pressed(Controller::DOWN)) {
menuaction = MENU_ACTION_DOWN;
- repeat_timer.start(MENU_REPEAT_INITIAL);
+ menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
}
- if(main_controller->hold(Controller::DOWN) && !repeat_timer.check()) {
+ if(main_controller->hold(Controller::DOWN) &&
+ menu_repeat_time != 0 && real_time > menu_repeat_time) {
menuaction = MENU_ACTION_DOWN;
- repeat_timer.start(MENU_REPEAT_RATE);
+ menu_repeat_time = real_time + MENU_REPEAT_RATE;
}
if(main_controller->pressed(Controller::JUMP)
|| main_controller->pressed(Controller::ACTION)
hit_item = -1;
if(items.size() == 0)
return;
-
+
int last_active_item = active_item;
switch(menuaction) {
case MENU_ACTION_UP:
--active_item;
else
active_item = int(items.size())-1;
- } while ((items[active_item]->kind == MN_HL
+ } while ((items[active_item]->kind == MN_HL
|| items[active_item]->kind == MN_LABEL
|| items[active_item]->kind == MN_DEACTIVE)
&& (active_item != last_active_item));
-
+
break;
-
+
case MENU_ACTION_DOWN:
do {
if(active_item < int(items.size())-1 )
|| items[active_item]->kind == MN_LABEL
|| items[active_item]->kind == MN_DEACTIVE)
&& (active_item != last_active_item));
-
+
break;
-
+
case MENU_ACTION_LEFT:
if(items[active_item]->kind == MN_STRINGSELECT) {
if(items[active_item]->selected > 0)
items[active_item]->selected = items[active_item]->list.size()-1;
}
break;
-
+
case MENU_ACTION_RIGHT:
if(items[active_item]->kind == MN_STRINGSELECT) {
if(items[active_item]->selected+1 < items[active_item]->list.size())
items[active_item]->selected = 0;
}
break;
-
+
case MENU_ACTION_HIT: {
hit_item = active_item;
switch (items[active_item]->kind) {
assert(items[active_item]->target_menu != 0);
Menu::push_current(items[active_item]->target_menu);
break;
-
+
case MN_TOGGLE:
items[active_item]->toggled = !items[active_item]->toggled;
menu_action(items[active_item]);
break;
-
+
case MN_CONTROLFIELD:
menu_action(items[active_item]);
break;
-
+
case MN_ACTION:
menu_action(items[active_item]);
break;
-
+
case MN_TEXTFIELD:
case MN_NUMFIELD:
menuaction = MENU_ACTION_DOWN;
- action();
+ update();
break;
-
+
case MN_BACK:
Menu::pop_current();
break;
}
break;
}
-
+
case MENU_ACTION_REMOVE:
if(items[active_item]->kind == MN_TEXTFIELD
|| items[active_item]->kind == MN_NUMFIELD)
if(!items[active_item]->input.empty())
{
int i = items[active_item]->input.size();
-
+
while(delete_character > 0) /* remove charactes */
{
items[active_item]->input.resize(i-1);
}
}
break;
-
+
case MENU_ACTION_INPUT:
if(items[active_item]->kind == MN_TEXTFIELD
- || (items[active_item]->kind == MN_NUMFIELD
+ || (items[active_item]->kind == MN_NUMFIELD
&& mn_input_char >= '0' && mn_input_char <= '9'))
{
items[active_item]->input.push_back(mn_input_char);
}
break;
-
+
case MENU_ACTION_BACK:
Menu::pop_current();
break;
void
Menu::draw_item(DrawingContext& context, int index)
{
- int menu_height = get_height();
- int menu_width = get_width();
+ float menu_height = get_height();
+ float menu_width = get_width();
MenuItem& pitem = *(items[index]);
int effect_offset = 0;
- {
- int effect_time = 0;
-
- if(effect.check())
- effect_time = effect.get_left() / 4;
-
- effect_offset = (index % 2) ? effect_time : -effect_time;
+ 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;
- int x_pos = pos_x;
- int y_pos = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
+ float x_pos = pos_x;
+ float y_pos = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
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);
if(pitem.list.size() > 0) {
list_width = (int) text_font->get_text_width(pitem.list[pitem.selected]);
}
-
+
if (arrange_left)
x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
{
context.draw_text(deactive_font, pitem.text,
Vector(SCREEN_WIDTH/2, y_pos - int(deactive_font->get_height()/2)),
- CENTER_ALLIGN, LAYER_GUI);
+ ALIGN_CENTER, LAYER_GUI);
break;
}
case MN_HL:
{
// TODO
- int x = pos_x - menu_width/2;
- int y = y_pos - 12 - effect_offset;
+ float x = pos_x - menu_width/2;
+ float y = y_pos - 12 - effect_offset;
/* Draw a horizontal line with a little 3d effect */
context.draw_filled_rect(Vector(x, y + 6),
- Vector(menu_width, 4), Color(150,200,255,225), LAYER_GUI);
+ Vector(menu_width, 4),
+ Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
context.draw_filled_rect(Vector(x, y + 6),
- Vector(menu_width, 2), Color(255,255,255,255), LAYER_GUI);
+ Vector(menu_width, 2),
+ Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
break;
}
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);
+ ALIGN_CENTER, LAYER_GUI);
break;
}
case MN_TEXTFIELD:
case MN_NUMFIELD:
case MN_CONTROLFIELD:
{
- int width = text_width + input_width + 5;
- int text_pos = SCREEN_WIDTH/2 - width/2;
- int input_pos = text_pos + text_width + 10;
+ 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(255,255,255,255), LAYER_GUI-5);
+ 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,128), LAYER_GUI-4);
+ Color(0, 0, 0, 0.5f), LAYER_GUI-4);
if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
{
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);
+ ALIGN_LEFT, 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);
+ ALIGN_LEFT, 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);
+ ALIGN_LEFT, LAYER_GUI);
context.draw_text(text_font, pitem.text,
Vector(text_pos, y_pos - int(text_font->get_height()/2)),
- LEFT_ALLIGN, LAYER_GUI);
+ ALIGN_LEFT, LAYER_GUI);
break;
}
case MN_STRINGSELECT:
int text_pos = (text_width + 16)/2;
/* Draw arrows */
- context.draw_surface(arrow_left,
+ context.draw_surface(arrow_left.get(),
Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
LAYER_GUI);
- context.draw_surface(arrow_right,
+ context.draw_surface(arrow_right.get(),
Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8),
LAYER_GUI);
context.draw_filled_rect(
Vector(x_pos - list_pos + text_pos - 1, y_pos - 10),
Vector(list_pos_2 + 2, 20),
- Color(255,255,255,255), LAYER_GUI - 4);
+ Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI - 4);
context.draw_filled_rect(
Vector(x_pos - list_pos + text_pos, y_pos - 9),
Vector(list_pos_2, 18),
- Color(0,0,0,128), LAYER_GUI - 5);
+ 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);
+ 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);
+ 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);
- context.draw_surface(back,
+ ALIGN_CENTER, LAYER_GUI);
+ context.draw_surface(back.get(),
Vector(x_pos + text_width/2 + 16, y_pos - 8),
LAYER_GUI);
break;
{
context.draw_text(text_font, pitem.text,
Vector(SCREEN_WIDTH/2, y_pos - (text_font->get_height()/2)),
- CENTER_ALLIGN, LAYER_GUI);
+ ALIGN_CENTER, LAYER_GUI);
if(pitem.toggled)
- context.draw_surface(checkbox_checked,
+ context.draw_surface(checkbox_checked.get(),
Vector(x_pos + (text_width+16)/2, y_pos - 8),
LAYER_GUI + 1);
else
- context.draw_surface(checkbox,
+ context.draw_surface(checkbox.get(),
Vector(x_pos + (text_width+16)/2, 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);
+ 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);
+ ALIGN_CENTER, LAYER_GUI);
break;
}
}
-int 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 */
+ float menu_width = 0;
+ for(unsigned int i = 0; i < items.size(); ++i)
{
- /* The width of the menu has to be more than the width of the text
- with the most characters */
- int menu_width = 0;
- for(unsigned int i = 0; i < items.size(); ++i)
- {
- int w = items[i]->text.size() + items[i]->input.size() + 1;
- if(w > menu_width)
- {
- menu_width = w;
- if( items[i]->kind == MN_TOGGLE)
- menu_width += 2;
- }
- }
+ Font* font = default_font;
+ if(items[i]->kind == MN_LABEL)
+ font = label_font;
- return (menu_width * 16 + 24);
- }
+ float w = font->get_text_width(items[i]->text) +
+ label_font->get_text_width(items[i]->input) + 16;
+ if(items[i]->kind == MN_TOGGLE)
+ w += 32;
-int Menu::get_height() const
- {
- return items.size() * 24;
+ if(w > menu_width)
+ menu_width = w;
}
+ return menu_width + 24;
+}
+
+float Menu::get_height() const
+{
+ return items.size() * 24;
+}
+
/* Draw the current menu. */
void
Menu::draw(DrawingContext& context)
{
- int menu_height = get_height();
- int menu_width = get_width();
+ if(MouseCursor::current()) {
+ MouseCursor::current()->draw(context);
+ }
+
+ 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(150,180,200,125), LAYER_GUI-10);
+ Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-10);
for(unsigned int i = 0; i < items.size(); ++i)
{
for(std::vector<MenuItem*>::iterator i = items.begin();
i != items.end(); ++i) {
MenuItem& item = **i;
-
+
if(item.id == id)
return item;
}
for(std::vector<MenuItem*>::const_iterator i = items.begin();
i != items.end(); ++i) {
const MenuItem& item = **i;
-
+
if(item.id == id)
return item;
}
void
Menu::event(const SDL_Event& event)
{
- if(effect.started())
+ if(effect_time != 0)
return;
switch(event.type) {
case SDL_MOUSEMOTION:
{
- int x = int(event.motion.x * float(SCREEN_WIDTH)/screen->w);
- int y = int(event.motion.y * float(SCREEN_HEIGHT)/screen->h);
+ float x = event.motion.x * SCREEN_WIDTH/screen->w;
+ float y = event.motion.y * SCREEN_HEIGHT/screen->h;
if(x > pos_x - get_width()/2 &&
x < pos_x + get_width()/2 &&
y > pos_y - get_height()/2 &&
y < pos_y + get_height()/2)
{
- int new_active_item = (y - (pos_y - get_height()/2)) / 24;
-
+ int new_active_item
+ = static_cast<int> ((y - (pos_y - get_height()/2)) / 24);
+
/* only change the mouse focus to a selectable item */
if ((items[new_active_item]->kind != MN_HL)
&& (items[new_active_item]->kind != MN_LABEL)
&& (items[new_active_item]->kind != MN_DEACTIVE))
active_item = new_active_item;
-
+
if(MouseCursor::current())
MouseCursor::current()->set_state(MC_LINK);
}
void
Menu::set_active_item(int id)
{
- for(int i = 0; i < items.size(); ++i) {
- MenuItem* item = items[i];
- if(item->id == id) {
- active_item = i;
- break;
- }
- }
+ for(size_t i = 0; i < items.size(); ++i) {
+ MenuItem* item = items[i];
+ if(item->id == id) {
+ active_item = i;
+ break;
+ }
+ }
}
-