stage 1 of the dialog refactoring :

the construct_dialog files allow you to build customized dialogs
also: cleaned up some of my bad event::handler code
This commit is contained in:
Patrick Parker 2006-07-19 21:14:13 +00:00
parent 7246e2e43d
commit 7446e31386
21 changed files with 1094 additions and 75 deletions

750
src/construct_dialog.cpp Normal file
View File

@ -0,0 +1,750 @@
/*
Copyright (C) 2006 by Patrick Parker <patrick_x99@hotmail.com>
wesnoth widget Copyright (C) 2003-5 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "global.hpp"
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "construct_dialog.hpp"
#include "config.hpp"
#include "cursor.hpp"
#include "display.hpp"
#include "events.hpp"
#include "gettext.hpp"
#include "help.hpp"
#include "hotkeys.hpp"
#include "image.hpp"
#include "key.hpp"
#include "log.hpp"
#include "marked-up_text.hpp"
#include "thread.hpp"
#include "language.hpp"
#include "sdl_utils.hpp"
#include "tooltips.hpp"
#include "util.hpp"
#include "video.hpp"
#include "widgets/button.hpp"
#include "widgets/menu.hpp"
#include "widgets/progressbar.hpp"
#include "widgets/textbox.hpp"
#include "wassert.hpp"
#include "sdl_ttf/SDL_ttf.h"
#include <iostream>
#include <numeric>
#define ERR_DP LOG_STREAM(err, display)
#define LOG_DP LOG_STREAM(info, display)
#define ERR_G LOG_STREAM(err, general)
namespace gui {
//static initialization
const std::string dialog::default_style("menu");
const std::string dialog::no_help("");
const int dialog::message_font_size = font::SIZE_PLUS;
const int dialog::caption_font_size = font::SIZE_LARGE;
const size_t dialog::left_padding = font::relative_size(10);
const size_t dialog::right_padding = font::relative_size(10);
const size_t dialog::image_h_pad = font::relative_size(/*image_ == NULL ? 0 :*/ 10);
const size_t dialog::top_padding = font::relative_size(10);
const size_t dialog::bottom_padding = font::relative_size(10);
#ifdef USE_TINY_GUI
const int dialog::max_menu_width = 150;
#else
const int dialog::max_menu_width = -1;
#endif
}
namespace {
SDL_Rect empty_rect = { 0, 0, 0, 0 };
struct help_handler : public hotkey::command_executor
{
help_handler(display& disp, const std::string& topic) : disp_(disp), topic_(topic)
{}
private:
void show_help()
{
if(topic_.empty() == false) {
help::show_help(disp_,topic_);
}
}
bool can_execute_command(hotkey::HOTKEY_COMMAND cmd) const
{
return (topic_.empty() == false && cmd == hotkey::HOTKEY_HELP) || cmd == hotkey::HOTKEY_SCREENSHOT;
}
display& disp_;
std::string topic_;
};
} //end anonymous namespace
namespace gui {
dialog::dialog(display &disp, const std::string& title, const std::string& message,
const DIALOG_TYPE type, const std::string& dialog_style,
const std::string& help_topic) : disp_(disp),
title_(title), message_(message), type_(type), image_(NULL),
style_(dialog_style), help_button_(disp, help_topic), menu_(NULL),
text_widget_(NULL), result_(CONTINUE_DIALOG), action_(NULL),
x_(-1), y_(-1)
{
switch(type)
{
case MESSAGE:
default:
break;
case OK_ONLY: {
add_button(new standard_dialog_button(disp.video(),_("OK"),0,true), BUTTON_STANDARD);
break;
}
case YES_NO: {
add_button(new standard_dialog_button(disp.video(),_("Yes"),0,false), BUTTON_STANDARD);
add_button(new standard_dialog_button(disp.video(),_("No"),1,true), BUTTON_STANDARD);
break;
}
case OK_CANCEL: {
add_button(new standard_dialog_button(disp.video(),_("OK"),0,false), BUTTON_STANDARD);
add_button(new standard_dialog_button(disp.video(),_("Cancel"),1,true), BUTTON_STANDARD);
break;
}
case CANCEL_ONLY: {
add_button(new standard_dialog_button(disp.video(),_("Cancel"),0,true), BUTTON_STANDARD);
break;
}
case CLOSE_ONLY: {
add_button(new standard_dialog_button(disp.video(),_("Close"),0,true), BUTTON_STANDARD);
break;
}
}
//dialog creator should catch(button::error&) ?
message_rect_ = empty_rect;
frame_rect_ = empty_rect;
}
dialog::~dialog()
{
if(menu_ != empty_menu)
{
delete menu_;
}
delete text_widget_;
delete image_;
//delete action_;
button_pool_iterator b;
pp_iterator p;
for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
delete b->first;
}
for (p = preview_panes_.begin(); p != preview_panes_.end(); ++p) {
delete p;
}
}
const bool dialog::option_checked(unsigned int option_index)
{
unsigned int i = 0;
button_pool_iterator b;
for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
if(b->first->is_option()) {
if(option_index == i++) {
return b->first->checked();
}
}
}
return false;
}
void dialog::add_button(dialog_button *const btn, BUTTON_LOCATION loc)
{
std::pair<dialog_button *, BUTTON_LOCATION> new_pair(btn,loc);
button_pool_.push_back(new_pair);
}
void dialog::set_textbox(const std::string& text_widget_label,
const std::string& text_widget_text,
const int text_widget_max_chars, const unsigned int text_box_width)
{
label *label_ptr = new label(disp_.video(), text_widget_label, message_font_size, font::NORMAL_COLOUR, false);
const bool editable_textbox = std::find(text_widget_text.begin(),text_widget_text.end(),'\n') == text_widget_text.end();
text_widget_ = new dialog_textbox(label_ptr, disp_.video(), text_box_width, text_widget_text, editable_textbox, text_widget_max_chars);
text_widget_->set_wrap(!editable_textbox);
}
menu *dialog::menu()
{
if(menu_ == NULL)
{
if(empty_menu == NULL) {
empty_menu = new gui::menu(disp_.video(),empty_string_vector,false,-1,-1,NULL,&menu::simple_style);
empty_menu->leave();
}
menu_ = empty_menu;
}
return menu_;
}
int dialog::show(int &xloc, int &yloc)
{
x_ = xloc;
y_ = yloc;
show();
xloc = x_;
yloc = y_;
return result();
}
int dialog::show()
{
if(disp_.update_locked())
return CLOSE_DIALOG;
LOG_DP << "showing dialog '" << title_ << "' '" << message_ << "'\n";
//create the event context, remember to instruct any passed-in widgets to join it
const events::event_context outer_context;
const events::event_context dialog_events_context;
const dialog_manager manager;
const events::resize_lock prevent_resizing;
help_handler helper(disp_,help_button_.topic());
hotkey::basic_handler help_dispatcher(&disp_,&helper);
//layout
layout(x_, y_);
//draw
surface_restorer restorer;
draw(restorer);
//process
dialog_process_info dp_info;
disp_.invalidate_all();
do
{
events::pump();
set_result(process(dp_info));
if(!done()) {
refresh();
}
action();
} while(!done());
return result();
}
void dialog::draw(surface_restorer &restorer)
{
CVideo& screen = disp_.video();
surface const scr = screen.getSurface();
unsigned int image_h_padding = 0;
unsigned int image_width = 0;
unsigned int caption_height = 0;
if(image_ != NULL) {
image_h_padding = image_h_pad;
image_width = image_->width();
caption_height = (image_->caption() == NULL)? 0 : image_->caption()->height();
}
/* const std::string& title = image_ == NULL ? caption_ : "";
*/
std::vector<button*> frame_buttons;
for(button_iterator b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b)
{
frame_buttons.push_back(*b);
}
draw_dialog(frame_rect_.x, frame_rect_.y, frame_rect_.w, frame_rect_.h,
screen, title_, &style_, &frame_buttons, &restorer,
help_button_.topic().empty() ? NULL : &help_button_);
//draw textbox, textbox label, image, and image caption
events::raise_draw_event();
/*
if(text_widget_ != NULL) {
events::raise_draw_event();
font::draw_text(&screen, clipRect, message_font_size,
font::NORMAL_COLOUR, text_widget_label_,
xloc + left_padding,text_widget_y_unpadded);
}
if(image_ != NULL) {
screen.blit_surface(x,y,image_);
font::draw_text(&screen, clipRect, caption_font_size,
font::NORMAL_COLOUR, caption_,
xloc+image_width+left_padding+image_h_padding,
yloc+top_padding);
}
*/
font::draw_text(&screen, message_rect_, message_font_size,
font::NORMAL_COLOUR, message_,
x_+image_width+left_padding+image_h_padding,
y_+top_padding+caption_height);
disp_.flip();
}
void dialog::refresh()
{
disp_.flip();
disp_.delay(10);
}
void dialog::layout(int &xloc, int &yloc)
{
if(frame_rect_ != empty_rect) //already finished layout
return;
CVideo& screen = disp_.video();
surface const scr = screen.getSurface();
SDL_Rect clipRect = disp_.screen_area();
const bool use_textbox = (text_widget_ != NULL);
int text_widget_width = 0;
int text_widget_height = 0;
if(use_textbox) {
text_widget_->join();
const SDL_Rect& area = font::text_area(text_widget_->text(),message_font_size);
text_widget_->set_width(minimum<size_t>(screen.getx()/2,maximum<size_t>(area.w,text_widget_->width())));
text_widget_->set_height(minimum<size_t>(screen.gety()/2,maximum<size_t>(area.h,text_widget_->height())));
text_widget_width = text_widget_->width();
text_widget_width += (text_widget_->label() == NULL) ? 0 : text_widget_->label()->width();
text_widget_height = text_widget_->height() + message_font_size;
}
// const std::vector<std::string> &menu_items =
// (menu_items_ptr == NULL) ? std::vector<std::string>() : *menu_items_ptr;
// menu menu_(screen,menu_items,type == MESSAGE,-1,max_menu_width,sorter,menu_style);
const bool use_menu = (menu()->height() > 0);
if(use_menu)
{
menu_->join();
menu_->set_numeric_keypress_selection(use_textbox == false);
}
try {
message_ = font::word_wrap_text(message_, message_font_size, screen.getx() / 2, screen.gety() / 2);
} catch(utils::invalid_utf8_exception&) {
ERR_DP << "Problem handling utf8 in message '" << message_ << "'\n";
}
if(!message_.empty()) {
message_rect_ = font::draw_text(NULL, clipRect, message_font_size,
font::NORMAL_COLOUR, message_, 0, 0);
}
int caption_width = 0;
int caption_height = 0;
if (image_ != NULL && image_->caption() != NULL) {
caption_width = image_->caption()->width();
caption_height = image_->caption()->height();
}
int check_button_height = 0;
// int check_button_width = 0;
int left_check_button_height = 0;
// int left_check_button_width = 0;
const int button_height_padding = 5;
for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
switch(b->second)
{
case BUTTON_EXTRA:
case BUTTON_CHECKBOX:
check_button_height += b->first->height() + button_height_padding;
// check_button_width = maximum<int>(b->first->width(),check_button_width);
extra_buttons_.push_back(b->first);
b->first->set_parent(this);
b->first->join();
break;
case BUTTON_CHECKBOX_LEFT:
left_check_button_height += b->first->height() + button_height_padding;
// left_check_button_width = maximum<int>(b->first->width(),left_check_button_width);
extra_buttons_.push_back(b->first);
b->first->set_parent(this);
b->first->join();
break;
case BUTTON_STANDARD:
standard_buttons_.push_back(b->first);
default:
b->first->set_parent(this);
b->first->join();
break;
}
}
check_button_height = maximum<int>(check_button_height, left_check_button_height);
size_t above_preview_pane_height = 0, above_left_preview_pane_width = 0, above_right_preview_pane_width = 0;
size_t preview_pane_height = 0, left_preview_pane_width = 0, right_preview_pane_width = 0;
if(!preview_panes_.empty()) {
for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
(**i).join(); //join the event context now, since it was created outside this scope
const SDL_Rect& rect = (**i).location();
if((**i).show_above() == false) {
preview_pane_height = maximum<size_t>(rect.h,preview_pane_height);
if((**i).left_side()) {
left_preview_pane_width += rect.w;
} else {
right_preview_pane_width += rect.w;
}
} else {
above_preview_pane_height = maximum<size_t>(rect.h,above_preview_pane_height);
if((**i).left_side()) {
above_left_preview_pane_width += rect.w;
} else {
above_right_preview_pane_width += rect.w;
}
}
}
}
const int menu_hpadding = font::relative_size((message_rect_.h > 0 && use_menu) ? 10 : 0);
const size_t image_h_padding = (image_ == NULL)? 0 : image_h_pad;
const size_t padding_width = left_padding + right_padding + image_h_padding;
const size_t padding_height = top_padding + bottom_padding + menu_hpadding;
const size_t image_width = (image_ == NULL) ? 0 : image_->width();
const size_t image_height = (image_ == NULL) ? 0 : image_->height();
const size_t total_text_height = message_rect_.h + caption_height;
size_t text_width = message_rect_.w;
if(caption_width > text_width)
text_width = caption_width;
// Prevent the menu to be larger than the screen
if(menu_->width() + image_width + padding_width > size_t(scr->w))
menu_->set_width(scr->w - image_width - padding_width);
if(menu_->width() > text_width)
text_width = menu_->width();
size_t total_width = image_width + text_width + padding_width;
if(text_widget_width+left_padding+right_padding > total_width)
total_width = text_widget_width+left_padding+right_padding;
//Prevent the menu from being too skinny
if(use_menu && preview_panes_.empty() &&
total_width > menu_->width() + image_width + padding_width) {
menu_->set_width(total_width - image_width - padding_width);
}
const size_t text_and_image_height = image_height > total_text_height ? image_height : total_text_height;
const int total_height = text_and_image_height +
padding_height + menu_->height() +
text_widget_height + check_button_height;
int frame_width = maximum<int>(total_width,above_left_preview_pane_width + above_right_preview_pane_width);
int frame_height = maximum<int>(total_height,int(preview_pane_height));
int xframe = maximum<int>(0,xloc >= 0 ? xloc : scr->w/2 - (frame_width + left_preview_pane_width + right_preview_pane_width)/2);
int yframe = maximum<int>(0,yloc >= 0 ? yloc : scr->h/2 - (frame_height + above_preview_pane_height)/2);
LOG_DP << "above_preview_pane_height: " << above_preview_pane_height << "; "
<< "yframe: " << scr->h/2 << " - " << (frame_height + above_preview_pane_height)/2 << " = "
<< yframe << "; " << "frame_height: " << frame_height << "\n";
if(xloc <= -1 || yloc <= -1) {
xloc = xframe + left_preview_pane_width;
yloc = yframe + above_preview_pane_height;
}
if(xloc + frame_width > scr->w) {
xloc = scr->w - frame_width;
if(xloc < xframe) {
xframe = xloc;
}
}
if(yloc + frame_height > scr->h) {
yloc = scr->h - frame_height;
if(yloc < yframe) {
yframe = yloc;
}
}
frame_width += left_preview_pane_width + right_preview_pane_width;
frame_height += above_preview_pane_height;
frame_rect_.x = xframe;
frame_rect_.y = yframe;
frame_rect_.w = frame_width;
frame_rect_.h = frame_height;
//calculate the positions of the preview panes to the sides of the dialog
if(!preview_panes_.empty()) {
int left_preview_pane = xframe;
int right_preview_pane = xframe + total_width + left_preview_pane_width;
int above_left_preview_pane = xframe + frame_width/2;
int above_right_preview_pane = above_left_preview_pane;
for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
SDL_Rect area = (**i).location();
if((**i).show_above() == false) {
area.y = yloc;
if((**i).left_side()) {
area.x = left_preview_pane;
left_preview_pane += area.w;
} else {
area.x = right_preview_pane;
right_preview_pane += area.w;
}
} else {
area.y = yframe;
if((**i).left_side()) {
area.x = above_left_preview_pane - area.w;
above_left_preview_pane -= area.w;
} else {
area.x = above_right_preview_pane;
above_right_preview_pane += area.w;
}
}
(**i).set_location(area);
}
}
const int text_widget_y = yloc+top_padding+text_and_image_height-6+menu_hpadding;
if(use_textbox) {
const int text_widget_y_unpadded = text_widget_y + (text_widget_height - text_widget_->location().h)/2;
text_widget_->set_location(xloc + left_padding +
text_widget_width - text_widget_->location().w,
text_widget_y_unpadded);
if(text_widget_->label() != NULL) {
text_widget_->label()->set_location(xloc+left_padding, text_widget_y_unpadded);
}
}
const int menu_xpos = xloc+image_width+left_padding+image_h_padding;
const int menu_ypos = yloc+top_padding+text_and_image_height+menu_hpadding+ (use_textbox ? text_widget_->location().h + top_padding : 0);
if(use_menu) {
menu_->set_location(menu_xpos,menu_ypos);
}
message_rect_.x = xloc + left_padding;
message_rect_.y = yloc + top_padding + caption_height;
if(image_ != NULL) {
const int x = xloc + left_padding;
const int y = yloc + top_padding;
message_rect_.x += image_width + image_h_padding;
image_->set_location(x,y);
if(image_->caption() != NULL) {
image_->caption()->set_location(xloc+image_width+left_padding+image_h_padding, yloc+top_padding);
}
}
//set the position of any tick boxes. by default, they go right below the menu,
//slammed against the right side of the dialog
if(extra_buttons_.empty() == false) {
int options_y = text_widget_y + text_widget_height + menu_->height() + button_height_padding + menu_hpadding;
int options_left_y = options_y;
for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
switch(b->second)
{
case BUTTON_EXTRA:
case BUTTON_CHECKBOX:
b->first->set_location(xloc + total_width - b->first->width() - ButtonHPadding, options_y);
options_y += b->first->height() + button_height_padding;
break;
case BUTTON_CHECKBOX_LEFT:
b->first->set_location(xloc + ButtonHPadding, options_left_y);
options_left_y += b->first->height() + button_height_padding;
break;
case BUTTON_STANDARD:
default:
break;
}
}
}
/* if(options != NULL && i < options->size()) {
check_buttons[i].set_check((*options)[i].checked);
*/
}
int dialog::process(dialog_process_info &info)
{
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
const bool new_right_button = (mouse_flags&SDL_BUTTON_RMASK) != 0;
const bool new_left_button = (mouse_flags&SDL_BUTTON_LMASK) != 0;
const bool new_key_down = info.key[SDLK_SPACE] || info.key[SDLK_RETURN] ||
info.key[SDLK_ESCAPE];
const bool use_menu = (menu()->height() > 0);
const bool use_textbox = (text_widget_ != NULL);
if((!info.key_down && info.key[SDLK_RETURN] || menu_->double_clicked()) &&
(type_ == YES_NO || type_ == OK_CANCEL || type_ == OK_ONLY || type_ == CLOSE_ONLY)) {
/* if(text_widget_out_ != NULL && use_textbox)
*text_widget_out_ = text_widget_->text();*/
return (use_menu ? menu_->selection() : 0);
}
if(!info.key_down && info.key[SDLK_ESCAPE] && type_ == MESSAGE) {
return (ESCAPE_DIALOG);
}
//escape quits from the dialog -- unless it's an "ok" dialog with a menu,
//since such dialogs require a selection of some kind.
if(!info.key_down && info.key[SDLK_ESCAPE] && (type_ != OK_ONLY || !use_menu)) {
return ((use_menu) ? CLOSE_DIALOG : 1);
}
if((menu_->selection() != info.selection) || info.first_time) {
info.selection = menu_->selection();
int selection = info.selection;
if(selection < 0) {
selection = 0;
}
if(!preview_panes_.empty()) {
for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
(**i).set_selection(selection);
if(info.first_time) {
(**i).set_dirty();
}
}
}
}
info.first_time = false;
if(use_menu) {
const int selection = menu_->process();
if(selection != -1)
{
return (selection);
}
}
events::raise_process_event();
events::raise_draw_event();
// const SDL_Rect menu_rect = {menu_xpos,menu_ypos,menu_->width(),menu_->height()};
const SDL_Rect menu_rect = menu_->location();
if(
(
standard_buttons_.empty() &&
(
(
new_left_button && !info.left_button &&
!point_in_rect(mousex,mousey,menu_rect)
) || (
new_right_button && !info.right_button
)
)
) || (
standard_buttons_.size() < 2 && new_key_down && !info.key_down && !use_menu
)
)
{
return (CLOSE_DIALOG);
}
info.left_button = new_left_button;
info.right_button = new_right_button;
info.key_down = new_key_down;
for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
if(b->first->pressed()) {
return b->first->action(info);
}
}
if(help_button_.pressed()) {
return help_button_.action(info);
}
return CONTINUE_DIALOG;
}
int dialog_button::action(dialog_process_info &info) {
if(handler_ != NULL) {
menu &menu = *(parent_->menu());
dialog_button_action::RESULT res = handler_->button_pressed(menu.selection());
if(res == DELETE_ITEM) {
info.first_time = true;
menu.erase_item(menu.selection());
if(menu.nitems() == 0) {
return CLOSE_DIALOG;
}
} else if(res == CLOSE_DIALOG) {
return CLOSE_DIALOG;
}
//reset button-tracking flags so that if the action displays a dialog, a button-press
//at the end of the dialog won't be mistaken for a button-press in this dialog.
//(We should eventually use a proper event-handling system instead of tracking
//flags to avoid problems like this altogether).
info.clear_buttons();
return CONTINUE_DIALOG;
}
return simple_result_;
}
int standard_dialog_button::action(dialog_process_info &info) {
//if the menu is not used, then return the index of the
//button pressed, otherwise return the index of the menu
//item selected if the last button is not pressed, and
//cancel (-1) otherwise
if(dialog()->menu()->height() <= 0) {
return simple_result_;
} else if((simple_result_ == 0 && is_last_) || !is_last_) {
return (dialog()->menu()->selection());
}
return CLOSE_DIALOG;
}
dialog::help_button::help_button(display& disp, const std::string &help_topic)
: dialog_button(disp.video(), _("Help")), disp_(disp), topic_(help_topic)
{}
int dialog::help_button::action(dialog_process_info &info) {
if(!topic_.empty()) {
help::show_help(disp_,topic_);
info.clear_buttons();
}
return CONTINUE_DIALOG;
}
void dialog_image::draw_contents()
{
video().blit_surface(location().x, location().y, surf_);
}
}//end namespace gui

238
src/construct_dialog.hpp Normal file
View File

@ -0,0 +1,238 @@
/*
Copyright (C) 2006 by Patrick Parker <patrick_x99@hotmail.com>
wesnoth widget Copyright (C) 2003-5 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef CONSTRUCT_DIALOG_H_INCLUDED
#define CONSTRUCT_DIALOG_H_INCLUDED
#include "show_dialog.hpp"
#include "widgets/label.hpp"
#include "widgets/textbox.hpp"
#include "widgets/button.hpp"
#include "widgets/menu.hpp"
#include "key.hpp"
#include "sdl_utils.hpp"
namespace {
static std::vector<std::string> empty_string_vector;
typedef surface sdlutil_surface;
}
namespace gui {
struct dialog_process_info
{
public:
dialog_process_info() : left_button(true), right_button(true), key_down(true), first_time(true), selection(-1)
{}
void clear_buttons() {
left_button = true;
right_button = true;
key_down = true;
}
CKey key;
bool left_button, right_button, key_down, first_time;
int selection;
};
class dialog_image : public widget {
public:
dialog_image(label *const caption, CVideo &video, surface img) : widget(video, false),
surf_(img), caption_(caption)
{
if(!img.null()) {
set_measurements(img->w, img->h);
}
}
~dialog_image() { delete caption_; }
sdlutil_surface surface() const { return surf_; }
label *caption() const { return caption_; }
void draw_contents();
private:
handler_vector handler_members() {
handler_vector h;
h.push_back(caption_);
return h;
}
sdlutil_surface surf_;
label *caption_;
};
class dialog_textbox : public textbox {
public:
dialog_textbox(label *const label, CVideo &video, int width, const std::string& text="", bool editable=true, size_t max_size = 256, double alpha = 0.4, double alpha_focus = 0.2)
: textbox(video, width, text, editable, max_size, alpha, alpha_focus, false),
label_(label)
{}
~dialog_textbox() { delete label_; }
gui::label *label() const { return label_; }
private:
handler_vector handler_members() {
handler_vector h;
h.push_back(label_);
return h;
}
gui::label *label_;
};
class dialog_button : public button {
public:
dialog_button(CVideo& video, const std::string& label, TYPE type=TYPE_PRESS,
int simple_result=CONTINUE_DIALOG, dialog_button_action *handler=NULL)
: button(video,label,type,"",DEFAULT_SPACE,false), parent_(NULL),
handler_(handler), simple_result_(simple_result)
{}
void set_parent(class dialog *parent) {
parent_ = parent;
}
bool is_option() const {
return (type_ == TYPE_CHECK);
}
virtual int action(dialog_process_info &info);
protected:
class dialog *dialog() const { return parent_; }
const int simple_result_;
private:
class dialog *parent_;
dialog_button_action *handler_;
};
class standard_dialog_button : public dialog_button {
public:
standard_dialog_button(CVideo& video, const std::string& label, const int index, const bool is_last)
: dialog_button(video,label,TYPE_PRESS,index), is_last_(is_last)
{}
int action(dialog_process_info &info);
private:
const bool is_last_;
};
class dialog {
public:
enum BUTTON_LOCATION { BUTTON_STANDARD, BUTTON_EXTRA, BUTTON_CHECKBOX, BUTTON_CHECKBOX_LEFT };
private:
typedef std::vector<preview_pane *>::iterator pp_iterator;
typedef std::vector<dialog_button *>::iterator button_iterator;
typedef std::vector< std::pair<dialog_button *, BUTTON_LOCATION> >::iterator button_pool_iterator;
public:
//Static members
static const std::string default_style;
static const std::string no_help;
static const int message_font_size;
static const int caption_font_size;
static const int max_menu_width;
static const size_t left_padding;
static const size_t right_padding;
static const size_t image_h_pad;
static const size_t top_padding;
static const size_t bottom_padding;
//Constructor & destructor
dialog(display &disp, const std::string& title="", const std::string& message="",
const DIALOG_TYPE type=MESSAGE, const std::string& dialog_style=default_style,
const std::string& help_topic=no_help);
~dialog();
//Adding components - the dialog will manage the memory of these widgets,
//therefore do not attempt to reference its widgets after destroying it
void set_image(dialog_image *const img) { delete image_; image_ = img; }
void set_menu(menu *const m) { if(menu_ != empty_menu) delete menu_; menu_ = m; }
void add_pane(preview_pane *const pp) { preview_panes_.push_back(pp); }
void set_textbox(dialog_textbox *const box) {
delete text_widget_;
text_widget_ = box;
}
void set_textbox(const std::string& text_widget_label="",
const std::string &text_widget_text="",
const int text_widget_max_chars = 256,
const unsigned int text_box_width = font::relative_size(350));
void add_button(dialog_button *const btn, BUTTON_LOCATION loc);
//Launching the dialog
//show - the return value of this method should be the same as result()
int show(int &xloc, int &yloc);
int show();
//Results
int result() const { return result_; }
menu *menu();
bool done() const { return (result_ != CONTINUE_DIALOG); }
const std::string textbox_text() const { return text_widget_->text();}
const bool option_checked(unsigned int option_index=0);
//Backwards compatibility
//set_action - deprecated, subclass dialog instead and override action()
void set_action(dialog_action *const da) {action_ = da;}
protected:
void set_result(const int result) { result_ = result; }
//action - invoked at the end of the dialog-processing loop
virtual void action() {
if(!done() && action_ != NULL) {
set_result(action_->do_action());
}
}
//refresh - forces the display to refresh
void refresh();
private:
//layout - prepare components for display, return the dialog frame
void layout(int &xloc, int &yloc);
void draw(surface_restorer &restorer);
//process - execute a single dialog processing loop and return the result
int process(dialog_process_info &info);
class help_button : public dialog_button {
public:
help_button(display& disp, const std::string &help_topic);
int action(dialog_process_info &info);
const std::string topic() const { return topic_; }
private:
display &disp_;
const std::string topic_;
};
//Members
display &disp_;
dialog_image *image_;
const std::string title_, style_;
std::string message_;
const DIALOG_TYPE type_;
gui::menu *menu_;
std::vector<preview_pane*> preview_panes_;
std::vector< std::pair<dialog_button*,BUTTON_LOCATION> > button_pool_;
std::vector<dialog_button*> standard_buttons_;
std::vector<dialog_button*> extra_buttons_;
help_button help_button_;
dialog_textbox *text_widget_;
dialog_action *action_;
int result_;
SDL_Rect message_rect_;
SDL_Rect frame_rect_;
int x_, y_;
};
} //end namespace gui
#endif

View File

@ -648,10 +648,11 @@ unit_preview_pane::unit_preview_pane(display& disp, const gamemap* map, std::vec
set_measurements(font::relative_size(200), font::relative_size(370));
}
void unit_preview_pane::join()
handler_vector unit_preview_pane::handler_members()
{
widget::join();
details_button_.join();
handler_vector h;
h.push_back(&details_button_);
return h;
}
bool unit_preview_pane::show_above() const

View File

@ -19,6 +19,7 @@ class display;
#include "map.hpp"
#include "show_dialog.hpp"
#include "unit.hpp"
#include "events.hpp"
#include "widgets/button.hpp"
@ -71,10 +72,10 @@ public:
bool show_above() const;
bool left_side() const;
void set_selection(int index);
void join();
private:
display& disp_;
handler_vector handler_members();
void draw_contents();
void process_event();

View File

@ -156,11 +156,12 @@ event_context::~event_context()
event_contexts.pop_back();
}
handler::handler(bool auto_join) : unicode_(SDL_EnableUNICODE(1)), has_joined_(false)
handler::handler(const bool auto_join) : unicode_(SDL_EnableUNICODE(1)), has_joined_(false)
{
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
if(auto_join) {
join();
event_contexts.back().add_handler(this);
has_joined_ = true;
}
}
@ -175,19 +176,36 @@ void handler::join()
if(has_joined_) {
leave(); // should not be in multiple event contexts
}
//instruct members to join
handler_vector members = handler_members();
if(!members.empty()) {
for(handler_vector::iterator i = members.begin(); i != members.end(); i++) {
(*i)->join();
}
}
//join self
event_contexts.back().add_handler(this);
has_joined_ = true;
}
void handler::leave()
{
wassert(event_contexts.empty() == false);
handler_vector members = handler_members();
if(!members.empty()) {
for(handler_vector::iterator i = members.begin(); i != members.end(); i++) {
(*i)->leave();
}
} else {
wassert(event_contexts.empty() == false);
}
for(std::deque<context>::reverse_iterator i = event_contexts.rbegin(); i != event_contexts.rend(); ++i) {
if(i->remove_handler(this)) {
break;
}
}
//has_joined_ = false;
has_joined_ = false;
}
void focus_handler(const handler* ptr)

View File

@ -15,6 +15,7 @@
#define EVENTS_HPP_INCLUDED
#include "SDL.h"
#include <vector>
//our user-defined double-click event type
#define DOUBLE_CLICK_EVENT SDL_USEREVENT
@ -50,15 +51,15 @@ public:
virtual void process_help_string(int /*mousex*/, int /*mousey*/) {}
virtual void join(); /*joins the current event context*/
protected:
handler(bool auto_join=true);
virtual ~handler();
private:
void join(); /*joins the current event context*/
void leave(); /*leave the event context*/
protected:
handler(const bool auto_join=true);
virtual ~handler();
virtual std::vector<handler*> handler_members() {std::vector<handler*> h; return h;}
private:
int unicode_;
bool has_joined_;
};
@ -93,4 +94,6 @@ void raise_volatile_undraw_event();
void raise_help_string_event(int mousex, int mousey);
}
typedef std::vector<events::handler*> handler_vector;
#endif

View File

@ -144,12 +144,13 @@ std::string wait::leader_preview_pane::get_selected_leader()
return leaders_.get_leader();
}
void wait::leader_preview_pane::join()
{
widget::join();
leader_combo_.join();
handler_vector wait::leader_preview_pane::handler_members() {
handler_vector h;
h.push_back(&leader_combo_);
return h;
}
wait::wait(display& disp, const config& cfg, const game_data& data, mp::chat& c, config& gamelist) :
ui(disp, _("Game Lobby"), cfg, c, gamelist),

View File

@ -57,8 +57,8 @@ private:
void set_selection(int index);
std::string get_selected_leader();
void join();
private:
handler_vector handler_members();
virtual void draw_contents();
virtual void process_event();

View File

@ -181,10 +181,9 @@ public:
TYPE type;
};
void join();
private:
handler_vector handler_members();
void process_event();
bool left_side() const { return false; }
void set_selection(int index);
@ -329,43 +328,41 @@ preferences_dialog::preferences_dialog(display& disp, const config& game_cfg)
set_advanced_menu();
}
void preferences_dialog::join()
handler_vector preferences_dialog::handler_members()
{
//join the current event_context
widget::join();
//instruct all member widgets to join the current event_context
music_slider_.join();
sound_slider_.join();
scroll_slider_.join();
gamma_slider_.join();
chat_lines_slider_.join();
fullscreen_button_.join();
turbo_button_.join();
show_ai_moves_button_.join();
show_grid_button_.join();
show_lobby_joins_button_.join();
show_floating_labels_button_.join();
turn_dialog_button_.join();
turn_bell_button_.join();
show_team_colours_button_.join();
show_colour_cursors_button_.join();
show_haloing_button_.join();
video_mode_button_.join();
theme_button_.join();
hotkeys_button_.join();
gamma_button_.join();
flip_time_button_.join();
advanced_button_.join();
sound_button_.join();
music_button_.join();
chat_timestamp_button_.join();
music_label_.join();
sound_label_.join();
scroll_label_.join();
gamma_label_.join();
chat_lines_label_.join();
advanced_.join();
handler_vector h;
h.push_back(&music_slider_);
h.push_back(&sound_slider_);
h.push_back(&scroll_slider_);
h.push_back(&gamma_slider_);
h.push_back(&chat_lines_slider_);
h.push_back(&fullscreen_button_);
h.push_back(&turbo_button_);
h.push_back(&show_ai_moves_button_);
h.push_back(&show_grid_button_);
h.push_back(&show_lobby_joins_button_);
h.push_back(&show_floating_labels_button_);
h.push_back(&turn_dialog_button_);
h.push_back(&turn_bell_button_);
h.push_back(&show_team_colours_button_);
h.push_back(&show_colour_cursors_button_);
h.push_back(&show_haloing_button_);
h.push_back(&video_mode_button_);
h.push_back(&theme_button_);
h.push_back(&hotkeys_button_);
h.push_back(&gamma_button_);
h.push_back(&flip_time_button_);
h.push_back(&advanced_button_);
h.push_back(&sound_button_);
h.push_back(&music_button_);
h.push_back(&chat_timestamp_button_);
h.push_back(&music_label_);
h.push_back(&sound_label_);
h.push_back(&scroll_label_);
h.push_back(&gamma_label_);
h.push_back(&chat_lines_label_);
h.push_back(&advanced_);
return h;
}
void preferences_dialog::update_location(SDL_Rect const &rect)

