Refactoring the listbox widget.

Created a new widget to hold most of the knowlegde of the scrollbar so
it can be reused for other widgets. Still needs more work which will
follow in separate commits.
This commit is contained in:
Mark de Wever 2008-09-10 06:47:28 +00:00
parent 7fdab031f7
commit 508d237a41
10 changed files with 461 additions and 293 deletions

View file

@ -1,3 +1,7 @@
Version 1.5.4+svn:
* User interface:
* Various minor cleanups and refactoring of the new widgets.
Version 1.5.4:
* Editor2:
* Rotate clipboard 60 deg. cw/ccw, ctrl+r/ctrl+shift+r respectively

View file

@ -34,6 +34,7 @@ src/gui/widgets/toggle_button.cpp
src/gui/widgets/toggle_panel.cpp
src/gui/widgets/tooltip.cpp
src/gui/widgets/vertical_scrollbar.cpp
src/gui/widgets/vertical_scrollbar_container.cpp
src/gui/widgets/widget.cpp
src/gui/widgets/window_builder.cpp
src/gui/widgets/window.cpp

View file

@ -258,6 +258,7 @@ SET(wesnoth-main_SRC
gui/widgets/toggle_panel.cpp
gui/widgets/tooltip.cpp
gui/widgets/vertical_scrollbar.cpp
gui/widgets/vertical_scrollbar_container.cpp
gui/widgets/widget.cpp
gui/widgets/window.cpp
gui/widgets/window_builder.cpp

View file

@ -101,6 +101,7 @@ wesnoth_source = \
gui/widgets/toggle_panel.cpp \
gui/widgets/tooltip.cpp \
gui/widgets/vertical_scrollbar.cpp \
gui/widgets/vertical_scrollbar_container.cpp \
gui/widgets/widget.cpp \
gui/widgets/window.cpp \
gui/widgets/window_builder.cpp \

View file

@ -231,6 +231,7 @@ wesnoth_sources = Split("""
gui/widgets/toggle_panel.cpp
gui/widgets/tooltip.cpp
gui/widgets/vertical_scrollbar.cpp
gui/widgets/vertical_scrollbar_container.cpp
gui/widgets/widget.cpp
gui/widgets/window.cpp
gui/widgets/window_builder.cpp

View file

@ -15,7 +15,6 @@
#include "gui/widgets/listbox.hpp"
#include "foreach.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/event_handler.hpp"
#include "gui/widgets/helper.hpp"
#include "gui/widgets/scrollbar.hpp"
@ -64,18 +63,8 @@ static void callback_select_list_item(twidget* caller)
get_listbox(caller)->list_item_selected(caller);
}
static void callback_scrollbar(twidget* caller)
{
get_listbox(caller)->scrollbar_moved(caller);
}
static void callback_scrollbar_button(twidget* caller)
{
get_listbox(caller)->scrollbar_click(caller);
}
tlistbox::tlistbox() :
tcontainer_(COUNT),
tvertical_scrollbar_container_(COUNT),
state_(ENABLED),
list_builder_(0),
assume_fixed_row_size_(true),
@ -87,7 +76,6 @@ tlistbox::tlistbox() :
list_rect_(),
list_background_(),
best_spacer_size_(0, 0),
callback_value_change_(NULL),
rows_()
{
}
@ -98,9 +86,9 @@ void tlistbox::list_item_selected(twidget* caller)
get_window()->keyboard_capture(this);
if(assume_fixed_row_size_) {
for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
for(unsigned i = 0; i < find_scrollbar()->get_visible_items(); ++i) {
const unsigned row = i + scrollbar()->get_item_position();
const unsigned row = i + find_scrollbar()->get_item_position();
if(list_row_selected(row, caller)) {
return;
@ -119,34 +107,6 @@ void tlistbox::list_item_selected(twidget* caller)
assert(false);
}
void tlistbox::scrollbar_click(twidget* caller)
{
/** @todo Hack to capture the keyboard focus. */
get_window()->keyboard_capture(this);
if(caller->id() == "_begin") {
scrollbar()->scroll(tscrollbar_::BEGIN);
} else if(caller->id() == "_line_up") {
scrollbar()->scroll(tscrollbar_::ITEM_BACKWARDS);
} else if(caller->id() == "_half_page_up") {
scrollbar()->scroll(tscrollbar_::HALF_JUMP_BACKWARDS);
} else if(caller->id() == "_page_up") {
scrollbar()->scroll(tscrollbar_::JUMP_BACKWARDS);
} else if(caller->id() == "_end") {
scrollbar()->scroll(tscrollbar_::END);
} else if(caller->id() == "_line_down") {
scrollbar()->scroll(tscrollbar_::ITEM_FORWARD);
} else if(caller->id() == "_half_page_down") {
scrollbar()->scroll(tscrollbar_::HALF_JUMP_FORWARD);
} else if(caller->id() == "_page_down") {
scrollbar()->scroll(tscrollbar_::JUMP_FORWARD);
} else {
assert(false);
}
set_scrollbar_button_status();
}
void tlistbox::add_row(const std::map<
std::string /* member id */, t_string /* member value */>& item)
{
@ -173,14 +133,14 @@ void tlistbox::add_row(const std::map<std::string /* widget id */, std::map<
}
if(assume_fixed_row_size_) {
scrollbar()->set_item_count(get_item_count());
find_scrollbar()->set_item_count(get_item_count());
} else {
unsigned height = 0;
foreach(trow& row, rows_) {
height += row.get_height();
}
std::cerr << "Items " << height << ".\n";
scrollbar()->set_item_count(height);
find_scrollbar()->set_item_count(height);
}
set_scrollbar_button_status();
@ -196,36 +156,6 @@ void tlistbox::add_rows(const std::vector< std::map<std::string, t_string> >& da
}
}
bool tlistbox::select_row(const unsigned row, const bool select)
{
if(!select && must_select_ && selection_count_ < 2) {
return false;
}
if((select && rows_[row].get_selected())
|| (!select && !rows_[row].get_selected())) {
return true;
}
if(select && !multi_select_ && selection_count_ == 1) {
assert(selected_row_ < get_item_count());
rows_[selected_row_].set_selected(false);
--selection_count_;
}
if(select) {
++selection_count_;
} else {
--selection_count_;
}
assert(row < get_item_count());
selected_row_ = row;
rows_[row].set_selected();
return true;
}
void tlistbox::set_row_active(const unsigned row, const bool active)
{
assert(row < get_item_count());
@ -251,75 +181,6 @@ void tlistbox::mouse_left_button_down(tevent_handler& event)
event.keyboard_capture(this);
}
void tlistbox::key_press(tevent_handler& /*event*/, bool& handled,
SDLKey key, SDLMod /*modifier*/, Uint16 /*unicode*/)
{
DBG_G_E << "Listbox: key press.\n";
tscrollbar_* sb = scrollbar();
int row = get_selected_row();
switch(key) {
case SDLK_PAGEUP :
row -= sb->get_visible_items() - 1;
if(row <= 0) {
row = 1;
}
// FALL DOWN
case SDLK_UP :
--row;
while(row >= 0 && !rows_[row].get_active()) {
--row;
}
if(row >= 0) {
select_row(row);
handled = true;
if(row < sb->get_item_position()) {
sb->set_item_position(row);
set_scrollbar_button_status();
}
if(callback_value_change_) {
callback_value_change_(this);
}
}
break;
case SDLK_PAGEDOWN :
row += sb->get_visible_items() - 1;
if(row + 1 >= rows_.size()) {
row = rows_.size() - 2;
}
// FALL DOWN
case SDLK_DOWN :
++row;
while(row < rows_.size() && !rows_[row].get_active()) {
++row;
}
if(row < rows_.size()) {
select_row(row);
handled = true;
if(row >= sb->get_item_position() + sb->get_visible_items()) {
sb->set_item_position(row + 1 - sb->get_visible_items());
set_scrollbar_button_status();
}
if(callback_value_change_) {
callback_value_change_(this);
}
}
break;
default :
/* DO NOTHING */
break;
}
}
tpoint tlistbox::get_best_size(const tpoint& maximum_size) const
{
log_scope2(gui, "Listbox: Get best size");
@ -478,7 +339,9 @@ void tlistbox::set_size(const SDL_Rect& rect)
if(assume_fixed_row_size_) {
const unsigned row_height = rows_[0].grid()->get_best_size().y;
const unsigned orig_height = best_spacer_size_.y;
best_spacer_size_.y = (best_spacer_size_.y / row_height) * row_height;
best_spacer_size_.y =
(best_spacer_size_.y / row_height) * row_height;
best_rect.h -= (orig_height - best_spacer_size_.y);
}
// This call is required to update the best size.
@ -506,7 +369,7 @@ void tlistbox::set_size(const SDL_Rect& rect)
total_height += height;
}
if(!assume_fixed_row_size_) {
scrollbar()->set_item_count(total_height);
find_scrollbar()->set_item_count(total_height);
}
if(rows_.size() > 0) {
@ -514,16 +377,16 @@ void tlistbox::set_size(const SDL_Rect& rect)
const unsigned row_height = rows_[0].get_height();
if(row_height) {
const unsigned rows = list()->get_best_size().y / row_height;
scrollbar()->set_visible_items(rows);
find_scrollbar()->set_visible_items(rows);
} else {
WRN_G << "Listbox row 0 has no height, making all rows visible.\n";
scrollbar()->set_visible_items(rows_.size());
find_scrollbar()->set_visible_items(rows_.size());
}
} else {
scrollbar()->set_visible_items(best_spacer_size_.y);
find_scrollbar()->set_visible_items(best_spacer_size_.y);
}
} else {
scrollbar()->set_visible_items(1);
find_scrollbar()->set_visible_items(1);
}
set_scrollbar_button_status();
@ -531,64 +394,34 @@ void tlistbox::set_size(const SDL_Rect& rect)
best_spacer_size_ = tpoint(0, 0);
}
void tlistbox::set_scrollbar_button_status()
bool tlistbox::select_row(const unsigned row, const bool select)
{
// Set scroll up button status
static std::vector<std::string> button_up_names;
if(button_up_names.empty()) {
button_up_names.push_back("_begin");
button_up_names.push_back("_line_up");
button_up_names.push_back("_half_page_up");
button_up_names.push_back("_page_up");
if(!select && must_select_ && selection_count_ < 2) {
return false;
}
foreach(const std::string& name, button_up_names) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
if(button) {
button->set_active(!scrollbar()->at_begin());
}
if((select && rows_[row].get_selected())
|| (!select && !rows_[row].get_selected())) {
return true;
}
// Set scroll down button status
static std::vector<std::string> button_down_names;
if(button_down_names.empty()) {
button_down_names.push_back("_end");
button_down_names.push_back("_line_down");
button_down_names.push_back("_half_page_down");
button_down_names.push_back("_page_down");
if(select && !multi_select_ && selection_count_ == 1) {
assert(selected_row_ < get_item_count());
rows_[selected_row_].set_selected(false);
--selection_count_;
}
foreach(const std::string& name, button_down_names) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
if(button) {
button->set_active(!scrollbar()->at_end());
}
if(select) {
++selection_count_;
} else {
--selection_count_;
}
// Set the scrollbar itself
scrollbar()->set_active(!(scrollbar()->at_begin() && scrollbar()->at_end()));
}
assert(row < get_item_count());
selected_row_ = row;
rows_[row].set_selected();
tscrollbar_* tlistbox::scrollbar()
{
// Note we don't cache the result, we might want change things later.
tscrollbar_* result =
dynamic_cast<tscrollbar_*>(tcontainer_::find_widget("_scrollbar", false));
assert(result);
return result;
}
const tscrollbar_* tlistbox::scrollbar() const
{
// Note we don't cache the result, we might want change things later.
const tscrollbar_* result =
dynamic_cast<const tscrollbar_*>(tcontainer_::find_widget("_scrollbar", false));
assert(result);
return result;
return true;
}
tspacer* tlistbox::list()
@ -613,9 +446,7 @@ bool tlistbox::list_row_selected(const size_t row, twidget* caller)
if(rows_[row].grid()->has_widget(caller)) {
if(select_row(row, !rows_[row].get_selected())) {
if(callback_value_change_) {
callback_value_change_(this);
}
value_changed();
} else {
// if not allowed to deselect reselect.
tselectable_* selectable = dynamic_cast<tselectable_*>(caller);
@ -633,10 +464,10 @@ void tlistbox::draw_list_area_fixed_row_height(surface& surface,
const bool force, const bool invalidate_background)
{
unsigned offset = list_rect_.y;
for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
for(unsigned i = 0; i < find_scrollbar()->get_visible_items(); ++i) {
// make sure we stay inside the valid range.
const unsigned index = i + scrollbar()->get_item_position();
const unsigned index = i + find_scrollbar()->get_item_position();
if(index >= rows_.size()) {
return;
}
@ -661,8 +492,8 @@ void tlistbox::draw_list_area_variable_row_height(surface& surface,
{
// Get the start and end offset to draw on.
const unsigned start = scrollbar()->get_item_position();
const unsigned end = start + scrollbar()->get_visible_items();
const unsigned start = find_scrollbar()->get_item_position();
const unsigned end = start + find_scrollbar()->get_visible_items();
unsigned offset = 0;
foreach(trow& row, rows_) {
@ -716,15 +547,15 @@ size_t tlistbox::row_at_offset(int offset, int& offset_in_widget) const
// The position of the scrollbar is the offset of rows,
// so add to the result.
offset_in_widget = offset % rows_[0].get_height();
const size_t result =
(offset / rows_[0].get_height()) + scrollbar()->get_item_position();
const size_t result = (offset / rows_[0].get_height())
+ find_scrollbar()->get_item_position();
return result < rows_.size() ? result : -1;
}
} else {
// The position of the scrollbar is the number of pixels scrolled,
// so add to the offset.
offset += scrollbar()->get_item_position();
offset += find_scrollbar()->get_item_position();
for(unsigned i = 0; i < rows_.size(); ++i) {
offset -= rows_[i].get_height();
@ -738,31 +569,11 @@ size_t tlistbox::row_at_offset(int offset, int& offset_in_widget) const
}
}
void tlistbox::finalize_setup()
bool tlistbox::get_item_active(const unsigned item) const
{
scrollbar()->set_callback_positioner_move(callback_scrollbar);
assert(item < rows_.size());
static std::vector<std::string> button_names;
if(button_names.empty()) {
button_names.push_back("_begin");
button_names.push_back("_line_up");
button_names.push_back("_half_page_up");
button_names.push_back("_page_up");
button_names.push_back("_end");
button_names.push_back("_line_down");
button_names.push_back("_half_page_down");
button_names.push_back("_page_down");
}
foreach(const std::string& name, button_names) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
if(button) {
button->set_callback_mouse_left_click(callback_scrollbar_button);
}
}
return rows_[item].get_active();
}
tlistbox::trow::trow(const tbuilder_grid& list_builder_,
@ -846,6 +657,5 @@ void tlistbox::trow::select_in_grid(tgrid* grid, const bool selected)
}
}
} // namespace gui2

View file

@ -15,12 +15,11 @@
#ifndef GUI_WIDGETS_LISTBOX_HPP_INCLUDED
#define GUI_WIDGETS_LISTBOX_HPP_INCLUDED
#include "tstring.hpp"
#include "gui/widgets/container.hpp"
#include "tstring.hpp" //NEEDED?
#include "gui/widgets/vertical_scrollbar_container.hpp"
namespace gui2 {
class tscrollbar_;
class tspacer;
/**
@ -33,9 +32,8 @@ class tspacer;
*/
/** The listbox class. */
class tlistbox : public tcontainer_
class tlistbox : public tvertical_scrollbar_container_
{
friend class tbuilder_listbox;
public:
tlistbox();
@ -47,16 +45,6 @@ public:
*/
void list_item_selected(twidget* caller);
/** Callback when the scrollbar moves. */
void scrollbar_moved(twidget* /*caller*/)
{ set_scrollbar_button_status(); set_dirty(); }
/**
* When an item scrollbar control button is clicked we need to move the
* scrollbar and update the list.
*/
void scrollbar_click(twidget* caller);
/***** ***** ***** row handling ****** *****/
/**
@ -105,17 +93,6 @@ public:
unsigned get_item_count() const { return rows_.size(); }
/**
* Selects an entire row.
*
* @param row The row to (de)select.
* @param select true select, false deselect.
*
* @returns false if deselecting wasn't allowed.
* true otherwise.
*/
bool select_row(const unsigned row, const bool select = true);
/**
* Makes a row active or inactive.
*
@ -145,16 +122,9 @@ public:
/** Inherited from tevent_executor. */
void mouse_left_button_down(tevent_handler& event);
/** Inherited from tevent_executor. */
void key_press(tevent_handler& event, bool& handled,
SDLKey key, SDLMod modifier, Uint16 unicode);
/** Inherited from twidget. */
tpoint get_best_size(const tpoint& maximum_size) const;
/** Inherited from twidget. */
bool has_vertical_scrollbar() const { return true; }
/** Inherited from tcontainer_. */
tpoint get_best_size() const;
@ -170,7 +140,7 @@ public:
const bool must_be_active) const;
/** Import overloaded versions. */
using tcontainer_::find_widget;
using tvertical_scrollbar_container_::find_widget;
/** Inherited from tcontainer_. */
void set_size(const SDL_Rect& rect);
@ -179,15 +149,15 @@ public:
void set_self_active(const bool active)
{ state_ = active ? ENABLED : DISABLED; }
/** Inherited from tvertical_scrollbar_container_. */
bool select_row(const unsigned row, const bool select = true);
/***** ***** ***** setters / getters for members ***** ****** *****/
bool get_active() const { return state_ != DISABLED; }
unsigned get_state() const { return state_; }
void set_callback_value_change(void (*callback) (twidget* caller))
{ callback_value_change_ = callback; }
void set_list_builder(tbuilder_grid_ptr list_builder)
{ list_builder_ = list_builder; }
@ -198,14 +168,6 @@ public:
private:
/**
* Sets the status of the scrollbar buttons.
*
* This is needed after the scrollbar moves so the status of the buttons
* will be active or inactive as needed.
*/
void set_scrollbar_button_status();
/**
* Possible states of the widget.
*
@ -230,12 +192,6 @@ private:
*/
tbuilder_grid_ptr list_builder_;
/** Returns the scrollbar widget. */
tscrollbar_* scrollbar();
/** Returns the scrollbar widget. */
const tscrollbar_* scrollbar() const;
/** Returns the spacer widget which is used to reserve space of the real list. */
tspacer* list();
@ -291,13 +247,6 @@ private:
/** The best size for the spacer, if not set it's calculated. */
tpoint best_spacer_size_;
/**
* This callback is used when the selection is changed due to a user event.
* The name is not fully appropriate for the event but it's choosen to be
* generic.
*/
void (*callback_value_change_) (twidget* caller);
/**
* Draws the list area if assume_fixed_row_size_ is true.
*
@ -326,10 +275,10 @@ private:
* @returns The row number in which the widget was found.
* @retval -1 If the offset wasn't found.
*/
size_t row_at_offset(int offset, int& offset_in_widget) const;
size_t row_at_offset(int offset, int& offset_in_widget) const;
/** The builder needs to call us so we can write in the proper callbacks. */
void finalize_setup();
/** Inherited. */
bool get_item_active(const unsigned item) const;
/** Contains the info for a row in the listbox. */
class trow {

View file

@ -0,0 +1,234 @@
/* $Id$ */
/*
copyright (C) 2008 by mark de wever <koraq@xs4all.nl>
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 version 2
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 "gui/widgets/vertical_scrollbar_container.hpp"
#include "foreach.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/scrollbar.hpp"
#include "gui/widgets/window.hpp"
#include "log.hpp"
#define DBG_G LOG_STREAM_INDENT(debug, gui)
#define LOG_G LOG_STREAM_INDENT(info, gui)
#define WRN_G LOG_STREAM_INDENT(warn, gui)
#define ERR_G LOG_STREAM_INDENT(err, gui)
#define DBG_G_D LOG_STREAM_INDENT(debug, gui_draw)
#define LOG_G_D LOG_STREAM_INDENT(info, gui_draw)
#define WRN_G_D LOG_STREAM_INDENT(warn, gui_draw)
#define ERR_G_D LOG_STREAM_INDENT(err, gui_draw)
#define DBG_G_E LOG_STREAM_INDENT(debug, gui_event)
#define LOG_G_E LOG_STREAM_INDENT(info, gui_event)
#define WRN_G_E LOG_STREAM_INDENT(warn, gui_event)
#define ERR_G_E LOG_STREAM_INDENT(err, gui_event)
#define DBG_G_P LOG_STREAM_INDENT(debug, gui_parse)
#define LOG_G_P LOG_STREAM_INDENT(info, gui_parse)
#define WRN_G_P LOG_STREAM_INDENT(warn, gui_parse)
#define ERR_G_P LOG_STREAM_INDENT(err, gui_parse)
namespace gui2 {
void callback_scrollbar_button(twidget* caller)
{
get_parent<tvertical_scrollbar_container_>(caller)->scrollbar_click(caller);
}
void callback_scrollbar(twidget* caller)
{
get_parent<tvertical_scrollbar_container_>(caller)->scrollbar_moved(caller);
}
namespace {
static const std::string button_up_names[] = {
"_begin", "_line_up", "_half_page_up", "_page_up" };
static const std::string button_down_names[] = {
"_end", "_line_down", "_half_page_down", "_page_down" };
/**
* Returns a map with the names of all buttons and the scrollbar jump they're
* supposed to execute.
*/
const std::map<std::string, tscrollbar_::tscroll>& scroll_lookup()
{
static std::map<std::string, tscrollbar_::tscroll> lookup;
if(lookup.empty()) {
lookup["_begin"] = tscrollbar_::BEGIN;
lookup["_line_up"] = tscrollbar_::ITEM_BACKWARDS;
lookup["_half_page_up"] = tscrollbar_::HALF_JUMP_BACKWARDS;
lookup["_page_up"] = tscrollbar_::JUMP_BACKWARDS;
lookup["_end"] = tscrollbar_::END;
lookup["_line_down"] = tscrollbar_::ITEM_FORWARD;
lookup["_half_page_down"] = tscrollbar_::HALF_JUMP_FORWARD;
lookup["_page_down"] = tscrollbar_::JUMP_FORWARD;
}
return lookup;
}
} // namespace
void tvertical_scrollbar_container_::key_press(tevent_handler& /*event*/,
bool& handled, SDLKey key, SDLMod /*modifier*/, Uint16 /*unicode*/)
{
DBG_G_E << "Listbox: key press.\n";
tscrollbar_* sb = find_scrollbar();
int row = get_selected_row();
switch(key) {
case SDLK_PAGEUP :
row -= sb->get_visible_items() - 1;
if(row <= 0) {
row = 1;
}
// FALL DOWN
case SDLK_UP :
--row;
while(row >= 0 && !get_item_active(row)) {
--row;
}
if(row >= 0) {
select_row(row);
handled = true;
if(row < sb->get_item_position()) {
sb->set_item_position(row);
set_scrollbar_button_status();
}
value_changed();
}
break;
case SDLK_PAGEDOWN :
row += sb->get_visible_items() - 1;
if(row + 1 >= sb->get_item_count()) {
row = sb->get_item_count() - 2;
}
// FALL DOWN
case SDLK_DOWN :
++row;
while(row < sb->get_item_count() && !get_item_active(row)) {
++row;
}
if(row < sb->get_item_count()) {
select_row(row);
handled = true;
if(row >= sb->get_item_position() + sb->get_visible_items()) {
sb->set_item_position(row + 1 - sb->get_visible_items());
set_scrollbar_button_status();
}
value_changed();
}
break;
default :
/* DO NOTHING */
break;
}
}
void tvertical_scrollbar_container_::value_changed()
{
if(callback_value_change_) {
callback_value_change_(this);
}
}
tscrollbar_* tvertical_scrollbar_container_::find_scrollbar(const bool must_exist)
{
return find_widget<tscrollbar_>("_scrollbar", false, must_exist);
}
const tscrollbar_* tvertical_scrollbar_container_::find_scrollbar(
const bool must_exist) const
{
return find_widget<const tscrollbar_>("_scrollbar", false, must_exist);
}
void tvertical_scrollbar_container_::set_scrollbar_button_status()
{
// Set scroll up button status
foreach(const std::string& name, button_up_names) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
if(button) {
button->set_active(!find_scrollbar()->at_begin());
}
}
// Set scroll down button status
foreach(const std::string& name, button_down_names) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
if(button) {
button->set_active(!find_scrollbar()->at_end());
}
}
// Set the scrollbar itself
find_scrollbar()->set_active(
!(find_scrollbar()->at_begin() && find_scrollbar()->at_end()));
}
void tvertical_scrollbar_container_::finalize_setup()
{
find_scrollbar()->set_callback_positioner_move(callback_scrollbar);
// typedef boost problem work around.
typedef std::pair<std::string, tscrollbar_::tscroll> hack;
foreach(const hack& item, scroll_lookup()) {
tbutton* button =
dynamic_cast<tbutton*>(tcontainer_::find_widget(item.first, false));
if(button) {
button->set_callback_mouse_left_click(callback_scrollbar_button);
}
}
}
void tvertical_scrollbar_container_::scrollbar_click(twidget* caller)
{
/** @todo Hack to capture the keyboard focus. */
get_window()->keyboard_capture(this);
const std::map<std::string, tscrollbar_::tscroll>::const_iterator
itor = scroll_lookup().find(caller->id());
assert(itor != scroll_lookup().end());
find_scrollbar()->scroll(itor->second);
set_scrollbar_button_status();
}
unsigned tvertical_scrollbar_container_::get_selected_row() const
{
return find_scrollbar()->get_item_position();
}
} // namespace gui2

View file

@ -0,0 +1,145 @@
/* $Id$ */
/*
copyright (C) 2008 by mark de wever <koraq@xs4all.nl>
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 version 2
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.
*/
#ifndef GUI_WIDGETS_VERTICAL_SCROLLBAR_CONTAINER_HPP_INCLUDED
#define GUI_WIDGETS_VERTICAL_SCROLLBAR_CONTAINER_HPP_INCLUDED
#include "gui/widgets/container.hpp"
namespace gui2 {
class tscrollbar_;
/** Base class for creating containers with a vertical scrollbar. */
class tvertical_scrollbar_container_ : public tcontainer_
{
// Builders need to be able to finalize the object.
friend class tbuilder_listbox;
// Callbacks can call update rountines. Note these are not further declared
// here only need extrnal linkage to be friends.
friend void callback_scrollbar_button(twidget*);
friend void callback_scrollbar(twidget*);
public:
tvertical_scrollbar_container_(const unsigned canvas_count)
: tcontainer_(canvas_count),
callback_value_change_(NULL)
{
}
/***** ***** ***** inherited ****** *****/
/** Inherited from tevent_executor. */
void key_press(tevent_handler& event, bool& handled,
SDLKey key, SDLMod modifier, Uint16 unicode);
/** Inherited from twidget. */
bool has_vertical_scrollbar() const { return true; }
/**
* Selects an entire row.
*
* @param row The row to (de)select.
* @param select true select, false deselect.
*
* @returns false if deselecting wasn't allowed.
* true otherwise.
*/
virtual bool select_row(const unsigned row, const bool select = true) = 0;
/***** ***** ***** setters / getters for members ***** ****** *****/
void set_callback_value_change(void (*callback) (twidget* caller))
{ callback_value_change_ = callback; }
protected:
/**
* When the value of the selected item has been changed this function
* should be called.
*/
void value_changed();
/**
* Returns the scroll widget.
*
* This always returns the wdiget, regardless of the mode.
*
* @param must_exist If true the widget must exist and the
* function will fail if that's not the case. If
* true the pointer returned is always valid.
*
* @returns A pointer to the widget or NULL.
*/
tscrollbar_* find_scrollbar(const bool must_exist = true);
/** The const version. */
const tscrollbar_* find_scrollbar(const bool must_exist = true) const;
/**
* Sets the status of the scrollbar buttons.
*
* This is needed after the scrollbar moves so the status of the buttons
* will be active or inactive as needed.
*/
void set_scrollbar_button_status();
private:
/**
* This callback is used when the selection is changed due to a user event.
* The name is not fully appropriate for the event but it's choosen to be
* generic.
*/
void (*callback_value_change_) (twidget* caller);
/** The builder needs to call us so we can write in the proper callbacks. */
void finalize_setup();
/** Callback when the scrollbar moves. */
void scrollbar_moved(twidget* /*caller*/)
{ set_scrollbar_button_status(); set_dirty(); }
/**
* When an item scrollbar control button is clicked we need to move the
* scrollbar and update the list.
*/
void scrollbar_click(twidget* caller);
/**
* Is the wanted item active?
*
* Some subclasses might have items that can be inactive, those shouldn't
* be selected. So add a test here.
*
* @param item The item to check.
*
* @returns True active, false inactive.
*/
virtual bool get_item_active(const unsigned /*item*/) const { return true; }
/** Returns the selected row. */
virtual unsigned get_selected_row() const;
};
} // namespace gui2
#endif

View file

@ -22,6 +22,7 @@
#include "SDL.h"
#include <string>
#include <cassert>
namespace gui2 {
@ -641,6 +642,27 @@ private:
};
/**
* Returns the first parent of a widget with a certain type.
*
* @param widget The widget to get the parent from,
* @param T The class of the widget to return.
*
* @returns The parent widget.
*/
template<class T> T* get_parent(twidget* widget)
{
T* result;
do {
widget = widget->parent();
result = dynamic_cast<T*>(widget);
} while (widget && !result);
assert(result);
return result;
}
/**
* Small abstract helper class.
*