Changeset - r11456:7f48bd533d22
[Not reviewed]
master
0 4 0
alberth - 15 years ago 2009-03-22 21:15:45
alberth@openttd.org
(svn r15819) -Add: Nested widgets framework
4 files changed with 1236 insertions and 14 deletions:
0 comments (0 inline, 0 general)
src/widget.cpp
Show inline comments
 
@@ -3,12 +3,13 @@
 
/** @file widget.cpp Handling of the default/simple widgets. */
 

	
 
#include "stdafx.h"
 
#include "company_func.h"
 
#include "gfx_func.h"
 
#include "window_gui.h"
 
#include "debug.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
static const char *UPARROW   = "\xEE\x8A\xA0";
 
static const char *DOWNARROW = "\xEE\x8A\xAA";
 
@@ -624,6 +625,861 @@ void Window::DrawSortButtonState(int wid
 
{
 
	if (state == SBS_OFF) return;
 

	
 
	int offset = this->IsWidgetLowered(widget) ? 1 : 0;
 
	DrawString(this->widget[widget].right - 11 + offset, this->widget[widget].right, this->widget[widget].top + 1 + offset, state == SBS_DOWN ? DOWNARROW : UPARROW, TC_BLACK);
 
}
 

	
 

	
 
/* == Nested widgets == */
 

	
 
/**
 
 * Base class constructor.
 
 * @param tp Nested widget type.
 
 */
 
NWidgetBase::NWidgetBase(WidgetType tp) : ZeroedMemoryAllocator()
 
{
 
	this->type = tp;
 
}
 

	
 
/* ~NWidgetContainer() takes care of #next and #prev data members. */
 

	
 
/**
 
 * @fn int NWidgetBase::ComputeMinimalSize()
 
 * @brief Compute minimal size needed by the widget.
 
 *
 
 * The minimal size of a widget is the smallest size that a widget needs to
 
 * display itself properly.
 
 * In addition, filling and resizing of the widget are computed.
 
 * @return Biggest index in the widget array of all child widgets.
 
 *
 
 * @note After the computation, the results can be queried by accessing the data members of the widget.
 
 */
 

	
 
/**
 
 * @fn void NWidgetBase::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl)
 
 * @brief Assign minimal size and position to the widget.
 
 * @param x              Horizontal offset of the widget relative to the left edge of the window.
 
 * @param y              Vertical offset of the widget relative to the top edge of the window.
 
 * @param given_width    Width allocated to the widget.
 
 * @param given_height   Height allocated to the widget.
 
 * @param allow_resize_x Horizontal resizing is allowed.
 
 * @param allow_resize_y Vertical resizing is allowed.
 
 * @param rtl            Adapt for right-to-left languages (position contents of horizontal containers backwards).
 
 */
 

	
 
/**
 
 * @fn void NWidgetBase::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
 * @brief Store all child widgets with a valid index into the widget array.
 
 * @param widgets     Widget array to store the nested widgets in.
 
 * @param length      Length of the array.
 
 * @param left_moving Left edge of the widget may move due to resizing (right edge if \a rtl).
 
 * @param top_moving  Top edge of the widget may move due to reisizing.
 
 * @param rtl         Adapt for right-to-left languages (position contents of horizontal containers backwards).
 
 *
 
 * @note When storing a nested widget, the function should check first that the type in the \a widgets array is #WWT_LAST.
 
 *       This is used to detect double widget allocations as well as holes in the widget array.
 
 */
 

	
 
/**
 
 * Constructor for resizable nested widgets.
 
 * @param tp     Nested widget type.
 
 * @param fill_x Allow horizontal filling from initial size.
 
 * @param fill_y Allow vertical filling from initial size.
 
 */
 
NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, bool fill_x, bool fill_y) : NWidgetBase(tp)
 
{
 
	this->fill_x = fill_x;
 
	this->fill_y = fill_y;
 
}
 

	
 
/**
 
 * Set minimal size of the widget.
 
 * @param min_x Horizontal minimal size of the widget.
 
 * @param min_y Vertical minimal size of the widget.
 
 */
 
void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
 
{
 
	this->min_x = min_x;
 
	this->min_y = min_y;
 
}
 

	
 
/**
 
 * Set the filling of the widget from initial size.
 
 * @param fill_x Allow horizontal filling from initial size.
 
 * @param fill_y Allow vertical filling from initial size.
 
 */
 
void NWidgetResizeBase::SetFill(bool fill_x, bool fill_y)
 
{
 
	this->fill_x = fill_x;
 
	this->fill_y = fill_y;
 
}
 

	
 
/**
 
 * Set resize step of the widget.
 
 * @param resize_x Resize step in horizontal direction, value \c 0 means no resize, otherwise the step size in pixels.
 
 * @param resize_y Resize step in vertical direction, value \c 0 means no resize, otherwise the step size in pixels.
 
 */
 
void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
 
{
 
	this->resize_x = resize_x;
 
	this->resize_y = resize_y;
 
}
 

	
 
void NWidgetResizeBase::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl)
 
{
 
	this->pos_x = x;
 
	this->pos_y = y;
 
	this->min_x = given_width;
 
	this->min_y = given_height;
 
	if (!allow_resize_x) this->resize_x = 0;
 
	if (!allow_resize_y) this->resize_y = 0;
 
}
 

	
 
/**
 
 * Initialization of a 'real' widget.
 
 * @param tp          Type of the widget.
 
 * @param colour      Colour of the widget.
 
 * @param fill_x      Default horizontal filling.
 
 * @param fill_y      Default vertical filling.
 
 * @param widget_data Data component of the widget. @see Widget::data
 
 * @param tool_tip    Tool tip of the widget. @see Widget::tootips
 
 */
 
NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, bool fill_x, bool fill_y, uint16 widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y)
 
{
 
	this->colour = colour;
 
	this->index = -1;
 
	this->widget_data = widget_data;
 
	this->tool_tip = tool_tip;
 
}
 

	
 
/**
 
 * Set index of the nested widget in the widget array.
 
 * @param index Index to use.
 
 */
 
void NWidgetCore::SetIndex(int index)
 
{
 
	assert(index >= 0);
 
	this->index = index;
 
}
 

	
 
/**
 
 * Set data and tool tip of the nested widget.
 
 * @param widget_data Data to use.
 
 * @param tool_tip    Tool tip string to use.
 
 */
 
