Removed a whole bunch of GUI1 stuff
This almost completely removes GUI1, save for the button widget (will remove once I get all the Theme handling sorted out) and the editor's own GUI1 widgets. This includes the GUI1 textbox, scrollbar, scrollarea, and menu widgets, as well as the dialog_frame and dialog_manager classes. I've also removed floating_textbox. It was only kept around for reference (it's unused as of the inclusion of the GUI2 command console). gui::in_dialog has been replaced by GUI2's is_in_dialog directly now that we have no more GUI1 dialogs. \o/
This commit is contained in:
parent
e8d23ccf33
commit
88d8b9c933
23 changed files with 14 additions and 5276 deletions
|
@ -41,7 +41,6 @@ pathfind/astarsearch.cpp
|
|||
pathutils.cpp
|
||||
quit_confirmation.cpp
|
||||
reports.cpp
|
||||
show_dialog.cpp
|
||||
sound.cpp
|
||||
sound_music_track.cpp
|
||||
soundsource.cpp
|
||||
|
@ -54,10 +53,5 @@ tooltips.cpp
|
|||
utils/make_enum.cpp
|
||||
video.cpp
|
||||
widgets/button.cpp
|
||||
widgets/menu.cpp
|
||||
widgets/menu_style.cpp
|
||||
widgets/scrollarea.cpp
|
||||
widgets/scrollbar.cpp
|
||||
widgets/textbox.cpp
|
||||
widgets/widget.cpp
|
||||
wml_exception.cpp
|
||||
|
|
|
@ -103,7 +103,6 @@ editor/toolkit/editor_toolkit.cpp
|
|||
fake_unit_manager.cpp
|
||||
fake_unit_ptr.cpp
|
||||
filesystem_sdl.cpp
|
||||
floating_textbox.cpp
|
||||
formula/callable_objects.cpp
|
||||
formula/debugger.cpp
|
||||
formula/debugger_fwd.cpp
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "display.hpp"
|
||||
#include "events.hpp"
|
||||
#include "game_config_manager.hpp"
|
||||
#include "gui/core/event/handler.hpp" // gui2::is_in_dialog
|
||||
#include "gui/dialogs/loading_screen.hpp"
|
||||
#include "hotkey/command_executor.hpp"
|
||||
#include "hotkey/hotkey_command.hpp"
|
||||
#include "log.hpp"
|
||||
|
@ -25,8 +27,6 @@
|
|||
#include "mouse_handler_base.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "scripting/plugins/context.hpp"
|
||||
#include "show_dialog.hpp" //gui::in_dialog
|
||||
#include "gui/core/event/handler.hpp" // gui2::is_in_dialog
|
||||
#include "soundsource.hpp"
|
||||
static lg::log_domain log_display("display");
|
||||
#define ERR_DP LOG_STREAM(err, log_display)
|
||||
|
@ -50,7 +50,17 @@ controller_base::~controller_base()
|
|||
|
||||
void controller_base::handle_event(const SDL_Event& event)
|
||||
{
|
||||
if(gui::in_dialog()) {
|
||||
/* TODO: since GUI2 and the main game are now part of the same event context, there is some conflict
|
||||
* between the GUI2 and event handlers such as these. By design, the GUI2 sdl handler is always on top
|
||||
* of the handler queue, so its events are handled last. This means events here have a chance to fire
|
||||
* first. have_keyboard_focus currently returns false if a dialog open, but this is just as stopgap
|
||||
* measure. We need to figure out a better way to filter out events.
|
||||
*/
|
||||
//if(gui2::is_in_dialog()) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
if(gui2::dialogs::loading_screen::displaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -162,7 +172,7 @@ void controller_base::keyup_listener::handle_event(const SDL_Event& event)
|
|||
|
||||
bool controller_base::have_keyboard_focus()
|
||||
{
|
||||
return true;
|
||||
return !gui2::is_in_dialog();
|
||||
}
|
||||
|
||||
bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags, double x_axis, double y_axis)
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "units/animation_component.hpp"
|
||||
#include "units/drawer.hpp"
|
||||
#include "whiteboard/manager.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "gui/dialogs/loading_screen.hpp"
|
||||
|
||||
#include <SDL_image.h>
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "floating_textbox.hpp"
|
||||
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "floating_label.hpp"
|
||||
#include "font/standard_colors.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
static lg::log_domain log_display("display");
|
||||
#define ERR_DP LOG_STREAM(err, log_display)
|
||||
|
||||
namespace gui{
|
||||
floating_textbox::floating_textbox() :
|
||||
box_(nullptr),
|
||||
check_(nullptr),
|
||||
mode_(TEXTBOX_NONE),
|
||||
label_string_(),
|
||||
label_(0)
|
||||
{}
|
||||
|
||||
void floating_textbox::close(game_display& gui)
|
||||
{
|
||||
if(!active()) {
|
||||
return;
|
||||
}
|
||||
if(check_ != nullptr) {
|
||||
if(mode_ == TEXTBOX_MESSAGE) {
|
||||
preferences::set_message_private(check_->checked());
|
||||
}
|
||||
}
|
||||
box_.reset(nullptr);
|
||||
check_.reset(nullptr);
|
||||
font::remove_floating_label(label_);
|
||||
mode_ = TEXTBOX_NONE;
|
||||
gui.invalidate_all();
|
||||
}
|
||||
|
||||
void floating_textbox::update_location(game_display& gui)
|
||||
{
|
||||
if (box_ == nullptr)
|
||||
return;
|
||||
|
||||
const SDL_Rect& area = gui.map_outside_area();
|
||||
|
||||
const int border_size = 10;
|
||||
|
||||
const int ypos = area.y+area.h-30 - (check_ != nullptr ? check_->height() + border_size : 0);
|
||||
|
||||
if (label_ != 0)
|
||||
font::remove_floating_label(label_);
|
||||
|
||||
font::floating_label flabel(label_string_);
|
||||
flabel.set_color(font::YELLOW_COLOR);
|
||||
flabel.set_position(area.x + border_size, ypos);
|
||||
flabel.set_alignment(font::LEFT_ALIGN);
|
||||
flabel.set_clip_rect(area);
|
||||
|
||||
label_ = font::add_floating_label(flabel);
|
||||
|
||||
if (label_ == 0)
|
||||
return;
|
||||
|
||||
const SDL_Rect& label_area = font::get_floating_label_rect(label_);
|
||||
const int textbox_width = area.w - label_area.w - border_size*3;
|
||||
|
||||
if(textbox_width <= 0) {
|
||||
font::remove_floating_label(label_);
|
||||
return;
|
||||
}
|
||||
|
||||
if(box_ != nullptr) {
|
||||
box_->set_volatile(true);
|
||||
const SDL_Rect rect {
|
||||
area.x + label_area.w + border_size * 2
|
||||
, ypos
|
||||
, textbox_width
|
||||
, box_->height()
|
||||
};
|
||||
|
||||
box_->set_location(rect);
|
||||
}
|
||||
|
||||
if(check_ != nullptr) {
|
||||
check_->set_volatile(true);
|
||||
check_->set_location(box_->location().x,box_->location().y + box_->location().h + border_size);
|
||||
}
|
||||
}
|
||||
|
||||
void floating_textbox::show(gui::TEXTBOX_MODE mode, const std::string& label,
|
||||
const std::string& check_label, bool checked, game_display& gui)
|
||||
{
|
||||
close(gui);
|
||||
|
||||
label_string_ = label;
|
||||
mode_ = mode;
|
||||
|
||||
if(!check_label.empty()) {
|
||||
check_.reset(new gui::button(gui.video(),check_label,gui::button::TYPE_CHECK));
|
||||
check_->set_check(checked);
|
||||
}
|
||||
|
||||
|
||||
box_.reset(new gui::textbox(gui.video(),100,"",true,256,font::SIZE_PLUS,0.8,0.6));
|
||||
|
||||
update_location(gui);
|
||||
}
|
||||
|
||||
void floating_textbox::tab(const std::set<std::string>& dictionary)
|
||||
{
|
||||
if(active() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string text = box_->text();
|
||||
std::vector<std::string> matches(dictionary.begin(), dictionary.end());
|
||||
const bool line_start = utils::word_completion(text, matches);
|
||||
|
||||
if (matches.empty()) return;
|
||||
if (matches.size() == 1 && mode_ == gui::TEXTBOX_MESSAGE) {
|
||||
text.append(line_start ? ": " : " ");
|
||||
} else if (matches.size() > 1) {
|
||||
std::string completion_list = utils::join(matches, " ");
|
||||
game_display::get_singleton()->get_chat_manager().add_chat_message(time(nullptr), "", 0, completion_list,
|
||||
events::chat_handler::MESSAGE_PRIVATE, false);
|
||||
}
|
||||
box_->set_text(text);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Scoped_resource can't use a pointer to an incomplete pointer with MSVC.
|
||||
#include "widgets/textbox.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
class game_display;
|
||||
|
||||
namespace gui{
|
||||
|
||||
class button;
|
||||
|
||||
enum TEXTBOX_MODE { TEXTBOX_NONE, TEXTBOX_SEARCH, TEXTBOX_MESSAGE,
|
||||
TEXTBOX_COMMAND, TEXTBOX_AI };
|
||||
|
||||
class floating_textbox{
|
||||
public:
|
||||
floating_textbox();
|
||||
|
||||
TEXTBOX_MODE mode() const { return mode_; }
|
||||
const std::unique_ptr<gui::button>& check() const { return check_; }
|
||||
const std::unique_ptr<gui::textbox>& box() const { return box_; }
|
||||
|
||||
void close(game_display& gui);
|
||||
void update_location(game_display& gui);
|
||||
void show(gui::TEXTBOX_MODE mode, const std::string& label,
|
||||
const std::string& check_label, bool checked, game_display& gui);
|
||||
void tab(const std::set<std::string>& dictionary);
|
||||
bool active() const { return box_.get() != nullptr; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<gui::textbox> box_;
|
||||
std::unique_ptr<gui::button> check_;
|
||||
|
||||
TEXTBOX_MODE mode_;
|
||||
|
||||
std::string label_string_;
|
||||
int label_;
|
||||
};
|
||||
}
|
|
@ -33,7 +33,6 @@
|
|||
#include "key.hpp" // for CKey
|
||||
#include "log.hpp" // for LOG_STREAM, log_domain
|
||||
#include "sdl/surface.hpp" // for surface
|
||||
#include "show_dialog.hpp" // for dialog_frame, etc
|
||||
#include "terrain/terrain.hpp" // for terrain_type
|
||||
#include "units/unit.hpp" // for unit
|
||||
#include "units/types.hpp" // for unit_type, unit_type_data, etc
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "display.hpp"
|
||||
#include "quit_confirmation.hpp"
|
||||
#include "sdl/surface.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "../resources.hpp"
|
||||
#include "../playmp_controller.hpp"
|
||||
|
||||
|
|
|
@ -1,367 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "show_dialog.hpp"
|
||||
|
||||
#include "floating_label.hpp"
|
||||
#include "font/sdl_ttf.hpp"
|
||||
#include "picture.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "gui/core/event/handler.hpp"
|
||||
#include "help/help.hpp"
|
||||
#include "hotkey/command_executor.hpp"
|
||||
#include "log.hpp"
|
||||
#include "font/marked-up_text.hpp"
|
||||
#include "font/standard_colors.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
|
||||
static lg::log_domain log_display("display");
|
||||
#define ERR_DP LOG_STREAM(err, log_display)
|
||||
#define ERR_G LOG_STREAM(err, lg::general)
|
||||
|
||||
namespace {
|
||||
bool is_in_dialog = false;
|
||||
}
|
||||
|
||||
namespace gui {
|
||||
|
||||
//static initialization
|
||||
const int ButtonHPadding = 10;
|
||||
const int ButtonVPadding = 10;
|
||||
|
||||
//note: style names are directly related to the panel image file names
|
||||
const dialog_frame::style dialog_frame::default_style("opaque", 0);
|
||||
const dialog_frame::style dialog_frame::message_style("translucent65", 3);
|
||||
const dialog_frame::style dialog_frame::preview_style("../dialogs/selection", 0);
|
||||
const dialog_frame::style dialog_frame::titlescreen_style("translucent54", 1);
|
||||
|
||||
const int dialog_frame::title_border_w = 10;
|
||||
const int dialog_frame::title_border_h = 5;
|
||||
|
||||
|
||||
|
||||
bool in_dialog()
|
||||
{
|
||||
return is_in_dialog || gui2::is_in_dialog();
|
||||
}
|
||||
|
||||
dialog_manager::dialog_manager() : cursor::setter(cursor::NORMAL), reset_to(is_in_dialog)
|
||||
{
|
||||
is_in_dialog = true;
|
||||
}
|
||||
|
||||
dialog_manager::~dialog_manager()
|
||||
{
|
||||
is_in_dialog = reset_to;
|
||||
int mousex, mousey;
|
||||
SDL_GetMouseState(&mousex, &mousey);
|
||||
SDL_Event pb_event;
|
||||
pb_event.type = SDL_MOUSEMOTION;
|
||||
pb_event.motion.state = 0;
|
||||
pb_event.motion.x = mousex;
|
||||
pb_event.motion.y = mousey;
|
||||
pb_event.motion.xrel = 0;
|
||||
pb_event.motion.yrel = 0;
|
||||
SDL_PushEvent(&pb_event);
|
||||
}
|
||||
|
||||
dialog_frame::dialog_frame(CVideo& video, const std::string& title,
|
||||
const style& style, bool auto_restore,
|
||||
std::vector<button*>* buttons, button* help_button) :
|
||||
title_(title),
|
||||
video_(video),
|
||||
dialog_style_(style),
|
||||
buttons_(buttons),
|
||||
help_button_(help_button),
|
||||
restorer_(nullptr),
|
||||
auto_restore_(auto_restore),
|
||||
dim_(),
|
||||
top_(image::get_image("dialogs/" + dialog_style_.panel + "-border-top.png")),
|
||||
bot_(image::get_image("dialogs/" + dialog_style_.panel + "-border-bottom.png")),
|
||||
left_(image::get_image("dialogs/" + dialog_style_.panel + "-border-left.png")),
|
||||
right_(image::get_image("dialogs/" + dialog_style_.panel + "-border-right.png")),
|
||||
top_left_(image::get_image("dialogs/" + dialog_style_.panel + "-border-topleft.png")),
|
||||
bot_left_(image::get_image("dialogs/" + dialog_style_.panel + "-border-botleft.png")),
|
||||
top_right_(image::get_image("dialogs/" + dialog_style_.panel + "-border-topright.png")),
|
||||
bot_right_(image::get_image("dialogs/" + dialog_style_.panel + "-border-botright.png")),
|
||||
bg_(image::get_image("dialogs/" + dialog_style_.panel + "-background.png")),
|
||||
have_border_(top_ != nullptr && bot_ != nullptr && left_ != nullptr && right_ != nullptr),
|
||||
dirty_(true)
|
||||
{
|
||||
}
|
||||
|
||||
dialog_frame::~dialog_frame()
|
||||
{
|
||||
delete restorer_;
|
||||
}
|
||||
|
||||
dialog_frame::dimension_measurements::dimension_measurements() :
|
||||
interior(sdl::empty_rect), exterior(sdl::empty_rect), title(sdl::empty_rect), button_row(sdl::empty_rect)
|
||||
{}
|
||||
|
||||
dialog_frame::dimension_measurements dialog_frame::layout(const SDL_Rect& rect) {
|
||||
return layout(rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
|
||||
int dialog_frame::top_padding() const {
|
||||
int padding = 0;
|
||||
if(have_border_) {
|
||||
padding += top_->h;
|
||||
}
|
||||
if(!title_.empty()) {
|
||||
padding += font::get_max_height(font::SIZE_TITLE) + 2*dialog_frame::title_border_h;
|
||||
}
|
||||
return padding;
|
||||
}
|
||||
|
||||
void dialog_frame::set_dirty(bool dirty) {
|
||||
dirty_ = dirty;
|
||||
}
|
||||
|
||||
void dialog_frame::handle_window_event(const SDL_Event& event) {
|
||||
|
||||
if (event.type == SDL_WINDOWEVENT) {
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dialog_frame::handle_event(const SDL_Event& event) {
|
||||
|
||||
if (event.type == DRAW_ALL_EVENT) {
|
||||
set_dirty();
|
||||
|
||||
if (buttons_) {
|
||||
for(std::vector<button *>::iterator it = buttons_->begin(); it != buttons_->end(); ++it) {
|
||||
(*it)->set_dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == DRAW_EVENT || event.type == DRAW_ALL_EVENT) {
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
int dialog_frame::bottom_padding() const {
|
||||
int padding = 0;
|
||||
if(buttons_ != nullptr) {
|
||||
for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
|
||||
padding = std::max<int>((**b).height() + ButtonVPadding, padding);
|
||||
}
|
||||
}
|
||||
if(have_border_) {
|
||||
padding += bot_->h;
|
||||
}
|
||||
return padding;
|
||||
}
|
||||
|
||||
dialog_frame::dimension_measurements dialog_frame::layout(int x, int y, int w, int h) {
|
||||
dim_ = dimension_measurements();
|
||||
if(!title_.empty()) {
|
||||
dim_.title = draw_title(nullptr);
|
||||
dim_.title.w += title_border_w;
|
||||
}
|
||||
if(buttons_ != nullptr) {
|
||||
for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
|
||||
dim_.button_row.w += (**b).width() + ButtonHPadding;
|
||||
dim_.button_row.h = std::max<int>((**b).height() + ButtonVPadding,dim_.button_row.h);
|
||||
}
|
||||
|
||||
dim_.button_row.x = -dim_.button_row.w;
|
||||
dim_.button_row.y = y + h;
|
||||
|
||||
dim_.button_row.w += ButtonHPadding;
|
||||
}
|
||||
|
||||
std::size_t buttons_width = dim_.button_row.w;
|
||||
|
||||
if(help_button_ != nullptr) {
|
||||
buttons_width += help_button_->width() + ButtonHPadding*2;
|
||||
dim_.button_row.y = y + h;
|
||||
}
|
||||
|
||||
y -= dim_.title.h;
|
||||
w = std::max(w, std::max(dim_.title.w, static_cast<int>(buttons_width)));
|
||||
h += dim_.title.h + dim_.button_row.h;
|
||||
dim_.button_row.x += x + w;
|
||||
|
||||
SDL_Rect bounds = video_.screen_area();
|
||||
if(have_border_) {
|
||||
bounds.x += left_->w;
|
||||
bounds.y += top_->h;
|
||||
bounds.w -= left_->w;
|
||||
bounds.h -= top_->h;
|
||||
}
|
||||
if(x < bounds.x) {
|
||||
w += x;
|
||||
x = bounds.x;
|
||||
}
|
||||
if(y < bounds.y) {
|
||||
h += y;
|
||||
y = bounds.y;
|
||||
}
|
||||
if(x > bounds.w) {
|
||||
w = 0;
|
||||
} else if(x + w > bounds.w) {
|
||||
w = bounds.w - x;
|
||||
}
|
||||
if(y > bounds.h) {
|
||||
h = 0;
|
||||
} else if(y + h > bounds.h) {
|
||||
h = bounds.h - y;
|
||||
}
|
||||
dim_.interior.x = x;
|
||||
dim_.interior.y = y;
|
||||
dim_.interior.w = w;
|
||||
dim_.interior.h = h;
|
||||
if(have_border_) {
|
||||
dim_.exterior.x = dim_.interior.x - left_->w;
|
||||
dim_.exterior.y = dim_.interior.y - top_->h;
|
||||
dim_.exterior.w = dim_.interior.w + left_->w + right_->w;
|
||||
dim_.exterior.h = dim_.interior.h + top_->h + bot_->h;
|
||||
} else {
|
||||
dim_.exterior = dim_.interior;
|
||||
}
|
||||
dim_.title.x = dim_.interior.x + title_border_w;
|
||||
dim_.title.y = dim_.interior.y + title_border_h;
|
||||
return dim_;
|
||||
}
|
||||
|
||||
void dialog_frame::draw_border()
|
||||
{
|
||||
if(have_border_ == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
surface top_image(scale_surface(top_, dim_.interior.w, top_->h));
|
||||
|
||||
if(top_image != nullptr) {
|
||||
video_.blit_surface(dim_.interior.x, dim_.exterior.y, top_image);
|
||||
}
|
||||
|
||||
surface bot_image(scale_surface(bot_, dim_.interior.w, bot_->h));
|
||||
|
||||
if(bot_image != nullptr) {
|
||||
video_.blit_surface(dim_.interior.x, dim_.interior.y + dim_.interior.h, bot_image);
|
||||
}
|
||||
|
||||
surface left_image(scale_surface(left_, left_->w, dim_.interior.h));
|
||||
|
||||
if(left_image != nullptr) {
|
||||
video_.blit_surface(dim_.exterior.x, dim_.interior.y, left_image);
|
||||
}
|
||||
|
||||
surface right_image(scale_surface(right_, right_->w, dim_.interior.h));
|
||||
|
||||
if(right_image != nullptr) {
|
||||
video_.blit_surface(dim_.interior.x + dim_.interior.w, dim_.interior.y, right_image);
|
||||
}
|
||||
|
||||
if(top_left_ == nullptr || bot_left_ == nullptr || top_right_ == nullptr || bot_right_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
video_.blit_surface(dim_.interior.x - left_->w, dim_.interior.y - top_->h, top_left_);
|
||||
video_.blit_surface(dim_.interior.x - left_->w, dim_.interior.y + dim_.interior.h + bot_->h - bot_left_->h, bot_left_);
|
||||
video_.blit_surface(dim_.interior.x + dim_.interior.w + right_->w - top_right_->w, dim_.interior.y - top_->h, top_right_);
|
||||
video_.blit_surface(dim_.interior.x + dim_.interior.w + right_->w - bot_right_->w, dim_.interior.y + dim_.interior.h + bot_->h - bot_right_->h, bot_right_);
|
||||
}
|
||||
|
||||
void dialog_frame::clear_background()
|
||||
{
|
||||
delete restorer_;
|
||||
restorer_ = nullptr;
|
||||
}
|
||||
|
||||
void dialog_frame::draw_background()
|
||||
{
|
||||
if(auto_restore_) {
|
||||
clear_background();
|
||||
restorer_ = new surface_restorer(&video_, dim_.exterior);
|
||||
}
|
||||
|
||||
if (dialog_style_.blur_radius) {
|
||||
surface surf = ::get_surface_portion(video_.getSurface(), dim_.exterior);
|
||||
surf = blur_surface(surf, dialog_style_.blur_radius);
|
||||
sdl_blit(surf, nullptr, video_.getSurface(), &dim_.exterior);
|
||||
}
|
||||
|
||||
if(bg_ == nullptr) {
|
||||
ERR_DP << "could not find dialog background '" << dialog_style_.panel << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < dim_.interior.w; i += bg_->w) {
|
||||
for(int j = 0; j < dim_.interior.h; j += bg_->h) {
|
||||
SDL_Rect src {0,0,0,0};
|
||||
src.w = std::min(dim_.interior.w - i, bg_->w);
|
||||
src.h = std::min(dim_.interior.h - j, bg_->h);
|
||||
SDL_Rect dst = src;
|
||||
dst.x = dim_.interior.x + i;
|
||||
dst.y = dim_.interior.y + j;
|
||||
sdl_blit(bg_, &src, video_.getSurface(), &dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect dialog_frame::draw_title(CVideo* video)
|
||||
{
|
||||
SDL_Rect rect = CVideo::get_singleton().screen_area();
|
||||
return font::draw_text(video, rect, font::SIZE_TITLE, font::TITLE_COLOR,
|
||||
title_, dim_.title.x, dim_.title.y, false, TTF_STYLE_NORMAL);
|
||||
}
|
||||
|
||||
void dialog_frame::draw()
|
||||
{
|
||||
if (!dirty_)
|
||||
return;
|
||||
|
||||
//draw background
|
||||
draw_background();
|
||||
|
||||
//draw frame border
|
||||
draw_border();
|
||||
|
||||
//draw title
|
||||
if (!title_.empty()) {
|
||||
draw_title(&video_);
|
||||
}
|
||||
|
||||
//draw buttons
|
||||
SDL_Rect buttons_area = dim_.button_row;
|
||||
if(buttons_ != nullptr) {
|
||||
#ifdef OK_BUTTON_ON_RIGHT
|
||||
std::reverse(buttons_->begin(),buttons_->end());
|
||||
#endif
|
||||
for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
|
||||
(**b).set_location(buttons_area.x, buttons_area.y);
|
||||
buttons_area.x += (**b).width() + ButtonHPadding;
|
||||
}
|
||||
}
|
||||
|
||||
if(help_button_ != nullptr) {
|
||||
help_button_->set_location(dim_.interior.x+ButtonHPadding, buttons_area.y);
|
||||
}
|
||||
|
||||
dirty_ = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class surface;
|
||||
|
||||
#include "cursor.hpp"
|
||||
#include "floating_label.hpp"
|
||||
#include "tooltips.hpp"
|
||||
#include "video.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
extern const int ButtonHPadding;
|
||||
extern const int ButtonVPadding;
|
||||
enum DIALOG_RESULT {
|
||||
DIALOG_BACK=-7,
|
||||
DIALOG_FORWARD=-6,
|
||||
CREATE_ITEM =-5,
|
||||
DELETE_ITEM=-4,
|
||||
ESCAPE_DIALOG=-3, //special return used by WML event dialogs
|
||||
CONTINUE_DIALOG=-2,
|
||||
CLOSE_DIALOG=-1
|
||||
/* results (0..N) reserved for standard button indices */
|
||||
};
|
||||
|
||||
bool in_dialog();
|
||||
|
||||
struct dialog_manager : private cursor::setter, private font::floating_label_context {
|
||||
dialog_manager();
|
||||
~dialog_manager();
|
||||
|
||||
private:
|
||||
bool reset_to;
|
||||
};
|
||||
|
||||
class dialog_frame :public video2::draw_layering {
|
||||
public:
|
||||
struct dimension_measurements {
|
||||
dimension_measurements();
|
||||
SDL_Rect interior, exterior, title, button_row;
|
||||
};
|
||||
class style {
|
||||
public:
|
||||
style(const std::string& p, int br) : panel(p), blur_radius(br) {}
|
||||
std::string panel;
|
||||
int blur_radius;
|
||||
};
|
||||
|
||||
//Static members
|
||||
static const int title_border_w, title_border_h;
|
||||
static const style default_style;
|
||||
static const style message_style;
|
||||
static const style preview_style;
|
||||
static const style titlescreen_style;
|
||||
|
||||
dialog_frame(CVideo &video, const std::string& title="",
|
||||
const style& dialog_style=default_style,
|
||||
bool auto_restore=true, std::vector<button*>* buttons=nullptr,
|
||||
button* help_button=nullptr);
|
||||
~dialog_frame();
|
||||
|
||||
dimension_measurements layout(int x, int y, int w, int h);
|
||||
dimension_measurements layout(const SDL_Rect& frame_area);
|
||||
void set_layout(dimension_measurements &new_dim) { dim_ = new_dim; }
|
||||
dimension_measurements get_layout() const { return dim_; }
|
||||
|
||||
int top_padding() const;
|
||||
int bottom_padding() const;
|
||||
|
||||
void draw();
|
||||
|
||||
//called by draw
|
||||
void draw_border();
|
||||
void draw_background();
|
||||
|
||||
//also called by layout with null param
|
||||
SDL_Rect draw_title(CVideo *video);
|
||||
|
||||
void set_dirty(bool dirty = true);
|
||||
|
||||
virtual void handle_event(const SDL_Event&);
|
||||
void handle_window_event(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
void clear_background();
|
||||
|
||||
std::string title_;
|
||||
CVideo &video_;
|
||||
const style& dialog_style_;
|
||||
std::vector<button*>* buttons_;
|
||||
button* help_button_;
|
||||
surface_restorer* restorer_;
|
||||
bool auto_restore_;
|
||||
dimension_measurements dim_;
|
||||
surface top_, bot_, left_, right_, top_left_, bot_left_, top_right_, bot_right_, bg_;
|
||||
bool have_border_;
|
||||
bool dirty_;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,592 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/button.hpp"
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "font/sdl_ttf.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_errors.hpp"
|
||||
#include "picture.hpp"
|
||||
#include "log.hpp"
|
||||
#include "font/marked-up_text.hpp"
|
||||
#include "font/standard_colors.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "video.hpp"
|
||||
#include "wml_separators.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
static lg::log_domain log_display("display");
|
||||
#define ERR_DP LOG_STREAM(err, log_display)
|
||||
|
||||
namespace gui {
|
||||
|
||||
const int font_size = font::SIZE_NORMAL;
|
||||
const int horizontal_padding = font::SIZE_SMALL;
|
||||
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,
|
||||
const bool auto_join, std::string overlay_image)
|
||||
: widget(video, auto_join), type_(type),
|
||||
label_text_(label),
|
||||
image_(nullptr), pressedImage_(nullptr), activeImage_(nullptr), pressedActiveImage_(nullptr),
|
||||
disabledImage_(nullptr), pressedDisabledImage_(nullptr),
|
||||
overlayImage_(nullptr), overlayPressedImage_(nullptr), overlayActiveImage_(nullptr),
|
||||
state_(NORMAL), pressed_(false),
|
||||
spacing_(spacing), base_height_(0), base_width_(0),
|
||||
button_image_name_(), button_overlay_image_name_(overlay_image),
|
||||
button_image_path_suffix_()
|
||||
{
|
||||
if (button_image_name.empty()) {
|
||||
|
||||
switch (type_) {
|
||||
case TYPE_PRESS:
|
||||
button_image_name_ = "buttons/button_normal/button_H22";
|
||||
break;
|
||||
case TYPE_TURBO:
|
||||
button_image_name_ = "buttons/button_menu/menu_button_copper_H20";
|
||||
break;
|
||||
case TYPE_CHECK:
|
||||
button_image_name_ = "buttons/checkbox";
|
||||
break;
|
||||
case TYPE_RADIO:
|
||||
button_image_name_ = "buttons/radiobox";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
button_image_name_ = "buttons/" + button_image_name;
|
||||
}
|
||||
|
||||
load_images();
|
||||
}
|
||||
|
||||
void button::load_images() {
|
||||
|
||||
std::string size_postfix;
|
||||
|
||||
switch (location().h) {
|
||||
case 25:
|
||||
size_postfix = "_25";
|
||||
break;
|
||||
case 30:
|
||||
size_postfix = "_30";
|
||||
break;
|
||||
case 60:
|
||||
size_postfix = "_60";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
surface button_image(image::get_image(button_image_name_ + ".png" + button_image_path_suffix_));
|
||||
surface pressed_image(image::get_image(button_image_name_ + "-pressed.png"+ button_image_path_suffix_));
|
||||
surface active_image(image::get_image(button_image_name_ + "-active.png"+ button_image_path_suffix_));
|
||||
surface disabled_image;
|
||||
if (filesystem::file_exists(game_config::path + "/images/" + button_image_name_ + "-disabled.png"))
|
||||
disabled_image.assign((image::get_image(button_image_name_ + "-disabled.png"+ button_image_path_suffix_)));
|
||||
surface pressed_disabled_image, pressed_active_image, touched_image;
|
||||
|
||||
if (!button_overlay_image_name_.empty()) {
|
||||
|
||||
if (button_overlay_image_name_.length() > size_postfix.length() &&
|
||||
boost::algorithm::ends_with(button_overlay_image_name_, size_postfix)) {
|
||||
button_overlay_image_name_.resize(button_overlay_image_name_.length() - size_postfix.length());
|
||||
}
|
||||
|
||||
overlayImage_.assign(image::get_image(button_overlay_image_name_ + size_postfix + ".png"+ button_image_path_suffix_));
|
||||
overlayPressedImage_.assign(image::get_image(button_overlay_image_name_ + size_postfix + "-pressed.png"+ button_image_path_suffix_));
|
||||
|
||||
if (filesystem::file_exists(game_config::path + "/images/" + button_overlay_image_name_ + size_postfix + "-active.png"))
|
||||
overlayActiveImage_.assign(image::get_image(button_overlay_image_name_ + size_postfix + "-active.png"+ button_image_path_suffix_));
|
||||
|
||||
if (filesystem::file_exists(game_config::path + "/images/" + button_overlay_image_name_ + size_postfix + "-disabled.png"))
|
||||
overlayDisabledImage_.assign(image::get_image(button_overlay_image_name_ + size_postfix + "-disabled.png"+ button_image_path_suffix_));
|
||||
if (overlayDisabledImage_.null())
|
||||
overlayDisabledImage_ = image::get_image(button_overlay_image_name_ + size_postfix + ".png~GS()" + button_image_path_suffix_);
|
||||
|
||||
if (filesystem::file_exists(game_config::path + "/images/" + button_overlay_image_name_ + size_postfix + "-disabled-pressed.png"))
|
||||
overlayPressedDisabledImage_.assign(image::get_image(button_overlay_image_name_ + size_postfix + "-disabled-pressed.png"+ button_image_path_suffix_));
|
||||
if (overlayPressedDisabledImage_.null())
|
||||
overlayPressedDisabledImage_ = image::get_image(button_overlay_image_name_ + size_postfix + "-pressed.png~GS()"+ button_image_path_suffix_);
|
||||
} else {
|
||||
overlayImage_.assign(nullptr);
|
||||
}
|
||||
|
||||
if (disabled_image == nullptr) {
|
||||
disabled_image = image::get_image(button_image_name_ + ".png~GS()" + button_image_path_suffix_);
|
||||
}
|
||||
|
||||
if (pressed_image.null())
|
||||
pressed_image.assign(button_image);
|
||||
|
||||
if (active_image.null())
|
||||
active_image.assign(button_image);
|
||||
|
||||
if (type_ == TYPE_CHECK || type_ == TYPE_RADIO) {
|
||||
touched_image.assign(image::get_image(button_image_name_ + "-touched.png"+ button_image_path_suffix_));
|
||||
if (touched_image.null())
|
||||
touched_image.assign(pressed_image);
|
||||
|
||||
pressed_active_image.assign(image::get_image(button_image_name_ + "-active-pressed.png"+ button_image_path_suffix_));
|
||||
if (pressed_active_image.null())
|
||||
pressed_active_image.assign(pressed_image);
|
||||
|
||||
if (filesystem::file_exists(game_config::path + "/images/" + button_image_name_ + size_postfix + "-disabled-pressed.png"))
|
||||
pressed_disabled_image.assign(image::get_image(button_image_name_ + "-disabled-pressed.png"+ button_image_path_suffix_));
|
||||
if (pressed_disabled_image.null())
|
||||
pressed_disabled_image = image::get_image(button_image_name_ + "-pressed.png~GS()"+ button_image_path_suffix_);
|
||||
}
|
||||
|
||||
if (button_image.null()) {
|
||||
std::string err_msg = "error initializing button images! file name: ";
|
||||
err_msg += button_image_name_;
|
||||
err_msg += ".png";
|
||||
ERR_DP << err_msg << std::endl;
|
||||
throw game::error(err_msg);
|
||||
}
|
||||
|
||||
base_height_ = button_image->h;
|
||||
base_width_ = button_image->w;
|
||||
|
||||
if (type_ != TYPE_IMAGE) {
|
||||
set_label(label_text_);
|
||||
}
|
||||
|
||||
if(type_ == TYPE_PRESS || type_ == TYPE_TURBO) {
|
||||
image_.assign(scale_surface(button_image,location().w,location().h));
|
||||
pressedImage_.assign(scale_surface(pressed_image,location().w,location().h));
|
||||
activeImage_.assign(scale_surface(active_image,location().w,location().h));
|
||||
disabledImage_.assign(scale_surface(disabled_image,location().w,location().h));
|
||||
} else {
|
||||
image_.assign(scale_surface(button_image,button_image->w,button_image->h));
|
||||
activeImage_.assign(scale_surface(active_image,button_image->w,button_image->h));
|
||||
disabledImage_.assign(scale_surface(disabled_image,button_image->w,button_image->h));
|
||||
pressedImage_.assign(scale_surface(pressed_image,button_image->w,button_image->h));
|
||||
if (type_ == TYPE_CHECK || type_ == TYPE_RADIO) {
|
||||
pressedDisabledImage_.assign(scale_surface(pressed_disabled_image,button_image->w,button_image->h));
|
||||
pressedActiveImage_.assign(scale_surface(pressed_active_image, button_image->w, button_image->h));
|
||||
touchedImage_.assign(scale_surface(touched_image, button_image->w, button_image->h));
|
||||
}
|
||||
}
|
||||
|
||||
if (type_ == TYPE_IMAGE){
|
||||
calculate_size();
|
||||
}
|
||||
}
|
||||
|
||||
button::~button()
|
||||
{
|
||||
}
|
||||
|
||||
void button::calculate_size()
|
||||
{
|
||||
if (type_ == TYPE_IMAGE){
|
||||
SDL_Rect loc_image = location();
|
||||
loc_image.h = image_->h;
|
||||
loc_image.w = image_->w;
|
||||
set_location(loc_image);
|
||||
return;
|
||||
}
|
||||
const SDL_Rect& loc = location();
|
||||
bool change_size = loc.h == 0 || loc.w == 0;
|
||||
|
||||
if (!change_size) {
|
||||
unsigned w = loc.w - (type_ == TYPE_PRESS || type_ == TYPE_TURBO ? horizontal_padding : checkbox_horizontal_padding + base_width_);
|
||||
if (type_ != TYPE_IMAGE)
|
||||
{
|
||||
int fs = font_size;
|
||||
int style = TTF_STYLE_NORMAL;
|
||||
std::string::const_iterator i_beg = label_text_.begin(), i_end = label_text_.end(),
|
||||
i = font::parse_markup(i_beg, i_end, &fs, nullptr, &style);
|
||||
if (i != i_end) {
|
||||
std::string tmp(i, i_end);
|
||||
label_text_.erase(i - i_beg, i_end - i_beg);
|
||||
label_text_ += font::make_text_ellipsis(tmp, fs, w, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type_ != TYPE_IMAGE){
|
||||
textRect_ = font::draw_text(nullptr, video().screen_area(), font_size,
|
||||
font::BUTTON_COLOR, label_text_, 0, 0);
|
||||
}
|
||||
|
||||
// TODO: There's a weird text clipping bug, allowing the code below to run fixes it.
|
||||
// The proper fix should possibly be in the draw_contents() function.
|
||||
#if 0
|
||||
if (!change_size)
|
||||
return;
|
||||
#endif
|
||||
|
||||
set_height(std::max(textRect_.h+vertical_padding,base_height_));
|
||||
if(type_ == TYPE_PRESS || type_ == TYPE_TURBO) {
|
||||
if(spacing_ == MINIMUM_SPACE) {
|
||||
set_width(textRect_.w + horizontal_padding);
|
||||
} else {
|
||||
set_width(std::max(textRect_.w+horizontal_padding,base_width_));
|
||||
}
|
||||
} else {
|
||||
if(label_text_.empty()) {
|
||||
set_width(base_width_);
|
||||
} else {
|
||||
set_width(checkbox_horizontal_padding + textRect_.w + base_width_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void button::set_check(bool check)
|
||||
{
|
||||
if (type_ != TYPE_CHECK && type_ != TYPE_RADIO && type_ != TYPE_IMAGE)
|
||||
return;
|
||||
STATE new_state;
|
||||
|
||||
if (check) {
|
||||
new_state = (state_ == ACTIVE || state_ == PRESSED_ACTIVE)? PRESSED_ACTIVE : PRESSED;
|
||||
} else {
|
||||
new_state = (state_ == ACTIVE || state_ == PRESSED_ACTIVE)? ACTIVE : NORMAL;
|
||||
}
|
||||
|
||||
if (state_ != new_state) {
|
||||
state_ = new_state;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void button::set_active(bool active)
|
||||
{
|
||||
if ((state_ == NORMAL) && active) {
|
||||
state_ = ACTIVE;
|
||||
set_dirty();
|
||||
} else if ((state_ == ACTIVE) && !active) {
|
||||
state_ = NORMAL;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool button::checked() const
|
||||
{
|
||||
return state_ == PRESSED || state_ == PRESSED_ACTIVE || state_ == TOUCHED_PRESSED;
|
||||
}
|
||||
|
||||
void button::enable(bool new_val)
|
||||
{
|
||||
if(new_val != enabled())
|
||||
{
|
||||
pressed_ = false;
|
||||
// check buttons should keep their state
|
||||
if(type_ != TYPE_CHECK) {
|
||||
state_ = NORMAL;
|
||||
}
|
||||
widget::enable(new_val);
|
||||
}
|
||||
}
|
||||
|
||||
void button::draw_contents()
|
||||
{
|
||||
surface image = image_;
|
||||
const int image_w = image_->w;
|
||||
|
||||
int offset = 0;
|
||||
switch(state_) {
|
||||
case ACTIVE:
|
||||
image = activeImage_;
|
||||
break;
|
||||
case PRESSED:
|
||||
image = pressedImage_;
|
||||
if (type_ == TYPE_PRESS)
|
||||
offset = 1;
|
||||
break;
|
||||
case PRESSED_ACTIVE:
|
||||
image = pressedActiveImage_;
|
||||
break;
|
||||
case TOUCHED_NORMAL:
|
||||
case TOUCHED_PRESSED:
|
||||
image = touchedImage_;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const SDL_Rect& loc = location();
|
||||
SDL_Rect clipArea = loc;
|
||||
const int texty = loc.y + loc.h / 2 - textRect_.h / 2 + offset;
|
||||
int textx;
|
||||
|
||||
if (type_ != TYPE_CHECK && type_ != TYPE_RADIO && type_ != TYPE_IMAGE)
|
||||
textx = loc.x + image->w / 2 - textRect_.w / 2 + offset;
|
||||
else {
|
||||
clipArea.w += image_w + checkbox_horizontal_padding;
|
||||
textx = loc.x + image_w + checkbox_horizontal_padding / 2;
|
||||
}
|
||||
|
||||
color_t button_color = font::BUTTON_COLOR;
|
||||
|
||||
if (!enabled()) {
|
||||
|
||||
if (state_ == PRESSED || state_ == PRESSED_ACTIVE)
|
||||
image = pressedDisabledImage_;
|
||||
else image = disabledImage_;
|
||||
|
||||
button_color = font::GRAY_COLOR;
|
||||
}
|
||||
|
||||
if (!overlayImage_.null()) {
|
||||
|
||||
surface noverlay = make_neutral_surface(
|
||||
enabled() ? overlayImage_ : overlayDisabledImage_);
|
||||
|
||||
if (!overlayPressedImage_.null()) {
|
||||
switch (state_) {
|
||||
case ACTIVE:
|
||||
if (!overlayActiveImage_.null())
|
||||
noverlay = make_neutral_surface(overlayActiveImage_);
|
||||
break;
|
||||
case PRESSED:
|
||||
case PRESSED_ACTIVE:
|
||||
case TOUCHED_NORMAL:
|
||||
case TOUCHED_PRESSED:
|
||||
noverlay = make_neutral_surface( enabled() ?
|
||||
overlayPressedImage_ : overlayPressedDisabledImage_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
surface nimage = make_neutral_surface(image);
|
||||
sdl_blit(noverlay, nullptr, nimage, nullptr);
|
||||
image = nimage;
|
||||
}
|
||||
|
||||
video().blit_surface(loc.x, loc.y, image);
|
||||
if (type_ != TYPE_IMAGE){
|
||||
clipArea.x += offset;
|
||||
clipArea.y += offset;
|
||||
clipArea.w -= 2*offset;
|
||||
clipArea.h -= 2*offset;
|
||||
font::draw_text(&video(), clipArea, font_size, button_color, label_text_, textx, texty);
|
||||
}
|
||||
}
|
||||
|
||||
bool button::hit(int x, int y) const
|
||||
{
|
||||
return sdl::point_in_rect(x,y,location());
|
||||
}
|
||||
|
||||
static bool is_valid_image(const std::string& str) { return !str.empty() && str[0] != IMAGE_PREFIX; }
|
||||
|
||||
void button::set_image(const std::string& image_file)
|
||||
{
|
||||
if(!is_valid_image(image_file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
button_image_name_ = "buttons/" + image_file;
|
||||
load_images();
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void button::set_overlay(const std::string& image_file)
|
||||
{
|
||||
// We allow empty paths for overlays
|
||||
if(image_file[0] == IMAGE_PREFIX) {
|
||||
return;
|
||||
}
|
||||
|
||||
button_overlay_image_name_ = image_file;
|
||||
load_images();
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void button::set_label(const std::string& val)
|
||||
{
|
||||
label_text_ = val;
|
||||
|
||||
//if we have a list of items, use the first one that isn't an image
|
||||
if (std::find(label_text_.begin(), label_text_.end(), COLUMN_SEPARATOR) != label_text_.end()) {
|
||||
const std::vector<std::string>& items = utils::split(label_text_, COLUMN_SEPARATOR);
|
||||
const std::vector<std::string>::const_iterator i = std::find_if(items.begin(),items.end(),is_valid_image);
|
||||
if(i != items.end()) {
|
||||
label_text_ = *i;
|
||||
}
|
||||
}
|
||||
|
||||
calculate_size();
|
||||
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void button::mouse_motion(const SDL_MouseMotionEvent& event)
|
||||
{
|
||||
if (hit(event.x, event.y)) {
|
||||
// the cursor is over the widget
|
||||
if (state_ == NORMAL)
|
||||
state_ = ACTIVE;
|
||||
else if (state_ == PRESSED && (type_ == TYPE_CHECK || type_ == TYPE_RADIO))
|
||||
state_ = PRESSED_ACTIVE;
|
||||
} else {
|
||||
// the cursor is not over the widget
|
||||
|
||||
if (type_ == TYPE_CHECK || type_ == TYPE_RADIO) {
|
||||
|
||||
switch (state_) {
|
||||
case TOUCHED_NORMAL:
|
||||
state_ = NORMAL;
|
||||
break;
|
||||
case TOUCHED_PRESSED:
|
||||
state_ = PRESSED;
|
||||
break;
|
||||
case PRESSED_ACTIVE:
|
||||
state_ = PRESSED;
|
||||
break;
|
||||
case ACTIVE:
|
||||
state_ = NORMAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if ((type_ != TYPE_IMAGE) || state_ != PRESSED)
|
||||
state_ = NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
void button::mouse_down(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
if (hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT) {
|
||||
|
||||
switch (type_) {
|
||||
case TYPE_RADIO:
|
||||
case TYPE_CHECK:
|
||||
if (state_ == PRESSED_ACTIVE)
|
||||
state_ = TOUCHED_PRESSED;
|
||||
else if (state_ == ACTIVE)
|
||||
state_ = TOUCHED_NORMAL;
|
||||
break;
|
||||
case TYPE_TURBO:
|
||||
sound::play_UI_sound(game_config::sounds::button_press);
|
||||
state_ = PRESSED;
|
||||
break;
|
||||
default:
|
||||
state_ = PRESSED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void button::release(){
|
||||
state_ = NORMAL;
|
||||
draw_contents();
|
||||
}
|
||||
|
||||
void button::mouse_up(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
if (!(hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT))
|
||||
return;
|
||||
|
||||
// the user has stopped pressing the mouse left button while on the widget
|
||||
switch (type_) {
|
||||
case TYPE_CHECK:
|
||||
|
||||
switch (state_) {
|
||||
case TOUCHED_NORMAL:
|
||||
state_ = PRESSED_ACTIVE;
|
||||
pressed_ = true;
|
||||
break;
|
||||
case TOUCHED_PRESSED:
|
||||
state_ = ACTIVE;
|
||||
pressed_ = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (pressed_) sound::play_UI_sound(game_config::sounds::checkbox_release);
|
||||
break;
|
||||
case TYPE_RADIO:
|
||||
if (state_ == TOUCHED_NORMAL || state_ == TOUCHED_PRESSED) {
|
||||
state_ = PRESSED_ACTIVE;
|
||||
pressed_ = true;
|
||||
// exit(0);
|
||||
sound::play_UI_sound(game_config::sounds::checkbox_release);
|
||||
}
|
||||
//} else if (state_ == TOUCHED_PRESSED) {
|
||||
// state_ = PRESSED_ACTIVE;
|
||||
//}
|
||||
break;
|
||||
case TYPE_PRESS:
|
||||
if (state_ == PRESSED) {
|
||||
state_ = ACTIVE;
|
||||
pressed_ = true;
|
||||
sound::play_UI_sound(game_config::sounds::button_press);
|
||||
}
|
||||
break;
|
||||
case TYPE_TURBO:
|
||||
state_ = ACTIVE;
|
||||
break;
|
||||
case TYPE_IMAGE:
|
||||
pressed_ = true;
|
||||
sound::play_UI_sound(game_config::sounds::button_press);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void button::handle_event(const SDL_Event& event)
|
||||
{
|
||||
gui::widget::handle_event(event);
|
||||
|
||||
if (hidden() || !enabled())
|
||||
return;
|
||||
|
||||
STATE start_state = state_;
|
||||
|
||||
if (!mouse_locked())
|
||||
{
|
||||
switch(event.type) {
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
mouse_down(event.button);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
mouse_up(event.button);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
mouse_motion(event.motion);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (start_state != state_)
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
bool button::pressed()
|
||||
{
|
||||
if (type_ != TYPE_TURBO) {
|
||||
const bool res = pressed_;
|
||||
pressed_ = false;
|
||||
return res;
|
||||
} else
|
||||
return state_ == PRESSED || state_ == PRESSED_ACTIVE || state_ == TOUCHED_PRESSED;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "widget.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
class button : public widget
|
||||
{
|
||||
public:
|
||||
struct error : public game::error {
|
||||
error()
|
||||
: game::error("GUI1 button error")
|
||||
{}
|
||||
};
|
||||
|
||||
enum TYPE { TYPE_PRESS, TYPE_CHECK, TYPE_TURBO, TYPE_IMAGE, TYPE_RADIO };
|
||||
TYPE get_type() const { return type_; }
|
||||
|
||||
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,
|
||||
const bool auto_join=true, std::string overlay_image="");
|
||||
|
||||
|
||||
/** Default implementation, but defined out-of-line for efficiency reasons. */
|
||||
virtual ~button();
|
||||
void set_check(bool check);
|
||||
void set_active(bool active);
|
||||
bool checked() const;
|
||||
|
||||
void set_label(const std::string& val);
|
||||
void set_image(const std::string& image_file_base);
|
||||
void set_overlay(const std::string& image_file_base);
|
||||
void set_image_path_suffix(const std::string& suffix) { button_image_path_suffix_ = suffix; load_images(); }
|
||||
|
||||
bool pressed();
|
||||
bool hit(int x, int y) const;
|
||||
virtual void enable(bool new_val=true);
|
||||
void release();
|
||||
|
||||
protected:
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
virtual void mouse_motion(const SDL_MouseMotionEvent& event);
|
||||
virtual void mouse_down(const SDL_MouseButtonEvent& event);
|
||||
virtual void mouse_up(const SDL_MouseButtonEvent& event);
|
||||
virtual void draw_contents();
|
||||
|
||||
TYPE type_;
|
||||
|
||||
private:
|
||||
|
||||
void load_images();
|
||||
|
||||
void calculate_size();
|
||||
|
||||
std::string label_text_;
|
||||
|
||||
surface image_, pressedImage_, activeImage_, pressedActiveImage_,
|
||||
touchedImage_, disabledImage_, pressedDisabledImage_,
|
||||
overlayImage_, overlayPressedImage_, overlayPressedDisabledImage_, overlayDisabledImage_,
|
||||
overlayActiveImage_;
|
||||
SDL_Rect textRect_;
|
||||
|
||||
enum STATE { UNINIT, NORMAL, ACTIVE, PRESSED, PRESSED_ACTIVE, TOUCHED_NORMAL, TOUCHED_PRESSED };
|
||||
STATE state_;
|
||||
|
||||
bool pressed_;
|
||||
|
||||
SPACE_CONSUMPTION spacing_;
|
||||
|
||||
int base_height_, base_width_;
|
||||
|
||||
std::string button_image_name_;
|
||||
std::string button_overlay_image_name_;
|
||||
std::string button_image_path_suffix_;
|
||||
|
||||
}; //end class button
|
||||
|
||||
}
|
1248
src/widgets/menu.cpp
1248
src/widgets/menu.cpp
File diff suppressed because it is too large
Load diff
|
@ -1,317 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "scrollarea.hpp"
|
||||
|
||||
namespace image{
|
||||
class locator;
|
||||
}
|
||||
|
||||
namespace gui {
|
||||
|
||||
class menu : public scrollarea
|
||||
{
|
||||
public:
|
||||
|
||||
enum ROW_TYPE { NORMAL_ROW, SELECTED_ROW, HEADING_ROW };
|
||||
//basic menu style
|
||||
class style
|
||||
{
|
||||
public:
|
||||
style();
|
||||
virtual ~style();
|
||||
virtual void init() {}
|
||||
|
||||
virtual SDL_Rect item_size(const std::string& item) const;
|
||||
virtual void draw_row_bg(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
|
||||
virtual void draw_row(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
|
||||
void scale_images(int max_width, int max_height);
|
||||
surface get_item_image(const image::locator &i_locator) const;
|
||||
std::size_t get_font_size() const;
|
||||
std::size_t get_cell_padding() const;
|
||||
std::size_t get_thickness() const;
|
||||
|
||||
protected:
|
||||
std::size_t font_size_;
|
||||
std::size_t cell_padding_;
|
||||
std::size_t thickness_; //additional cell padding for style use only
|
||||
|
||||
int normal_rgb_, selected_rgb_, heading_rgb_;
|
||||
double normal_alpha_, selected_alpha_, heading_alpha_;
|
||||
int max_img_w_, max_img_h_;
|
||||
};
|
||||
|
||||
//image-border selection style
|
||||
class imgsel_style : public style
|
||||
{
|
||||
public:
|
||||
imgsel_style(const std::string &img_base, bool has_bg,
|
||||
int normal_rgb, int selected_rgb, int heading_rgb,
|
||||
double normal_alpha, double selected_alpha, double heading_alpha);
|
||||
virtual ~imgsel_style();
|
||||
|
||||
virtual SDL_Rect item_size(const std::string& item) const;
|
||||
virtual void draw_row_bg(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
|
||||
virtual void draw_row(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
|
||||
|
||||
virtual void init() { load_images(); }
|
||||
bool load_images();
|
||||
|
||||
protected:
|
||||
const std::string img_base_;
|
||||
std::map<std::string,surface> img_map_;
|
||||
|
||||
private:
|
||||
bool load_image(const std::string &img_sub);
|
||||
bool has_background_;
|
||||
bool initialized_;
|
||||
bool load_failed_;
|
||||
int normal_rgb2_, selected_rgb2_, heading_rgb2_;
|
||||
double normal_alpha2_, selected_alpha2_, heading_alpha2_;
|
||||
//FIXME: why is this better than a plain surface?
|
||||
struct bg_cache
|
||||
{
|
||||
bg_cache() : surf(), width(-1), height(-1)
|
||||
{}
|
||||
|
||||
surface surf;
|
||||
int width, height;
|
||||
};
|
||||
bg_cache bg_cache_;
|
||||
};
|
||||
|
||||
friend class style;
|
||||
friend class imgsel_style;
|
||||
static style &default_style;
|
||||
static style simple_style;
|
||||
static imgsel_style bluebg_style;
|
||||
|
||||
struct item
|
||||
{
|
||||
item() : fields(), help(), id(0)
|
||||
{}
|
||||
|
||||
item(const std::vector<std::string>& fields, std::size_t id)
|
||||
: fields(fields), help(), id(id)
|
||||
{}
|
||||
|
||||
std::vector<std::string> fields;
|
||||
std::vector<std::string> help;
|
||||
std::size_t id;
|
||||
};
|
||||
|
||||
class sorter
|
||||
{
|
||||
public:
|
||||
virtual ~sorter() {}
|
||||
virtual bool column_sortable(int column) const = 0;
|
||||
virtual bool less(int column, const item& row1, const item& row2) const = 0;
|
||||
};
|
||||
|
||||
class basic_sorter : public sorter
|
||||
{
|
||||
public:
|
||||
basic_sorter();
|
||||
virtual ~basic_sorter() {}
|
||||
|
||||
basic_sorter& set_alpha_sort(int column);
|
||||
basic_sorter& set_numeric_sort(int column);
|
||||
basic_sorter& set_xp_sort(int column);
|
||||
basic_sorter& set_level_sort(int level_column, int xp_column);
|
||||
basic_sorter& set_id_sort(int column);
|
||||
basic_sorter& set_redirect_sort(int column, int to);
|
||||
basic_sorter& set_position_sort(int column, const std::vector<int>& pos);
|
||||
protected:
|
||||
virtual bool column_sortable(int column) const;
|
||||
virtual bool less(int column, const item& row1, const item& row2) const;
|
||||
|
||||
private:
|
||||
std::set<int> alpha_sort_, numeric_sort_, id_sort_, xp_sort_, level_sort_;
|
||||
std::map<int,int> redirect_sort_;
|
||||
std::map<int,std::vector<int>> pos_sort_;
|
||||
int xp_col_; //used by level sort
|
||||
};
|
||||
|
||||
menu(CVideo& video, const std::vector<std::string>& items,
|
||||
bool click_selects=false, int max_height=-1, int max_width=-1,
|
||||
const sorter* sorter_obj=nullptr, style *menu_style=nullptr, const bool auto_join=true);
|
||||
|
||||
/** Default implementation, but defined out-of-line for efficiency reasons. */
|
||||
~menu();
|
||||
|
||||
int selection() const;
|
||||
|
||||
void move_selection(std::size_t id);
|
||||
void move_selection_keeping_viewport(std::size_t id);
|
||||
void reset_selection();
|
||||
|
||||
// allows user to change_item while running (dangerous)
|
||||
void change_item(int pos1,int pos2,const std::string& str);
|
||||
|
||||
virtual void erase_item(std::size_t index);
|
||||
|
||||
void set_heading(const std::vector<std::string>& heading);
|
||||
|
||||
/// Set new items to show and redraw/recalculate everything. If
|
||||
/// strip_spaces is false, spaces will remain at the item edges. If
|
||||
/// keep_viewport is true, the menu tries to keep the selection at
|
||||
/// the same position as it were before the items were set.
|
||||
virtual void set_items(const std::vector<std::string>& items, bool strip_spaces=true,
|
||||
bool keep_viewport=false);
|
||||
|
||||
/// Set a new max height for this menu. Note that this does not take
|
||||
/// effect immediately, only after certain operations that clear
|
||||
/// everything, such as set_items().
|
||||
void set_max_height(const int new_max_height);
|
||||
void set_max_width(const int new_max_width);
|
||||
|
||||
int get_max_height() const { return max_height_; }
|
||||
int get_max_width() const { return max_width_; }
|
||||
|
||||
std::size_t number_of_items() const { return items_.size(); }
|
||||
|
||||
int process();
|
||||
|
||||
bool double_clicked();
|
||||
|
||||
void set_click_selects(bool value);
|
||||
void set_numeric_keypress_selection(bool value);
|
||||
|
||||
void scroll(unsigned int pos);
|
||||
|
||||
//currently, menus do not manage the memory of their sorter
|
||||
//this should be changed to a more object-oriented approach
|
||||
void set_sorter(sorter *s);
|
||||
void sort_by(int column);
|
||||
int get_sort_by() const {return sortby_;}
|
||||
bool get_sort_reversed() const {return sortreversed_;}
|
||||
|
||||
protected:
|
||||
bool item_ends_with_image(const std::string& item) const;
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
void set_inner_location(const SDL_Rect& rect);
|
||||
|
||||
bool requires_event_focus(const SDL_Event *event=nullptr) const;
|
||||
const std::vector<int>& column_widths() const;
|
||||
virtual void draw_row(const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
|
||||
|
||||
style *style_;
|
||||
bool silent_;
|
||||
|
||||
int hit(int x, int y) const;
|
||||
|
||||
std::pair<int,int> hit_cell(int x, int y) const;
|
||||
int hit_column(int x) const;
|
||||
|
||||
int hit_heading(int x, int y) const;
|
||||
|
||||
void invalidate_row(std::size_t id);
|
||||
void invalidate_row_pos(std::size_t pos);
|
||||
void invalidate_heading();
|
||||
|
||||
private:
|
||||
std::size_t max_items_onscreen() const;
|
||||
|
||||
std::size_t heading_height() const;
|
||||
|
||||
int max_height_, max_width_;
|
||||
mutable int max_items_, item_height_;
|
||||
|
||||
void adjust_viewport_to_selection();
|
||||
void key_press(SDL_Keycode key);
|
||||
|
||||
std::vector<item> items_;
|
||||
std::vector<std::size_t> item_pos_;
|
||||
|
||||
std::vector<std::string> heading_;
|
||||
mutable int heading_height_;
|
||||
|
||||
void create_help_strings();
|
||||
void process_help_string(int mousex, int mousey);
|
||||
|
||||
std::pair<int,int> cur_help_;
|
||||
int help_string_;
|
||||
|
||||
mutable std::vector<int> column_widths_;
|
||||
|
||||
std::size_t selected_;
|
||||
bool click_selects_;
|
||||
bool out_;
|
||||
bool previous_button_;
|
||||
//std::set<std::size_t> undrawn_items_;
|
||||
|
||||
bool show_result_;
|
||||
|
||||
bool double_clicked_;
|
||||
|
||||
void column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const;
|
||||
|
||||
void clear_item(int item);
|
||||
void draw_contents();
|
||||
void draw();
|
||||
|
||||
mutable std::map<int,SDL_Rect> itemRects_;
|
||||
|
||||
SDL_Rect get_item_rect(int item) const;
|
||||
SDL_Rect get_item_rect_internal(std::size_t pos) const;
|
||||
std::size_t get_item_height_internal(const std::vector<std::string>& item) const;
|
||||
std::size_t get_item_height(int item) const;
|
||||
int items_start() const;
|
||||
|
||||
int items_end() const;
|
||||
int items_height() const;
|
||||
|
||||
void update_scrollbar_grip_height();
|
||||
|
||||
///variable which determines whether a numeric keypress should
|
||||
///select an item on the dialog
|
||||
bool num_selects_;
|
||||
// These two variables are used to get the correct double click
|
||||
// behavior so that a click that causes one double click won't be
|
||||
// counted as a first click in the "next" double click.
|
||||
bool ignore_next_doubleclick_;
|
||||
bool last_was_doubleclick_;
|
||||
|
||||
//ellipsis calculation is slightly off, so default to false
|
||||
bool use_ellipsis_;
|
||||
|
||||
const sorter* sorter_;
|
||||
int sortby_;
|
||||
bool sortreversed_;
|
||||
int highlight_heading_;
|
||||
|
||||
/// Set new items to show. If strip_spaces is false, spaces will
|
||||
/// remain at the item edges.
|
||||
void fill_items(const std::vector<std::string>& items, bool strip_spaces);
|
||||
|
||||
void do_sort();
|
||||
void recalculate_pos();
|
||||
void assert_pos();
|
||||
|
||||
void update_size();
|
||||
enum SELECTION_MOVE_VIEWPORT { MOVE_VIEWPORT, NO_MOVE_VIEWPORT };
|
||||
void set_selection_pos(std::size_t pos, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
|
||||
void move_selection_to(std::size_t id, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
|
||||
void move_selection_up(std::size_t dep);
|
||||
void move_selection_down(std::size_t dep);
|
||||
|
||||
std::set<int> invalid_;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
/*
|
||||
wesnoth menu styles Copyright (C) 2006 - 2018 by Patrick Parker <patrick_x99@hotmail.com>
|
||||
wesnoth menu Copyright (C) 2003-5 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/menu.hpp"
|
||||
|
||||
#include "font/constants.hpp"
|
||||
#include "picture.hpp"
|
||||
#include "lexical_cast.hpp"
|
||||
#include "sdl/utils.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
//static initializations
|
||||
menu::imgsel_style menu::bluebg_style("dialogs/selection", true,
|
||||
0x000000, 0x000000, 0x333333,
|
||||
0.35, 0.0, 0.3);
|
||||
menu::style menu::simple_style;
|
||||
|
||||
menu::style &menu::default_style = menu::bluebg_style;
|
||||
|
||||
//constructors
|
||||
menu::style::style() : font_size_(font::SIZE_NORMAL),
|
||||
cell_padding_(font::SIZE_NORMAL * 3/5), thickness_(0),
|
||||
normal_rgb_(0x000000), selected_rgb_(0x000099), heading_rgb_(0x333333),
|
||||
normal_alpha_(0.2), selected_alpha_(0.6), heading_alpha_(0.3),
|
||||
max_img_w_(-1), max_img_h_(-1)
|
||||
{}
|
||||
|
||||
menu::style::~style()
|
||||
{}
|
||||
menu::imgsel_style::imgsel_style(const std::string &img_base, bool has_bg,
|
||||
int normal_rgb, int selected_rgb, int heading_rgb,
|
||||
double normal_alpha, double selected_alpha, double heading_alpha)
|
||||
: img_base_(img_base), has_background_(has_bg), initialized_(false), load_failed_(false),
|
||||
normal_rgb2_(normal_rgb), selected_rgb2_(selected_rgb), heading_rgb2_(heading_rgb),
|
||||
normal_alpha2_(normal_alpha), selected_alpha2_(selected_alpha), heading_alpha2_(heading_alpha)
|
||||
{}
|
||||
menu::imgsel_style::~imgsel_style()
|
||||
{}
|
||||
|
||||
std::size_t menu::style::get_font_size() const { return font_size_; }
|
||||
std::size_t menu::style::get_cell_padding() const { return cell_padding_; }
|
||||
std::size_t menu::style::get_thickness() const { return thickness_; }
|
||||
|
||||
void menu::style::scale_images(int max_width, int max_height)
|
||||
{
|
||||
max_img_w_ = max_width;
|
||||
max_img_h_ = max_height;
|
||||
}
|
||||
|
||||
surface menu::style::get_item_image(const image::locator& img_loc) const
|
||||
{
|
||||
surface surf = image::get_image(img_loc);
|
||||
if(!surf.null())
|
||||
{
|
||||
int scale = 100;
|
||||
if(max_img_w_ > 0 && surf->w > max_img_w_) {
|
||||
scale = (max_img_w_ * 100) / surf->w;
|
||||
}
|
||||
if(max_img_h_ > 0 && surf->h > max_img_h_) {
|
||||
scale = std::min<int>(scale, ((max_img_h_ * 100) / surf->h));
|
||||
}
|
||||
if(scale != 100)
|
||||
{
|
||||
return scale_surface(surf, (scale * surf->w)/100, (scale * surf->h)/100);
|
||||
}
|
||||
}
|
||||
return surf;
|
||||
}
|
||||
|
||||
bool menu::imgsel_style::load_image(const std::string &img_sub)
|
||||
{
|
||||
std::string path = img_base_ + "-" + img_sub + ".png";
|
||||
const surface image = image::get_image(path);
|
||||
img_map_[img_sub] = image;
|
||||
return(!image.null());
|
||||
}
|
||||
|
||||
bool menu::imgsel_style::load_images()
|
||||
{
|
||||
if(!initialized_)
|
||||
{
|
||||
|
||||
if( load_image("border-botleft")
|
||||
&& load_image("border-botright")
|
||||
&& load_image("border-topleft")
|
||||
&& load_image("border-topright")
|
||||
&& load_image("border-left")
|
||||
&& load_image("border-right")
|
||||
&& load_image("border-top")
|
||||
&& load_image("border-bottom") )
|
||||
{
|
||||
thickness_ = std::min(
|
||||
img_map_["border-top"]->h,
|
||||
img_map_["border-left"]->w);
|
||||
|
||||
|
||||
if(has_background_ && !load_image("background"))
|
||||
{
|
||||
load_failed_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
normal_rgb_ = normal_rgb2_;
|
||||
normal_alpha_ = normal_alpha2_;
|
||||
selected_rgb_ = selected_rgb2_;
|
||||
selected_alpha_ = selected_alpha2_;
|
||||
heading_rgb_ = heading_rgb2_;
|
||||
heading_alpha_ = heading_alpha2_;
|
||||
|
||||
load_failed_ = false;
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
thickness_ = 0;
|
||||
initialized_ = true;
|
||||
load_failed_ = true;
|
||||
}
|
||||
}
|
||||
return (!load_failed_);
|
||||
}
|
||||
|
||||
void menu::imgsel_style::draw_row_bg(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
|
||||
{
|
||||
if(type == SELECTED_ROW && has_background_ && !load_failed_) {
|
||||
if(bg_cache_.width != rect.w || bg_cache_.height != rect.h)
|
||||
{
|
||||
//draw scaled background image
|
||||
//scale image each time (to prevent loss of quality)
|
||||
bg_cache_.surf = scale_surface(img_map_["background"], rect.w, rect.h);
|
||||
bg_cache_.width = rect.w;
|
||||
bg_cache_.height = rect.h;
|
||||
}
|
||||
SDL_Rect clip = rect;
|
||||
menu_ref.video().blit_surface(rect.x,rect.y,bg_cache_.surf,nullptr,&clip);
|
||||
}
|
||||
else {
|
||||
style::draw_row_bg(menu_ref, row_index, rect, type);
|
||||
}
|
||||
}
|
||||
|
||||
void menu::imgsel_style::draw_row(menu& menu_ref, const std::size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
|
||||
{
|
||||
if(!load_failed_) {
|
||||
//draw item inside
|
||||
style::draw_row(menu_ref, row_index, rect, type);
|
||||
|
||||
if(type == SELECTED_ROW) {
|
||||
// draw border
|
||||
surface image;
|
||||
SDL_Rect area;
|
||||
SDL_Rect clip = rect;
|
||||
area.x = rect.x;
|
||||
area.y = rect.y;
|
||||
|
||||
image = img_map_["border-top"];
|
||||
area.x = rect.x;
|
||||
area.y = rect.y;
|
||||
do {
|
||||
menu_ref.video().blit_surface(area.x,area.y,image,nullptr,&clip);
|
||||
area.x += image->w;
|
||||
} while( area.x < rect.x + rect.w );
|
||||
|
||||
image = img_map_["border-left"];
|
||||
area.x = rect.x;
|
||||
area.y = rect.y;
|
||||
do {
|
||||
menu_ref.video().blit_surface(area.x,area.y,image,nullptr,&clip);
|
||||
area.y += image->h;
|
||||
} while( area.y < rect.y + rect.h );
|
||||
|
||||
image = img_map_["border-right"];
|
||||
area.x = rect.x + rect.w - thickness_;
|
||||
area.y = rect.y;
|
||||
do {
|
||||
menu_ref.video().blit_surface(area.x,area.y,image,nullptr,&clip);
|
||||
area.y += image->h;
|
||||
} while( area.y < rect.y + rect.h );
|
||||
|
||||
image = img_map_["border-bottom"];
|
||||
area.x = rect.x;
|
||||
area.y = rect.y + rect.h - thickness_;
|
||||
do {
|
||||
menu_ref.video().blit_surface(area.x,area.y,image,nullptr,&clip);
|
||||
area.x += image->w;
|
||||
} while( area.x < rect.x + rect.w );
|
||||
|
||||
image = img_map_["border-topleft"];
|
||||
area.x = rect.x;
|
||||
area.y = rect.y;
|
||||
menu_ref.video().blit_surface(area.x,area.y,image);
|
||||
|
||||
image = img_map_["border-topright"];
|
||||
area.x = rect.x + rect.w - image->w;
|
||||
area.y = rect.y;
|
||||
menu_ref.video().blit_surface(area.x,area.y,image);
|
||||
|
||||
image = img_map_["border-botleft"];
|
||||
area.x = rect.x;
|
||||
area.y = rect.y + rect.h - image->h;
|
||||
menu_ref.video().blit_surface(area.x,area.y,image);
|
||||
|
||||
image = img_map_["border-botright"];
|
||||
area.x = rect.x + rect.w - image->w;
|
||||
area.y = rect.y + rect.h - image->h;
|
||||
menu_ref.video().blit_surface(area.x,area.y,image);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//default drawing
|
||||
style::draw_row(menu_ref, row_index, rect, type);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect menu::imgsel_style::item_size(const std::string& item) const
|
||||
{
|
||||
SDL_Rect bounds = style::item_size(item);
|
||||
|
||||
bounds.w += 2 * thickness_;
|
||||
bounds.h += 2 * thickness_;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
||||
} //namesapce gui
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2004 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
|
||||
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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/scrollarea.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
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)
|
||||
{
|
||||
scrollbar_.hide(true);
|
||||
}
|
||||
|
||||
bool scrollarea::has_scrollbar() const
|
||||
{
|
||||
return shown_size_ < full_size_ && scrollbar_.is_valid_height(location().h);
|
||||
}
|
||||
|
||||
sdl_handler_vector scrollarea::handler_members()
|
||||
{
|
||||
sdl_handler_vector h;
|
||||
h.push_back(&scrollbar_);
|
||||
return h;
|
||||
}
|
||||
|
||||
void scrollarea::update_location(const SDL_Rect& rect)
|
||||
{
|
||||
SDL_Rect r = rect;
|
||||
shown_scrollbar_ = has_scrollbar();
|
||||
if (shown_scrollbar_) {
|
||||
int w = r.w - scrollbar_.width();
|
||||
r.x += w;
|
||||
r.w -= w;
|
||||
scrollbar_.set_location(r);
|
||||
r.x -= w;
|
||||
r.w = w;
|
||||
}
|
||||
|
||||
if (!hidden())
|
||||
scrollbar_.hide(!shown_scrollbar_);
|
||||
set_inner_location(r);
|
||||
}
|
||||
|
||||
void scrollarea::test_scrollbar()
|
||||
{
|
||||
if (recursive_)
|
||||
return;
|
||||
recursive_ = true;
|
||||
if (shown_scrollbar_ != has_scrollbar()) {
|
||||
bg_restore();
|
||||
bg_cancel();
|
||||
update_location(location());
|
||||
}
|
||||
recursive_ = false;
|
||||
}
|
||||
|
||||
void scrollarea::hide(bool value)
|
||||
{
|
||||
widget::hide(value);
|
||||
if (shown_scrollbar_)
|
||||
scrollbar_.hide(value);
|
||||
}
|
||||
|
||||
unsigned scrollarea::get_position() const
|
||||
{
|
||||
return scrollbar_.get_position();
|
||||
}
|
||||
|
||||
unsigned scrollarea::get_max_position() const
|
||||
{
|
||||
return scrollbar_.get_max_position();
|
||||
}
|
||||
|
||||
void scrollarea::set_position(unsigned pos)
|
||||
{
|
||||
scrollbar_.set_position(pos);
|
||||
}
|
||||
|
||||
void scrollarea::adjust_position(unsigned pos)
|
||||
{
|
||||
scrollbar_.adjust_position(pos);
|
||||
}
|
||||
|
||||
void scrollarea::move_position(int dep)
|
||||
{
|
||||
scrollbar_.move_position(dep);
|
||||
}
|
||||
|
||||
void scrollarea::set_shown_size(unsigned h)
|
||||
{
|
||||
scrollbar_.set_shown_size(h);
|
||||
shown_size_ = h;
|
||||
test_scrollbar();
|
||||
}
|
||||
|
||||
void scrollarea::set_full_size(unsigned h)
|
||||
{
|
||||
scrollbar_.set_full_size(h);
|
||||
full_size_ = h;
|
||||
test_scrollbar();
|
||||
}
|
||||
|
||||
void scrollarea::set_scroll_rate(unsigned r)
|
||||
{
|
||||
scrollbar_.set_scroll_rate(r);
|
||||
}
|
||||
|
||||
void scrollarea::process_event()
|
||||
{
|
||||
int grip_position = scrollbar_.get_position();
|
||||
if (grip_position == old_position_)
|
||||
return;
|
||||
old_position_ = grip_position;
|
||||
scroll(grip_position);
|
||||
}
|
||||
|
||||
SDL_Rect scrollarea::inner_location() const
|
||||
{
|
||||
SDL_Rect r = location();
|
||||
if (shown_scrollbar_)
|
||||
r.w -= scrollbar_.width();
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned scrollarea::scrollbar_width() const
|
||||
{
|
||||
return scrollbar_.width();
|
||||
}
|
||||
|
||||
void scrollarea::handle_event(const SDL_Event& event)
|
||||
{
|
||||
gui::widget::handle_event(event);
|
||||
|
||||
if (mouse_locked() || hidden())
|
||||
return;
|
||||
|
||||
if (event.type != SDL_MOUSEWHEEL)
|
||||
return;
|
||||
|
||||
const SDL_MouseWheelEvent &ev = event.wheel;
|
||||
int x, y;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
if (sdl::point_in_rect(x, y, inner_location())) {
|
||||
if (ev.y > 0) {
|
||||
scrollbar_.scroll_up();
|
||||
} else if (ev.y < 0) {
|
||||
scrollbar_.scroll_down();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2004 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
|
||||
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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scrollbar.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
class scrollarea : public widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a zone with automatic handling of scrollbar.
|
||||
* @todo FIXME: parameterlist ??
|
||||
*/
|
||||
//- \param d the display object
|
||||
//- \param pane the widget where wheel events take place
|
||||
scrollarea(CVideo &video, bool auto_join=true);
|
||||
|
||||
virtual void hide(bool value = true);
|
||||
|
||||
protected:
|
||||
virtual sdl_handler_vector handler_members();
|
||||
virtual void update_location(const SDL_Rect& rect);
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
virtual void process_event();
|
||||
virtual void scroll(unsigned int pos) = 0;
|
||||
virtual void set_inner_location(const SDL_Rect& rect) = 0;
|
||||
|
||||
SDL_Rect inner_location() const;
|
||||
unsigned scrollbar_width() const;
|
||||
|
||||
unsigned get_position() const;
|
||||
unsigned get_max_position() const;
|
||||
void set_position(unsigned pos);
|
||||
void adjust_position(unsigned pos);
|
||||
void move_position(int dep);
|
||||
void set_shown_size(unsigned h);
|
||||
void set_full_size(unsigned h);
|
||||
void set_scroll_rate(unsigned r);
|
||||
bool has_scrollbar() const;
|
||||
|
||||
private:
|
||||
scrollbar scrollbar_;
|
||||
int old_position_;
|
||||
bool recursive_, shown_scrollbar_;
|
||||
unsigned shown_size_;
|
||||
unsigned full_size_;
|
||||
|
||||
void test_scrollbar();
|
||||
};
|
||||
|
||||
} // end namespace gui
|
|
@ -1,396 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
2004 - 2015 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
|
||||
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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/scrollbar.hpp"
|
||||
#include "picture.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
const std::string scrollbar_top = "buttons/scrollbars_large/scrolltop.png";
|
||||
const std::string scrollbar_bottom = "buttons/scrollbars_large/scrollbottom.png";
|
||||
const std::string scrollbar_mid = "buttons/scrollbars_large/scrollmid.png";
|
||||
|
||||
const std::string scrollbar_top_hl = "buttons/scrollbars_large/scrolltop-active.png";
|
||||
const std::string scrollbar_bottom_hl = "buttons/scrollbars_large/scrollbottom-active.png";
|
||||
const std::string scrollbar_mid_hl = "buttons/scrollbars_large/scrollmid-active.png";
|
||||
|
||||
const std::string scrollbar_top_pressed = "buttons/scrollbars_large/scrolltop-pressed.png";
|
||||
const std::string scrollbar_bottom_pressed = "buttons/scrollbars_large/scrollbottom-pressed.png";
|
||||
const std::string scrollbar_mid_pressed = "buttons/scrollbars_large/scrollmid-pressed.png";
|
||||
|
||||
const std::string groove_top = "buttons/scrollbars_large/scrollgroove-top.png";
|
||||
const std::string groove_mid = "buttons/scrollbars_large/scrollgroove-mid.png";
|
||||
const std::string groove_bottom = "buttons/scrollbars_large/scrollgroove-bottom.png";
|
||||
|
||||
}
|
||||
|
||||
namespace gui {
|
||||
|
||||
scrollbar::scrollbar(CVideo &video)
|
||||
: widget(video)
|
||||
, mid_scaled_(nullptr)
|
||||
, groove_scaled_(nullptr)
|
||||
, uparrow_(video, "", button::TYPE_TURBO, "button_square/button_square_25"
|
||||
, gui::button::DEFAULT_SPACE, true,"icons/arrows/arrows_ornate_up_25")
|
||||
, downarrow_(video, "", button::TYPE_TURBO, "button_square/button_square_25"
|
||||
, gui::button::DEFAULT_SPACE, true,"icons/arrows/arrows_ornate_down_25")
|
||||
, state_(NORMAL)
|
||||
, minimum_grip_height_(0)
|
||||
, mousey_on_grip_(0)
|
||||
, grip_position_(0)
|
||||
, grip_height_(0)
|
||||
, full_height_(0)
|
||||
, scroll_rate_(1)
|
||||
{
|
||||
uparrow_.enable(false);
|
||||
downarrow_.enable(false);
|
||||
|
||||
static const surface img(image::get_image(scrollbar_mid));
|
||||
|
||||
if (img != nullptr) {
|
||||
set_width(img->w);
|
||||
// this is a bit rough maybe
|
||||
minimum_grip_height_ = 2 * img->h;
|
||||
}
|
||||
}
|
||||
|
||||
sdl_handler_vector scrollbar::handler_members()
|
||||
{
|
||||
sdl_handler_vector h;
|
||||
h.push_back(&uparrow_);
|
||||
h.push_back(&downarrow_);
|
||||
return h;
|
||||
}
|
||||
|
||||
void scrollbar::update_location(const SDL_Rect& rect)
|
||||
{
|
||||
int uh = uparrow_.height(), dh = downarrow_.height();
|
||||
uparrow_.set_location(rect.x, rect.y);
|
||||
downarrow_.set_location(rect.x, rect.y + rect.h - dh);
|
||||
SDL_Rect r = rect;
|
||||
r.y += uh;
|
||||
r.h -= uh + dh;
|
||||
|
||||
widget::update_location(r);
|
||||
//TODO comment or remove
|
||||
//bg_register(r);
|
||||
}
|
||||
|
||||
void scrollbar::hide(bool value)
|
||||
{
|
||||
widget::hide(value);
|
||||
uparrow_.hide(value);
|
||||
downarrow_.hide(value);
|
||||
}
|
||||
|
||||
unsigned scrollbar::get_position() const
|
||||
{
|
||||
return grip_position_;
|
||||
}
|
||||
|
||||
unsigned scrollbar::get_max_position() const
|
||||
{
|
||||
return full_height_ - grip_height_;
|
||||
}
|
||||
|
||||
void scrollbar::set_position(unsigned pos)
|
||||
{
|
||||
if (pos > full_height_ - grip_height_)
|
||||
pos = full_height_ - grip_height_;
|
||||
if (pos == grip_position_)
|
||||
return;
|
||||
grip_position_ = pos;
|
||||
uparrow_.enable(grip_position_ != 0);
|
||||
downarrow_.enable(grip_position_ < full_height_ - grip_height_);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void scrollbar::adjust_position(unsigned pos)
|
||||
{
|
||||
if (pos < grip_position_)
|
||||
set_position(pos);
|
||||
else if (pos >= grip_position_ + grip_height_)
|
||||
set_position(pos - (grip_height_ - 1));
|
||||
}
|
||||
|
||||
void scrollbar::move_position(int dep)
|
||||
{
|
||||
int pos = grip_position_ + dep;
|
||||
if (pos > 0)
|
||||
set_position(pos);
|
||||
else
|
||||
set_position(0);
|
||||
}
|
||||
|
||||
void scrollbar::set_shown_size(unsigned h)
|
||||
{
|
||||
if (h > full_height_)
|
||||
h = full_height_;
|
||||
if (h == grip_height_)
|
||||
return;
|
||||
bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
|
||||
grip_height_ = h;
|
||||
if (at_bottom)
|
||||
grip_position_ = get_max_position();
|
||||
set_position(grip_position_);
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void scrollbar::set_full_size(unsigned h)
|
||||
{
|
||||
if (h == full_height_)
|
||||
return;
|
||||
bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
|
||||
full_height_ = h;
|
||||
if (at_bottom)
|
||||
grip_position_ = get_max_position();
|
||||
downarrow_.enable(grip_position_ < full_height_ - grip_height_);
|
||||
set_shown_size(grip_height_);
|
||||
set_position(grip_position_);
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void scrollbar::set_scroll_rate(unsigned r)
|
||||
{
|
||||
scroll_rate_ = r;
|
||||
}
|
||||
|
||||
bool scrollbar::is_valid_height(int height) const
|
||||
{
|
||||
int uh = uparrow_.height();
|
||||
int dh = downarrow_.height();
|
||||
if(uh + dh >= height) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void scrollbar::scroll_down()
|
||||
{
|
||||
move_position(scroll_rate_);
|
||||
}
|
||||
|
||||
void scrollbar::scroll_up()
|
||||
{
|
||||
move_position(-scroll_rate_);
|
||||
}
|
||||
|
||||
void scrollbar::process_event()
|
||||
{
|
||||
if (uparrow_.pressed())
|
||||
scroll_up();
|
||||
|
||||
if (downarrow_.pressed())
|
||||
scroll_down();
|
||||
}
|
||||
|
||||
SDL_Rect scrollbar::groove_area() const
|
||||
{
|
||||
SDL_Rect loc = location();
|
||||
int uh = uparrow_.height();
|
||||
int dh = downarrow_.height();
|
||||
if(uh + dh >= loc.h) {
|
||||
loc.h = 0;
|
||||
} else {
|
||||
loc.y += uh;
|
||||
loc.h -= uh + dh;
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
SDL_Rect scrollbar::grip_area() const
|
||||
{
|
||||
const SDL_Rect& loc = groove_area();
|
||||
if (full_height_ == grip_height_)
|
||||
return loc;
|
||||
int h = static_cast<int>(loc.h) * grip_height_ / full_height_;
|
||||
if (h < minimum_grip_height_)
|
||||
h = minimum_grip_height_;
|
||||
int y = loc.y + (static_cast<int>(loc.h) - h) * grip_position_ / (full_height_ - grip_height_);
|
||||
return {loc.x, y, loc.w, h};
|
||||
}
|
||||
|
||||
void scrollbar::draw_contents()
|
||||
{
|
||||
surface mid_img;
|
||||
surface bottom_img;
|
||||
surface top_img;
|
||||
|
||||
switch (state_) {
|
||||
|
||||
case NORMAL:
|
||||
top_img.assign(image::get_image(scrollbar_top));
|
||||
mid_img.assign(image::get_image(scrollbar_mid));
|
||||
bottom_img.assign(image::get_image(scrollbar_bottom));
|
||||
break;
|
||||
|
||||
case ACTIVE:
|
||||
top_img.assign(image::get_image(scrollbar_top_hl));
|
||||
mid_img.assign(image::get_image(scrollbar_mid_hl));
|
||||
bottom_img.assign(image::get_image(scrollbar_bottom_hl));
|
||||
break;
|
||||
|
||||
case DRAGGED:
|
||||
top_img.assign(image::get_image(scrollbar_top_pressed));
|
||||
mid_img.assign(image::get_image(scrollbar_mid_pressed));
|
||||
bottom_img.assign(image::get_image(scrollbar_bottom_pressed));
|
||||
break;
|
||||
|
||||
case UNINIT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const surface top_grv(image::get_image(groove_top));
|
||||
const surface mid_grv(image::get_image(groove_mid));
|
||||
const surface bottom_grv(image::get_image(groove_bottom));
|
||||
|
||||
if (mid_img == nullptr || bottom_img == nullptr || top_img == nullptr
|
||||
|| top_grv == nullptr || bottom_grv == nullptr || mid_grv == nullptr) {
|
||||
std::cerr << "Failure to load scrollbar image.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Rect grip = grip_area();
|
||||
int mid_height = grip.h - top_img->h - bottom_img->h;
|
||||
if (mid_height <= 0) {
|
||||
// For now, minimum size of the middle piece is 1.
|
||||
// This should never really be encountered, and if it is,
|
||||
// it's a symptom of a larger problem, I think.
|
||||
mid_height = 1;
|
||||
}
|
||||
|
||||
if(mid_scaled_.null() || mid_scaled_->h != mid_height) {
|
||||
mid_scaled_.assign(scale_surface(mid_img, mid_img->w, mid_height));
|
||||
}
|
||||
|
||||
SDL_Rect groove = groove_area();
|
||||
int groove_height = groove.h - top_grv->h - bottom_grv->h;
|
||||
if (groove_height <= 0) {
|
||||
groove_height = 1;
|
||||
}
|
||||
|
||||
if (groove_scaled_.null() || groove_scaled_->h != groove_height) {
|
||||
groove_scaled_.assign(scale_surface(mid_grv, mid_grv->w, groove_height));
|
||||
}
|
||||
|
||||
if (mid_scaled_.null() || groove_scaled_.null()) {
|
||||
std::cerr << "Failure during scrollbar image scale.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (grip.h > groove.h) {
|
||||
std::cerr << "abort draw scrollbar: grip too large\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw scrollbar "groove"
|
||||
video().blit_surface(groove.x, groove.y, top_grv);
|
||||
video().blit_surface(groove.x, groove.y + top_grv->h, groove_scaled_);
|
||||
video().blit_surface(groove.x, groove.y + top_grv->h + groove_height, bottom_grv);
|
||||
|
||||
// Draw scrollbar "grip"
|
||||
video().blit_surface(grip.x, grip.y, top_img);
|
||||
video().blit_surface(grip.x, grip.y + top_img->h, mid_scaled_);
|
||||
video().blit_surface(grip.x, grip.y + top_img->h + mid_height, bottom_img);
|
||||
}
|
||||
|
||||
void scrollbar::handle_event(const SDL_Event& event)
|
||||
{
|
||||
gui::widget::handle_event(event);
|
||||
|
||||
if (mouse_locked() || hidden())
|
||||
return;
|
||||
|
||||
STATE new_state = state_;
|
||||
const SDL_Rect& grip = grip_area();
|
||||
const SDL_Rect& groove = groove_area();
|
||||
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
const SDL_MouseButtonEvent& e = event.button;
|
||||
bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
|
||||
new_state = on_grip ? ACTIVE : NORMAL;
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
const SDL_MouseButtonEvent& e = event.button;
|
||||
bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
|
||||
bool on_groove = sdl::point_in_rect(e.x, e.y, groove);
|
||||
if (on_grip && e.button == SDL_BUTTON_LEFT) {
|
||||
mousey_on_grip_ = e.y - grip.y;
|
||||
new_state = DRAGGED;
|
||||
} else if (on_groove && e.button == SDL_BUTTON_LEFT && groove.h != grip.h) {
|
||||
if (e.y < grip.y)
|
||||
move_position(-static_cast<int>(grip_height_));
|
||||
else
|
||||
move_position(grip_height_);
|
||||
} else if (on_groove && e.button == SDL_BUTTON_MIDDLE && groove.h != grip.h) {
|
||||
int y_dep = e.y - grip.y - grip.h/2;
|
||||
int dep = y_dep * int(full_height_ - grip_height_)/ (groove.h - grip.h);
|
||||
move_position(dep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
const SDL_MouseMotionEvent& e = event.motion;
|
||||
if (state_ == NORMAL || state_ == ACTIVE) {
|
||||
bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
|
||||
new_state = on_grip ? ACTIVE : NORMAL;
|
||||
} else if (state_ == DRAGGED && groove.h != grip.h) {
|
||||
int y_dep = e.y - grip.y - mousey_on_grip_;
|
||||
int dep = y_dep * static_cast<int>(full_height_ - grip_height_) / (groove.h - grip.h);
|
||||
move_position(dep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
const SDL_MouseWheelEvent& e = event.wheel;
|
||||
int x, y;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
bool on_groove = sdl::point_in_rect(x, y, groove);
|
||||
if (on_groove && e.y < 0) {
|
||||
move_position(scroll_rate_);
|
||||
} else if (on_groove && e.y > 0) {
|
||||
move_position(-scroll_rate_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (new_state != state_) {
|
||||
set_dirty();
|
||||
mid_scaled_.assign(nullptr);
|
||||
state_ = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
2004 - 2015 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
|
||||
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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "button.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
class scrollarea;
|
||||
|
||||
/** Scrollbar */
|
||||
class scrollbar : public widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a scrollbar.
|
||||
* @todo FIXME: parameterlist ??
|
||||
*/
|
||||
//- @param d the display object
|
||||
//- @param pane the widget where wheel events take place
|
||||
//- @param callback a callback interface for warning that the grip has been moved
|
||||
scrollbar(CVideo &video);
|
||||
|
||||
virtual void hide(bool value = true);
|
||||
|
||||
/**
|
||||
* Determine where the scrollbar is.
|
||||
*
|
||||
* @return the position.
|
||||
* @retval returns 0 if the scrollbar is at the top,
|
||||
* @retval returns (full_size - shown_size) if it is at the bottom.
|
||||
*/
|
||||
unsigned get_position() const;
|
||||
|
||||
unsigned get_max_position() const;
|
||||
|
||||
/** Manually update the scrollbar. */
|
||||
void set_position(unsigned pos);
|
||||
|
||||
/** Ensure the viewport contains the position. */
|
||||
void adjust_position(unsigned pos);
|
||||
|
||||
/** Move the scrollbar. */
|
||||
void move_position(int dep);
|
||||
|
||||
/** Set the relative size of the grip. */
|
||||
void set_shown_size(unsigned h);
|
||||
|
||||
/** Set the relative size of the scrollbar. */
|
||||
void set_full_size(unsigned h);
|
||||
|
||||
/** Set scroll rate. */
|
||||
void set_scroll_rate(unsigned r);
|
||||
|
||||
/** Return true if the scrollbar has a valid size. */
|
||||
bool is_valid_height(int height) const;
|
||||
|
||||
/** Scrolls down one step */
|
||||
void scroll_down();
|
||||
|
||||
/** Scrolls up one step */
|
||||
void scroll_up();
|
||||
|
||||
protected:
|
||||
virtual sdl_handler_vector handler_members();
|
||||
virtual void update_location(const SDL_Rect& rect);
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
virtual void process_event();
|
||||
virtual void draw_contents();
|
||||
|
||||
private:
|
||||
SDL_Rect grip_area() const;
|
||||
SDL_Rect groove_area() const;
|
||||
surface mid_scaled_, groove_scaled_;
|
||||
|
||||
button uparrow_, downarrow_;
|
||||
|
||||
enum STATE { UNINIT, NORMAL, ACTIVE, DRAGGED };
|
||||
STATE state_;
|
||||
|
||||
int minimum_grip_height_, mousey_on_grip_;
|
||||
// Relative data
|
||||
unsigned int grip_position_, grip_height_, full_height_;
|
||||
int scroll_rate_;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace gui
|
|
@ -1,730 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/textbox.hpp"
|
||||
|
||||
#include "desktop/clipboard.hpp"
|
||||
#include "font/sdl_ttf.hpp"
|
||||
#include "log.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
static lg::log_domain log_display("display");
|
||||
#define WRN_DP LOG_STREAM(warn, log_display)
|
||||
#define DBG_G LOG_STREAM(debug, lg::general())
|
||||
|
||||
namespace gui {
|
||||
|
||||
textbox::textbox(CVideo &video, int width, const std::string& text, bool editable, std::size_t max_size, int font_size, double alpha, double alpha_focus, const bool auto_join)
|
||||
: scrollarea(video, auto_join), max_size_(max_size), font_size_(font_size), text_(unicode_cast<std::u32string>(text)),
|
||||
cursor_(text_.size()), selstart_(-1), selend_(-1),
|
||||
grabmouse_(false), text_pos_(0), editable_(editable),
|
||||
show_cursor_(true), show_cursor_at_(0), text_image_(nullptr),
|
||||
wrap_(false), line_height_(0), yscroll_(0), alpha_(alpha),
|
||||
alpha_focus_(alpha_focus),
|
||||
edit_target_(nullptr)
|
||||
,listening_(false)
|
||||
{
|
||||
// static const SDL_Rect area = video.screen_area();
|
||||
// const int height = font::draw_text(nullptr,area,font_size,font::NORMAL_COLOR,"ABCD",0,0).h;
|
||||
set_measurements(width, font::get_max_height(font_size_));
|
||||
set_scroll_rate(font::get_max_height(font_size_) / 2);
|
||||
update_text_cache(true);
|
||||
}
|
||||
|
||||
textbox::~textbox()
|
||||
{
|
||||
}
|
||||
|
||||
void textbox::update_location(const SDL_Rect& rect)
|
||||
{
|
||||
scrollarea::update_location(rect);
|
||||
update_text_cache(true);
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void textbox::set_inner_location(const SDL_Rect& rect)
|
||||
{
|
||||
bg_register(rect);
|
||||
if (text_image_.null()) return;
|
||||
text_pos_ = 0;
|
||||
update_text_cache(false);
|
||||
}
|
||||
|
||||
const std::string textbox::text() const
|
||||
{
|
||||
const std::string &ret = unicode_cast<std::string>(text_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// set_text does not respect max_size_
|
||||
void textbox::set_text(const std::string& text, const color_t& color)
|
||||
{
|
||||
text_ = unicode_cast<std::u32string>(text);
|
||||
cursor_ = text_.size();
|
||||
text_pos_ = 0;
|
||||
selstart_ = -1;
|
||||
selend_ = -1;
|
||||
set_dirty(true);
|
||||
update_text_cache(true, color);
|
||||
handle_text_changed(text_);
|
||||
}
|
||||
|
||||
void textbox::append_text(const std::string& text, bool auto_scroll, const color_t& color)
|
||||
{
|
||||
if(text_image_.get() == nullptr) {
|
||||
set_text(text, color);
|
||||
return;
|
||||
}
|
||||
|
||||
//disallow adding multi-line text to a single-line text box
|
||||
if(wrap_ == false && std::find_if(text.begin(),text.end(),utils::isnewline) != text.end()) {
|
||||
return;
|
||||
}
|
||||
const bool is_at_bottom = get_position() == get_max_position();
|
||||
const std::u32string& wtext = unicode_cast<std::u32string>(text);
|
||||
|
||||
surface new_text = add_text_line(wtext, color);
|
||||
surface new_surface = create_compatible_surface(text_image_,std::max<std::size_t>(text_image_->w,new_text->w),text_image_->h+new_text->h);
|
||||
|
||||
adjust_surface_alpha(new_text, SDL_ALPHA_TRANSPARENT);
|
||||
adjust_surface_alpha(text_image_, SDL_ALPHA_TRANSPARENT);
|
||||
SDL_SetSurfaceBlendMode(text_image_, SDL_BLENDMODE_NONE);
|
||||
sdl_blit(text_image_,nullptr,new_surface,nullptr);
|
||||
SDL_SetSurfaceBlendMode(text_image_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
SDL_Rect target {
|
||||
0
|
||||
, text_image_->h
|
||||
, new_text->w
|
||||
, new_text->h
|
||||
};
|
||||
SDL_SetSurfaceBlendMode(new_text, SDL_BLENDMODE_NONE);
|
||||
sdl_blit(new_text,nullptr,new_surface,&target);
|
||||
text_image_.assign(new_surface);
|
||||
|
||||
text_.insert(text_.end(), wtext.begin(), wtext.end());
|
||||
|
||||
set_dirty(true);
|
||||
update_text_cache(false);
|
||||
if(auto_scroll && is_at_bottom) scroll_to_bottom();
|
||||
handle_text_changed(text_);
|
||||
}
|
||||
|
||||
void textbox::clear()
|
||||
{
|
||||
text_.clear();
|
||||
cursor_ = 0;
|
||||
cursor_pos_ = 0;
|
||||
text_pos_ = 0;
|
||||
selstart_ = -1;
|
||||
selend_ = -1;
|
||||
set_dirty(true);
|
||||
update_text_cache(true);
|
||||
handle_text_changed(text_);
|
||||
}
|
||||
|
||||
void textbox::set_selection(const int selstart, const int selend)
|
||||
{
|
||||
if (!editable_) {
|
||||
return;
|
||||
}
|
||||
if (selstart < 0 || selend < 0 || std::size_t(selstart) > text_.size() ||
|
||||
std::size_t(selend) > text_.size()) {
|
||||
WRN_DP << "out-of-boundary selection" << std::endl;
|
||||
return;
|
||||
}
|
||||
selstart_= selstart;
|
||||
selend_ = selend;
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void textbox::set_cursor_pos(const int cursor_pos)
|
||||
{
|
||||
if (!editable_) {
|
||||
return;
|
||||
}
|
||||
if (cursor_pos < 0 || std::size_t(cursor_pos) > text_.size()) {
|
||||
WRN_DP << "out-of-boundary selection" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
cursor_ = cursor_pos;
|
||||
update_text_cache(false);
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void textbox::draw_cursor(int pos) const
|
||||
{
|
||||
if(show_cursor_ && editable_ && enabled()) {
|
||||
SDL_Rect rect {
|
||||
location().x + pos
|
||||
, location().y
|
||||
, 1
|
||||
, location().h
|
||||
};
|
||||
|
||||
sdl::fill_rectangle(rect, {255, 255, 255, 255});
|
||||
}
|
||||
}
|
||||
|
||||
void textbox::draw_contents()
|
||||
{
|
||||
const SDL_Rect& loc = inner_location();
|
||||
|
||||
surface& surf = video().getSurface();
|
||||
|
||||
color_t c(0, 0, 0);
|
||||
|
||||
double& alpha = focus(nullptr) ? alpha_focus_ : alpha_;
|
||||
c.a = 255 * alpha;
|
||||
|
||||
sdl::fill_rectangle(loc, c);
|
||||
|
||||
SDL_Rect src;
|
||||
|
||||
if(text_image_ == nullptr) {
|
||||
update_text_cache(true);
|
||||
}
|
||||
|
||||
if(text_image_ != nullptr) {
|
||||
src.y = yscroll_;
|
||||
src.w = std::min<std::size_t>(loc.w,text_image_->w);
|
||||
src.h = std::min<std::size_t>(loc.h,text_image_->h);
|
||||
src.x = text_pos_;
|
||||
SDL_Rect dest = video().screen_area();
|
||||
dest.x = loc.x;
|
||||
dest.y = loc.y;
|
||||
|
||||
// Fills the selected area
|
||||
if(enabled() && is_selection()) {
|
||||
const int start = std::min<int>(selstart_,selend_);
|
||||
const int end = std::max<int>(selstart_,selend_);
|
||||
int startx = char_x_[start];
|
||||
int starty = char_y_[start];
|
||||
const int endx = char_x_[end];
|
||||
const int endy = char_y_[end];
|
||||
|
||||
while(starty <= endy) {
|
||||
const std::size_t right = starty == endy ? endx : text_image_->w;
|
||||
if(right <= std::size_t(startx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Rect rect = sdl::create_rect(loc.x + startx
|
||||
, loc.y + starty - src.y
|
||||
, right - startx
|
||||
, line_height_);
|
||||
|
||||
const clip_rect_setter clipper(surf, &loc);
|
||||
|
||||
color_t c2(0, 0, 160, 140);
|
||||
sdl::fill_rectangle(rect, c2);
|
||||
|
||||
starty += int(line_height_);
|
||||
startx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(enabled()) {
|
||||
sdl_blit(text_image_, &src, surf, &dest);
|
||||
} else {
|
||||
// HACK: using 30% opacity allows white text to look as though it is grayed out,
|
||||
// while not changing any applicable non-grayscale AA. Actual colored text will
|
||||
// not look as good, but this is not currently a concern since GUI1 textboxes
|
||||
// are not used much nowadays, and they will eventually all go away.
|
||||
adjust_surface_alpha(text_image_, ftofxp(0.3));
|
||||
sdl_blit(text_image_, &src, surf, &dest);
|
||||
}
|
||||
}
|
||||
|
||||
draw_cursor(cursor_pos_ == 0 ? 0 : cursor_pos_ - 1);
|
||||
}
|
||||
|
||||
void textbox::set_editable(bool value)
|
||||
{
|
||||
editable_ = value;
|
||||
}
|
||||
|
||||
bool textbox::editable() const
|
||||
{
|
||||
return editable_;
|
||||
}
|
||||
|
||||
int textbox::font_size() const
|
||||
{
|
||||
return font_size_;
|
||||
}
|
||||
|
||||
void textbox::set_font_size(int fs)
|
||||
{
|
||||
font_size_ = fs;
|
||||
}
|
||||
|
||||
void textbox::scroll_to_bottom()
|
||||
{
|
||||
set_position(get_max_position());
|
||||
}
|
||||
|
||||
void textbox::set_wrap(bool val)
|
||||
{
|
||||
if(wrap_ != val) {
|
||||
wrap_ = val;
|
||||
update_text_cache(true);
|
||||
set_dirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
void textbox::scroll(unsigned int pos)
|
||||
{
|
||||
yscroll_ = pos;
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
surface textbox::add_text_line(const std::u32string& text, const color_t& color)
|
||||
{
|
||||
line_height_ = font::get_max_height(font_size_);
|
||||
|
||||
if(char_y_.empty()) {
|
||||
char_y_.push_back(0);
|
||||
} else {
|
||||
char_y_.push_back(char_y_.back() + line_height_);
|
||||
}
|
||||
|
||||
char_x_.push_back(0);
|
||||
|
||||
// Re-calculate the position of each glyph. We approximate this by asking the
|
||||
// width of each substring, but this is a flawed assumption which won't work with
|
||||
// some more complex scripts (that is, RTL languages). This part of the work should
|
||||
// actually be done by the font-rendering system.
|
||||
std::string visible_string;
|
||||
std::u32string wrapped_text;
|
||||
|
||||
std::u32string::const_iterator backup_itor = text.end();
|
||||
|
||||
std::u32string::const_iterator itor = text.begin();
|
||||
while(itor != text.end()) {
|
||||
//If this is a space, save copies of the current state so we can roll back
|
||||
if(char(*itor) == ' ') {
|
||||
backup_itor = itor;
|
||||
}
|
||||
visible_string.append(unicode_cast<std::string>(*itor));
|
||||
|
||||
if(char(*itor) == '\n') {
|
||||
backup_itor = text.end();
|
||||
visible_string = "";
|
||||
}
|
||||
|
||||
int w = font::line_width(visible_string, font_size_);
|
||||
|
||||
if(wrap_ && w >= inner_location().w) {
|
||||
if(backup_itor != text.end()) {
|
||||
int backup = itor - backup_itor;
|
||||
itor = backup_itor + 1;
|
||||
if(backup > 0) {
|
||||
char_x_.erase(char_x_.end()-backup, char_x_.end());
|
||||
char_y_.erase(char_y_.end()-backup, char_y_.end());
|
||||
wrapped_text.erase(wrapped_text.end()-backup, wrapped_text.end());
|
||||
}
|
||||
} else {
|
||||
if (visible_string == std::string("").append(unicode_cast<std::string>(*itor))) {
|
||||
break; //breaks infinite loop where when running with a fake display, we word wrap a single character infinitely.
|
||||
}
|
||||
}
|
||||
backup_itor = text.end();
|
||||
wrapped_text.push_back(char32_t('\n'));
|
||||
char_x_.push_back(0);
|
||||
char_y_.push_back(char_y_.back() + line_height_);
|
||||
visible_string = "";
|
||||
} else {
|
||||
wrapped_text.push_back(*itor);
|
||||
char_x_.push_back(w);
|
||||
char_y_.push_back(char_y_.back() + (char(*itor) == '\n' ? line_height_ : 0));
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string s = unicode_cast<std::string>(wrapped_text);
|
||||
const surface res(font::get_rendered_text(s, font_size_, color));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void textbox::update_text_cache(bool changed, const color_t& color)
|
||||
{
|
||||
if(changed) {
|
||||
char_x_.clear();
|
||||
char_y_.clear();
|
||||
|
||||
text_image_.assign(add_text_line(text_, color));
|
||||
}
|
||||
|
||||
int cursor_x = char_x_[cursor_];
|
||||
|
||||
if(cursor_x - text_pos_ > location().w) {
|
||||
text_pos_ = cursor_x - location().w;
|
||||
} else if(cursor_x - text_pos_ < 0) {
|
||||
text_pos_ = cursor_x;
|
||||
}
|
||||
cursor_pos_ = cursor_x - text_pos_;
|
||||
|
||||
if (!text_image_.null()) {
|
||||
set_full_size(text_image_->h);
|
||||
set_shown_size(location().h);
|
||||
}
|
||||
}
|
||||
|
||||
bool textbox::is_selection()
|
||||
{
|
||||
return (selstart_ != -1) && (selend_ != -1) && (selstart_ != selend_);
|
||||
}
|
||||
|
||||
void textbox::erase_selection()
|
||||
{
|
||||
if(!is_selection())
|
||||
return;
|
||||
|
||||
std::u32string::iterator itor = text_.begin() + std::min(selstart_, selend_);
|
||||
text_.erase(itor, itor + std::abs(selend_ - selstart_));
|
||||
cursor_ = std::min(selstart_, selend_);
|
||||
selstart_ = selend_ = -1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
const unsigned int copypaste_modifier =
|
||||
#ifdef __APPLE__
|
||||
KMOD_LGUI | KMOD_RGUI
|
||||
#else
|
||||
KMOD_CTRL
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
bool textbox::requires_event_focus(const SDL_Event* event) const
|
||||
{
|
||||
if(!focus_ || hidden() || !enabled()) {
|
||||
return false;
|
||||
}
|
||||
if(event == nullptr) {
|
||||
//when event is not specified, signal that focus may be desired later
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == SDL_KEYDOWN) {
|
||||
SDL_Keycode key = event->key.keysym.sym;
|
||||
switch(key) {
|
||||
case SDLK_UP:
|
||||
case SDLK_DOWN:
|
||||
case SDLK_PAGEUP:
|
||||
case SDLK_PAGEDOWN:
|
||||
//in the future we may need to check for input history or multi-line support
|
||||
//for now, just return false since these events are not handled.
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//mouse events are processed regardless of focus
|
||||
return false;
|
||||
}
|
||||
|
||||
void textbox::handle_event(const SDL_Event& event)
|
||||
{
|
||||
gui::widget::handle_event(event);
|
||||
handle_event(event, false);
|
||||
}
|
||||
|
||||
bool textbox::handle_text_input(const SDL_Event& event)
|
||||
{
|
||||
bool changed = false;
|
||||
std::string str = event.text.text;
|
||||
std::u32string s = unicode_cast<std::u32string>(str);
|
||||
|
||||
DBG_G << "Char: " << str << "\n";
|
||||
|
||||
if (editable_) {
|
||||
changed = true;
|
||||
if (is_selection())
|
||||
erase_selection();
|
||||
|
||||
if (text_.size() + 1 <= max_size_) {
|
||||
|
||||
text_.insert(text_.begin() + cursor_, s.begin(), s.end());
|
||||
cursor_ += s.size();
|
||||
}
|
||||
} else {
|
||||
pass_event_to_target(event);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool textbox::handle_key_down(const SDL_Event &event)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
const SDL_Keysym& key = reinterpret_cast<const SDL_KeyboardEvent&>(event).keysym;
|
||||
const SDL_Keymod modifiers = SDL_GetModState();
|
||||
|
||||
const int c = key.sym;
|
||||
const int old_cursor = cursor_;
|
||||
|
||||
listening_ = true;
|
||||
|
||||
if(editable_) {
|
||||
if(c == SDLK_LEFT && cursor_ > 0)
|
||||
--cursor_;
|
||||
|
||||
if(c == SDLK_RIGHT && cursor_ < static_cast<int>(text_.size()))
|
||||
++cursor_;
|
||||
|
||||
// ctrl-a, ctrl-e and ctrl-u are readline style shortcuts, even on Macs
|
||||
if(c == SDLK_END || (c == SDLK_e && (modifiers & KMOD_CTRL)))
|
||||
cursor_ = text_.size();
|
||||
|
||||
if(c == SDLK_HOME || (c == SDLK_a && (modifiers & KMOD_CTRL)))
|
||||
cursor_ = 0;
|
||||
|
||||
if((old_cursor != cursor_) && (modifiers & KMOD_SHIFT)) {
|
||||
if(selstart_ == -1)
|
||||
selstart_ = old_cursor;
|
||||
selend_ = cursor_;
|
||||
}
|
||||
} else if(c == SDLK_LEFT || c == SDLK_RIGHT || c == SDLK_END || c == SDLK_HOME) {
|
||||
pass_event_to_target(event);
|
||||
}
|
||||
|
||||
if(editable_) {
|
||||
if(c == SDLK_BACKSPACE) {
|
||||
changed = true;
|
||||
if(is_selection()) {
|
||||
erase_selection();
|
||||
} else if(cursor_ > 0) {
|
||||
--cursor_;
|
||||
text_.erase(text_.begin()+cursor_);
|
||||
}
|
||||
}
|
||||
|
||||
if(c == SDLK_u && (modifiers & KMOD_CTRL)) { // clear line
|
||||
changed = true;
|
||||
cursor_ = 0;
|
||||
text_.resize(0);
|
||||
}
|
||||
|
||||
if(c == SDLK_DELETE && !text_.empty()) {
|
||||
changed = true;
|
||||
if(is_selection()) {
|
||||
erase_selection();
|
||||
} else {
|
||||
if(cursor_ < static_cast<int>(text_.size())) {
|
||||
text_.erase(text_.begin()+cursor_);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(c == SDLK_BACKSPACE || c == SDLK_DELETE || (c == SDLK_u && (modifiers & KMOD_CTRL))) {
|
||||
pass_event_to_target(event);
|
||||
}
|
||||
|
||||
|
||||
//movement characters may have a "Unicode" field on some platforms, so ignore it.
|
||||
if(!(c == SDLK_UP || c == SDLK_DOWN || c == SDLK_LEFT || c == SDLK_RIGHT ||
|
||||
c == SDLK_DELETE || c == SDLK_BACKSPACE || c == SDLK_END || c == SDLK_HOME ||
|
||||
c == SDLK_PAGEUP || c == SDLK_PAGEDOWN)) {
|
||||
if((event.key.keysym.mod & copypaste_modifier)
|
||||
//on windows SDL fires for AltGr lctrl+ralt (needed to access @ etc on certain keyboards)
|
||||
#ifdef _WIN32
|
||||
&& !(event.key.keysym.mod & KMOD_ALT)
|
||||
#endif
|
||||
) {
|
||||
switch(c) {
|
||||
case SDLK_v: // paste
|
||||
{
|
||||
if(!editable()) {
|
||||
pass_event_to_target(event);
|
||||
break;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
if(is_selection())
|
||||
erase_selection();
|
||||
|
||||
std::string str = desktop::clipboard::copy_from_clipboard(false);
|
||||
|
||||
//cut off anything after the first newline
|
||||
str.erase(std::find_if(str.begin(),str.end(),utils::isnewline),str.end());
|
||||
|
||||
std::u32string s = unicode_cast<std::u32string>(str);
|
||||
|
||||
if(text_.size() < max_size_) {
|
||||
if(s.size() + text_.size() > max_size_) {
|
||||
s.resize(max_size_ - text_.size());
|
||||
}
|
||||
text_.insert(text_.begin()+cursor_, s.begin(), s.end());
|
||||
cursor_ += s.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SDLK_c: // copy
|
||||
{
|
||||
if(is_selection())
|
||||
{
|
||||
const std::size_t beg = std::min<std::size_t>(std::size_t(selstart_),std::size_t(selend_));
|
||||
const std::size_t end = std::max<std::size_t>(std::size_t(selstart_),std::size_t(selend_));
|
||||
|
||||
std::u32string ws(text_.begin() + beg, text_.begin() + end);
|
||||
std::string s = unicode_cast<std::string>(ws);
|
||||
desktop::clipboard::copy_to_clipboard(s, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pass_event_to_target(event);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void textbox::handle_event(const SDL_Event& event, bool was_forwarded)
|
||||
{
|
||||
if(!enabled())
|
||||
return;
|
||||
|
||||
scrollarea::handle_event(event);
|
||||
if(hidden())
|
||||
return;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
const int old_selstart = selstart_;
|
||||
const int old_selend = selend_;
|
||||
|
||||
//Sanity check: verify that selection start and end are within text
|
||||
//boundaries
|
||||
if(is_selection() && !(std::size_t(selstart_) <= text_.size() && std::size_t(selend_) <= text_.size())) {
|
||||
WRN_DP << "out-of-boundary selection" << std::endl;
|
||||
selstart_ = selend_ = -1;
|
||||
}
|
||||
|
||||
int mousex, mousey;
|
||||
const uint8_t mousebuttons = SDL_GetMouseState(&mousex,&mousey);
|
||||
if(!(mousebuttons & SDL_BUTTON(1))) {
|
||||
grabmouse_ = false;
|
||||
}
|
||||
|
||||
const SDL_Rect& loc = inner_location();
|
||||
bool clicked_inside = !mouse_locked() && (event.type == SDL_MOUSEBUTTONDOWN
|
||||
&& (mousebuttons & SDL_BUTTON(1))
|
||||
&& sdl::point_in_rect(mousex, mousey, loc));
|
||||
if(clicked_inside) {
|
||||
set_focus(true);
|
||||
}
|
||||
if ((grabmouse_ && (!mouse_locked() && event.type == SDL_MOUSEMOTION)) || clicked_inside) {
|
||||
const int x = mousex - loc.x + text_pos_;
|
||||
const int y = mousey - loc.y;
|
||||
int pos = 0;
|
||||
int distance = x;
|
||||
|
||||
for(unsigned int i = 1; i < char_x_.size(); ++i) {
|
||||
if(static_cast<int>(yscroll_) + y < char_y_[i]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check individually each distance (if, one day, we support
|
||||
// RTL languages, char_x_[c] may not be monotonous.)
|
||||
if(std::abs(x - char_x_[i]) < distance && yscroll_ + y < char_y_[i] + line_height_) {
|
||||
pos = i;
|
||||
distance = std::abs(x - char_x_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
cursor_ = pos;
|
||||
|
||||
if(grabmouse_)
|
||||
selend_ = cursor_;
|
||||
|
||||
update_text_cache(false);
|
||||
|
||||
if(!grabmouse_ && (mousebuttons & SDL_BUTTON(1))) {
|
||||
grabmouse_ = true;
|
||||
selstart_ = selend_ = cursor_;
|
||||
} else if (! (mousebuttons & SDL_BUTTON(1))) {
|
||||
grabmouse_ = false;
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
//if we don't have the focus, then see if we gain the focus,
|
||||
//otherwise return
|
||||
if(!was_forwarded && focus(&event) == false) {
|
||||
if (!mouse_locked() && event.type == SDL_MOUSEMOTION && sdl::point_in_rect(mousex, mousey, loc))
|
||||
events::focus_handler(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int old_cursor = cursor_;
|
||||
|
||||
if (event.type == SDL_TEXTINPUT && listening_) {
|
||||
changed = handle_text_input(event);
|
||||
} else
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
changed = handle_key_down(event);
|
||||
}
|
||||
else {
|
||||
if(event.type != SDL_KEYDOWN || (!was_forwarded && focus(&event) != true)) {
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(is_selection() && (selend_ != cursor_))
|
||||
selstart_ = selend_ = -1;
|
||||
|
||||
//since there has been cursor activity, make the cursor appear for
|
||||
//at least the next 500ms.
|
||||
show_cursor_ = true;
|
||||
show_cursor_at_ = SDL_GetTicks();
|
||||
|
||||
if(changed || old_cursor != cursor_ || old_selstart != selstart_ || old_selend != selend_) {
|
||||
text_image_ = nullptr;
|
||||
handle_text_changed(text_);
|
||||
}
|
||||
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
void textbox::pass_event_to_target(const SDL_Event& event)
|
||||
{
|
||||
if(edit_target_ && edit_target_->editable()) {
|
||||
edit_target_->handle_event(event, true);
|
||||
}
|
||||
}
|
||||
|
||||
void textbox::set_edit_target(textbox* target)
|
||||
{
|
||||
edit_target_ = target;
|
||||
}
|
||||
|
||||
} //end namespace gui
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "serialization/unicode.hpp"
|
||||
#include "font/constants.hpp"
|
||||
#include "font/standard_colors.hpp"
|
||||
|
||||
#include "scrollarea.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
class textbox : public scrollarea
|
||||
{
|
||||
public:
|
||||
textbox(CVideo &video, int width, const std::string& text="", bool editable=true, std::size_t max_size = 256, int font_size = font::SIZE_PLUS, double alpha = 0.4, double alpha_focus = 0.2, const bool auto_join = true);
|
||||
virtual ~textbox();
|
||||
|
||||
const std::string text() const;
|
||||
void set_text(const std::string& text, const color_t& color =font::NORMAL_COLOR);
|
||||
void append_text(const std::string& text,bool auto_scroll = false, const color_t& color =font::NORMAL_COLOR);
|
||||
void clear();
|
||||
|
||||
void set_selection(const int selstart, const int selend);
|
||||
void set_cursor_pos(const int cursor_pos);
|
||||
|
||||
void set_editable(bool value);
|
||||
bool editable() const;
|
||||
|
||||
int font_size() const;
|
||||
void set_font_size(int fs);
|
||||
|
||||
void scroll_to_bottom();
|
||||
|
||||
void set_wrap(bool val);
|
||||
|
||||
void set_edit_target(textbox* target);
|
||||
|
||||
protected:
|
||||
virtual void draw_contents();
|
||||
virtual void update_location(const SDL_Rect& rect);
|
||||
virtual void set_inner_location(const SDL_Rect& );
|
||||
virtual void scroll(unsigned int pos);
|
||||
|
||||
private:
|
||||
virtual void handle_text_changed(const std::u32string&) {}
|
||||
|
||||
std::size_t max_size_;
|
||||
|
||||
int font_size_;
|
||||
|
||||
std::u32string text_;
|
||||
|
||||
// mutable unsigned int firstOnScreen_;
|
||||
int cursor_;
|
||||
int selstart_;
|
||||
int selend_;
|
||||
bool grabmouse_;
|
||||
|
||||
int text_pos_;
|
||||
int cursor_pos_;
|
||||
std::vector<int> char_x_, char_y_;
|
||||
|
||||
bool editable_;
|
||||
|
||||
bool show_cursor_;
|
||||
|
||||
//records the time the cursor was shown at last
|
||||
//the cursor should be inverted every 500 ms.
|
||||
//this will be reset when keyboard input events occur
|
||||
int show_cursor_at_;
|
||||
surface text_image_;
|
||||
|
||||
bool wrap_;
|
||||
|
||||
std::size_t line_height_, yscroll_;
|
||||
|
||||
double alpha_;
|
||||
double alpha_focus_;
|
||||
|
||||
textbox* edit_target_;
|
||||
|
||||
/* This boolean is used to filter out any TextInput events that are received without
|
||||
* the corresponding KeyPress events. This is needed to avoid a bug when creating a
|
||||
* textbox using a hotkey.
|
||||
* */
|
||||
bool listening_;
|
||||
|
||||
void handle_event(const SDL_Event& event, bool was_forwarded);
|
||||
|
||||
void handle_event(const SDL_Event& event);
|
||||
|
||||
void pass_event_to_target(const SDL_Event& event);
|
||||
|
||||
void draw_cursor(int pos) const;
|
||||
void update_text_cache(bool reset = false, const color_t& color =font::NORMAL_COLOR);
|
||||
surface add_text_line(const std::u32string& text, const color_t& color =font::NORMAL_COLOR);
|
||||
bool is_selection();
|
||||
void erase_selection();
|
||||
|
||||
//make it so that only one textbox object can be receiving
|
||||
//events at a time.
|
||||
bool requires_event_focus(const SDL_Event *event=nullptr) const;
|
||||
|
||||
bool show_scrollbar() const;
|
||||
bool handle_text_input(const SDL_Event& event);
|
||||
bool handle_key_down(const SDL_Event &event);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
/*
|
||||
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project https://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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "widgets/widget.hpp"
|
||||
#include "video.hpp"
|
||||
#include "sdl/rect.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace {
|
||||
const SDL_Rect EmptyRect {-1234,-1234,0,0};
|
||||
}
|
||||
|
||||
namespace gui {
|
||||
|
||||
bool widget::mouse_lock_ = false;
|
||||
|
||||
widget::widget(const widget &o)
|
||||
: events::sdl_handler(), focus_(o.focus_), video_(o.video_), restorer_(o.restorer_), rect_(o.rect_),
|
||||
needs_restore_(o.needs_restore_), state_(o.state_), hidden_override_(o.hidden_override_),
|
||||
enabled_(o.enabled_), clip_(o.clip_), clip_rect_(o.clip_rect_), volatile_(o.volatile_),
|
||||
help_text_(o.help_text_), tooltip_text_(o.tooltip_text_), help_string_(o.help_string_), id_(o.id_), mouse_lock_local_(o.mouse_lock_local_)
|
||||
{
|
||||
}
|
||||
|
||||
widget::widget(CVideo& video, const bool auto_join)
|
||||
: events::sdl_handler(auto_join), focus_(true), video_(&video), rect_(EmptyRect), needs_restore_(false),
|
||||
state_(UNINIT), hidden_override_(false), enabled_(true), clip_(false),
|
||||
clip_rect_(EmptyRect), volatile_(false), help_string_(0), mouse_lock_local_(false)
|
||||
{
|
||||
}
|
||||
|
||||
widget::~widget()
|
||||
{
|
||||
bg_cancel();
|
||||
free_mouse_lock();
|
||||
}
|
||||
|
||||
void widget::aquire_mouse_lock()
|
||||
{
|
||||
assert(!mouse_lock_);
|
||||
mouse_lock_ = true;
|
||||
mouse_lock_local_ = true;
|
||||
}
|
||||
|
||||
void widget::free_mouse_lock()
|
||||
{
|
||||
if (mouse_lock_local_)
|
||||
{
|
||||
mouse_lock_local_ = false;
|
||||
mouse_lock_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool widget::mouse_locked() const
|
||||
{
|
||||
return mouse_lock_ && !mouse_lock_local_;
|
||||
}
|
||||
|
||||
void widget::bg_cancel()
|
||||
{
|
||||
for(std::vector< surface_restorer >::iterator i = restorer_.begin(),
|
||||
i_end = restorer_.end(); i != i_end; ++i)
|
||||
i->cancel();
|
||||
restorer_.clear();
|
||||
}
|
||||
|
||||
void widget::set_location(const SDL_Rect& rect)
|
||||
{
|
||||
if(rect_.x == rect.x && rect_.y == rect.y && rect_.w == rect.w && rect_.h == rect.h)
|
||||
return;
|
||||
if(state_ == UNINIT && rect.x != -1234 && rect.y != -1234)
|
||||
state_ = DRAWN;
|
||||
|
||||
bg_restore();
|
||||
bg_cancel();
|
||||
rect_ = rect;
|
||||
set_dirty(true);
|
||||
update_location(rect);
|
||||
}
|
||||
|
||||
void widget::update_location(const SDL_Rect& rect)
|
||||
{
|
||||
bg_register(rect);
|
||||
}
|
||||
|
||||
const SDL_Rect* widget::clip_rect() const
|
||||
{
|
||||
return clip_ ? &clip_rect_ : nullptr;
|
||||
}
|
||||
|
||||
void widget::bg_register(const SDL_Rect& rect)
|
||||
{
|
||||
restorer_.emplace_back(&video(), rect);
|
||||
}
|
||||
|
||||
void widget::set_location(int x, int y)
|
||||
{
|
||||
set_location({x, y, rect_.w, rect_.h});
|
||||
}
|
||||
|
||||
void widget::set_width(int w)
|
||||
{
|
||||
set_location({rect_.x, rect_.y, w, rect_.h});
|
||||
}
|
||||
|
||||
void widget::set_height(int h)
|
||||
{
|
||||
set_location({rect_.x, rect_.y, rect_.w, h});
|
||||
}
|
||||
|
||||
void widget::set_measurements(int w, int h)
|
||||
{
|
||||
set_location({rect_.x, rect_.y, w, h});
|
||||
}
|
||||
|
||||
int widget::width() const
|
||||
{
|
||||
return rect_.w;
|
||||
}
|
||||
|
||||
int widget::height() const
|
||||
{
|
||||
return rect_.h;
|
||||
}
|
||||
|
||||
const SDL_Rect& widget::location() const
|
||||
{
|
||||
return rect_;
|
||||
}
|
||||
|
||||
void widget::set_focus(bool focus)
|
||||
{
|
||||
if (focus)
|
||||
events::focus_handler(this);
|
||||
focus_ = focus;
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
bool widget::focus(const SDL_Event* event)
|
||||
{
|
||||
return events::has_focus(this, event) && focus_;
|
||||
}
|
||||
|
||||
void widget::hide(bool value)
|
||||
{
|
||||
if (value) {
|
||||
if ((state_ == DIRTY || state_ == DRAWN) && !hidden_override_)
|
||||
bg_restore();
|
||||
state_ = HIDDEN;
|
||||
} else if (state_ == HIDDEN) {
|
||||
state_ = DRAWN;
|
||||
if (!hidden_override_) {
|
||||
bg_update();
|
||||
set_dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void widget::hide_override(bool value) {
|
||||
if (hidden_override_ != value) {
|
||||
hidden_override_ = value;
|
||||
if (state_ == DIRTY || state_ == DRAWN) {
|
||||
if (value) {
|
||||
bg_restore();
|
||||
} else {
|
||||
bg_update();
|
||||
set_dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void widget::set_clip_rect(const SDL_Rect& rect)
|
||||
{
|
||||
clip_rect_ = rect;
|
||||
clip_ = true;
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
bool widget::hidden() const
|
||||
{
|
||||
return (state_ == HIDDEN || hidden_override_ || state_ == UNINIT
|
||||
|| (clip_ && !sdl::rects_overlap(clip_rect_, rect_)));
|
||||
}
|
||||
|
||||
void widget::enable(bool new_val)
|
||||
{
|
||||
if (enabled_ != new_val) {
|
||||
enabled_ = new_val;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool widget::enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void widget::set_dirty(bool dirty)
|
||||
{
|
||||
if ((dirty && (volatile_ || hidden_override_ || state_ != DRAWN)) || (!dirty && state_ != DIRTY))
|
||||
return;
|
||||
|
||||
state_ = dirty ? DIRTY : DRAWN;
|
||||
if (!dirty)
|
||||
needs_restore_ = true;
|
||||
}
|
||||
|
||||
bool widget::dirty() const
|
||||
{
|
||||
return state_ == DIRTY;
|
||||
}
|
||||
|
||||
const std::string& widget::id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
void widget::set_id(const std::string& id)
|
||||
{
|
||||
if (id_.empty()){
|
||||
id_ = id;
|
||||
}
|
||||
}
|
||||
|
||||
void widget::bg_update()
|
||||
{
|
||||
for(std::vector< surface_restorer >::iterator i = restorer_.begin(),
|
||||
i_end = restorer_.end(); i != i_end; ++i)
|
||||
i->update();
|
||||
}
|
||||
|
||||
void widget::bg_restore() const
|
||||
{
|
||||
clip_rect_setter clipper(video().getSurface(), &clip_rect_, clip_);
|
||||
|
||||
if (needs_restore_) {
|
||||
for(std::vector< surface_restorer >::const_iterator i = restorer_.begin(),
|
||||
i_end = restorer_.end(); i != i_end; ++i)
|
||||
i->restore();
|
||||
needs_restore_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void widget::bg_restore(const SDL_Rect& rect) const
|
||||
{
|
||||
clip_rect_setter clipper(video().getSurface(), &clip_rect_, clip_);
|
||||
|
||||
for(std::vector< surface_restorer >::const_iterator i = restorer_.begin(),
|
||||
i_end = restorer_.end(); i != i_end; ++i)
|
||||
i->restore(rect);
|
||||
}
|
||||
|
||||
void widget::set_volatile(bool val)
|
||||
{
|
||||
volatile_ = val;
|
||||
if (volatile_ && state_ == DIRTY)
|
||||
state_ = DRAWN;
|
||||
}
|
||||
|
||||
void widget::draw()
|
||||
{
|
||||
if (hidden() || !dirty())
|
||||
return;
|
||||
|
||||
bg_restore();
|
||||
|
||||
clip_rect_setter clipper(video().getSurface(), &clip_rect_, clip_);
|
||||
|
||||
draw_contents();
|
||||
|
||||
set_dirty(false);
|
||||
}
|
||||
|
||||
void widget::volatile_draw()
|
||||
{
|
||||
if (!volatile_ || state_ != DRAWN || hidden_override_)
|
||||
return;
|
||||
state_ = DIRTY;
|
||||
bg_update();
|
||||
draw();
|
||||
}
|
||||
|
||||
void widget::volatile_undraw()
|
||||
{
|
||||
if (!volatile_)
|
||||
return;
|
||||
bg_restore();
|
||||
}
|
||||
|
||||
void widget::set_help_string(const std::string& str)
|
||||
{
|
||||
help_text_ = str;
|
||||
}
|
||||
|
||||
void widget::set_tooltip_string(const std::string& str)
|
||||
{
|
||||
tooltip_text_ = str;
|
||||
}
|
||||
|
||||
void widget::process_help_string(int mousex, int mousey)
|
||||
{
|
||||
if (!hidden() && sdl::point_in_rect(mousex, mousey, rect_)) {
|
||||
if(help_string_ == 0 && !help_text_.empty()) {
|
||||
//std::cerr << "setting help string to '" << help_text_ << "'\n";
|
||||
help_string_ = video().set_help_string(help_text_);
|
||||
}
|
||||
} else if(help_string_ > 0) {
|
||||
video().clear_help_string(help_string_);
|
||||
help_string_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void widget::process_tooltip_string(int mousex, int mousey)
|
||||
{
|
||||
if (!hidden() && sdl::point_in_rect(mousex, mousey, rect_)) {
|
||||
if (!tooltip_text_.empty())
|
||||
tooltips::add_tooltip(rect_, tooltip_text_ );
|
||||
}
|
||||
}
|
||||
|
||||
void widget::handle_event(const SDL_Event& event) {
|
||||
if (event.type == DRAW_ALL_EVENT) {
|
||||
set_dirty();
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
void widget::handle_window_event(const SDL_Event& event) {
|
||||
if (event.type == SDL_WINDOWEVENT) {
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project https://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 as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "events.hpp"
|
||||
#include "sdl/surface.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class CVideo;
|
||||
|
||||
namespace gui {
|
||||
|
||||
class widget : public events::sdl_handler
|
||||
{
|
||||
public:
|
||||
const SDL_Rect& location() const;
|
||||
virtual void set_location(const SDL_Rect& rect);
|
||||
void set_location(int x, int y);
|
||||
void set_width(int w);
|
||||
void set_height(int h);
|
||||
void set_measurements(int w, int h);
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
//focus() may gain the focus if the currently focused handler doesn't require this event
|
||||
bool focus(const SDL_Event* event);
|
||||
void set_focus(bool focus);
|
||||
|
||||
virtual void hide(bool value = true);
|
||||
bool hidden() const;
|
||||
virtual void enable(bool new_val = true);
|
||||
bool enabled() const;
|
||||
|
||||
void set_clip_rect(const SDL_Rect& rect);
|
||||
|
||||
//Function to set the widget to draw in 'volatile' mode.
|
||||
//When in 'volatile' mode, instead of using the normal
|
||||
//save-background-redraw-when-dirty procedure, redrawing is done
|
||||
//every frame, and then after every frame the area under the widget
|
||||
//is restored to the state it was in before the frame. This is useful
|
||||
//for drawing widgets with alpha components in volatile settings where
|
||||
//the background may change at any time.
|
||||
//(e.g. for putting widgets on top of the game map)
|
||||
void set_volatile(bool val=true);
|
||||
|
||||
void set_dirty(bool dirty=true);
|
||||
bool dirty() const;
|
||||
const std::string& id() const;
|
||||
void set_id(const std::string& id);
|
||||
|
||||
void set_help_string(const std::string& str);
|
||||
void set_tooltip_string(const std::string& str);
|
||||
|
||||
virtual void process_help_string(int mousex, int mousey);
|
||||
virtual void process_tooltip_string(int mousex, int mousey);
|
||||
|
||||
protected:
|
||||
widget(const widget& o);
|
||||
widget(CVideo& video, const bool auto_join=true);
|
||||
virtual ~widget();
|
||||
|
||||
// During each relocation, this function should be called to register
|
||||
// the rectangles the widget needs to refresh automatically
|
||||
void bg_register(const SDL_Rect& rect);
|
||||
void bg_restore() const;
|
||||
void bg_restore(const SDL_Rect& rect) const;
|
||||
void bg_update();
|
||||
void bg_cancel();
|
||||
|
||||
CVideo& video() const { return *video_; }
|
||||
|
||||
public:
|
||||
virtual void draw();
|
||||
protected:
|
||||
virtual void draw_contents() {}
|
||||
virtual void update_location(const SDL_Rect& rect);
|
||||
|
||||
const SDL_Rect* clip_rect() const;
|
||||
virtual sdl_handler_vector member_handlers() { return sdl_handler::handler_members(); }
|
||||
|
||||
virtual void handle_event(const SDL_Event&);
|
||||
virtual void handle_window_event(const SDL_Event& event);
|
||||
bool focus_; // Should user input be ignored?
|
||||
|
||||
bool mouse_locked() const;
|
||||
|
||||
void aquire_mouse_lock();
|
||||
void free_mouse_lock();
|
||||
private:
|
||||
void volatile_draw();
|
||||
void volatile_undraw();
|
||||
|
||||
void hide_override(bool value = true);
|
||||
|
||||
CVideo* video_;
|
||||
std::vector< surface_restorer > restorer_;
|
||||
SDL_Rect rect_;
|
||||
mutable bool needs_restore_; // Have we drawn ourselves, so that if moved, we need to restore the background?
|
||||
|
||||
enum { UNINIT, HIDDEN, DIRTY, DRAWN } state_;
|
||||
bool hidden_override_;
|
||||
bool enabled_;
|
||||
bool clip_;
|
||||
SDL_Rect clip_rect_;
|
||||
|
||||
bool volatile_;
|
||||
|
||||
std::string help_text_;
|
||||
std::string tooltip_text_;
|
||||
int help_string_;
|
||||
std::string id_;
|
||||
|
||||
bool mouse_lock_local_;
|
||||
static bool mouse_lock_;
|
||||
|
||||
friend class dialog;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue