wesnoth/src/gui/widgets/window.hpp
2011-02-13 18:41:53 +00:00

660 lines
18 KiB
C++

/* $Id$ */
/*
Copyright (C) 2007 - 2011 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 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
* This file contains the window object, this object is a top level container
* which has the event management as well.
*/
#ifndef GUI_WIDGETS_WINDOW_HPP_INCLUDED
#define GUI_WIDGETS_WINDOW_HPP_INCLUDED
#include "cursor.hpp"
#include "gui/auxiliary/formula.hpp"
#include "gui/widgets/helper.hpp"
#include "gui/widgets/panel.hpp"
#include "events.hpp"
#include "SDL.h"
#include <string>
#include <boost/function.hpp>
class CVideo;
namespace gui2{
class tdialog;
class tdebug_layout_graph;
namespace event {
class tdistributor;
} // namespace event
/**
* base class of top level items, the only item
* which needs to store the final canvase to draw on
*/
class twindow
: public tpanel
, public cursor::setter
{
friend class tdebug_layout_graph;
friend twindow *build(CVideo &, const twindow_builder::tresolution *);
friend struct twindow_implementation;
friend class tinvalidate_layout_blocker;
public:
twindow(CVideo& video,
tformula<unsigned>x,
tformula<unsigned>y,
tformula<unsigned>w,
tformula<unsigned>h,
const bool automatic_placement,
const unsigned horizontal_placement,
const unsigned vertical_placement,
const unsigned maximum_width,
const unsigned maximum_height,
const std::string& definition,
const twindow_builder::tresolution::ttip& tooltip,
const twindow_builder::tresolution::ttip& helptip);
~twindow();
/**
* Update the size of the screen variables in settings.
*
* Before a window gets build the screen sizes need to be updates. This
* function does that. It's only done when no other window is active, if
* another window is active it already updates the sizes with it's resize
* event.
*/
static void update_screen_size();
/**
* Returns the intance of a window.
*
* @param handle The instance id of the window.
*
* @returns The window or NULL.
*/
static twindow* window_instance(const unsigned handle);
/**
* Default return values.
*
* These values are named return values and most are assigned to a widget
* automatically when using a certain id for that widget. The automatic
* return values are always a negative number.
*
* Note this might be moved somewhere else since it will force people to
* include the button, while it should be and implementation detail for most
* callers.
*/
enum tretval {
NONE = 0, /**<
* Dialog is closed with no return
* value, should be rare but eg a
* message popup can do it.
*/
OK = -1, /**< Dialog is closed with ok button. */
CANCEL = -2, /**<
* Dialog is closed with the cancel
* button.
*/
AUTO_CLOSE = -3 /**<
* The dialog is closed automatically
* since it's timeout has been
* triggered.
*/
};
/** Gets the retval for the default buttons. */
static tretval get_retval_by_id(const std::string& id);
/**
* Shows the window.
*
* @param restore Restore the screenarea the window was on
* after closing it?
* @param auto_close_timeout The time in ms after which the window will
* automatically close, if 0 it doesn't close.
* @note the timeout is a minimum time and
* there's no quarantee about how fast it closes
* after the minimum.
*
* @returns The close code of the window, predefined
* values are listed in tretval.
*/
int show(const bool restore = true,
const unsigned auto_close_timeout = 0);
/**
* Shows the window as a tooltip.
*
* A tooltip can't be interacted with and is just shown.
*
* @todo implement @p auto_close_timeout.
*
* @param auto_close_timeout The time in ms after which the window will
* automatically close, if 0 it doesn't close.
* @note the timeout is a minimum time and
* there's no quarantee about how fast it closes
* after the minimum.
*/
void show_tooltip(/*const unsigned auto_close_timeout = 0*/);
/**
* Draws the window.
*
* This routine draws the window if needed, it's called from the event
* handler. This is done by a drawing event. When a window is shown it
* manages an SDL timer which fires a drawing event every X milliseconds,
* that event calls this routine. Don't call it manually.
*/
void draw();
/**
* Undraws the window.
*/
void undraw();
/**
* Adds an item to the dirty_list_.
*
* @param call_stack The list of widgets traversed to get to the
* dirty wiget.
*/
void add_to_dirty_list(const std::vector<twidget*>& call_stack)
{
dirty_list_.push_back(call_stack);
}
/** The status of the window. */
enum tstatus{
NEW, /**< The window is new and not yet shown. */
SHOWING, /**< The window is being shown. */
REQUEST_CLOSE, /**< The window has been requested to be
* closed but still needs to evalueate the
* request.
*/
CLOSED /**< The window has been closed. */
};
/**
* Requests to close the window.
*
* At the moment the request is always honoured but that might change in the
* future.
*/
void close() { status_ = REQUEST_CLOSE; }
/**
* Helper class to block invalidate_layout.
*
* Some widgets can handling certain layout aspects without help. For
* example a listbox can handle hiding and showing rows without help but
* setting the visibility calls invalidate_layout(). When this blocker is
* instanciated the call to invalidate_layout() becomes a nop.
*
* @note The class can't be used recursively.
*/
class tinvalidate_layout_blocker
{
public:
tinvalidate_layout_blocker(twindow& window);
~tinvalidate_layout_blocker();
private:
twindow& window_;
};
/**
* Updates the size of the window.
*
* If the window has automatic placement set this function recacluates the
* window. To be used after creation and after modification or items which
* can have different sizes eg listboxes.
*/
void invalidate_layout();
/** Inherited from tevent_handler. */
twindow& get_window() { return *this; }
/** Inherited from tevent_handler. */
const twindow& get_window() const { return *this; }
/** Inherited from tevent_handler. */
twidget* find_at(const tpoint& coordinate, const bool must_be_active)
{ return tpanel::find_at(coordinate, must_be_active); }
/** Inherited from tevent_handler. */
const twidget* find_at(const tpoint& coordinate,
const bool must_be_active) const
{ return tpanel::find_at(coordinate, must_be_active); }
/** Inherited from twidget. */
tdialog* dialog() { return owner_; }
/** Inherited from tcontainer_. */
twidget* find(const std::string& id, const bool must_be_active)
{ return tcontainer_::find(id, must_be_active); }
/** Inherited from tcontainer_. */
const twidget* find(const std::string& id,
const bool must_be_active) const
{ return tcontainer_::find(id, must_be_active); }
#if 0
/** @todo Implement these functions. */
/**
* Register a widget that prevents easy closing.
*
* Duplicate registration are ignored. See click_dismiss_ for more info.
*
* @param id The id of the widget to register.
*/
void add_click_dismiss_blocker(const std::string& id);
/**
* Unregister a widget the prevents easy closing.
*
* Removing a non registered id is allowed but will do nothing. See
* click_dismiss_ for more info.
*
* @param id The id of the widget to register.
*/
void remove_click_dismiss_blocker(const std::string& id);
#endif
/**
* Does the window close easily?
*
* The behaviour can change at run-time, but that might cause oddities
* with the easy close button (when one is needed).
*
* @returns Whether or not the window closes easily.
*/
bool does_click_dismiss() const
{
return click_dismiss_ && !disable_click_dismiss();
}
/**
* Disable the enter key.
*
* This is added to block dialogs from being closed automatically.
*
* @todo this function should be merged with the hotkey support once
* that has been added.
*/
void set_enter_disabled(const bool enter_disabled)
{ enter_disabled_ = enter_disabled; }
/**
* Disable the escape key.
*
* This is added to block dialogs from being closed automatically.
*
* @todo this function should be merged with the hotkey support once
* that has been added.
*/
void set_escape_disabled(const bool escape_disabled)
{ escape_disabled_ = escape_disabled; }
/**
* Initializes a linked size group.
*
* Note at least one of fixed_width or fixed_height must be true.
*
* @param id The id of the group.
* @param fixed_width Does the group have a fixed width?
* @param fixed_height Does the group have a fixed height?
*/
void init_linked_size_group(const std::string& id,
const bool fixed_width, const bool fixed_height);
/**
* Is the linked size group defined for this window?
*
* @param id The id of the group.
*
* @returns True if defined, false otherwise.
*/
bool has_linked_size_group(const std::string& id);
/**
* Adds a widget to a linked size group.
*
* The group needs to exist, which is done by calling
* init_linked_size_group. A widget may only be member of one group.
* @todo Untested if a new widget is added after showing the widgets.
*
* @param id The id of the group.
* @param widget The widget to add to the group.
*/
void add_linked_widget(const std::string& id, twidget* widget);
/**
* Removes a widget from a linked size group.
*
* The group needs to exist, which is done by calling
* init_linked_size_group. If the widget is no member of the group the
* function does nothing.
*
* @param id The id of the group.
* @param widget The widget to remove from the group.
*/
void remove_linked_widget(const std::string& id, const twidget* widget);
/***** ***** ***** setters / getters for members ***** ****** *****/
CVideo& video() { return video_; }
/**
* Sets there return value of the window.
*
* @param retval The return value for the window.
* @param close_window Close the window after setting the value.
*/
void set_retval(const int retval, const bool close_window = true)
{ retval_ = retval; if(close_window) close(); }
void set_owner(tdialog* owner) { owner_ = owner; }
void set_click_dismiss(const bool click_dismiss)
{
click_dismiss_ = click_dismiss;
}
static void set_sunset(const unsigned interval)
{ sunset_ = interval ? interval : 5; }
bool get_need_layout() const { return need_layout_; }
void set_variable(const std::string& key, const variant& value)
{
variables_.add(key, value);
set_dirty();
}
private:
/** Needed so we can change what's drawn on the screen. */
CVideo& video_;
/** The status of the window. */
tstatus status_;
enum tshow_mode {
none
, modal
, tooltip
};
/**
* The mode in which the window is shown.
*
* This is used to determine whether or not to remove the tip.
*/
tshow_mode show_mode_;
// return value of the window, 0 default.
int retval_;
/** The dialog that owns the window. */
tdialog* owner_;
/**
* When set the form needs a full layout redraw cycle.
*
* This happens when either a widget changes it's size or visibility or
* the window is resized.
*/
bool need_layout_;
/** The variables of the canvas. */
game_logic::map_formula_callable variables_;
/** Is invalidate layout blocked see tinvalidate_layout_blocker. */
bool invalidate_layout_blocked_;
/** Avoid drawing the window. */
bool suspend_drawing_;
/** When the window closes this surface is used to undraw the window. */
surface restorer_;
/** Do we wish to place the widget automatically? */
const bool automatic_placement_;
/**
* Sets the horizontal placement.
*
* Only used if automatic_placement_ is true.
* The value should be a tgrid placement flag.
*/
const unsigned horizontal_placement_;
/**
* Sets the vertical placement.
*
* Only used if automatic_placement_ is true.
* The value should be a tgrid placement flag.
*/
const unsigned vertical_placement_;
/** The maximum width if automatic_placement_ is true. */
unsigned maximum_width_;
/** The maximum height if automatic_placement_ is true. */
unsigned maximum_height_;
/** The formula to calulate the x value of the dialog. */
tformula<unsigned>x_;
/** The formula to calulate the y value of the dialog. */
tformula<unsigned>y_;
/** The formula to calulate the width of the dialog. */
tformula<unsigned>w_;
/** The formula to calulate the height of the dialog. */
tformula<unsigned>h_;
/** The settings for the tooltip. */
twindow_builder::tresolution::ttip tooltip_;
/** The settings for the helptip. */
twindow_builder::tresolution::ttip helptip_;
/**
* Do we want to have easy close behaviour?
*
* Easy closing means that whenever a mouse click is done the dialog will
* be closed. The widgets in the window may override this behaviour by
* registering themselves as blockers. This is tested by the function
* disable_click_dismiss().
*
* The handling of easy close is done in the window, in order to do so a
* window either needs a click_dismiss or an ok button. Both will be hidden
* when not needed and when needed first the ok is tried and then the
* click_dismiss button. this allows adding a click_dismiss button to the
* window definition and use the ok from the window instance.
*
* @todo After testing the click dismiss feature it should be documented in
* the wiki.
*/
bool click_dismiss_;
/** Disable the enter key see our setter for more info. */
bool enter_disabled_;
/** Disable the escape key see our setter for more info. */
bool escape_disabled_;
/**
* Controls the sunset feature.
*
* If this value is not 0 it will darken the entire screen every
* sunset_th drawing request that nothing has been modified. It's a debug
* feature.
*/
static unsigned sunset_;
/**
* Helper struct to force widgets the have the same size.
*
* Widget which are linked will get the same width and/or height. This
* can especialy be useful for listboxes, but can also be used for other
* applications.
*/
struct tlinked_size
{
tlinked_size(const bool width = false, const bool height = false)
: widgets()
, width(width)
, height(height)
{
}
/** The widgets linked. */
std::vector<twidget*> widgets;
/** Link the widgets in the width? */
bool width;
/** Link the widgets in the height? */
bool height;
};
/** List of the widgets, whose size are linked together. */
std::map<std::string, tlinked_size> linked_size_;
/**
* Layouts the window.
*
* This part does the pre and post processing for the actual layout
* algorithm.
*
* @see layout_algorihm for more information.
*/
void layout();
/**
* Layouts the linked widgets.
*
* @see layout_algorihm for more information.
*/
void layout_linked_widgets();
/** Inherited from tevent_handler. */
bool click_dismiss();
/** Inherited from tcontrol. */
const std::string& get_control_type() const;
/**
* Inherited from tpanel.
*
* Don't call this function it's only asserts.
*/
void draw(surface& surface, const bool force = false,
const bool invalidate_background = false);
/**
* The list with dirty items in the window.
*
* When drawing only the widgets that are dirty are updated. The draw()
* function has more information about the dirty_list_.
*/
std::vector<std::vector<twidget*> > dirty_list_;
/**
* Finishes the initialization of the grid.
*
* @param content_grid The new contents for the content grid.
*/
void finalize(const boost::intrusive_ptr<tbuilder_grid>& content_grid);
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
tdebug_layout_graph* debug_layout_;
public:
/** wrapper for tdebug_layout_graph::generate_dot_file. */
void generate_dot_file(
const std::string& generator, const unsigned domain);
private:
#else
void generate_dot_file(const std::string&,
const unsigned) {}
#endif
event::tdistributor* event_distributor_;
public:
// mouse and keyboard_capture should be renamed and stored in the
// dispatcher. Chaining probably should remain exclusive to windows.
void mouse_capture(const bool capture = true);
void keyboard_capture(twidget* widget);
/**
* Adds the widget to the keyboard chain.
*
* @todo rename to keyboard_add_to_chain.
* @param widget The widget to add to the chain. The widget
* should be valid widget, which hasn't been
* added to the chain yet.
*/
void add_to_keyboard_chain(twidget* widget);
/**
* Remove the widget from the keyborad chain.
*
* @todo rename to keyboard_remove_from_chain.
*
* @param widget The widget to be removed from the chain.
*/
void remove_from_keyboard_chain(twidget* widget);
private:
/***** ***** ***** signal handlers ***** ****** *****/
void signal_handler_sdl_video_resize(
const event::tevent event, bool& handled, const tpoint& new_size);
void signal_handler_click_dismiss(
const event::tevent event, bool& handled, bool& halt);
void signal_handler_sdl_key_down(
const event::tevent event, bool& handled, const SDLKey key);
void signal_handler_message_show_tooltip(
const event::tevent event
, bool& handled
, event::tmessage& message);
void signal_handler_message_show_helptip(
const event::tevent event
, bool& handled
, event::tmessage& message);
};
} // namespace gui2
#endif