(svn r21448) -Add: generic widget for creating a scrollable (one direction) and resizable (both directions) area with same sized widgets

replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
rubidium 14 years ago
parent 0848d92af1
commit b91ae2483e

@ -591,6 +591,7 @@ void Window::DrawSortButtonState(int widget, SortButtonState state) const
* <li> #NWidgetHorizontalLTR for organizing child widgets in a (horizontal) row, always in the same order. All childs below this container will also
* never swap order.
* <li> #NWidgetVertical for organizing child widgets underneath each other.
* <li> #NWidgetMatrix for organizing child widgets in a matrix form.
* <li> #NWidgetBackground for adding a background behind its child widget.
* <li> #NWidgetStacked for stacking child widgets on top of each other.
* </ul>
@ -1352,6 +1353,226 @@ NWidgetCore *NWidgetSpacer::GetWidgetFromPos(int x, int y)
return NULL;
}
NWidgetMatrix::NWidgetMatrix() : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(-1), clicked(-1), count(-1)
{
}
void NWidgetMatrix::SetIndex(int index)
{
this->index = index;
}
void NWidgetMatrix::SetColour(Colours colour)
{
this->colour = colour;
}
/**
* Sets the clicked widget in the matrix.
* @param clicked The clicked widget.
*/
void NWidgetMatrix::SetClicked(int clicked)
{
this->clicked = clicked;
}
/**
* Set the number of elements in this matrix.
* @note Updates the number of elements/capacity of the real scrollbar.
* @param count The number of elements.
*/
void NWidgetMatrix::SetCount(int count)
{
this->count = count;
if (this->sb == NULL || this->widgets_x == 0) return;
/* We need to get the number of pixels the matrix is high/wide.
* So, determine the number of rows/columns based on the number of
* columns/rows (one is constant/unscrollable).
* Then multiply that by the height of a widget, and add the pre
* and post spacing "offsets". */
count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
count *= (this->sb->IsVertical() ? this->head->smallest_y : this->head->smallest_x) + this->pip_inter;
count += -this->pip_inter + this->pip_pre + this->pip_post; // We counted an inter too much in the multiplication above
this->sb->SetCount(count);
this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
}
/**
* Assign a scrollbar to this matrix.
* @param sb The scrollbar to assign to us.
*/
void NWidgetMatrix::SetScrollbar(Scrollbar *sb)
{
this->sb = sb;
}
void NWidgetMatrix::SetupSmallestSize(Window *w, bool init_array)
{
assert(this->head != NULL);
assert(this->head->next == NULL);
if (this->index >= 0 && init_array) { // Fill w->nested_array[]
assert(w->nested_array_size > (uint)this->index);
w->nested_array[this->index] = this;
}
this->head->SetupSmallestSize(w, init_array);
Dimension padding = {this->pip_pre + this->pip_post, this->pip_pre + this->pip_post};
Dimension size = {this->head->smallest_x + padding.width, this->head->smallest_y + padding.height};
Dimension fill = {0, 0};
Dimension resize = {this->pip_inter + this->head->smallest_x, this->pip_inter + this->head->smallest_y};
if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
this->smallest_x = size.width;
this->smallest_y = size.height;
this->fill_x = fill.width;
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
}
void NWidgetMatrix::AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
{
assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
this->pos_x = x;
this->pos_y = y;
this->current_x = given_width;
this->current_y = given_height;
/* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
this->widget_w = this->head->smallest_x + this->pip_inter;
this->widget_h = this->head->smallest_y + this->pip_inter;
/* Account for the pip_inter is between widgets, so we need to account for that when
* the division assumes pip_inter is used for all widgets. */
this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
/* When resizing, update the scrollbar's count. E.g. with a vertical
* scrollbar becoming wider or narrower means the amount of rows in
* the scrollbar becomes respectively smaller or higher. */
if (sizing == ST_RESIZE) {
this->SetCount(this->count);
}
}
void NWidgetMatrix::FillNestedArray(NWidgetBase **array, uint length)
{
if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
NWidgetContainer::FillNestedArray(array, length);
}
NWidgetCore *NWidgetMatrix::GetWidgetFromPos(int x, int y)
{
/* Falls outside of the matrix widget. */
if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
int start_x, start_y, base_offs_x, base_offs_y;
this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
/* Swap the x offset around for RTL, so it'll behave like LTR for RTL as well. */
bool rtl = _current_text_dir == TD_RTL;
if (rtl) base_offs_x -= (this->widgets_x - 1) * this->widget_w;
int widget_col = (x - base_offs_x - (int)this->pip_pre - (int)this->pos_x) / this->widget_w;
int widget_row = (y - base_offs_y - (int)this->pip_pre - (int)this->pos_y) / this->widget_h;
if (widget_row * this->widgets_x + widget_col >= this->count) return NULL;
NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->head);
child->AssignSizePosition(ST_RESIZE,
this->pos_x + this->pip_pre + widget_col * this->widget_w + base_offs_x,
this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
child->smallest_x, child->smallest_y, rtl);
/* "Undo" the RTL swap here to get the right widget index. */
if (rtl) widget_col = this->widgets_x - widget_col - 1;
SB(child->index, 16, 16, (widget_row + start_y) * this->widgets_x + widget_col + start_x);
return child->GetWidgetFromPos(x, y);
}
/* virtual */ void NWidgetMatrix::Draw(const Window *w)
{
/* Fill the background. */
GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, _colour_gradient[this->colour & 0xF][5]);
/* Set up a clipping area for the previews. */
DrawPixelInfo tmp_dpi;
if (!FillDrawPixelInfo(&tmp_dpi, this->pos_x + this->pip_pre, this->pos_y + this->pip_pre, this->current_x - this->pip_pre - this->pip_post, this->current_y - this->pip_pre - this->pip_post)) return;
DrawPixelInfo *old_dpi = _cur_dpi;
_cur_dpi = &tmp_dpi;
/* Get the appropriate offsets so we can draw the right widgets. */
NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->head);
bool rtl = _current_text_dir == TD_RTL;
int start_x, start_y, base_offs_x, base_offs_y;
this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
int offs_y = base_offs_y;
for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
/* Are we within bounds? */
if (offs_y + child->smallest_y <= 0) continue;
if (offs_y >= (int)this->current_y) break;
/* We've passed our amount of widgets. */
if (y * this->widgets_x >= this->count) break;
int offs_x = base_offs_x;
for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
/* Are we within bounds? */
if (offs_x + child->smallest_x <= 0) continue;
if (offs_x >= (int)this->current_x) continue;
/* Do we have this many widgets? */
int sub_wid = y * this->widgets_x + x;
if (sub_wid >= this->count) break;
child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
child->SetLowered(this->clicked == sub_wid);
SB(child->index, 16, 16, sub_wid);
child->Draw(w);
}
}
/* Restore the clipping area. */
_cur_dpi = old_dpi;
}
/**
* Get the different offsets that are influenced by scrolling.
* @param [out] start_x The start position in columns,
* @param [out] start_y The start position in rows.
* @param [out] base_offs_x The base horizontal offset in pixels.
* @param [out] base_offs_y The base vertical offset in pixels.
*/
void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
{
base_offs_x = 0;
base_offs_y = 0;
start_x = 0;
start_y = 0;
if (this->sb != NULL) {
if (this->sb->IsVertical()) {
start_y = this->sb->GetPosition() / this->widget_h;
base_offs_y = -this->sb->GetPosition() + start_y * this->widget_h;
if (_current_text_dir == TD_RTL) base_offs_x = this->pip_pre + this->widget_w * (this->widgets_x - 1) - this->pip_inter;
} else {
start_x = this->sb->GetPosition() / this->widget_w;
if (_current_text_dir == TD_RTL) {
base_offs_x = this->sb->GetCapacity() + this->sb->GetPosition() - (start_x + 1) * this->widget_w + this->pip_inter - this->pip_post - this->pip_pre;
} else {
base_offs_x = -this->sb->GetPosition() + start_x * this->widget_w;
}
}
}
}
/**
* Constructor parent nested widgets.
* @param tp Type of parent widget.
@ -2165,6 +2386,17 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest,
*fill_dest = true;
break;
case NWID_MATRIX: {
if (*dest != NULL) return num_used;
NWidgetMatrix *nwm = new NWidgetMatrix();
*dest = nwm;
*fill_dest = true;
nwm->SetIndex(parts->u.widget.index);
nwm->SetColour(parts->u.widget.colour);
*biggest_index = max(*biggest_index, (int)parts->u.widget.index);
break;
}
case WPT_FUNCTION: {
if (*dest != NULL) return num_used;
/* Ensure proper functioning even when the called code simply writes its largest index. */
@ -2308,7 +2540,7 @@ static int MakeWidgetTree(const NWidgetPart *parts, int count, NWidgetBase **par
/* If sub-widget is a container, recursively fill that container. */
WidgetType tp = sub_widget->type;
if (fill_sub && (tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL
if (fill_sub && (tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
|| tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION)) {
NWidgetBase *sub_ptr = sub_widget;
int num_used = MakeWidgetTree(parts, count - total_used, &sub_ptr, biggest_index);

@ -73,6 +73,7 @@ enum WidgetType {
NWID_HORIZONTAL, ///< Horizontal container.
NWID_HORIZONTAL_LTR, ///< Horizontal container that doesn't change the order of the widgets for RTL languages.
NWID_VERTICAL, ///< Vertical container.
NWID_MATRIX, ///< Matrix container.
NWID_SPACER, ///< Invisible widget that takes some space.
NWID_SELECTION, ///< Stacked widgets, only one visible at a time (eg in a panel with tabs).
NWID_VIEWPORT, ///< Nested widget containing a viewport.
@ -445,6 +446,45 @@ public:
void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl);
};
/**
* Matrix container with implicitly equal sized (virtual) sub-widgets.
* This widget must have exactly one sub-widget. After that this sub-widget
* is used to draw all of the data within the matrix piece by piece.
* DrawWidget and OnClick calls will be done to that sub-widget, where the
* 16 high bits are used to encode the index into the matrix.
* @ingroup NestedWidgets
*/
class NWidgetMatrix : public NWidgetPIPContainer {
public:
NWidgetMatrix();
void SetIndex(int index);
void SetColour(Colours colour);
void SetClicked(int clicked);
void SetCount(int count);
void SetScrollbar(Scrollbar *sb);
void SetupSmallestSize(Window *w, bool init_array);
void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl);
/* virtual */ void FillNestedArray(NWidgetBase **array, uint length);
/* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y);
/* virtual */ void Draw(const Window *w);
protected:
int index; ///< If non-negative, index in the #Window::nested_array.
Colours colour; ///< Colour of this widget.
int clicked; ///< The currently clicked widget.
int count; ///< Amount of valid widgets.
Scrollbar *sb; ///< The scrollbar we're associated with.
private:
int widget_w; ///< The width of the child widget including inter spacing.
int widget_h; ///< The height of the child widget including inter spacing.
int widgets_x; ///< The number of visible widgets in horizontal direction.
int widgets_y; ///< The number of visible widgets in vertical direction.
void GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y);
};
/**
* Spacer widget.
@ -714,7 +754,7 @@ static FORCEINLINE uint ComputeMaxSize(uint base, uint max_space, uint step)
* - #SetResize Define how the widget may resize.
* - #SetPadding Create additional space around the widget.
*
* - Container widgets #NWidgetHorizontal, #NWidgetHorizontalLTR, and #NWidgetVertical, start with a #NWidget(WidgetType tp) part.
* - Container widgets #NWidgetHorizontal, #NWidgetHorizontalLTR, #NWidgetVertical, and #NWidgetMatrix, start with a #NWidget(WidgetType tp) part.
* Their properties are derived from the child widgets so they cannot be specified.
* You can however use
* - #SetPadding Define additional padding around the container.
@ -1001,7 +1041,7 @@ static inline NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx = -1)
/**
* Widget part function for starting a new horizontal container, vertical container, or spacer widget.
* @param tp Type of the new nested widget, #NWID_HORIZONTAL(_LTR), #NWID_VERTICAL, #NWID_SPACER, or #NWID_SELECTION.
* @param tp Type of the new nested widget, #NWID_HORIZONTAL(_LTR), #NWID_VERTICAL, #NWID_SPACER, #NWID_SELECTION, and #NWID_MATRIX.
* @param cont_flags Flags for the containers (#NWID_HORIZONTAL(_LTR) and #NWID_VERTICAL).
* @ingroup NestedWidgetParts
*/

Loading…
Cancel
Save