mirror of https://github.com/OpenTTD/OpenTTD
Change: Use CRTP-mixins to compose dropdown list items.
This allows list items to built from component parts as required, and additional functionality is added: * Icons and text can be positioned at the start or end of the space (templated.) * Font size of text can be changed (templated.) * Palette of sprites can be set (runtime.)pull/11530/head
parent
62e4d1e507
commit
49532914dd
|
@ -575,36 +575,15 @@ static const LiveryClass _livery_class[LS_END] = {
|
||||||
LC_ROAD, LC_ROAD,
|
LC_ROAD, LC_ROAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DropDownListColourItem : public DropDownListStringItem {
|
/**
|
||||||
|
* Colour selection list item, with icon and string components.
|
||||||
|
* @tparam TSprite Recolourable sprite to draw as icon.
|
||||||
|
*/
|
||||||
|
template <SpriteID TSprite = SPR_SQUARE>
|
||||||
|
class DropDownListColourItem : public DropDownIcon<DropDownString<DropDownListItem>> {
|
||||||
public:
|
public:
|
||||||
DropDownListColourItem(int result, bool masked) : DropDownListStringItem(result >= COLOUR_END ? STR_COLOUR_DEFAULT : _colour_dropdown[result], result, masked) {}
|
DropDownListColourItem(int colour, bool masked) : DropDownIcon<DropDownString<DropDownListItem>>(TSprite, PALETTE_RECOLOUR_START + (colour % COLOUR_END), colour < COLOUR_END ? _colour_dropdown[colour] : STR_COLOUR_DEFAULT, colour, masked)
|
||||||
|
|
||||||
uint Width() const override
|
|
||||||
{
|
{
|
||||||
return ScaleGUITrad(28) + WidgetDimensions::scaled.hsep_normal + GetStringBoundingBox(this->String()).width + WidgetDimensions::scaled.dropdowntext.Horizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint Height() const override
|
|
||||||
{
|
|
||||||
return std::max(GetCharacterHeight(FS_NORMAL), ScaleGUITrad(12) + WidgetDimensions::scaled.vsep_normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Selectable() const override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw(const Rect &r, bool sel, Colours) const override
|
|
||||||
{
|
|
||||||
bool rtl = _current_text_dir == TD_RTL;
|
|
||||||
int icon_y = CenterBounds(r.top, r.bottom, 0);
|
|
||||||
int text_y = CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
|
|
||||||
Rect tr = r.Shrink(WidgetDimensions::scaled.dropdowntext);
|
|
||||||
DrawSprite(SPR_VEH_BUS_SIDE_VIEW, PALETTE_RECOLOUR_START + (this->result % COLOUR_END),
|
|
||||||
rtl ? tr.right - ScaleGUITrad(14) : tr.left + ScaleGUITrad(14),
|
|
||||||
icon_y);
|
|
||||||
tr = tr.Indent(ScaleGUITrad(28) + WidgetDimensions::scaled.hsep_normal, rtl);
|
|
||||||
DrawString(tr.left, tr.right, text_y, this->String(), sel ? TC_WHITE : TC_BLACK);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -662,10 +641,10 @@ private:
|
||||||
if (default_livery != nullptr) {
|
if (default_livery != nullptr) {
|
||||||
/* Add COLOUR_END to put the colour out of range, but also allow us to show what the default is */
|
/* Add COLOUR_END to put the colour out of range, but also allow us to show what the default is */
|
||||||
default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END;
|
default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END;
|
||||||
list.push_back(std::make_unique<DropDownListColourItem>(default_col, false));
|
list.push_back(std::make_unique<DropDownListColourItem<>>(default_col, false));
|
||||||
}
|
}
|
||||||
for (uint i = 0; i < lengthof(_colour_dropdown); i++) {
|
for (uint i = 0; i < lengthof(_colour_dropdown); i++) {
|
||||||
list.push_back(std::make_unique<DropDownListColourItem>(i, HasBit(used_colours, i)));
|
list.push_back(std::make_unique<DropDownListColourItem<>>(i, HasBit(used_colours, i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
byte sel = (default_livery == nullptr || HasBit(livery->in_use, primary ? 0 : 1)) ? (primary ? livery->colour1 : livery->colour2) : default_col;
|
byte sel = (default_livery == nullptr || HasBit(livery->in_use, primary ? 0 : 1)) ? (primary ? livery->colour1 : livery->colour2) : default_col;
|
||||||
|
|
|
@ -358,7 +358,7 @@ static DropDownList BuildTownNameDropDown()
|
||||||
size_t newgrf_size = list.size();
|
size_t newgrf_size = list.size();
|
||||||
/* Insert newgrf_names at the top of the list */
|
/* Insert newgrf_names at the top of the list */
|
||||||
if (newgrf_size > 0) {
|
if (newgrf_size > 0) {
|
||||||
list.push_back(std::make_unique<DropDownListItem>(-1, false)); // separator line
|
list.push_back(std::make_unique<DropDownListDividerItem>(-1, false)); // separator line
|
||||||
newgrf_size++;
|
newgrf_size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ struct GameOptionsWindow : Window {
|
||||||
std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc);
|
std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc);
|
||||||
|
|
||||||
/* Append custom currency at the end */
|
/* Append custom currency at the end */
|
||||||
list.push_back(std::make_unique<DropDownListItem>(-1, false)); // separator line
|
list.push_back(std::make_unique<DropDownListDividerItem>(-1, false)); // separator line
|
||||||
list.push_back(std::make_unique<DropDownListStringItem>(STR_GAME_OPTIONS_CURRENCY_CUSTOM, CURRENCY_CUSTOM, HasBit(disabled, CURRENCY_CUSTOM)));
|
list.push_back(std::make_unique<DropDownListStringItem>(STR_GAME_OPTIONS_CURRENCY_CUSTOM, CURRENCY_CUSTOM, HasBit(disabled, CURRENCY_CUSTOM)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,97 +89,16 @@ enum CallBackFunction {
|
||||||
|
|
||||||
static CallBackFunction _last_started_action = CBF_NONE; ///< Last started user action.
|
static CallBackFunction _last_started_action = CBF_NONE; ///< Last started user action.
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop down list entry for showing a checked/unchecked toggle item.
|
* Company name list item, with company-colour icon, name, and lock components.
|
||||||
*/
|
*/
|
||||||
class DropDownListCheckedItem : public DropDownListStringItem {
|
class DropDownListCompanyItem : public DropDownIcon<DropDownIcon<DropDownString<DropDownListItem>, true>> {
|
||||||
uint checkmark_width;
|
|
||||||
public:
|
public:
|
||||||
bool checked;
|
DropDownListCompanyItem(CompanyID company, bool shaded) : DropDownIcon<DropDownIcon<DropDownString<DropDownListItem>, true>>(SPR_COMPANY_ICON, COMPANY_SPRITE_COLOUR(company), NetworkCompanyIsPassworded(company) ? SPR_LOCK : SPR_EMPTY, PAL_NONE, STR_NULL, company, false, shaded)
|
||||||
|
|
||||||
DropDownListCheckedItem(StringID string, int result, bool masked, bool checked) : DropDownListStringItem(string, result, masked), checked(checked)
|
|
||||||
{
|
{
|
||||||
this->checkmark_width = GetStringBoundingBox(STR_JUST_CHECKMARK).width + WidgetDimensions::scaled.hsep_wide;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint Width() const override
|
|
||||||
{
|
|
||||||
return DropDownListStringItem::Width() + this->checkmark_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw(const Rect &r, bool sel, Colours) const override
|
|
||||||
{
|
|
||||||
bool rtl = _current_text_dir == TD_RTL;
|
|
||||||
Rect tr = r.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero);
|
|
||||||
if (this->checked) {
|
|
||||||
DrawString(tr, STR_JUST_CHECKMARK, sel ? TC_WHITE : TC_BLACK);
|
|
||||||
}
|
|
||||||
DrawString(tr.Indent(this->checkmark_width, rtl), this->String(), sel ? TC_WHITE : TC_BLACK);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drop down list entry for showing a company entry, with companies 'blob'.
|
|
||||||
*/
|
|
||||||
class DropDownListCompanyItem : public DropDownListItem {
|
|
||||||
Dimension icon_size;
|
|
||||||
Dimension lock_size;
|
|
||||||
public:
|
|
||||||
bool greyed;
|
|
||||||
|
|
||||||
DropDownListCompanyItem(int result, bool masked, bool greyed) : DropDownListItem(result, masked), greyed(greyed)
|
|
||||||
{
|
|
||||||
this->icon_size = GetSpriteSize(SPR_COMPANY_ICON);
|
|
||||||
this->lock_size = GetSpriteSize(SPR_LOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Selectable() const override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint Width() const override
|
|
||||||
{
|
|
||||||
CompanyID company = (CompanyID)this->result;
|
|
||||||
SetDParam(0, company);
|
SetDParam(0, company);
|
||||||
SetDParam(1, company);
|
SetDParam(1, company);
|
||||||
return GetStringBoundingBox(STR_COMPANY_NAME_COMPANY_NUM).width + this->icon_size.width + this->lock_size.width + WidgetDimensions::scaled.dropdowntext.Horizontal() + WidgetDimensions::scaled.hsep_wide;
|
this->SetString(GetString(STR_COMPANY_NAME_COMPANY_NUM));
|
||||||
}
|
|
||||||
|
|
||||||
uint Height() const override
|
|
||||||
{
|
|
||||||
return std::max(std::max(this->icon_size.height, this->lock_size.height) + WidgetDimensions::scaled.imgbtn.Vertical(), (uint)GetCharacterHeight(FS_NORMAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw(const Rect &r, bool sel, Colours) const override
|
|
||||||
{
|
|
||||||
CompanyID company = (CompanyID)this->result;
|
|
||||||
bool rtl = _current_text_dir == TD_RTL;
|
|
||||||
|
|
||||||
/* It's possible the company is deleted while the dropdown is open */
|
|
||||||
if (!Company::IsValidID(company)) return;
|
|
||||||
|
|
||||||
Rect tr = r.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero);
|
|
||||||
int icon_y = CenterBounds(r.top, r.bottom, icon_size.height);
|
|
||||||
int text_y = CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
|
|
||||||
int lock_y = CenterBounds(r.top, r.bottom, lock_size.height);
|
|
||||||
|
|
||||||
DrawCompanyIcon(company, tr.WithWidth(this->icon_size.width, rtl).left, icon_y);
|
|
||||||
if (NetworkCompanyIsPassworded(company)) {
|
|
||||||
DrawSprite(SPR_LOCK, PAL_NONE, tr.WithWidth(this->lock_size.width, !rtl).left, lock_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetDParam(0, company);
|
|
||||||
SetDParam(1, company);
|
|
||||||
TextColour col;
|
|
||||||
if (this->greyed) {
|
|
||||||
col = (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
|
|
||||||
} else {
|
|
||||||
col = sel ? TC_WHITE : TC_BLACK;
|
|
||||||
}
|
|
||||||
tr = tr.Indent(this->icon_size.width + WidgetDimensions::scaled.hsep_normal, rtl).Indent(this->lock_size.width + WidgetDimensions::scaled.hsep_normal, !rtl);
|
|
||||||
DrawString(tr.left, tr.right, text_y, STR_COMPANY_NAME_COMPANY_NUM, col);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,7 +127,7 @@ static void PopupMainToolbarMenu(Window *w, int widget, const std::initializer_l
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (StringID string : strings) {
|
for (StringID string : strings) {
|
||||||
if (string == STR_NULL) {
|
if (string == STR_NULL) {
|
||||||
list.push_back(std::make_unique<DropDownListItem>(-1, false));
|
list.push_back(std::make_unique<DropDownListDividerItem>(-1, false));
|
||||||
} else {
|
} else {
|
||||||
list.push_back(std::make_unique<DropDownListStringItem>(string, i, false));
|
list.push_back(std::make_unique<DropDownListStringItem>(string, i, false));
|
||||||
i++;
|
i++;
|
||||||
|
@ -254,7 +173,7 @@ static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0)
|
||||||
|
|
||||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||||
if (!Company::IsValidID(c)) continue;
|
if (!Company::IsValidID(c)) continue;
|
||||||
list.push_back(std::make_unique<DropDownListCompanyItem>(c, false, HasBit(grey, c)));
|
list.push_back(std::make_unique<DropDownListCompanyItem>(c, HasBit(grey, c)));
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMainToolbarMenu(w, widget, std::move(list), _local_company == COMPANY_SPECTATOR ? (widget == WID_TN_COMPANIES ? CTMN_CLIENT_LIST : CTMN_SPECTATOR) : (int)_local_company);
|
PopupMainToolbarMenu(w, widget, std::move(list), _local_company == COMPANY_SPECTATOR ? (widget == WID_TN_COMPANIES ? CTMN_CLIENT_LIST : CTMN_SPECTATOR) : (int)_local_company);
|
||||||
|
@ -341,16 +260,16 @@ static CallBackFunction ToolbarOptionsClick(Window *w)
|
||||||
}
|
}
|
||||||
list.push_back(std::make_unique<DropDownListStringItem>(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false));
|
list.push_back(std::make_unique<DropDownListStringItem>(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false));
|
||||||
list.push_back(std::make_unique<DropDownListStringItem>(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false));
|
list.push_back(std::make_unique<DropDownListStringItem>(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false));
|
||||||
list.push_back(std::make_unique<DropDownListItem>(-1, false));
|
list.push_back(std::make_unique<DropDownListDividerItem>(-1, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false, HasBit(_display_opt, DO_SHOW_TOWN_NAMES)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_SHOW_TOWN_NAMES), STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false, HasBit(_display_opt, DO_SHOW_STATION_NAMES)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_SHOW_STATION_NAMES), STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED, OME_SHOW_WAYPOINTNAMES, false, HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES), STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED, OME_SHOW_WAYPOINTNAMES, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_SIGNS_DISPLAYED, OME_SHOW_SIGNS, false, HasBit(_display_opt, DO_SHOW_SIGNS)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_SHOW_SIGNS), STR_SETTINGS_MENU_SIGNS_DISPLAYED, OME_SHOW_SIGNS, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS, OME_SHOW_COMPETITOR_SIGNS, false, HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS), STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS, OME_SHOW_COMPETITOR_SIGNS, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_FULL_ANIMATION, OME_FULL_ANIMATION, false, HasBit(_display_opt, DO_FULL_ANIMATION)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_FULL_ANIMATION), STR_SETTINGS_MENU_FULL_ANIMATION, OME_FULL_ANIMATION, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_FULL_DETAIL, OME_FULL_DETAILS, false, HasBit(_display_opt, DO_FULL_DETAIL)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(_display_opt, DO_FULL_DETAIL), STR_SETTINGS_MENU_FULL_DETAIL, OME_FULL_DETAILS, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS, false, IsTransparencySet(TO_HOUSES)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(IsTransparencySet(TO_HOUSES), STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS, false));
|
||||||
list.push_back(std::make_unique<DropDownListCheckedItem>(STR_SETTINGS_MENU_TRANSPARENT_SIGNS, OME_SHOW_STATIONSIGNS, false, IsTransparencySet(TO_SIGNS)));
|
list.push_back(std::make_unique<DropDownListCheckedItem>(IsTransparencySet(TO_SIGNS), STR_SETTINGS_MENU_TRANSPARENT_SIGNS, OME_SHOW_STATIONSIGNS, false));
|
||||||
|
|
||||||
ShowDropDownList(w, std::move(list), 0, WID_TN_SETTINGS, 140, true);
|
ShowDropDownList(w, std::move(list), 0, WID_TN_SETTINGS, 140, true);
|
||||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||||
|
|
|
@ -22,82 +22,6 @@
|
||||||
#include "../safeguards.h"
|
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DropDownListIconItem::SetDimension(Dimension d)
|
|
||||||
{
|
|
||||||
this->dim = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const NWidgetPart _nested_dropdown_menu_widgets[] = {
|
static const NWidgetPart _nested_dropdown_menu_widgets[] = {
|
||||||
NWidget(NWID_HORIZONTAL),
|
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), SetMinimalSize(1, 1), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||||
|
@ -256,14 +180,12 @@ struct DropdownWindow : Window {
|
||||||
if (--pos >= 0) continue;
|
if (--pos >= 0) continue;
|
||||||
|
|
||||||
if (y + item_height - 1 <= ir.bottom) {
|
if (y + item_height - 1 <= ir.bottom) {
|
||||||
|
Rect full{ir.left, y, ir.right, y + item_height - 1};
|
||||||
|
|
||||||
bool selected = (this->selected_index == item->result);
|
bool selected = (this->selected_index == item->result);
|
||||||
if (selected) GfxFillRect(ir.left, y, ir.right, y + item_height - 1, PC_BLACK);
|
if (selected) GfxFillRect(full, PC_BLACK);
|
||||||
|
|
||||||
item->Draw({ir.left, y, ir.right, y + item_height - 1}, selected, colour);
|
item->Draw(full, full.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero), selected, colour);
|
||||||
|
|
||||||
if (item->masked) {
|
|
||||||
GfxFillRect(ir.left, y, ir.right, y + item_height - 1, _colour_gradient[colour][5], FILLRECT_CHECKER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
y += item_height;
|
y += item_height;
|
||||||
}
|
}
|
||||||
|
@ -343,6 +265,7 @@ Dimension GetDropDownListDimension(const DropDownList &list)
|
||||||
dim.height += item->Height();
|
dim.height += item->Height();
|
||||||
dim.width = std::max(dim.width, item->Width());
|
dim.width = std::max(dim.width, item->Width());
|
||||||
}
|
}
|
||||||
|
dim.width += WidgetDimensions::scaled.dropdowntext.Horizontal();
|
||||||
return dim;
|
return dim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,61 +12,200 @@
|
||||||
|
|
||||||
#include "../window_type.h"
|
#include "../window_type.h"
|
||||||
#include "../gfx_func.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"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base list item class from which others are derived. If placed in a list it
|
* Base list item class from which others are derived.
|
||||||
* will appear as a horizontal line in the menu.
|
|
||||||
*/
|
*/
|
||||||
class DropDownListItem {
|
class DropDownListItem {
|
||||||
public:
|
public:
|
||||||
int result; ///< Result code to return to window on selection
|
int result; ///< Result value to return to window on selection.
|
||||||
bool masked; ///< Masked and unselectable item
|
bool masked; ///< Masked and unselectable item.
|
||||||
|
bool shaded; ///< Shaded item, affects text colour.
|
||||||
|
|
||||||
DropDownListItem(int result, bool masked) : result(result), masked(masked) {}
|
explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
|
||||||
virtual ~DropDownListItem() = default;
|
virtual ~DropDownListItem() = default;
|
||||||
|
|
||||||
virtual bool Selectable() const { return false; }
|
virtual bool Selectable() const { return true; }
|
||||||
virtual uint Height() const { return GetCharacterHeight(FS_NORMAL); }
|
virtual uint Height() const { return 0; }
|
||||||
virtual uint Width() const { return 0; }
|
virtual uint Width() const { return 0; }
|
||||||
virtual void Draw(const Rect &r, bool sel, Colours bg_colour) const;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common string list item.
|
* Drop down divider component.
|
||||||
|
* @tparam TBase Base component.
|
||||||
|
* @tparam TFs Font size -- used to determine height.
|
||||||
*/
|
*/
|
||||||
class DropDownListStringItem : public DropDownListItem {
|
template<class TBase, FontSize TFs = FS_NORMAL>
|
||||||
|
class DropDownDivider : public TBase {
|
||||||
public:
|
public:
|
||||||
std::string string; ///< String of item
|
template <typename... Args>
|
||||||
|
explicit DropDownDivider(Args&&... args) : TBase(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
DropDownListStringItem(StringID string, int result, bool masked);
|
bool Selectable() const override { return false; }
|
||||||
DropDownListStringItem(const std::string &string, int result, bool masked);
|
uint Height() const override { return std::max<uint>(GetCharacterHeight(TFs), this->TBase::Height()); }
|
||||||
|
|
||||||
bool Selectable() const override { return true; }
|
void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const override
|
||||||
uint Width() const override;
|
{
|
||||||
void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
|
uint8_t c1 = _colour_gradient[bg_colour][3];
|
||||||
virtual const std::string &String() const { return this->string; }
|
uint8_t c2 = _colour_gradient[bg_colour][7];
|
||||||
|
|
||||||
static bool NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second);
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List item with icon and string.
|
* Drop down string component.
|
||||||
|
* @tparam TBase Base component.
|
||||||
|
* @tparam TFs Font size.
|
||||||
|
* @tparam TEnd Position string at end if true, or start if false.
|
||||||
*/
|
*/
|
||||||
class DropDownListIconItem : public DropDownListStringItem {
|
template<class TBase, FontSize TFs = FS_NORMAL, bool TEnd = false>
|
||||||
SpriteID sprite;
|
class DropDownString : public TBase {
|
||||||
PaletteID pal;
|
std::string string; ///< String to be drawn.
|
||||||
Dimension dim;
|
Dimension dim; ///< Dimensions of string.
|
||||||
uint sprite_y;
|
|
||||||
public:
|
public:
|
||||||
DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked);
|
template <typename... Args>
|
||||||
|
explicit DropDownString(StringID string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
this->SetString(GetString(string));
|
||||||
|
}
|
||||||
|
|
||||||
uint Height() const override;
|
template <typename... Args>
|
||||||
uint Width() const override;
|
explicit DropDownString(const std::string &string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||||
void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
|
{
|
||||||
void SetDimension(Dimension d);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override bounding box dimensions of sprite, to allow multiple options to have consistent spacing.
|
||||||
|
* @param dim New bounding box to assign.
|
||||||
|
*/
|
||||||
|
void SetDimension(const Dimension &dim)
|
||||||
|
{
|
||||||
|
this->dbounds = dim;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* A drop down list is a collection of drop down list items.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue