Merge branch 'master' into jgrpp
# Conflicts: # .github/workflows/ci-build.yml # src/ai/ai_gui.cpp # src/blitter/32bpp_optimized.cpp # src/blitter/32bpp_simple.cpp # src/blitter/32bpp_sse2.cpp # src/blitter/8bpp_optimized.cpp # src/blitter/8bpp_simple.cpp # src/blitter/null.cpp # src/blitter/null.hpp # src/company_gui.cpp # src/game/game_gui.cpp # src/genworld_gui.cpp # src/gfx.cpp # src/gfx_func.h # src/graph_gui.cpp # src/industry_gui.cpp # src/linkgraph/linkgraphjob.cpp # src/network/network_gui.cpp # src/newgrf_debug_gui.cpp # src/openttd.cpp # src/pathfinder/npf/aystar.h # src/road_gui.cpp # src/saveload/order_sl.cpp # src/saveload/saveload.cpp # src/saveload/saveload.h # src/script/api/script_log.cpp # src/script/api/script_town.cpp # src/script/script_gui.cpp # src/settings.cpp # src/settings_gui.cpp # src/settings_table.cpp # src/settings_type.h # src/smallmap_gui.cpp # src/sortlist_type.h # src/spritecache.cpp # src/spriteloader/grf.cpp # src/spriteloader/grf.hpp # src/spriteloader/spriteloader.hpp # src/station_cmd.cpp # src/station_cmd.h # src/station_gui.cpp # src/strings.cpp # src/toolbar_gui.cpp # src/town_cmd.cpp # src/town_gui.cpp # src/vehicle_gui.cpp # src/vehicle_gui_base.h # src/video/opengl.cpp # src/video/opengl.h # src/widgets/dropdown.cpp # src/widgets/dropdown_type.h # src/window_gui.h
This commit is contained in:
@@ -27,7 +27,7 @@ enum AIConfigWidgets {
|
||||
WID_AIC_MOVE_DOWN, ///< Move down button.
|
||||
WID_AIC_CHANGE, ///< Select another AI button.
|
||||
WID_AIC_CONFIGURE, ///< Change AI settings button.
|
||||
WID_AIC_CLOSE, ///< Close window button.
|
||||
WID_AIC_OPEN_URL, ///< Open AI URL.
|
||||
WID_AIC_TEXTFILE, ///< Open AI readme, changelog (+1) or license (+2).
|
||||
WID_AIC_CONTENT_DOWNLOAD = WID_AIC_TEXTFILE + TFT_CONTENT_END, ///< Download content button.
|
||||
};
|
||||
|
@@ -21,85 +21,9 @@
|
||||
#include "../safeguards.h"
|
||||
|
||||
|
||||
void DropDownListItem::Draw(const Rect &r, bool, Colours bg_colour) const
|
||||
{
|
||||
int c1 = _colour_gradient[bg_colour][3];
|
||||
int c2 = _colour_gradient[bg_colour][7];
|
||||
|
||||
int mid = CenterBounds(r.top, r.bottom, 0);
|
||||
GfxFillRect(r.left, mid - WidgetDimensions::scaled.bevel.bottom, r.right, mid - 1, c1);
|
||||
GfxFillRect(r.left, mid, r.right, mid + WidgetDimensions::scaled.bevel.top - 1, c2);
|
||||
}
|
||||
|
||||
DropDownListStringItem::DropDownListStringItem(StringID string, int result, bool masked) : DropDownListItem(result, masked), string(GetString(string))
|
||||
{
|
||||
}
|
||||
|
||||
DropDownListStringItem::DropDownListStringItem(const std::string &string, int result, bool masked) : DropDownListItem(result, masked)
|
||||
{
|
||||
/* A raw string may contain parsable tokens, so it needs to be passed through GetString. */
|
||||
SetDParamStr(0, string);
|
||||
this->string = GetString(STR_JUST_RAW_STRING);
|
||||
}
|
||||
|
||||
uint DropDownListStringItem::Width() const
|
||||
{
|
||||
return GetStringBoundingBox(this->String()).width + WidgetDimensions::scaled.dropdowntext.Horizontal();
|
||||
}
|
||||
|
||||
void DropDownListStringItem::Draw(const Rect &r, bool sel, Colours) const
|
||||
{
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.dropdowntext);
|
||||
DrawString(ir.left, ir.right, r.top, this->String(), (sel ? TC_WHITE : TC_BLACK) | this->colour_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Natural sorting comparator function for DropDownList::sort().
|
||||
* @param first Left side of comparison.
|
||||
* @param second Right side of comparison.
|
||||
* @return true if \a first precedes \a second.
|
||||
* @warning All items in the list need to be derivates of DropDownListStringItem.
|
||||
*/
|
||||
/* static */ bool DropDownListStringItem::NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second)
|
||||
{
|
||||
std::string str1 = static_cast<const DropDownListStringItem*>(first.get())->String();
|
||||
std::string str2 = static_cast<const DropDownListStringItem*>(second.get())->String();
|
||||
return StrNaturalCompare(str1, str2) < 0;
|
||||
}
|
||||
|
||||
DropDownListIconItem::DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked) : DropDownListStringItem(string, result, masked), sprite(sprite), pal(pal)
|
||||
{
|
||||
this->dim = GetSpriteSize(sprite);
|
||||
this->sprite_y = dim.height;
|
||||
}
|
||||
|
||||
uint DropDownListIconItem::Height() const
|
||||
{
|
||||
return std::max(this->dim.height, (uint)GetCharacterHeight(FS_NORMAL));
|
||||
}
|
||||
|
||||
uint DropDownListIconItem::Width() const
|
||||
{
|
||||
return DropDownListStringItem::Width() + this->dim.width + WidgetDimensions::scaled.hsep_wide;
|
||||
}
|
||||
|
||||
void DropDownListIconItem::Draw(const Rect &r, bool sel, Colours) const
|
||||
{
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.dropdowntext);
|
||||
Rect tr = ir.Indent(this->dim.width + WidgetDimensions::scaled.hsep_normal, rtl);
|
||||
DrawSprite(this->sprite, this->pal, ir.WithWidth(this->dim.width, rtl).left, CenterBounds(r.top, r.bottom, this->sprite_y));
|
||||
DrawString(tr.left, tr.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), this->String(), (sel ? TC_WHITE : TC_BLACK) | this->colour_flags);
|
||||
}
|
||||
|
||||
void DropDownListIconItem::SetDimension(Dimension d)
|
||||
{
|
||||
this->dim = d;
|
||||
}
|
||||
|
||||
static const NWidgetPart _nested_dropdown_menu_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_END, WID_DM_ITEMS), SetMinimalSize(1, 1), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_END, WID_DM_ITEMS), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_DM_SHOW_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_END, WID_DM_SCROLL),
|
||||
EndContainer(),
|
||||
@@ -118,71 +42,54 @@ struct DropdownWindow : Window {
|
||||
WindowClass parent_wnd_class; ///< Parent window class.
|
||||
WindowNumber parent_wnd_num; ///< Parent window number.
|
||||
int parent_button; ///< Parent widget number where the window is dropped from.
|
||||
Rect wi_rect; ///< Rect of the button that opened the dropdown.
|
||||
const DropDownList list; ///< List with dropdown menu items.
|
||||
int selected_index; ///< Index of the selected item in the list.
|
||||
byte click_delay; ///< Timer to delay selection.
|
||||
bool drag_mode;
|
||||
int selected_result; ///< Result value of the selected item in the list.
|
||||
byte click_delay = 0; ///< Timer to delay selection.
|
||||
bool drag_mode = true;
|
||||
bool instant_close; ///< Close the window when the mouse button is raised.
|
||||
int scrolling; ///< If non-zero, auto-scroll the item list (one time).
|
||||
int scrolling = 0; ///< If non-zero, auto-scroll the item list (one time).
|
||||
GUITimer scrolling_timer; ///< Timer for auto-scroll of the item list.
|
||||
Point position; ///< Position of the topleft corner of the window.
|
||||
Scrollbar *vscroll;
|
||||
DropDownSyncFocus sync_parent_focus; ///< Call parent window's OnFocus[Lost]().
|
||||
|
||||
Dimension items_dim; ///< Calculated cropped and padded dimension for the items widget.
|
||||
|
||||
/**
|
||||
* Create a dropdown menu.
|
||||
* @param parent Parent window.
|
||||
* @param list Dropdown item list.
|
||||
* @param selected Index of the selected item in the list.
|
||||
* @param selected Initial selected result of the list.
|
||||
* @param button Widget of the parent window doing the dropdown.
|
||||
* @param wi_rect Rect of the button that opened the dropdown.
|
||||
* @param instant_close Close the window when the mouse button is raised.
|
||||
* @param position Topleft position of the dropdown menu window.
|
||||
* @param size Size of the dropdown menu window.
|
||||
* @param wi_colour Colour of the parent widget.
|
||||
* @param scroll Dropdown menu has a scrollbar.
|
||||
*/
|
||||
DropdownWindow(Window *parent, DropDownList &&list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll, DropDownSyncFocus sync_parent_focus)
|
||||
: Window(&_dropdown_desc), list(std::move(list))
|
||||
DropdownWindow(Window *parent, DropDownList &&list, int selected, int button, const Rect wi_rect, bool instant_close, Colours wi_colour, DropDownSyncFocus sync_parent_focus)
|
||||
: Window(&_dropdown_desc)
|
||||
, parent_button(button)
|
||||
, wi_rect(wi_rect)
|
||||
, list(std::move(list))
|
||||
, selected_result(selected)
|
||||
, instant_close(instant_close)
|
||||
, sync_parent_focus(sync_parent_focus)
|
||||
{
|
||||
assert(!this->list.empty());
|
||||
|
||||
this->position = position;
|
||||
this->parent_wnd_class = parent->window_class;
|
||||
this->parent_wnd_num = parent->window_number;
|
||||
this->sync_parent_focus = sync_parent_focus;
|
||||
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->GetWidget<NWidgetCore>(WID_DM_ITEMS)->colour = wi_colour;
|
||||
this->GetWidget<NWidgetCore>(WID_DM_SCROLL)->colour = wi_colour;
|
||||
this->vscroll = this->GetScrollbar(WID_DM_SCROLL);
|
||||
|
||||
uint items_width = size.width - (scroll ? NWidgetScrollbar::GetVerticalDimension().width : 0);
|
||||
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_DM_ITEMS);
|
||||
nwi->SetMinimalSizeAbsolute(items_width, size.height + WidgetDimensions::scaled.fullbevel.Vertical() * 2);
|
||||
nwi->colour = wi_colour;
|
||||
|
||||
nwi = this->GetWidget<NWidgetCore>(WID_DM_SCROLL);
|
||||
nwi->colour = wi_colour;
|
||||
|
||||
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(scroll ? 0 : SZSP_NONE);
|
||||
this->UpdateSizeAndPosition(parent);
|
||||
|
||||
this->FinishInitNested(0);
|
||||
CLRBITS(this->flags, WF_WHITE_BORDER);
|
||||
|
||||
/* Total length of list */
|
||||
int list_height = 0;
|
||||
for (const auto &item : this->list) {
|
||||
list_height += item->Height();
|
||||
}
|
||||
|
||||
/* Capacity is the average number of items visible */
|
||||
this->vscroll->SetCapacity(size.height * this->list.size() / list_height);
|
||||
this->vscroll->SetCount(this->list.size());
|
||||
|
||||
this->parent_button = button;
|
||||
this->selected_index = selected;
|
||||
this->click_delay = 0;
|
||||
this->drag_mode = true;
|
||||
this->instant_close = instant_close;
|
||||
this->scrolling_timer = GUITimer(MILLISECONDS_PER_TICK);
|
||||
}
|
||||
|
||||
@@ -198,14 +105,81 @@ struct DropdownWindow : Window {
|
||||
Point pt = _cursor.pos;
|
||||
pt.x -= w2->left;
|
||||
pt.y -= w2->top;
|
||||
w2->OnDropdownClose(pt, this->parent_button, this->selected_index, this->instant_close);
|
||||
w2->OnDropdownClose(pt, this->parent_button, this->selected_result, this->instant_close);
|
||||
if (_focused_window == this) {
|
||||
SetFocusedWindow(w2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override
|
||||
/**
|
||||
* Fit dropdown list into available height, rounding to average item size. Width is adjusted if scrollbar is present.
|
||||
* @param[in,out] desired Desired dimensions of dropdown list.
|
||||
* @param list Dimensions of the list itself, without padding or cropping.
|
||||
* @param available_height Available height to fit list within.
|
||||
*/
|
||||
void FitAvailableHeight(Dimension &desired, const Dimension &list, uint available_height)
|
||||
{
|
||||
if (desired.height < available_height) return;
|
||||
|
||||
/* If the dropdown doesn't fully fit, we a need a dropdown. */
|
||||
uint avg_height = list.height / (uint)this->list.size();
|
||||
uint rows = std::max((available_height - WidgetDimensions::scaled.dropdownlist.Vertical()) / avg_height, 1U);
|
||||
|
||||
desired.width = std::max(list.width, desired.width - NWidgetScrollbar::GetVerticalDimension().width);
|
||||
desired.height = rows * avg_height + WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update size and position of window to fit dropdown list into available space.
|
||||
*/
|
||||
void UpdateSizeAndPosition(Window *parent)
|
||||
{
|
||||
Rect button_rect = this->wi_rect.Translate(parent->left, parent->top);
|
||||
|
||||
/* Get the dimensions required for the list. */
|
||||
Dimension list_dim = GetDropDownListDimension(this->list);
|
||||
|
||||
/* Set up dimensions for the items widget. */
|
||||
Dimension widget_dim = list_dim;
|
||||
widget_dim.width += WidgetDimensions::scaled.dropdownlist.Horizontal();
|
||||
widget_dim.height += WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
|
||||
/* Width should match at least the width of the parent widget. */
|
||||
widget_dim.width = std::max<uint>(widget_dim.width, button_rect.Width());
|
||||
|
||||
/* Available height below (or above, if the dropdown is placed above the widget). */
|
||||
uint available_height_below = std::max(GetMainViewBottom() - button_rect.bottom - 1, 0);
|
||||
uint available_height_above = std::max(button_rect.top - 1 - GetMainViewTop(), 0);
|
||||
|
||||
/* Is it better to place the dropdown above the widget? */
|
||||
if (widget_dim.height > available_height_below && available_height_above > available_height_below) {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_above);
|
||||
this->position.y = button_rect.top - widget_dim.height;
|
||||
} else {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_below);
|
||||
this->position.y = button_rect.bottom + 1;
|
||||
}
|
||||
|
||||
this->position.x = (_current_text_dir == TD_RTL) ? button_rect.right + 1 - (int)widget_dim.width : button_rect.left;
|
||||
|
||||
this->items_dim = widget_dim;
|
||||
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(list_dim.height > widget_dim.height ? 0 : SZSP_NONE);
|
||||
|
||||
/* Capacity is the average number of items visible */
|
||||
this->vscroll->SetCapacity((widget_dim.height - WidgetDimensions::scaled.dropdownlist.Vertical()) * this->list.size() / list_dim.height);
|
||||
this->vscroll->SetCount(this->list.size());
|
||||
|
||||
/* If the dropdown is positioned above the parent widget, start selection at the bottom. */
|
||||
if (this->position.y < button_rect.top && list_dim.height > widget_dim.height) this->vscroll->UpdatePosition(INT_MAX);
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(int widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
|
||||
{
|
||||
if (widget == WID_DM_ITEMS) *size = this->items_dim;
|
||||
}
|
||||
|
||||
Point OnInitialPosition([[maybe_unused]] int16_t sm_width, [[maybe_unused]] int16_t sm_height, [[maybe_unused]] int window_number) override
|
||||
{
|
||||
return this->position;
|
||||
}
|
||||
@@ -219,8 +193,8 @@ struct DropdownWindow : Window {
|
||||
{
|
||||
if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
|
||||
|
||||
const Rect &r = this->GetWidget<NWidgetBase>(WID_DM_ITEMS)->GetCurrentRect().Shrink(WidgetDimensions::scaled.fullbevel);
|
||||
int y = _cursor.pos.y - this->top - r.top - WidgetDimensions::scaled.fullbevel.top;
|
||||
const Rect &r = this->GetWidget<NWidgetBase>(WID_DM_ITEMS)->GetCurrentRect().Shrink(WidgetDimensions::scaled.dropdownlist);
|
||||
int y = _cursor.pos.y - this->top - r.top;
|
||||
int pos = this->vscroll->GetPosition();
|
||||
|
||||
for (const auto &item : this->list) {
|
||||
@@ -247,7 +221,7 @@ struct DropdownWindow : Window {
|
||||
|
||||
Colours colour = this->GetWidget<NWidgetCore>(widget)->colour;
|
||||
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.fullbevel).Shrink(RectPadding::zero, WidgetDimensions::scaled.fullbevel);
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.dropdownlist);
|
||||
int y = ir.top;
|
||||
int pos = this->vscroll->GetPosition();
|
||||
for (const auto &item : this->list) {
|
||||
@@ -257,14 +231,12 @@ struct DropdownWindow : Window {
|
||||
if (--pos >= 0) continue;
|
||||
|
||||
if (y + item_height - 1 <= ir.bottom) {
|
||||
bool selected = (this->selected_index == item->result);
|
||||
if (selected) GfxFillRect(ir.left, y, ir.right, y + item_height - 1, PC_BLACK);
|
||||
Rect full{ir.left, y, ir.right, y + item_height - 1};
|
||||
|
||||
item->Draw({ir.left, y, ir.right, y + item_height - 1}, selected, colour);
|
||||
bool selected = (this->selected_result == item->result) && item->Selectable();
|
||||
if (selected) GfxFillRect(full, PC_BLACK);
|
||||
|
||||
if (item->masked) {
|
||||
GfxFillRect(ir.left, y, ir.right, y + item_height - 1, _colour_gradient[colour][5], FILLRECT_CHECKER);
|
||||
}
|
||||
item->Draw(full, full.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero), selected, colour);
|
||||
}
|
||||
y += item_height;
|
||||
}
|
||||
@@ -276,7 +248,7 @@ struct DropdownWindow : Window {
|
||||
int item;
|
||||
if (this->GetDropDownItem(item)) {
|
||||
this->click_delay = 4;
|
||||
this->selected_index = item;
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
@@ -307,7 +279,7 @@ struct DropdownWindow : Window {
|
||||
this->window_class = WC_INVALID;
|
||||
this->SetDirty();
|
||||
|
||||
w2->OnDropdownSelect(this->parent_button, this->selected_index);
|
||||
w2->OnDropdownSelect(this->parent_button, this->selected_result);
|
||||
this->Close();
|
||||
return;
|
||||
}
|
||||
@@ -336,8 +308,8 @@ struct DropdownWindow : Window {
|
||||
if (!this->GetDropDownItem(item)) return;
|
||||
}
|
||||
|
||||
if (this->selected_index != item) {
|
||||
this->selected_index = item;
|
||||
if (this->selected_result != item) {
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
@@ -372,6 +344,7 @@ Dimension GetDropDownListDimension(const DropDownList &list)
|
||||
dim.height += item->Height();
|
||||
dim.width = std::max(dim.width, item->Width());
|
||||
}
|
||||
dim.width += WidgetDimensions::scaled.dropdowntext.Horizontal();
|
||||
return dim;
|
||||
}
|
||||
|
||||
@@ -388,66 +361,8 @@ Dimension GetDropDownListDimension(const DropDownList &list)
|
||||
*/
|
||||
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, int button, Rect wi_rect, Colours wi_colour, bool instant_close, DropDownSyncFocus sync_parent_focus)
|
||||
{
|
||||
CloseWindowById(WC_DROPDOWN_MENU, 0);
|
||||
|
||||
/* The preferred position is just below the dropdown calling widget */
|
||||
int top = w->top + wi_rect.bottom + 1;
|
||||
|
||||
/* The preferred width equals the calling widget */
|
||||
uint width = wi_rect.Width();
|
||||
|
||||
/* Get the height and width required for the list. */
|
||||
Dimension dim = GetDropDownListDimension(list);
|
||||
dim.width += WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
|
||||
/* Scrollbar needed? */
|
||||
bool scroll = false;
|
||||
|
||||
/* Is it better to place the dropdown above the widget? */
|
||||
bool above = false;
|
||||
|
||||
/* Available height below (or above, if the dropdown is placed above the widget). */
|
||||
uint available_height = std::max(GetMainViewBottom() - top - (int)WidgetDimensions::scaled.fullbevel.Vertical() * 2, 0);
|
||||
|
||||
/* If the dropdown doesn't fully fit below the widget... */
|
||||
if (dim.height > available_height) {
|
||||
|
||||
uint available_height_above = std::max(w->top + wi_rect.top - GetMainViewTop() - (int)WidgetDimensions::scaled.fullbevel.Vertical() * 2, 0);
|
||||
|
||||
/* Put the dropdown above if there is more available space. */
|
||||
if (available_height_above > available_height) {
|
||||
above = true;
|
||||
available_height = available_height_above;
|
||||
}
|
||||
|
||||
/* If the dropdown doesn't fully fit, we need a dropdown. */
|
||||
if (dim.height > available_height) {
|
||||
scroll = true;
|
||||
uint avg_height = dim.height / (uint)list.size();
|
||||
|
||||
/* Fit the list; create at least one row, even if there is no height available. */
|
||||
uint rows = std::max<uint>(available_height / avg_height, 1);
|
||||
dim.height = rows * avg_height;
|
||||
|
||||
/* Add space for the scrollbar. */
|
||||
dim.width += NWidgetScrollbar::GetVerticalDimension().width;
|
||||
}
|
||||
|
||||
/* Set the top position if needed. */
|
||||
if (above) {
|
||||
top = w->top + wi_rect.top - dim.height - WidgetDimensions::scaled.fullbevel.Vertical() * 2;
|
||||
}
|
||||
}
|
||||
|
||||
dim.width = std::max(width, dim.width);
|
||||
|
||||
Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - (int)width : wi_rect.left), top};
|
||||
DropdownWindow *dropdown = new DropdownWindow(w, std::move(list), selected, button, instant_close, dw_pos, dim, wi_colour, scroll, sync_parent_focus);
|
||||
|
||||
/* The dropdown starts scrolling downwards when opening it towards
|
||||
* the top and holding down the mouse button. It can be fooled by
|
||||
* opening the dropdown scrolled to the very bottom. */
|
||||
if (above && scroll) dropdown->vscroll->UpdatePosition(INT_MAX);
|
||||
CloseWindowByClass(WC_DROPDOWN_MENU);
|
||||
new DropdownWindow(w, std::move(list), selected, button, wi_rect, instant_close, wi_colour, sync_parent_focus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -12,7 +12,11 @@
|
||||
|
||||
#include "../window_type.h"
|
||||
#include "../gfx_func.h"
|
||||
#include "table/strings.h"
|
||||
#include "../gfx_type.h"
|
||||
#include "../string_func.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../table/strings.h"
|
||||
#include "../window_gui.h"
|
||||
#include <vector>
|
||||
|
||||
enum DropDownSyncFocus {
|
||||
@@ -23,60 +27,193 @@ enum DropDownSyncFocus {
|
||||
};
|
||||
|
||||
/**
|
||||
* Base list item class from which others are derived. If placed in a list it
|
||||
* will appear as a horizontal line in the menu.
|
||||
* Base list item class from which others are derived.
|
||||
*/
|
||||
class DropDownListItem {
|
||||
public:
|
||||
int result; ///< Result code to return to window on selection
|
||||
bool masked; ///< Masked and unselectable item
|
||||
|
||||
DropDownListItem(int result, bool masked) : result(result), masked(masked) {}
|
||||
virtual ~DropDownListItem() = default;
|
||||
|
||||
virtual bool Selectable() const { return false; }
|
||||
virtual uint Height() const { return GetCharacterHeight(FS_NORMAL); }
|
||||
virtual uint Width() const { return 0; }
|
||||
virtual void Draw(const Rect &r, bool sel, Colours bg_colour) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Common string list item.
|
||||
*/
|
||||
class DropDownListStringItem : public DropDownListItem {
|
||||
public:
|
||||
std::string string; ///< String of item
|
||||
int result; ///< Result value to return to window on selection.
|
||||
bool masked; ///< Masked and unselectable item.
|
||||
bool shaded; ///< Shaded item, affects text colour.
|
||||
TextColour colour_flags = TC_BEGIN;
|
||||
|
||||
DropDownListStringItem(StringID string, int result, bool masked);
|
||||
DropDownListStringItem(const std::string &string, int result, bool masked);
|
||||
explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
|
||||
virtual ~DropDownListItem() = default;
|
||||
|
||||
bool Selectable() const override { return true; }
|
||||
uint Width() const override;
|
||||
void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
|
||||
const std::string &String() const { return this->string; }
|
||||
void SetColourFlags(TextColour colour_flags) { this->colour_flags = colour_flags; }
|
||||
virtual bool Selectable() const { return true; }
|
||||
virtual uint Height() const { return 0; }
|
||||
virtual uint Width() const { return 0; }
|
||||
|
||||
static bool NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second);
|
||||
virtual void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const
|
||||
{
|
||||
if (this->masked) GfxFillRect(full, _colour_gradient[bg_colour][5], FILLRECT_CHECKER);
|
||||
}
|
||||
|
||||
TextColour GetColour(bool sel) const
|
||||
{
|
||||
if (this->shaded) return (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
|
||||
return (sel ? TC_WHITE : TC_BLACK) | this->colour_flags;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* List item with icon and string.
|
||||
* Drop down divider component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size -- used to determine height.
|
||||
*/
|
||||
class DropDownListIconItem : public DropDownListStringItem {
|
||||
SpriteID sprite;
|
||||
PaletteID pal;
|
||||
Dimension dim;
|
||||
uint sprite_y;
|
||||
template<class TBase, FontSize TFs = FS_NORMAL>
|
||||
class DropDownDivider : public TBase {
|
||||
public:
|
||||
DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked);
|
||||
template <typename... Args>
|
||||
explicit DropDownDivider(Args&&... args) : TBase(std::forward<Args>(args)...) {}
|
||||
|
||||
uint Height() const override;
|
||||
uint Width() const override;
|
||||
void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
|
||||
void SetDimension(Dimension d);
|
||||
bool Selectable() const override { return false; }
|
||||
uint Height() const override { return std::max<uint>(GetCharacterHeight(TFs), this->TBase::Height()); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const override
|
||||
{
|
||||
uint8_t c1 = _colour_gradient[bg_colour][3];
|
||||
uint8_t c2 = _colour_gradient[bg_colour][7];
|
||||
|
||||
int mid = CenterBounds(full.top, full.bottom, 0);
|
||||
GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1);
|
||||
GfxFillRect(full.left, mid, full.right, mid + WidgetDimensions::scaled.bevel.top - 1, c2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down string component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size.
|
||||
* @tparam TEnd Position string at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, FontSize TFs = FS_NORMAL, bool TEnd = false>
|
||||
class DropDownString : public TBase {
|
||||
std::string string; ///< String to be drawn.
|
||||
Dimension dim; ///< Dimensions of string.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownString(StringID string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||
{
|
||||
this->SetString(GetString(string));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
explicit DropDownString(const std::string &string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||
{
|
||||
SetDParamStr(0, string);
|
||||
this->SetString(GetString(STR_JUST_RAW_STRING));
|
||||
}
|
||||
|
||||
void SetString(std::string &&string)
|
||||
{
|
||||
this->string = std::move(string);
|
||||
this->dim = GetStringBoundingBox(this->string, TFs);
|
||||
}
|
||||
|
||||
uint Height() const override
|
||||
{
|
||||
return std::max<uint>(this->dim.height, this->TBase::Height());
|
||||
}
|
||||
|
||||
uint Width() const override { return this->dim.width + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
DrawStringMultiLine(r.WithWidth(this->dim.width, rtl), this->string, this->GetColour(sel), SA_CENTER, false, TFs);
|
||||
this->TBase::Draw(full, r.Indent(this->dim.width, rtl), sel, bg_colour);
|
||||
}
|
||||
|
||||
void SetColourFlags(TextColour colour_flags) { this->colour_flags = colour_flags; }
|
||||
|
||||
/**
|
||||
* Natural sorting comparator function for DropDownList::sort().
|
||||
* @param first Left side of comparison.
|
||||
* @param second Right side of comparison.
|
||||
* @return true if \a first precedes \a second.
|
||||
* @warning All items in the list need to be derivates of DropDownListStringItem.
|
||||
*/
|
||||
static bool NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second)
|
||||
{
|
||||
const std::string &str1 = static_cast<const DropDownString*>(first.get())->string;
|
||||
const std::string &str2 = static_cast<const DropDownString*>(second.get())->string;
|
||||
return StrNaturalCompare(str1, str2) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down icon component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TEnd Position icon at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, bool TEnd = false>
|
||||
class DropDownIcon : public TBase {
|
||||
SpriteID sprite; ///< Sprite ID to be drawn.
|
||||
PaletteID palette; ///< Palette ID to use.
|
||||
Dimension dsprite; ///< Bounding box dimensions of sprite.
|
||||
Dimension dbounds; ///< Bounding box dimensions of bounds.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownIcon(SpriteID sprite, PaletteID palette, Args&&... args) : TBase(std::forward<Args>(args)...), sprite(sprite), palette(palette)
|
||||
{
|
||||
this->dsprite = GetSpriteSize(this->sprite);
|
||||
this->dbounds = this->dsprite;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
explicit DropDownIcon(const Dimension &dim, SpriteID sprite, PaletteID palette, Args&&... args) : TBase(std::forward<Args>(args)...), sprite(sprite), palette(palette), dbounds(dim)
|
||||
{
|
||||
this->dsprite = GetSpriteSize(this->sprite);
|
||||
}
|
||||
|
||||
uint Height() const override { return std::max(this->dbounds.height, this->TBase::Height()); }
|
||||
uint Width() const override { return this->dbounds.width + WidgetDimensions::scaled.hsep_normal + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
Rect ir = r.WithWidth(this->dbounds.width, rtl);
|
||||
DrawSprite(this->sprite, this->palette, CenterBounds(ir.left, ir.right, this->dsprite.width), CenterBounds(r.top, r.bottom, this->dsprite.height));
|
||||
this->TBase::Draw(full, r.Indent(this->dbounds.width + WidgetDimensions::scaled.hsep_normal, rtl), sel, bg_colour);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down checkmark component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size.
|
||||
* @tparam TEnd Position checkmark at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, bool TEnd = false, FontSize TFs = FS_NORMAL>
|
||||
class DropDownCheck : public TBase {
|
||||
bool checked; ///< Is item checked.
|
||||
Dimension dim; ///< Dimension of checkmark.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownCheck(bool checked, Args&&... args) : TBase(std::forward<Args>(args)...), checked(checked)
|
||||
{
|
||||
this->dim = GetStringBoundingBox(STR_JUST_CHECKMARK, TFs);
|
||||
}
|
||||
|
||||
uint Height() const override { return std::max<uint>(this->dim.height, this->TBase::Height()); }
|
||||
uint Width() const override { return this->dim.width + WidgetDimensions::scaled.hsep_wide + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
if (this->checked) {
|
||||
DrawStringMultiLine(r.WithWidth(this->dim.width, rtl), STR_JUST_CHECKMARK, this->GetColour(sel), SA_CENTER, false, TFs);
|
||||
}
|
||||
this->TBase::Draw(full, r.Indent(this->dim.width + WidgetDimensions::scaled.hsep_wide, rtl), sel, bg_colour);
|
||||
}
|
||||
};
|
||||
|
||||
/* Commonly used drop down list items. */
|
||||
using DropDownListDividerItem = DropDownDivider<DropDownListItem>;
|
||||
using DropDownListStringItem = DropDownString<DropDownListItem>;
|
||||
using DropDownListIconItem = DropDownIcon<DropDownString<DropDownListItem>>;
|
||||
using DropDownListCheckedItem = DropDownCheck<DropDownString<DropDownListItem>>;
|
||||
|
||||
/**
|
||||
* A drop down list is a collection of drop down list items.
|
||||
*/
|
||||
|
@@ -19,9 +19,9 @@ enum GSConfigWidgets {
|
||||
WID_GSC_SETTINGS, ///< Panel to draw the Game Script settings on
|
||||
WID_GSC_SCROLLBAR, ///< Scrollbar to scroll through the selected AIs.
|
||||
WID_GSC_CHANGE, ///< Select another Game Script button.
|
||||
WID_GSC_OPEN_URL, ///< Open GS URL.
|
||||
WID_GSC_TEXTFILE, ///< Open GS readme, changelog (+1) or license (+2).
|
||||
WID_GSC_CONTENT_DOWNLOAD = WID_GSC_TEXTFILE + TFT_CONTENT_END, ///< Download content button.
|
||||
WID_GSC_ACCEPT, ///< Accept ("Close") button
|
||||
WID_GSC_RESET, ///< Reset button.
|
||||
|
||||
WID_GSC_SETTING_DROPDOWN = -1, ///< Dynamically created dropdown for changing setting value.
|
||||
|
@@ -34,7 +34,6 @@ enum NetworkGameWidgets {
|
||||
WID_NG_LASTJOINED_SPACER, ///< Spacer after last joined server panel.
|
||||
|
||||
WID_NG_DETAILS, ///< Panel with game details.
|
||||
WID_NG_DETAILS_SPACER, ///< Spacer for game actual details.
|
||||
WID_NG_JOIN, ///< 'Join game' button.
|
||||
WID_NG_REFRESH, ///< 'Refresh server' button.
|
||||
WID_NG_NEWGRF, ///< 'NewGRF Settings' button.
|
||||
|
@@ -46,6 +46,8 @@ enum SpriteAlignerWidgets {
|
||||
WID_SA_PICKER, ///< Sprite picker.
|
||||
WID_SA_LIST, ///< Queried sprite list.
|
||||
WID_SA_SCROLLBAR, ///< Scrollbar for sprite list.
|
||||
WID_SA_ZOOM, ///< Zoom level buttons (from ZOOM_LVL_BEGIN to ZOOM_LVL_SPR_COUNT).
|
||||
WID_SA_ZOOM_LAST = WID_SA_ZOOM + ZOOM_LVL_SPR_COUNT - 1, ///< Marker for last zoom level button.
|
||||
WID_SA_RESET_REL, ///< Reset relative sprite offset
|
||||
WID_SA_CENTRE, ///< Toggle centre sprite.
|
||||
WID_SA_CROSSHAIR, ///< Toggle crosshair.
|
||||
|
@@ -41,7 +41,7 @@ enum ScriptDebugWidgets {
|
||||
WID_SCRD_SCRIPT_GAME, ///< Game Script button.
|
||||
WID_SCRD_RELOAD_TOGGLE, ///< Reload button.
|
||||
WID_SCRD_LOG_PANEL, ///< Panel where the log is in.
|
||||
WID_SCRD_SCROLLBAR, ///< Scrollbar of the log panel.
|
||||
WID_SCRD_VSCROLLBAR, ///< Vertical scrollbar of the log panel.
|
||||
WID_SCRD_COMPANY_BUTTON_START, ///< Buttons in the VIEW.
|
||||
WID_SCRD_COMPANY_BUTTON_END = WID_SCRD_COMPANY_BUTTON_START + MAX_COMPANIES - 1, ///< Last possible button in the VIEW.
|
||||
WID_SCRD_BREAK_STRING_WIDGETS, ///< The panel to handle the breaking on string.
|
||||
@@ -49,6 +49,7 @@ enum ScriptDebugWidgets {
|
||||
WID_SCRD_BREAK_STR_EDIT_BOX, ///< Edit box for the string to break on.
|
||||
WID_SCRD_MATCH_CASE_BTN, ///< Checkbox to use match caching or not.
|
||||
WID_SCRD_CONTINUE_BTN, ///< Continue button.
|
||||
WID_SCRD_HSCROLLBAR, ///< Horizontal scrollbar of the log panel.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_SCRIPT_WIDGET_H */
|
||||
|
@@ -28,17 +28,20 @@ enum GameOptionsWidgets {
|
||||
WID_GO_GUI_SCALE_MAIN_TOOLBAR, ///< Toggle for bigger main toolbar.
|
||||
WID_GO_BASE_GRF_DROPDOWN, ///< Use to select a base GRF.
|
||||
WID_GO_BASE_GRF_PARAMETERS, ///< Base GRF parameters.
|
||||
WID_GO_BASE_GRF_OPEN_URL, ///< Open base GRF URL.
|
||||
WID_GO_BASE_GRF_TEXTFILE, ///< Open base GRF readme, changelog (+1) or license (+2).
|
||||
WID_GO_BASE_GRF_DESCRIPTION = WID_GO_BASE_GRF_TEXTFILE + TFT_CONTENT_END, ///< Description of selected base GRF.
|
||||
WID_GO_BASE_SFX_DROPDOWN, ///< Use to select a base SFX.
|
||||
WID_GO_TEXT_SFX_VOLUME, ///< Sound effects volume label.
|
||||
WID_GO_BASE_SFX_VOLUME, ///< Change sound effects volume.
|
||||
WID_GO_BASE_SFX_OPEN_URL, ///< Open base SFX URL.
|
||||
WID_GO_BASE_SFX_TEXTFILE, ///< Open base SFX readme, changelog (+1) or license (+2).
|
||||
WID_GO_BASE_SFX_DESCRIPTION = WID_GO_BASE_SFX_TEXTFILE + TFT_CONTENT_END, ///< Description of selected base SFX.
|
||||
WID_GO_BASE_MUSIC_DROPDOWN, ///< Use to select a base music set.
|
||||
WID_GO_TEXT_MUSIC_VOLUME, ///< Music volume label.
|
||||
WID_GO_BASE_MUSIC_VOLUME, ///< Change music volume.
|
||||
WID_GO_BASE_MUSIC_JUKEBOX, ///< Open the jukebox.
|
||||
WID_GO_BASE_MUSIC_OPEN_URL, ///< Open base music URL.
|
||||
WID_GO_BASE_MUSIC_TEXTFILE, ///< Open base music readme, changelog (+1) or license (+2).
|
||||
WID_GO_BASE_MUSIC_DESCRIPTION = WID_GO_BASE_MUSIC_TEXTFILE + TFT_CONTENT_END, ///< Description of selected base music set.
|
||||
WID_GO_VIDEO_ACCEL_BUTTON, ///< Toggle for video acceleration.
|
||||
|
Reference in New Issue
Block a user