void NWidgetCore::SetDataTip(uint16 widget_data, StringID tool_tip)
 
{
 
	this->widget_data = widget_data;
 
	this->tool_tip = tool_tip;
 
}
 

	
 
int NWidgetCore::ComputeMinimalSize()
 
{
 
	/* All data is already at the right place. */
 
	return this->index;
 
}
 

	
 
void NWidgetCore::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
{
 
	if (this->index < 0) return;
 

	
 
	assert(this->index < length);
 
	Widget *w = widgets + this->index;
 
	assert(w->type == WWT_LAST);
 

	
 
	DisplayFlags flags = RESIZE_NONE; // resize flags.
 
	/* Compute vertical resizing. */
 
	if (top_moving) {
 
		flags |= RESIZE_TB; // Only 1 widget can resize in the widget array.
 
	} else if(this->resize_y > 0) {
 
		flags |= RESIZE_BOTTOM;
 
	}
 
	/* Compute horizontal resizing. */
 
	if (left_moving) {
 
		flags |= RESIZE_LR; // Only 1 widget can resize in the widget array.
 
	} else if (this->resize_x > 0) {
 
		flags |= RESIZE_RIGHT;
 
	}
 

	
 
	/* Copy nested widget data into its widget array entry. */
 
	w->type = this->type;
 
	w->display_flags = flags;
 
	w->colour = this->colour;
 
	w->left = this->pos_x;
 
	w->right = this->pos_x + this->min_x - 1;
 
	w->top = this->pos_y;
 
	w->bottom = this->pos_y + this->min_y - 1;
 
	w->data = this->widget_data;
 
	w->tooltips = this->tool_tip;
 
}
 

	
 
/**
 
 * Constructor container baseclass.
 
 * @param tp Type of the container.
 
 */
 
NWidgetContainer::NWidgetContainer(WidgetType tp) : NWidgetBase(tp)
 
{
 
	this->head = NULL;
 
	this->tail = NULL;
 
}
 

	
 
NWidgetContainer::~NWidgetContainer()
 
{
 
	while (this->head != NULL) {
 
		NWidgetBase *wid = this->head->next;
 
		delete this->head;
 
		this->head = wid;
 
	}
 
	this->tail = NULL;
 
}
 

	
 
/**
 
 * Append widget \a wid to container.
 
 * @param wid Widget to append.
 
 */
 
void NWidgetContainer::Add(NWidgetBase *wid)
 
{
 
	assert(wid->next == NULL && wid->prev == NULL);
 

	
 
	if (this->head == NULL) {
 
		this->head = wid;
 
		this->tail = wid;
 
	} else {
 
		assert(this->tail != NULL);
 
		assert(this->tail->next == NULL);
 

	
 
		this->tail->next = wid;
 
		wid->prev = this->tail;
 
		this->tail = wid;
 
	}
 
}
 

	
 
NWidgetHorizontal::NWidgetHorizontal() : NWidgetContainer(NWID_HORIZONTAL)
 
{
 
}
 

	
 
int NWidgetHorizontal::ComputeMinimalSize()
 
{
 
	int biggest_index = -1;
 
	this->min_x = 0;      // Sum of minimal size of all childs.
 
	this->min_y = 0;      // Biggest child.
 
	this->fill_x = false; // true if at least one child allows fill_x.
 
	this->fill_y = true;  // true if all childs allow fill_y.
 
	this->resize_x = 0;   // smallest non-zero child widget resize step.
 
	this->resize_y = 1;   // smallest common child resize step
 

	
 
	for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		int idx = child_wid->ComputeMinimalSize();
 
		biggest_index = max(biggest_index, idx);
 

	
 
		this->min_x += child_wid->min_x;
 
		this->min_y = max(this->min_y, child_wid->min_y);
 
		this->fill_x |= child_wid->fill_x;
 
		this->fill_y &= child_wid->fill_y;
 

	
 
		if (child_wid->resize_x > 0) {
 
			if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
 
		}
 
		this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y);
 
	}
 

	
 
	return biggest_index;
 
}
 

	
 
void NWidgetHorizontal::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl)
 
{
 
	assert(given_width >= this->min_x && given_height >= this->min_y);
 

	
 
	uint additional_length = given_width - this->min_x; // Additional width given to us.
 
	this->pos_x = x;
 
	this->pos_y = y;
 
	this->min_x = given_width;
 
	this->min_y = given_height;
 
	if (!allow_resize_x) this->resize_x = 0;
 
	if (!allow_resize_y) this->resize_y = 0;
 

	
 
	/* Count number of childs that would like a piece of the pie. */
 
	int num_changing_childs = 0; // Number of childs that can change size.
 
	NWidgetBase *child_wid;
 
	for (child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		if (child_wid->fill_x) num_changing_childs++;
 
	}
 

	
 
	/* Fill and position the child widgets. */
 
	uint position = 0; // Place to put next child relative to origin of the container.
 
	allow_resize_x = (this->resize_x > 0);
 
	child_wid = rtl ? this->tail : this->head;
 
	while (child_wid != NULL) {
 
		/* Decide about vertical filling of the child. */
 
		uint child_height; // Height of the child widget.
 
		uint child_pos_y; // Vertical position of child relative to the top of the container.
 
		if (child_wid->fill_y) {
 
			child_height = given_height;
 
			child_pos_y = 0;
 
		} else {
 
			child_height = child_wid->min_y;
 
			child_pos_y = (given_height - child_height) / 2;
 
		}
 

	
 
		/* Decide about horizontal filling of the child. */
 
		uint child_width;
 
		child_width = child_wid->min_x;
 
		if (child_wid->fill_x && num_changing_childs > 0) {
 
			/* Hand out a piece of the pie while compensating for rounding errors. */
 
			uint increment = additional_length / num_changing_childs;
 
			additional_length -= increment;
 
			num_changing_childs--;
 

	
 
			child_width += increment;
 
		}
 

	
 
		child_wid->AssignMinimalPosition(x + position, y + child_pos_y, child_width, child_height, allow_resize_x, (this->resize_y > 0), rtl);
 
		position += child_width;
 
		if (child_wid->resize_x > 0) allow_resize_x = false; // Widget array allows only one child resizing
 

	
 
		child_wid = rtl ? child_wid->prev : child_wid->next;
 
	}
 
}
 

	
 
void NWidgetHorizontal::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
{
 
	NWidgetBase *child_wid = rtl ? this->tail : this->head;
 
	while (child_wid != NULL) {
 
		child_wid->StoreWidgets(widgets, length, left_moving, top_moving, rtl);
 
		left_moving |= (child_wid->resize_x > 0);
 

	
 
		child_wid = rtl ? child_wid->prev : child_wid->next;
 
	}
 
}
 

	
 
NWidgetVertical::NWidgetVertical() : NWidgetContainer(NWID_VERTICAL)
 
{
 
}
 

	
 
int NWidgetVertical::ComputeMinimalSize()
 
{
 
	int biggest_index = -1;
 
	this->min_x = 0;      // Biggest child.
 
	this->min_y = 0;      // Sum of minimal size of all childs.
 
	this->fill_x = true;  // true if all childs allow fill_x.
 
	this->fill_y = false; // true if at least one child allows fill_y.
 
	this->resize_x = 1;   // smallest common child resize step
 
	this->resize_y = 0;   // smallest non-zero child widget resize step.
 

	
 
	for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		int idx = child_wid->ComputeMinimalSize();
 
		biggest_index = max(biggest_index, idx);
 

	
 
		this->min_y += child_wid->min_y;
 
		this->min_x = max(this->min_x, child_wid->min_x);
 
		this->fill_y |= child_wid->fill_y;
 
		this->fill_x &= child_wid->fill_x;
 

	
 
		if (child_wid->resize_y > 0) {
 
			if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
 
		}
 
		this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x);
 
	}
 

	
 
	return biggest_index;
 
}
 

	
 
void NWidgetVertical::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl)
 
{
 
	assert(given_width >= this->min_x && given_height >= this->min_y);
 

	
 
	int additional_length = given_height - this->min_y; // Additional height given to us.
 
	this->pos_x = x;
 
	this->pos_y = y;
 
	this->min_x = given_width;
 
	this->min_y = given_height;
 
	if (!allow_resize_x) this->resize_x = 0;
 
	if (!allow_resize_y) this->resize_y = 0;
 

	
 
	/* count number of childs that would like a piece of the pie. */
 
	int num_changing_childs = 0; // Number of childs that can change size.
 
	for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		if (child_wid->fill_y) num_changing_childs++;
 
	}
 

	
 
	/* Fill and position the child widgets. */
 
	uint position = 0; // Place to put next child relative to origin of the container.
 
	allow_resize_y = (this->resize_y > 0);
 
	for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		/* Decide about horizontal filling of the child. */
 
		uint child_width; // Width of the child widget.
 
		uint child_pos_x; // Horizontal position of child relative to the left of the container.
 
		if (child_wid->fill_x) {
 
			child_width = given_width;
 
			child_pos_x = 0;
 
		} else {
 
			child_width = child_wid->min_x;
 
			child_pos_x = (given_width - child_width) / 2;
 
		}
 

	
 
		/* Decide about vertical filling of the child. */
 
		uint child_height;
 
		child_height = child_wid->min_y;
 
		if (child_wid->fill_y && num_changing_childs > 0) {
 
			/* Hand out a piece of the pie while compensating for rounding errors. */
 
			uint increment = additional_length / num_changing_childs;
 
			additional_length -= increment;
 
			num_changing_childs--;
 

	
 
			child_height += increment;
 
		}
 

	
 
		child_wid->AssignMinimalPosition(x + child_pos_x, y + position, child_width, child_height, (this->resize_x > 0), allow_resize_y, rtl);
 
		position += child_height;
 
		if (child_wid->resize_y > 0) allow_resize_y = false; // Widget array allows only one child resizing
 
	}
 
}
 

	
 
void NWidgetVertical::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
{
 
	for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
 
		child_wid->StoreWidgets(widgets, length, left_moving, top_moving, rtl);
 
		top_moving |= (child_wid->resize_y > 0);
 
	}
 
}
 

	
 
/**
 
 * Generic spacer widget.
 
 * @param length Horizontal size of the spacer widget.
 
 * @param height Vertical size of the spacer widget.
 
 */
 
NWidgetSpacer::NWidgetSpacer(int length, int height) : NWidgetResizeBase(NWID_SPACER, false, false)
 
{
 
	this->SetMinimalSize(length, height);
 
	this->SetResize(0, 0);
 
}
 

	
 
int NWidgetSpacer::ComputeMinimalSize()
 
{
 
	/* No further computation needed. */
 
	return -1;
 
}
 

	
 
void NWidgetSpacer::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
{
 
	/* Spacer widgets are never stored in the widget array. */
 
}
 

	
 
/**
 
 * Constructor parent nested widgets.
 
 * @param tp     Type of parent widget.
 
 * @param colour Colour of the parent widget.
 
 * @param index  Index in the widget array used by the window system.
 
 * @param child  Child container widget (if supplied). If not supplied, a
 
 *               vertical container will be inserted while adding the first
 
 *               child widget.
 
 */
 
NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetContainer *child) : NWidgetCore(tp, colour, true, true, 0x0, STR_NULL)
 
{
 
	this->SetIndex(index);
 
	assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
 
	assert(index >= 0);
 
	this->child = child;
 
}
 

	
 
NWidgetBackground::~NWidgetBackground()
 
{
 
	if (this->child != NULL) delete this->child;
 
}
 

	
 
/**
 
 * Add a child to the parent.
 
 * @param nwid Nested widget to add to the background widget.
 
 *
 
 * Unless a child container has been given in the constructor, a parent behaves as a vertical container.
 
 * You can add several childs to it, and they are put underneath each other.
 
 */
 
void NWidgetBackground::Add(NWidgetBase *nwid)
 
{
 
	if (this->child == NULL) {
 
		this->child = new NWidgetVertical();
 
	}
 
	this->child->Add(nwid);
 
}
 

	
 
int NWidgetBackground::ComputeMinimalSize()
 
{
 
	int biggest_index = this->index;
 
	if (this->child != NULL) {
 
		int idx = this->child->ComputeMinimalSize();
 
		biggest_index = max(biggest_index, idx);
 

	
 
		this->min_x = this->child->min_x;
 
		this->min_y = this->child->min_y;
 
		this->fill_x = this->child->fill_x;
 
		this->fill_y = this->child->fill_y;
 
		this->resize_x = this->child->resize_x;
 
		this->resize_y = this->child->resize_y;
 
	}
 
	/* Otherwise, the program should have already set the above values. */
 

	
 
	return biggest_index;
 
}
 

	
 
void NWidgetBackground::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl)
 
{
 
	this->pos_x = x;
 
	this->pos_y = y;
 
	this->min_x = given_width;
 
	this->min_y = given_height;
 
	if (!allow_resize_x) this->resize_x = 0;
 
	if (!allow_resize_y) this->resize_y = 0;
 

	
 
	if (this->child != NULL) this->child->AssignMinimalPosition(x, y, given_width, given_height, (this->resize_x > 0), (this->resize_y > 0), rtl);
 
}
 

	
 
void NWidgetBackground::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl)
 
{
 
	NWidgetCore::StoreWidgets(widgets, length, left_moving, top_moving, rtl);
 
	if (this->child != NULL) this->child->StoreWidgets(widgets, length, left_moving, top_moving, rtl);
 
}
 

	
 
/**
 
 * Nested leaf widget.
 
 * @param tp    Type of leaf widget.
 
 * @param index Index in the widget array used by the window system.
 
 * @param data  Data of the widget.
 
 * @param tip   Tooltip of the widget.
 
 */
 
NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint16 data, StringID tip) : NWidgetCore(tp, colour, true, true, data, tip)
 
{
 
	this->SetIndex(index);
 
	this->SetMinimalSize(0, 0);
 
	this->SetResize(0, 0);
 

	
 
	switch (tp) {
 
		case WWT_EMPTY:
 
			break;
 

	
 
		case WWT_PUSHBTN:
 
			this->SetFill(false, false);
 
			break;
 

	
 
		case WWT_IMGBTN:
 
		case WWT_PUSHIMGBTN:
 
		case WWT_IMGBTN_2:
 
			this->SetFill(false, false);
 
			break;
 

	
 
		case WWT_TEXTBTN:
 
		case WWT_PUSHTXTBTN:
 
		case WWT_TEXTBTN_2:
 
		case WWT_LABEL:
 
		case WWT_TEXT:
 
		case WWT_MATRIX:
 
		case WWT_EDITBOX:
 
			this->SetFill(false, false);
 
			break;
 

	
 
		case WWT_SCROLLBAR:
 
		case WWT_SCROLL2BAR:
 
			this->SetFill(false, true);
 
			this->SetResize(0, 1);
 
			this->min_x = 12;
 
			this->SetDataTip(0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST);
 
			break;
 

	
 
		case WWT_CAPTION:
 
			this->SetFill(true, false);
 
			this->SetResize(1, 0);
 
			this->min_y = 14;
 
			this->SetDataTip(0x0, STR_018C_WINDOW_TITLE_DRAG_THIS);
 
			break;
 

	
 
		case WWT_HSCROLLBAR:
 
			this->SetFill(true, false);
 
			this->SetResize(1, 0);
 
			this->min_y = 12;
 
			this->SetDataTip(0x0, STR_HSCROLL_BAR_SCROLLS_LIST);
 
			break;
 

	
 
		case WWT_STICKYBOX:
 
			this->SetFill(false, false);
 
			this->SetMinimalSize(12, 14);
 
			this->SetDataTip(STR_NULL, STR_STICKY_BUTTON);
 
			break;
 

	
 
		case WWT_RESIZEBOX:
 
			this->SetFill(false, false);
 
			this->SetMinimalSize(12, 12);
 
			this->SetDataTip(STR_NULL, STR_RESIZE_BUTTON);
 
			break;
 

	
 
		case WWT_CLOSEBOX:
 
			this->SetFill(false, false);
 
			this->SetMinimalSize(11, 14);
 
			this->SetDataTip(STR_00C5, STR_018B_CLOSE_WINDOW);
 
			break;
 

	
 
		case WWT_DROPDOWN:
 
		case WWT_DROPDOWNIN:
 
			this->SetFill(false, false);
 
			this->min_y = 12;
 
			break;
 

	
 
		default:
 
			NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Intialize nested widget tree and convert to widget array.
 
 * @param nwid Nested widget tree.
 
 * @param rtl  Direction of the language.
 
 * @return Widget array with the converted widgets.
 
 * @note Caller should release returned widget array with \c free(widgets).
 
 */
 
Widget *InitializeNWidgets(NWidgetBase *nwid, bool rtl)
 
{
 
	/* Initialize nested widgets. */
 
	int biggest_index = nwid->ComputeMinimalSize();
 
	nwid->AssignMinimalPosition(0, 0, nwid->min_x, nwid->min_y, (nwid->resize_x > 0), (nwid->resize_y > 0), rtl);
 

	
 
	/* Construct a local widget array and initialize all its types to #WWT_LAST. */
 
	Widget *widgets = MallocT<Widget>(biggest_index + 2);
 
	int i;
 
	for (i = 0; i < biggest_index + 2; i++) {
 
		widgets[i].type = WWT_LAST;
 
	}
 

	
 
	/* Store nested widgets in the array. */
 
	nwid->StoreWidgets(widgets, biggest_index + 1, false, false, rtl);
 

	
 
	/* Check that all widgets are used. */
 
	for (i = 0; i < biggest_index + 2; i++) {
 
		if (widgets[i].type == WWT_LAST) break;
 
	}
 
	assert(i == biggest_index + 1);
 

	
 
	/* Fill terminating widget */
 
	static const Widget last_widget = {WIDGETS_END};
 
	widgets[biggest_index + 1] = last_widget;
 

	
 
	return widgets;
 
}
 

	
 
/**
 
 * Compare two widget arrays with each other, and report differences.
 
 * @param orig Pointer to original widget array.
 
 * @param gen  Pointer to generated widget array (from the nested widgets).
 
 * @param report Report differences to 'misc' debug stream.
 
 * @return Both widget arrays are equal.
 
 */
 
bool CompareWidgetArrays(const Widget *orig, const Widget *gen, bool report)
 
{
 
#define CHECK(var, prn) \
 
	if (ow->var != gw->var) { \
 
		same = false; \
 
		if (report) DEBUG(misc, 1, "index %d, \"" #var "\" field: original " prn ", generated " prn, idx, ow->var, gw->var); \
 
	}
 
#define CHECK_COORD(var) \
 
	if (ow->var != gw->var) { \
 
		same = false; \
 
		if (report) DEBUG(misc, 1, "index %d, \"" #var "\" field: original %d, generated %d, (difference %d)", idx, ow->var, gw->var, ow->var - gw->var); \
 
	}
 

	
 
	bool same = true;
 
	for(int idx = 0; ; idx++) {
 
		const Widget *ow = orig + idx;
 
		const Widget *gw = gen + idx;
 

	
 
		CHECK(type, "%d")
 
		CHECK(display_flags, "0x%x")
 
		CHECK(colour, "%d")
 
		CHECK_COORD(left)
 
		CHECK_COORD(right)
 
		CHECK_COORD(top)
 
		CHECK_COORD(bottom)
 
		CHECK(data, "%u")
 
		CHECK(tooltips, "%u")
 

	
 
		if (ow->type == WWT_LAST || gw->type == WWT_LAST) break;
 
	}
 

	
 
	return same;
 

	
 
#undef CHECK
 
#undef CHECK_COORD
 
}
 

	
 
/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
 

	
 
/**
 
 * Construct a single nested widget in \a *dest from its parts.
 
 *
 
 * Construct a NWidgetBase object from a #NWidget function, and apply all
 
 * settings that follow it, until encountering a #EndContainer, another
 
 * #NWidget, or the end of the parts array.
 
 *
 
 * @param parts Array with parts of the nested widget.
 
 * @param count Length of the \a parts array.
 
 * @param dest  Address of pointer to use for returning the composed widget.
 
 * @return Number of widget part elements used to compose the widget.
 
 */
 
static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest)
 
{
 
	int num_used = 0;
 

	
 
	*dest = NULL;
 

	
 
	while (count > num_used) {
 
		switch (parts->type) {
 
			case NWID_SPACER:
 
				if (*dest != NULL) return num_used;
 
				*dest = new NWidgetSpacer(0, 0);
 
				break;
 

	
 
			case NWID_HORIZONTAL:
 
				if (*dest != NULL) return num_used;
 
				*dest = new NWidgetHorizontal();
 
				break;
 

	
 
			case WWT_PANEL:
 
			case WWT_INSET:
 
			case WWT_FRAME:
 
				if (*dest != NULL) return num_used;
 
				*dest = new NWidgetBackground(parts->type, parts->u.widget.colour, parts->u.widget.index);
 
				break;
 

	
 
			case NWID_VERTICAL:
 
				if (*dest != NULL) return num_used;
 
				*dest = new NWidgetVertical();
 
				break;
 

	
 
			case WPT_RESIZE: {
 
				NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
 
				if (nwrb != NULL) {
 
					assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0);
 
					nwrb->SetResize(parts->u.xy.x, parts->u.xy.y);
 
				}
 
				break;
 
			}
 

	
 
			case WPT_RESIZE_PTR: {
 
				NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
 
				if (nwrb != NULL) {
 
					assert(parts->u.xy_ptr->x >= 0 && parts->u.xy_ptr->y >= 0);
 
					nwrb->SetResize(parts->u.xy_ptr->x, parts->u.xy_ptr->y);
 
				}
 
				break;
 
			}
 

	
 
			case WPT_MINSIZE: {
 
				NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
 
				if (nwrb != NULL) {
 
					assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0);
 
					nwrb->SetMinimalSize(parts->u.xy.x, parts->u.xy.y);
 
				}
 
				break;
 
			}
 

	
 
			case WPT_MINSIZE_PTR: {
 
				NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
 
				if (nwrb != NULL) {
 
					assert(parts->u.xy_ptr->x >= 0 && parts->u.xy_ptr->y >= 0);
 
					nwrb->SetMinimalSize((uint)(parts->u.xy_ptr->x), (uint)(parts->u.xy_ptr->y));
 
				}
 
				break;
 
			}
 

	
 
			case WPT_FILL: {
 
				NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
 
				if (nwrb != NULL) nwrb->SetFill(parts->u.xy.x != 0, parts->u.xy.y != 0);
 
				break;
 
			}
 

	
 
			case WPT_DATATIP: {
 
				NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
 
				if (nwc != NULL) {
 
					nwc->widget_data = parts->u.data_tip.data;
 
					nwc->tool_tip = parts->u.data_tip.tooltip;
 
				}
 
				break;
 
			}
 

	
 
			case WPT_DATATIP_PTR: {
 
				NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
 
				if (nwc != NULL) {
 
					nwc->widget_data = parts->u.datatip_ptr->data;
 
					nwc->tool_tip = parts->u.datatip_ptr->tooltip;
 
				}
 
				break;
 
			}
 

	
 
			case WPT_ENDCONTAINER:
 
				return num_used;
 

	
 
			default:
 
				if (*dest != NULL) return num_used;
 
				assert((parts->type & WWT_MASK) < NWID_HORIZONTAL);
 
				*dest = new NWidgetLeaf(parts->type, parts->u.widget.colour, parts->u.widget.index, 0x0, STR_NULL);
 
				break;
 
		}
 
		num_used++;
 
		parts++;
 
	}
 

	
 
	return num_used;
 
}
 

	
 
/**
 
 * Build a nested widget tree by recursively filling containers with nested widgets read from their parts.
 
 * @param parts  Array with parts of the nested widgets.
 
 * @param count  Length of the \a parts array.
 
 * @param parent Container to use for storing the child widgets.
 
 * @return Number of widget part elements used to fill the container.
 
 */
 
static int MakeWidgetTree(const NWidgetPart *parts, int count, NWidgetBase *parent)
 
{
 
	/* Given parent must be either a #NWidgetContainer or a #NWidgetBackground object. */
 
	NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent);
 
	NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent);
 
	assert((nwid_cont != NULL && nwid_parent == NULL) || (nwid_cont == NULL && nwid_parent != NULL));
 

	
 
	int total_used = 0;
 
	while (true) {
 
		NWidgetBase *sub_widget = NULL;
 
		int num_used = MakeNWidget(parts, count - total_used, &sub_widget);
 
		parts += num_used;
 
		total_used += num_used;
 

	
 
		/* Break out of loop when end reached */
 
		if (sub_widget == NULL) break;
 

	
 
		/* Add sub_widget to parent container. */
 
		if (nwid_cont) nwid_cont->Add(sub_widget);
 
		if (nwid_parent) nwid_parent->Add(sub_widget);
 

	
 
		/* If sub-widget is a container, recursively fill that container. */
 
		WidgetType tp = sub_widget->type;
 
		if (tp == NWID_HORIZONTAL || tp == NWID_VERTICAL || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET) {
 
			int num_used = MakeWidgetTree(parts, count - total_used, sub_widget);
 
			parts += num_used;
 
			total_used += num_used;
 
		}
 
	}
 

	
 
	if (count == total_used) return total_used; // Reached the end of the array of parts?
 

	
 
	assert(total_used < count);
 
	assert(parts->type == WPT_ENDCONTAINER);
 
	return total_used + 1; // *parts is also 'used'
 
}
 

	
 
/**
 
 * Construct a nested widget tree from an array of parts.
 
 * @param parts Array with parts of the widgets.
 
 * @param count Length of the \a parts array.
 
 * @return Root of the nested widget tree, a vertical container containing the entire GUI.
 
 */
 
NWidgetContainer *MakeNWidgets(const NWidgetPart *parts, int count)
 
{
 
	NWidgetContainer *cont = new NWidgetVertical();
 
	MakeWidgetTree(parts, count, cont);
 
	return cont;
 
}
src/widget_type.h
Show inline comments
 
@@ -63,15 +63,16 @@ DECLARE_ENUM_AS_BIT_SET(DisplayFlags);
 

	
 
enum {
 
	WIDGET_LIST_END = -1, ///< indicate the end of widgets' list for vararg functions
 
};
 

	
 
/**
 
 * Window widget types
 
 * Window widget types, nested widget types, and nested widget part types.
 
 */
 
enum WidgetType {
 
	/* Window widget types. */
 
	WWT_EMPTY,      ///< Empty widget, place holder to reserve space in widget array
 

	
 
	WWT_PANEL,      ///< Simple depressed panel
 
	WWT_INSET,      ///< Pressed (inset) panel, most commonly used as combo box _text_ area
 
	WWT_IMGBTN,     ///< Button with image
 
	WWT_IMGBTN_2,   ///< Button with diff image when clicked
 
@@ -92,12 +93,28 @@ enum WidgetType {
 
	WWT_CLOSEBOX,   ///< Close box (at top-left of a window)
 
	WWT_DROPDOWN,   ///< Raised drop down list (regular)
 
	WWT_DROPDOWNIN, ///< Inset drop down list (used on game options only)
 
	WWT_EDITBOX,    ///< a textbox for typing
 
	WWT_LAST,       ///< Last Item. use WIDGETS_END to fill up padding!!
 

	
 
	/* Nested widget types. */
 
	NWID_HORIZONTAL,  ///< Horizontal container.
 
	NWID_VERTICAL,    ///< Vertical container.
 
	NWID_SPACER,      ///< Invisible widget that takes some space.
 

	
 
	/* Nested widget part types. */
 
	WPT_RESIZE,       ///< Widget part for specifying resizing.
 
	WPT_RESIZE_PTR,   ///< Widget part for specifying resizing via a pointer.
 
	WPT_MINSIZE,      ///< Widget part for specifying minimal size.
 
	WPT_MINSIZE_PTR,  ///< Widget part for specifying minimal size via a pointer.
 
	WPT_FILL,         ///< Widget part for specifying fill.
 
	WPT_DATATIP,      ///< Widget part for specifying data and tooltip.
 
	WPT_DATATIP_PTR,  ///< Widget part for specifying data and tooltip via a pointer.
 
	WPT_ENDCONTAINER, ///< Widget part to denote end of a container.
 

	
 
	/* Pushable window widget types. */
 
	WWT_MASK = 0x7F,
 

	
 
	WWB_PUSHBUTTON  = 1 << 7,
 

	
 
	WWT_PUSHBTN     = WWT_PANEL   | WWB_PUSHBUTTON,
 
	WWT_PUSHTXTBTN  = WWT_TEXTBTN | WWB_PUSHBUTTON,
 
@@ -119,7 +136,310 @@ struct Widget {
 
	int16 top;                        ///< The top edge of the widget
 
	int16 bottom;                     ///< The bottom edge of the widget
 
	uint16 data;                      ///< The String/Image or special code (list-matrixes) of a widget
 
	StringID tooltips;                ///< Tooltips that are shown when rightclicking on a widget
 
};
 

	
 
/**
 
 * Baseclass for nested widgets.
 
 */
 
class NWidgetBase : public ZeroedMemoryAllocator {
 
public:
 
	NWidgetBase(WidgetType tp);
 

	
 
	virtual int ComputeMinimalSize() = 0;
 
	virtual void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) = 0;
 

	
 
	virtual void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) = 0;
 

	
 
	WidgetType type;   ///< Type of the widget / nested widget.
 
	uint min_x;        ///< Minimal horizontal size.
 
	uint min_y;        ///< Minimal vertical size.
 
	bool fill_x;       ///< Allow horizontal filling from initial size.
 
	bool fill_y;       ///< Allow vertical filling from initial size.
 
	uint resize_x;     ///< Horizontal resize step (\c 0 means not resizable).
 
	uint resize_y;     ///< Vertical resize step (\c 0 means not resizable).
 

	
 
	uint pos_x;        ///< Horizontal position of top-left corner of the widget in the window.
 
	uint pos_y;        ///< Vertical position of top-left corner of the widget in the window.
 

	
 
	NWidgetBase *next; ///< Pointer to next widget in container. Managed by parent container widget.
 
	NWidgetBase *prev; ///< Pointer to previous widget in container. Managed by parent container widget.
 
};
 

	
 
/** Base class for a resizable nested widget. */
 
class NWidgetResizeBase : public NWidgetBase {
 
public:
 
	NWidgetResizeBase(WidgetType tp, bool fill_x, bool fill_y);
 

	
 
	void SetMinimalSize(uint min_x, uint min_y);
 
	void SetFill(bool fill_x, bool fill_y);
 
	void SetResize(uint resize_x, uint resize_y);
 

	
 
	void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl);
 
};
 

	
 
/** Base class for a 'real' widget. */
 
class NWidgetCore : public NWidgetResizeBase {
 
public:
 
	NWidgetCore(WidgetType tp, Colours colour, bool def_fill_x, bool def_fill_y, uint16 widget_data, StringID tool_tip);
 

	
 
	void SetIndex(int index);
 
	void SetDataTip(uint16 widget_data, StringID tool_tip);
 

	
 
	int ComputeMinimalSize();
 
	void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl);
 

	
 
	Colours colour;     ///< Colour of this widget.
 
	int index;          ///< Index of the nested widget in the widget array of the window (\c -1 means 'not used').
 
	uint16 widget_data; ///< Data of the widget. @see Widget::data
 
	StringID tool_tip;  ///< Tooltip of the widget. @see Widget::tootips
 
};
 

	
 
/** Baseclass for container widgets. */
 
class NWidgetContainer : public NWidgetBase {
 
public:
 
	NWidgetContainer(WidgetType tp);
 
	~NWidgetContainer();
 

	
 
	void Add(NWidgetBase *wid);
 
protected:
 
	NWidgetBase *head; ///< Pointer to first widget in container.
 
	NWidgetBase *tail; ///< Pointer to last widget in container.
 
};
 

	
 
/** Horizontal container. */
 
class NWidgetHorizontal : public NWidgetContainer {
 
public:
 
	NWidgetHorizontal();
 

	
 
	int ComputeMinimalSize();
 
	void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl);
 

	
 
	void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl);
 
};
 

	
 
/** Vertical container */
 
class NWidgetVertical : public NWidgetContainer {
 
public:
 
	NWidgetVertical();
 

	
 
	int ComputeMinimalSize();
 
	void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl);
 

	
 
	void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl);
 
};
 

	
 

	
 
/** Spacer widget */
 
class NWidgetSpacer : public NWidgetResizeBase {
 
public:
 
	NWidgetSpacer(int length, int height);
 

	
 
	int ComputeMinimalSize();
 
	void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl);
 
};
 

	
 
/** Nested widget with a child. */
 
class NWidgetBackground : public NWidgetCore {
 
public:
 
	NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetContainer *child = NULL);
 
	~NWidgetBackground();
 

	
 
	void Add(NWidgetBase *nwid);
 

	
 
	int ComputeMinimalSize();
 
	void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl);
 

	
 
	void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl);
 
private:
 
	NWidgetContainer *child; ///< Child widget.
 
};
 

	
 
/** Leaf widget. */
 
class NWidgetLeaf : public NWidgetCore {
 
public:
 
	NWidgetLeaf(WidgetType tp, Colours colour, int index, uint16 data, StringID tip);
 
};
 

	
 
Widget *InitializeNWidgets(NWidgetBase *nwid, bool rtl = false);
 
bool CompareWidgetArrays(const Widget *orig, const Widget *gen, bool report = true);
 

	
 
/* == Nested widget parts == */
 

	
 
/** Widget part for storing data and tooltip information. */
 
struct NWidgetPartDataTip {
 
	uint16 data;      ///< Data value of the widget.
 
	StringID tooltip; ///< Tooltip of the widget.
 
};
 

	
 
/** Widget part for storing basic widget information. */
 
struct NWidgetPartWidget {
 
	Colours colour; ///< Widget colour.
 
	int16 index;    ///< Widget index in the widget array.
 
};
 

	
 
/** Partial widget specification to allow NWidgets to be written nested. */
 
struct NWidgetPart {
 
	WidgetType type;                         ///< Type of the part. @see NWidgetPartType.
 
	union {
 
		Point xy;                        ///< Part with an x/y size.
 
		Point *xy_ptr;                   ///< Part with a pointer to an x/y size.
 
		NWidgetPartDataTip data_tip;     ///< Part with a data/tooltip.
 
		NWidgetPartDataTip *datatip_ptr; ///< Part with a pointer to data/tooltip.
 
		NWidgetPartWidget widget;        ///< Part with a start of a widget.
 
	} u;
 
};
 

	
 
/**
 
 * Widget part function for setting the resize step.
 
 * @param dx Horizontal resize step. 0 means no horizontal resizing.
 
 * @param dy Vertical resize step. 0 means no horizontal resizing.
 
 */
 
static inline NWidgetPart SetResize(int16 dx, int16 dy)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_RESIZE;
 
	part.u.xy.x = dx;
 
	part.u.xy.y = dy;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for using a pointer to set the resize step.
 
 * @param ptr Pointer to horizontal and vertical resize step.
 
 */
 
static inline NWidgetPart SetResize(Point *ptr)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_RESIZE_PTR;
 
	part.u.xy_ptr = ptr;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for setting the minimal size.
 
 * @param dx Horizontal minimal size.
 
 * @param dy Vertical minimal size.
 
 */
 
static inline NWidgetPart SetMinimalSize(int16 x, int16 y)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_MINSIZE;
 
	part.u.xy.x = x;
 
	part.u.xy.y = y;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for using a pointer to set the minimal size.
 
 * @param ptr Pointer to horizontal and vertical minimal size.
 
 */
 
static inline NWidgetPart SetMinimalSize(Point *ptr)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_MINSIZE_PTR;
 
	part.u.xy_ptr = ptr;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for setting filling.
 
 * @param x_fill Allow horizontal filling from minimal size.
 
 * @param y_fill Allow vertical filling from minimal size.
 
 */
 
static inline NWidgetPart SetFill(bool x_fill, bool y_fill)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_FILL;
 
	part.u.xy.x = x_fill;
 
	part.u.xy.y = y_fill;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for denoting the end of a container
 
 * (horizontal, vertical, WWT_FRAME, WWT_INSET, or WWT_PANEL).
 
 */
 
static inline NWidgetPart EndContainer()
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_ENDCONTAINER;
 

	
 
	return part;
 
}
 

	
 
/** Widget part function for setting the data and tooltip.
 
 * @param data Data of the widget.
 
 * @param tip  Tooltip of the widget.
 
 */
 
static inline NWidgetPart SetDataTip(uint16 data, StringID tip)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_DATATIP;
 
	part.u.data_tip.data = data;
 
	part.u.data_tip.tooltip = tip;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for setting the data and tooltip via a pointer.
 
 * @param ptr Pointer to the data and tooltip of the widget.
 
 */
 
static inline NWidgetPart SetDataTip(NWidgetPartDataTip *ptr)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = WPT_DATATIP_PTR;
 
	part.u.datatip_ptr = ptr;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for starting a new 'real' widget.
 
 * @param tp  Type of the new nested widget.
 
 * @param col Colour of the new widget.
 
 * @param idx Index of the widget in the widget array.
 
 * @note with #WWT_PANEL, #WWT_FRAME, #WWT_INSET, a new container is started.
 
 *       Child widgets must have a index bigger than the parent index.
 
 */
 
static inline NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = tp;
 
	part.u.widget.colour = col;
 
	part.u.widget.index = idx;
 

	
 
	return part;
 
}
 

	
 