View File

@ -23,6 +23,8 @@
#include "wml_separators.hpp"
#include "serialization/string_utils.hpp"
#define ERR_DP LOG_STREAM(err, display)
namespace gui {
const int font_size = font::SIZE_SMALL;
@ -31,8 +33,8 @@ const int checkbox_horizontal_padding = font::SIZE_SMALL / 2;
const int vertical_padding = font::SIZE_SMALL / 2;
button::button(CVideo& video, const std::string& label, button::TYPE type,
std::string button_image_name, SPACE_CONSUMPTION spacing)
: widget(video), label_(label),
std::string button_image_name, SPACE_CONSUMPTION spacing, const bool auto_join)
: widget(video, auto_join), label_(label),
image_(NULL), pressedImage_(NULL), activeImage_(NULL), pressedActiveImage_(NULL),
button_(true), state_(NORMAL), type_(type), pressed_(false),
spacing_(spacing), base_height_(0), base_width_(0)
@ -62,8 +64,10 @@ button::button(CVideo& video, const std::string& label, button::TYPE type,
pressed_active_image.assign(pressed_image);
}
if (button_image.null())
if (button_image.null()) {
ERR_DP << "error initializing button!\n";
throw error();
}
base_height_ = button_image->h;
base_width_ = button_image->w;

View File

@ -35,7 +35,8 @@ public:
enum SPACE_CONSUMPTION { DEFAULT_SPACE, MINIMUM_SPACE };
button(CVideo& video, const std::string& label, TYPE type=TYPE_PRESS,
std::string button_image="", SPACE_CONSUMPTION spacing=DEFAULT_SPACE);
std::string button_image="", SPACE_CONSUMPTION spacing=DEFAULT_SPACE,
const bool auto_join=true);
virtual ~button() {}
void set_check(bool check);
@ -54,6 +55,8 @@ protected:
virtual void mouse_up(const SDL_MouseButtonEvent& event);
virtual void draw_contents();
TYPE type_;
private:
void calculate_size();
@ -67,8 +70,6 @@ private:
enum STATE { UNINIT, NORMAL, ACTIVE, PRESSED, PRESSED_ACTIVE };
STATE state_;
TYPE type_;
bool pressed_;
SPACE_CONSUMPTION spacing_;

View File

@ -19,7 +19,7 @@
namespace gui {
label::label(CVideo& video, const std::string& text, int size, const SDL_Color& colour) : widget(video), text_(text), size_(size), colour_(colour)
label::label(CVideo& video, const std::string& text, int size, const SDL_Color& colour, const bool auto_join) : widget(video, auto_join), text_(text), size_(size), colour_(colour)
{
update_label_size();
}

View File

@ -24,7 +24,7 @@ class label : public widget
{
public:
label(CVideo& video, const std::string& text, int size=font::SIZE_NORMAL,
const SDL_Color& colour=font::NORMAL_COLOUR);
const SDL_Color& colour=font::NORMAL_COLOUR, const bool auto_join=true);
const std::string& set_text(const std::string& text);
const std::string& get_text() const;

View File

@ -29,6 +29,9 @@
namespace gui {
class menu;
extern menu *empty_menu;
class menu : public scrollarea
{
public:
@ -90,7 +93,7 @@ public:
friend class style;
friend class imgsel_style;
static style &default_style;
//static style bluebox_style;
static style simple_style;
static imgsel_style slateborder_style;
static imgsel_style bluebg_style;

View File

@ -42,6 +42,8 @@ menu::imgsel_style menu::bluebg_style("misc/selection2", true,
0x000000, 0x000000, 0x333333,
0.35, 0.0, 0.3);
menu::style &menu::default_style = menu::bluebg_style;
menu::style menu::simple_style;
menu *empty_menu = NULL;
//constructors
menu::style::style() : font_size_(font::SIZE_NORMAL),

View File

@ -20,8 +20,8 @@
namespace gui {
scrollarea::scrollarea(CVideo &video)
: widget(video), scrollbar_(video),
scrollarea::scrollarea(CVideo &video, const bool auto_join)
: widget(video, auto_join), scrollbar_(video),
old_position_(0), recursive_(false), shown_scrollbar_(false),
shown_size_(0), full_size_(0)
{

View File

@ -27,7 +27,7 @@ public:
/// Create a zone with automatic handling of scrollbar.
/// \param d the display object
/// \param pane the widget where wheel events take place
scrollarea(CVideo &video);
scrollarea(CVideo &video, bool auto_join=true);
virtual void hide(bool value = true);

View File

@ -33,8 +33,8 @@ namespace gui {
const int font_size = font::SIZE_PLUS;
textbox::textbox(CVideo &video, int width, const std::string& text, bool editable, size_t max_size, double alpha, double alpha_focus)
: scrollarea(video), max_size_(max_size), text_(utils::string_to_wstring(text)),
textbox::textbox(CVideo &video, int width, const std::string& text, bool editable, size_t max_size, double alpha, double alpha_focus, const bool auto_join)
: scrollarea(video, auto_join), max_size_(max_size), text_(utils::string_to_wstring(text)),
cursor_(text_.size()), selstart_(-1), selend_(-1),
grabmouse_(false), text_pos_(0), editable_(editable),
show_cursor_(true), show_cursor_at_(0), text_image_(NULL),

View File

@ -26,7 +26,7 @@ namespace gui {
class textbox : public scrollarea
{
public:
textbox(CVideo &video, int width, const std::string& text="", bool editable=true, size_t max_size = 256, double alpha = 0.4, double alpha_focus = 0.2);
textbox(CVideo &video, int width, const std::string& text="", bool editable=true, size_t max_size = 256, double alpha = 0.4, double alpha_focus = 0.2, const bool auto_join = true);
const std::string text() const;
void set_text(const std::string& text);

View File

@ -30,7 +30,7 @@ widget::widget(const widget &o)
{
}
widget::widget(CVideo& video, bool auto_join)
widget::widget(CVideo& video, const bool auto_join)
: handler(auto_join), video_(&video), rect_(EmptyRect), focus_(true), needs_restore_(false),
state_(UNINIT), enabled_(true), clip_(false), volatile_(false), help_string_(0),
align_(RIGHT_ALIGN)

View File

@ -72,7 +72,7 @@ public:
protected:
widget(widget const &o);
widget(CVideo& video, bool auto_join=true);
widget(CVideo& video, const bool auto_join=true);
virtual ~widget();
// During each relocation, this function should be called to register