782 lines
19 KiB
C++
782 lines
19 KiB
C++
#include "menu.hpp"
|
|
|
|
#include "../font.hpp"
|
|
#include "../sdl_utils.hpp"
|
|
#include "../show_dialog.hpp"
|
|
#include "../util.hpp"
|
|
#include "../video.hpp"
|
|
|
|
#include <numeric>
|
|
|
|
namespace {
|
|
const size_t menu_font_size = 14;
|
|
const size_t menu_cell_padding = 10;
|
|
}
|
|
|
|
namespace gui {
|
|
|
|
menu::menu(display& disp, const std::vector<std::string>& items,
|
|
bool click_selects, int max_height)
|
|
: max_height_(max_height), max_items_(-1), item_height_(-1),
|
|
display_(&disp), x_(0), y_(0), cur_help_(-1,-1), help_string_(-1), buffer_(NULL),
|
|
selected_(click_selects ? -1:0), click_selects_(click_selects),
|
|
previous_button_(true), drawn_(false), show_result_(false),
|
|
height_(-1), width_(-1), first_item_on_screen_(0),
|
|
uparrow_(disp,"",gui::button::TYPE_PRESS,"uparrow-button"),
|
|
downarrow_(disp,"",gui::button::TYPE_PRESS,"downarrow-button"),
|
|
scrollbar_(disp,this), scrollbar_height_(0),
|
|
double_clicked_(false), num_selects_(true),
|
|
ignore_next_doubleclick_(false),
|
|
last_was_doubleclick_(false)
|
|
{
|
|
for(std::vector<std::string>::const_iterator item = items.begin();
|
|
item != items.end(); ++item) {
|
|
items_.push_back(config::quoted_split(*item,',',false));
|
|
|
|
//make sure there is always at least one item
|
|
if(items_.back().empty())
|
|
items_.back().push_back(" ");
|
|
|
|
//if the first character in an item is an asterisk,
|
|
//it means this item should be selected by default
|
|
std::string& first_item = items_.back().front();
|
|
if(first_item.empty() == false && first_item[0] == '*') {
|
|
selected_ = items_.size()-1;
|
|
first_item.erase(first_item.begin());
|
|
}
|
|
}
|
|
|
|
create_help_strings();
|
|
}
|
|
|
|
void menu::create_help_strings()
|
|
{
|
|
help_.clear();
|
|
for(std::vector<std::vector<std::string> >::iterator i = items_.begin(); i != items_.end(); ++i) {
|
|
help_.resize(help_.size()+1);
|
|
for(std::vector<std::string>::iterator j = i->begin(); j != i->end(); ++j) {
|
|
if(std::find(j->begin(),j->end(),HELP_STRING_SEPERATOR) == j->end()) {
|
|
help_.back().push_back("");
|
|
} else {
|
|
const std::vector<std::string>& items = config::split(*j,HELP_STRING_SEPERATOR,0);
|
|
if(items.size() >= 2) {
|
|
*j = items.front();
|
|
help_.back().push_back(items.back());
|
|
} else {
|
|
help_.back().push_back("");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The scrollbar height depends on the number of visible items versus
|
|
void menu::set_scrollbar_height()
|
|
{
|
|
int buttons_height = downarrow_.height() + uparrow_.height();
|
|
float pos_percent = (float)max_items_onscreen()/(float)items_.size();
|
|
scrollbar_height_ = (int)(pos_percent * (height()-buttons_height));
|
|
|
|
int min_height = scrollbar_.get_minimum_grip_height();
|
|
|
|
if (scrollbar_height_ < min_height)
|
|
scrollbar_height_ = min_height;
|
|
if (scrollbar_height_ > height()) {
|
|
std::cerr << "Strange. For some reason I want my scrollbar" <<
|
|
" to be larger than me!\n\n";
|
|
std::cerr << "pos_percent=" << pos_percent << " height()=" << height()
|
|
<< std::endl;
|
|
scrollbar_height_ = height() - buttons_height;
|
|
}
|
|
|
|
scrollbar_.set_grip_height(scrollbar_height_);
|
|
}
|
|
|
|
int menu::height() const
|
|
{
|
|
if(height_ == -1) {
|
|
height_ = 0;
|
|
for(size_t i = 0; i != items_.size() && i != max_items_onscreen(); ++i) {
|
|
height_ += get_item_rect(i).h;
|
|
}
|
|
}
|
|
|
|
return height_;
|
|
}
|
|
|
|
int menu::width() const
|
|
{
|
|
if(width_ == -1) {
|
|
const std::vector<int>& widths = column_widths();
|
|
width_ = std::accumulate(widths.begin(),widths.end(),0);
|
|
if(show_scrollbar()) {
|
|
width_ += scrollbar_.get_max_width();
|
|
}
|
|
}
|
|
|
|
return width_;
|
|
}
|
|
|
|
int menu::selection() const { return selected_; }
|
|
|
|
void menu::set_loc(int x, int y)
|
|
{
|
|
x_ = x;
|
|
y_ = y;
|
|
|
|
const int w = width();
|
|
|
|
SDL_Rect portion = {x_,y_,w,height()};
|
|
SDL_Surface* const screen = display_->video().getSurface();
|
|
buffer_.assign(get_surface_portion(screen, portion));
|
|
|
|
if(show_scrollbar()) {
|
|
const int menu_width = width() - scrollbar_.get_max_width();
|
|
|
|
scrollbar_.enable(true);
|
|
int scr_width = scrollbar_.get_width();
|
|
|
|
SDL_Rect scroll_rect = {x_ + menu_width, y_+uparrow_.height(),
|
|
scr_width,
|
|
height()-downarrow_.height()-uparrow_.height()};
|
|
scrollbar_.set_location(scroll_rect);
|
|
set_scrollbar_height();
|
|
|
|
uparrow_.set_location(x_ + menu_width,y_);
|
|
downarrow_.set_location(x_+ menu_width,scrollbar_.location().y + scrollbar_.location().h);
|
|
|
|
}
|
|
}
|
|
|
|
void menu::set_width(int w)
|
|
{
|
|
width_ = w;
|
|
set_loc(x_, y_);
|
|
itemRects_.clear();
|
|
}
|
|
|
|
void menu::redraw()
|
|
{
|
|
if(x_ == 0 && y_ == 0) {
|
|
return;
|
|
}
|
|
|
|
draw();
|
|
uparrow_.draw();
|
|
downarrow_.draw();
|
|
scrollbar_.redraw();
|
|
}
|
|
|
|
void menu::change_item(int pos1, int pos2,std::string str)
|
|
{
|
|
items_[pos1][pos2] = str;
|
|
undrawn_items_.insert(pos1);
|
|
}
|
|
|
|
void menu::erase_item(size_t index)
|
|
{
|
|
if(index < items_.size()) {
|
|
clear_item(items_.size()-1);
|
|
items_.erase(items_.begin() + index);
|
|
itemRects_.clear();
|
|
if(size_t(selected_) >= items_.size()) {
|
|
selected_ = int(items_.size()-1);
|
|
}
|
|
|
|
calculate_position();
|
|
|
|
drawn_ = false;
|
|
}
|
|
}
|
|
|
|
void menu::set_items(const std::vector<std::string>& items) {
|
|
items_.clear();
|
|
itemRects_.clear();
|
|
column_widths_.clear();
|
|
undrawn_items_.clear();
|
|
height_ = -1; // Force recalculation of the height.
|
|
width_ = -1; // Force recalculation of the width.
|
|
max_items_ = -1; // Force recalculation of the max items.
|
|
// Scrollbar and buttons will be reanabled if they are needed.
|
|
scrollbar_.enable(false);
|
|
uparrow_.hide(true);
|
|
downarrow_.hide(true);
|
|
first_item_on_screen_ = 0;
|
|
selected_ = click_selects_ ? -1:0;
|
|
for (std::vector<std::string>::const_iterator item = items.begin();
|
|
item != items.end(); ++item) {
|
|
items_.push_back(config::quoted_split(*item,',',false));
|
|
|
|
//make sure there is always at least one item
|
|
if(items_.back().empty())
|
|
items_.back().push_back(" ");
|
|
|
|
//if the first character in an item is an asterisk,
|
|
//it means this item should be selected by default
|
|
std::string& first_item = items_.back().front();
|
|
if(first_item.empty() == false && first_item[0] == '*') {
|
|
selected_ = items_.size()-1;
|
|
first_item.erase(first_item.begin());
|
|
}
|
|
}
|
|
set_loc(x_, y_); // Force some more updating.
|
|
calculate_position();
|
|
drawn_ = false;
|
|
}
|
|
|
|
void menu::set_max_height(const int new_max_height) {
|
|
max_height_ = new_max_height;
|
|
}
|
|
|
|
|
|
|
|
size_t menu::max_items_onscreen() const
|
|
{
|
|
if(max_items_ != -1) {
|
|
return size_t(max_items_);
|
|
}
|
|
|
|
const size_t max_height = max_height_ == -1 ? (display_->y()*66)/100 : max_height_;
|
|
std::vector<int> heights;
|
|
size_t n;
|
|
for(n = 0; n != items_.size(); ++n) {
|
|
heights.push_back(get_item_height(n));
|
|
}
|
|
|
|
std::sort(heights.begin(),heights.end(),std::greater<int>());
|
|
size_t sum = 0;
|
|
for(n = 0; n != items_.size() && sum < max_height; ++n) {
|
|
sum += heights[n];
|
|
}
|
|
|
|
if(sum > max_height && n > 1)
|
|
--n;
|
|
|
|
return max_items_ = n;
|
|
}
|
|
|
|
void menu::calculate_position()
|
|
{
|
|
if(click_selects_)
|
|
return;
|
|
|
|
if(selected_ < first_item_on_screen_) {
|
|
first_item_on_screen_ = selected_;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
|
|
if(selected_ >= first_item_on_screen_ + int(max_items_onscreen())) {
|
|
first_item_on_screen_ = selected_ - (max_items_onscreen() - 1);
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
}
|
|
|
|
void menu::key_press(SDLKey key)
|
|
{
|
|
switch(key) {
|
|
case SDLK_UP: {
|
|
if(!click_selects_ && selected_ > 0) {
|
|
--selected_;
|
|
calculate_position();
|
|
undrawn_items_.insert(selected_);
|
|
undrawn_items_.insert(selected_+1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDLK_DOWN: {
|
|
if(!click_selects_ && selected_ < int(items_.size())-1) {
|
|
++selected_;
|
|
calculate_position();
|
|
undrawn_items_.insert(selected_);
|
|
undrawn_items_.insert(selected_-1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDLK_PAGEUP: {
|
|
if(!click_selects_) {
|
|
selected_ -= max_items_onscreen();
|
|
if(selected_ < 0)
|
|
selected_ = 0;
|
|
|
|
calculate_position();
|
|
drawn_ = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDLK_PAGEDOWN: {
|
|
if(!click_selects_) {
|
|
selected_ += max_items_onscreen();
|
|
if(selected_ >= int(items_.size()))
|
|
selected_ = int(items_.size())-1;
|
|
|
|
calculate_position();
|
|
drawn_ = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(key >= SDLK_1 && key <= SDLK_9 && num_selects_) {
|
|
const int pos = key - SDLK_1;
|
|
if(size_t(pos) < items_.size()) {
|
|
undrawn_items_.insert(selected_);
|
|
selected_ = pos;
|
|
calculate_position();
|
|
undrawn_items_.insert(selected_);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void menu::handle_event(const SDL_Event& event)
|
|
{
|
|
if(event.type == SDL_KEYDOWN) {
|
|
key_press(event.key.keysym.sym);
|
|
} else if(event.type == SDL_MOUSEBUTTONDOWN &&
|
|
event.button.button == SDL_BUTTON_LEFT ||
|
|
event.type == DOUBLE_CLICK_EVENT) {
|
|
int x = 0;
|
|
int y = 0;
|
|
if(event.type == SDL_MOUSEBUTTONDOWN) {
|
|
x = event.button.x;
|
|
y = event.button.y;
|
|
} else {
|
|
x = (int)event.user.data1;
|
|
y = (int)event.user.data2;
|
|
}
|
|
|
|
const int item = hit(x,y);
|
|
if(item != -1) {
|
|
undrawn_items_.insert(selected_);
|
|
selected_ = item;
|
|
undrawn_items_.insert(selected_);
|
|
|
|
if(click_selects_) {
|
|
show_result_ = true;
|
|
}
|
|
|
|
if(event.type == DOUBLE_CLICK_EVENT) {
|
|
if (ignore_next_doubleclick_) {
|
|
ignore_next_doubleclick_ = false;
|
|
}
|
|
else {
|
|
double_clicked_ = true;
|
|
last_was_doubleclick_ = true;
|
|
}
|
|
}
|
|
else if (last_was_doubleclick_) {
|
|
// If we have a double click as the next event, it means
|
|
// this double click was generated from a click that
|
|
// already has helped in generating a double click.
|
|
SDL_Event ev;
|
|
SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
|
|
SDL_EVENTMASK(DOUBLE_CLICK_EVENT));
|
|
if (ev.type == DOUBLE_CLICK_EVENT) {
|
|
ignore_next_doubleclick_ = true;
|
|
}
|
|
last_was_doubleclick_ = false;
|
|
}
|
|
}
|
|
} else if(event.type == SDL_MOUSEMOTION && click_selects_) {
|
|
|
|
const int item = hit(event.motion.x,event.motion.y);
|
|
if(item != selected_) {
|
|
selected_ = item;
|
|
drawn_ = false;
|
|
}
|
|
} else if(event.type == SDL_MOUSEBUTTONDOWN
|
|
&& event.button.button == SDL_BUTTON_WHEELDOWN) {
|
|
if(first_item_on_screen_+max_items_onscreen()<items_.size()) {
|
|
++first_item_on_screen_;
|
|
if (selected_<first_item_on_screen_)
|
|
selected_=first_item_on_screen_;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
} else if(event.type == SDL_MOUSEBUTTONDOWN
|
|
&& event.button.button == SDL_BUTTON_WHEELUP) {
|
|
if(first_item_on_screen_>0) {
|
|
--first_item_on_screen_;
|
|
if (selected_>=first_item_on_screen_+int(max_items_onscreen()))
|
|
selected_=first_item_on_screen_+max_items_onscreen()-1;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
int menu::process(int x, int y, bool button,bool up_arrow,bool down_arrow,
|
|
bool page_up, bool page_down, int select_item)
|
|
{
|
|
static int last_scroll_position = 0;
|
|
bool scroll_in_use = false;
|
|
const int last_top_idx = items_.size() - max_items_onscreen();
|
|
int max_scroll_position = scrollbar_.location().h-scrollbar_height_;
|
|
if(show_scrollbar()) {
|
|
const bool up = uparrow_.process(x,y,button);
|
|
if(up && first_item_on_screen_ > 0) {
|
|
--first_item_on_screen_;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
const bool down = downarrow_.process(x,y,button);
|
|
if(down && first_item_on_screen_ < last_top_idx) {
|
|
++first_item_on_screen_;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
|
|
scrollbar_.process();
|
|
int scroll_position = scrollbar_.get_grip_position();
|
|
int new_first_item;
|
|
if (scroll_position != last_scroll_position) {
|
|
last_scroll_position = scroll_position;
|
|
if (scroll_position == 0) {
|
|
|
|
new_first_item = 0;
|
|
}
|
|
else if (scroll_position > (max_scroll_position)) {
|
|
new_first_item = last_top_idx;
|
|
}
|
|
else {
|
|
new_first_item = scroll_position * last_top_idx /
|
|
max_scroll_position;
|
|
}
|
|
if (new_first_item != first_item_on_screen_) {
|
|
scroll_in_use = true;
|
|
first_item_on_screen_ = new_first_item;
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
}
|
|
else {
|
|
int groove = scrollbar_.groove_clicked();
|
|
if (groove == -1) {
|
|
first_item_on_screen_ -= max_items_onscreen();
|
|
if (first_item_on_screen_ <= 0) {
|
|
first_item_on_screen_ = 0;
|
|
}
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
else if (groove == 1) {
|
|
first_item_on_screen_ += max_items_onscreen();
|
|
if (first_item_on_screen_ > last_top_idx) {
|
|
first_item_on_screen_ = last_top_idx;
|
|
}
|
|
itemRects_.clear();
|
|
drawn_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!drawn_) {
|
|
if (!scroll_in_use && show_scrollbar()) {
|
|
int new_scrollpos =
|
|
(first_item_on_screen_ * max_scroll_position)
|
|
/ last_top_idx;
|
|
scrollbar_.set_grip_position(new_scrollpos);
|
|
last_scroll_position = new_scrollpos;
|
|
}
|
|
draw();
|
|
}
|
|
|
|
if(show_result_) {
|
|
show_result_ = false;
|
|
return selected_;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool menu::show_scrollbar() const
|
|
{
|
|
return items_.size() > max_items_onscreen();
|
|
}
|
|
|
|
bool menu::double_clicked()
|
|
{
|
|
bool old = double_clicked_;
|
|
double_clicked_ = false;
|
|
return old;
|
|
}
|
|
|
|
void menu::set_numeric_keypress_selection(bool value)
|
|
{
|
|
num_selects_ = value;
|
|
}
|
|
|
|
void menu::scroll(int pos)
|
|
{
|
|
}
|
|
|
|
namespace {
|
|
const char ImagePrefix = '&';
|
|
|
|
SDL_Rect item_size(const std::string& item) {
|
|
SDL_Rect res = {0,0,0,0};
|
|
if(item.empty() == false && item[0] == ImagePrefix) {
|
|
const std::string image_name(item.begin()+1,item.end());
|
|
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
|
if(img != NULL) {
|
|
res.w = img->w;
|
|
res.h = img->h;
|
|
}
|
|
} else {
|
|
const SDL_Rect area = {0,0,10000,10000};
|
|
res = font::draw_text(NULL,area,menu_font_size,font::NORMAL_COLOUR,item,0,0);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
const std::vector<int>& menu::column_widths() const
|
|
{
|
|
if(column_widths_.empty()) {
|
|
for(size_t row = 0; row != items_.size(); ++row) {
|
|
for(size_t col = 0; col != items_[row].size(); ++col) {
|
|
static const SDL_Rect area = {0,0,display_->x(),display_->y()};
|
|
|
|
const SDL_Rect res = item_size(items_[row][col]);
|
|
|
|
if(col == column_widths_.size()) {
|
|
column_widths_.push_back(res.w + menu_cell_padding);
|
|
} else if(res.w > column_widths_[col] - menu_cell_padding) {
|
|
column_widths_[col] = res.w + menu_cell_padding;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return column_widths_;
|
|
}
|
|
|
|
void menu::clear_item(int item)
|
|
{
|
|
SDL_Rect rect = get_item_rect(item);
|
|
if(rect.w == 0) {
|
|
return;
|
|
}
|
|
|
|
if(buffer_.get() != NULL) {
|
|
const int ypos = (item-first_item_on_screen_)*rect.h;
|
|
SDL_Rect srcrect = {0,ypos,rect.w,rect.h};
|
|
SDL_Rect dstrect = rect;
|
|
SDL_BlitSurface(buffer_,&srcrect,
|
|
display_->video().getSurface(),&dstrect);
|
|
}
|
|
}
|
|
|
|
void menu::draw_item(int item)
|
|
{
|
|
SDL_Rect rect = get_item_rect(item);
|
|
if(rect.w == 0) {
|
|
return;
|
|
}
|
|
|
|
clear_item(item);
|
|
|
|
gui::draw_solid_tinted_rectangle(x_,rect.y,width()-scrollbar_.get_width(),rect.h,
|
|
item == selected_ ? 150:0,0,0,
|
|
item == selected_ ? 0.6 : 0.2,
|
|
display_->video().getSurface());
|
|
|
|
SDL_Rect area = display_->screen_area();
|
|
|
|
const std::vector<int>& widths = column_widths();
|
|
|
|
int xpos = rect.x;
|
|
for(size_t i = 0; i != items_[item].size(); ++i) {
|
|
const std::string& str = items_[item][i];
|
|
if(str.empty() == false && str[0] == ImagePrefix) {
|
|
const std::string image_name(str.begin()+1,str.end());
|
|
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
|
if(img != NULL && xpos+img->w < display_->x() && rect.y+img->h < display_->y()) {
|
|
display_->blit_surface(xpos,rect.y,img);
|
|
}
|
|
|
|
} else {
|
|
const SDL_Rect& text_size = font::text_area(str,menu_font_size);
|
|
const size_t y = rect.y + (rect.h - text_size.h)/2;
|
|
font::draw_text(display_,area,menu_font_size,font::NORMAL_COLOUR,str,xpos,y);
|
|
}
|
|
xpos += widths[i];
|
|
}
|
|
}
|
|
|
|
void menu::draw()
|
|
{
|
|
if(x_ == 0 && y_ == 0 || drawn_ && undrawn_items_.empty()) {
|
|
return;
|
|
}
|
|
|
|
if(drawn_) {
|
|
for(std::set<size_t>::const_iterator i = undrawn_items_.begin(); i != undrawn_items_.end(); ++i) {
|
|
if(*i < items_.size()) {
|
|
draw_item(*i);
|
|
update_rect(get_item_rect(*i));
|
|
}
|
|
}
|
|
|
|
undrawn_items_.clear();
|
|
return;
|
|
}
|
|
|
|
undrawn_items_.clear();
|
|
drawn_ = true;
|
|
|
|
// update enabled/disabled status for up/down buttons
|
|
if(show_scrollbar()) {
|
|
uparrow_.hide(first_item_on_screen_ == 0);
|
|
downarrow_.hide(first_item_on_screen_ >= items_.size() - max_items_onscreen());
|
|
}
|
|
|
|
for(size_t i = 0; i != items_.size(); ++i)
|
|
draw_item(i);
|
|
|
|
update_rect(x_,y_,width(),height());
|
|
}
|
|
|
|
int menu::hit(int x, int y) const
|
|
{
|
|
if(x > x_ && x < x_ + width() - scrollbar_.get_width() &&
|
|
y > y_ && y < y_ + height()) {
|
|
for(size_t i = 0; i != items_.size(); ++i) {
|
|
const SDL_Rect& rect = get_item_rect(i);
|
|
if(y > rect.y && y < rect.y + rect.h)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
std::pair<int,int> menu::hit_cell(int x, int y) const
|
|
{
|
|
if(x > x_ && x < x_ + width() - scrollbar_.get_width() &&
|
|
y > y_ && y < y_ + height()) {
|
|
for(size_t i = 0; i != items_.size(); ++i) {
|
|
const SDL_Rect& rect = get_item_rect(i);
|
|
if(y > rect.y && y < rect.y + rect.h) {
|
|
const std::vector<int>& widths = column_widths();
|
|
for(std::vector<int>::const_iterator w = widths.begin(); w != widths.end(); ++w) {
|
|
x -= *w;
|
|
if(x <= x_) {
|
|
return std::pair<int,int>(int(i),int(w-widths.begin()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::pair<int,int>(-1,-1);
|
|
}
|
|
|
|
SDL_Rect menu::get_item_rect(int item) const
|
|
{
|
|
const SDL_Rect empty_rect = {0,0,0,0};
|
|
if(item < first_item_on_screen_ ||
|
|
size_t(item) >= first_item_on_screen_ + max_items_onscreen()) {
|
|
return empty_rect;
|
|
}
|
|
|
|
const std::map<int,SDL_Rect>::const_iterator i = itemRects_.find(item);
|
|
if(i != itemRects_.end())
|
|
return i->second;
|
|
|
|
int y = y_;
|
|
if(item != first_item_on_screen_) {
|
|
const SDL_Rect& prev = get_item_rect(item-1);
|
|
y = prev.y + prev.h;
|
|
}
|
|
|
|
const SDL_Rect screen_area = display_->screen_area();
|
|
|
|
SDL_Rect res = {x_, y,
|
|
width() - scrollbar_.get_width(),
|
|
get_item_height(item)};
|
|
|
|
if(res.x > screen_area.w) {
|
|
return empty_rect;
|
|
} else if(res.x + res.w > screen_area.w) {
|
|
res.w = screen_area.w - res.x;
|
|
}
|
|
|
|
if(res.y > screen_area.h) {
|
|
return empty_rect;
|
|
} else if(res.y + res.h > screen_area.h) {
|
|
res.h = screen_area.h - res.y;
|
|
}
|
|
|
|
//only insert into the cache if the menu's co-ordinates have
|
|
//been initialized
|
|
if(x_ > 0 && y_ > 0)
|
|
itemRects_.insert(std::pair<int,SDL_Rect>(item,res));
|
|
|
|
return res;
|
|
}
|
|
|
|
size_t menu::get_item_height_internal(int item) const
|
|
{
|
|
size_t res = 0;
|
|
for(size_t n = 0; n != items_[item].size(); ++n) {
|
|
SDL_Rect rect = item_size(items_[item][n]);
|
|
res = maximum<int>(rect.h,res);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
size_t menu::get_item_height(int item) const
|
|
{
|
|
if(item_height_ != -1)
|
|
return size_t(item_height_);
|
|
|
|
size_t max_height = 0;
|
|
for(size_t n = 0; n != items_.size(); ++n) {
|
|
max_height = maximum<int>(max_height,get_item_height_internal(n));
|
|
}
|
|
|
|
return item_height_ = max_height;
|
|
}
|
|
|
|
void menu::process_help_string(int mousex, int mousey)
|
|
{
|
|
const std::pair<int,int> loc = hit_cell(mousex,mousey);
|
|
if(loc == cur_help_) {
|
|
return;
|
|
} else if(loc.first == -1) {
|
|
display_->clear_help_string(help_string_);
|
|
help_string_ = -1;
|
|
} else {
|
|
if(help_string_ != -1) {
|
|
display_->clear_help_string(help_string_);
|
|
help_string_ = -1;
|
|
}
|
|
|
|
if(size_t(loc.first) < help_.size()) {
|
|
const std::vector<std::string>& row = help_[loc.first];
|
|
if(size_t(loc.second) < help_.size()) {
|
|
const std::string& help = row[loc.second];
|
|
help_string_ = display_->set_help_string(help);
|
|
}
|
|
}
|
|
}
|
|
|
|
cur_help_ = loc;
|
|
}
|
|
|
|
}
|