/**
 
 * Widget part function for starting a new horizontal container, vertical container, or spacer widget.
 
 * @param tp Type of the new nested widget, #NWID_HORIZONTAL, #NWID_VERTICAL, or #NWID_SPACER
 
 */
 
static inline NWidgetPart NWidget(WidgetType tp)
 
{
 
	NWidgetPart part;
 

	
 
	part.type = tp;
 

	
 
	return part;
 
}
 

	
 
NWidgetContainer *MakeNWidgets(const NWidgetPart *parts, int count);
 

	
 
#endif /* WIDGET_TYPE_H */
src/window.cpp
Show inline comments
 
@@ -51,26 +51,64 @@ bool _scrolling_scrollbar;
 
bool _scrolling_viewport;
 

	
 
byte _special_mouse_mode;
 

	
 
/** Window description constructor. */
 
WindowDesc::WindowDesc(int16 left, int16 top, int16 min_width, int16 min_height, int16 def_width, int16 def_height,
 
			WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets)
 
			WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets,
 
			const NWidgetPart *nwid_parts, int16 nwid_length)
 
{
 
	this->left = left;
 
	this->top = top;
 
	this->minimum_width = min_width;
 
	this->minimum_height = min_height;
 
	this->default_width = def_width;
 
	this->default_height = def_height;
 
	this->cls = window_class;
 
	this->parent_cls = parent_class;
 
	this->flags = flags;
 
	this->widgets = widgets;
 
	this->nwid_parts = nwid_parts;
 
	this->nwid_length = nwid_length;
 
	this->new_widgets = NULL;
 
}
 

	
 
/** Get widget array of the window description. */
 
const Widget *WindowDesc::GetWidgets() const
 
{
 
	const bool rtl = false; // Direction of the language is left-to-right
 

	
 
	/* If nested widgets are present, convert them to a widget array. */
 
	if (this->nwid_parts != NULL && nwid_length > 0 && this->new_widgets == NULL) {
 
		NWidgetContainer *nwid = MakeNWidgets(this->nwid_parts, this->nwid_length);
 
		this->new_widgets = InitializeNWidgets(nwid, rtl);
 

	
 
		if (!rtl && this->widgets != NULL) {
 
			/* There are two descriptions, compare them.
 
			 * Comparing only makes sense when using a left-to-right language.
 
			 */
 
			bool ok = CompareWidgetArrays(this->widgets, this->new_widgets, false);
 
			if (ok) {
 
				DEBUG(misc, 1, "Nested widgets are equal, min-size(%u, %u)", nwid->min_x, nwid->min_y);
 
			} else {
 
				DEBUG(misc, 0, "Nested widgets give different results");
 
				CompareWidgetArrays(this->widgets, this->new_widgets, true);
 
			}
 
		}
 
		delete nwid;
 
	}
 

	
 
	const Widget *wids = (this->new_widgets != NULL) ? this->new_widgets : this->widgets;
 
	assert(wids != NULL);
 
	return wids;
 
}
 

	
 
WindowDesc::~WindowDesc()
 
{
 
	free(this->new_widgets);
 
}
 

	
 
/**
 
 * Set the window that has the focus
 
 * @param w The window to set the focus on
 
 */
 
void SetFocusedWindow(Window *w)
 
@@ -1156,13 +1194,13 @@ static Point LocalGetWindowPlacement(con
 
 *
 
 * @return Window pointer of the newly created window
 
 */
 
Window::Window(const WindowDesc *desc, WindowNumber window_number)
 
{
 
	Point pt = LocalGetWindowPlacement(desc, window_number);
 
	this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number);
 
	this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->GetWidgets(), window_number);
 
	this->desc_flags = desc->flags;
 
}
 

	
 
/** Do a search for a window at specific coordinates. For this we start
 
 * at the topmost window, obviously and work our way down to the bottom
 
 * @param x position x to query
src/window_gui.h
Show inline comments
 
@@ -38,24 +38,32 @@ extern Window *_focused_window;
 
/**
 
 * High level window description
 
 */
 
struct WindowDesc : ZeroedMemoryAllocator {
 

	
 
	WindowDesc(int16 left, int16 top, int16 min_width, int16 min_height, int16 def_width, int16 def_height,
 
			WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets);
 
			WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets,
 
			const NWidgetPart *nwid_parts = NULL, int16 nwid_length = 0);
 

	
 
	~WindowDesc();
 

	
 
	int16 left;             ///< Prefered x position of left edge of the window, @see WindowDefaultPosition()
 
	int16 top;              ///< Prefered y position of the top of the window, @see WindowDefaultPosition()
 
	int16 minimum_width;    ///< Minimal width of the window
 
	int16 minimum_height;   ///< Minimal height of the window
 
	int16 default_width;    ///< Prefered initial width of the window
 
	int16 default_height;   ///< Prefered initial height of the window
 
	WindowClass cls;        ///< Class of the window, @see WindowClass
 
	WindowClass parent_cls; ///< Class of the parent window, @see WindowClass
 
	uint32 flags;           ///< Flags, @see WindowDefaultFlags
 
	const Widget *widgets;  ///< List of widgets with their position and size for the window
 
	int16 left;                    ///< Prefered x position of left edge of the window. @see WindowDefaultPosition()
 
	int16 top;                     ///< Prefered y position of the top of the window. @see WindowDefaultPosition()
 
	int16 minimum_width;           ///< Minimal width of the window.
 
	int16 minimum_height;          ///< Minimal height of the window.
 
	int16 default_width;           ///< Prefered initial width of the window.
 
	int16 default_height;          ///< Prefered initial height of the window.
 
	WindowClass cls;               ///< Class of the window, @see WindowClass.
 
	WindowClass parent_cls;        ///< Class of the parent window. @see WindowClass
 
	uint32 flags;                  ///< Flags. @see WindowDefaultFlags
 
	const Widget *widgets;         ///< List of widgets with their position and size for the window.
 
	const NWidgetPart *nwid_parts; ///< Nested widget parts describing the window.
 
	int16 nwid_length;             ///< Length of the #nwid_parts array.
 
	mutable Widget *new_widgets;   ///< Widgets generated from #nwid_parts.
 

	
 
	const Widget *GetWidgets() const;
 
};
 

	
 
/**
 
 * Window default widget/window handling flags
 
 */
 
enum WindowDefaultFlag {
0 comments (0 inline, 0 general)