Changeset - r28217:105f26f06468
[Not reviewed]
master
0 6 0
Peter Nelson - 13 months ago 2023-11-11 15:17:12
peter1138@openttd.org
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.)
6 files changed with 201 insertions and 241 deletions:
0 comments (0 inline, 0 general)
src/company_gui.cpp
Show inline comments
 
@@ -575,36 +575,15 @@ static const LiveryClass _livery_class[L
 
	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:
 
	DropDownListColourItem(int result, bool masked) : DropDownListStringItem(result >= COLOUR_END ? STR_COLOUR_DEFAULT : _colour_dropdown[result], result, masked) {}
 

	
 
	uint Width() const override
 
	{
 
		return ScaleGUITrad(28) + WidgetDimensions::scaled.hsep_normal + GetStringBoundingBox(this->String()).width + WidgetDimensions::scaled.dropdowntext.Horizontal();
 
	}
 

	
 
	uint Height() const override
 
	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)
 
	{
 
		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) {
 
			/* 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;
 
			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++) {
 
			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;
src/genworld_gui.cpp
Show inline comments
 
@@ -358,7 +358,7 @@ static DropDownList BuildTownNameDropDow
 
	size_t newgrf_size = list.size();
 
	/* Insert newgrf_names at the top of the list */
 
	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++;
 
	}
 

	
src/settings_gui.cpp
Show inline comments
 
@@ -224,7 +224,7 @@ struct GameOptionsWindow : Window {
 
				std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc);
 

	
 
				/* 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)));
 
				break;
 
			}
src/toolbar_gui.cpp
Show inline comments
 
@@ -89,97 +89,16 @@ enum CallBackFunction {
 

	
 
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 {
 
	uint checkmark_width;
 
class DropDownListCompanyItem : public DropDownIcon<DropDownIcon<DropDownString<DropDownListItem>, true>> {
 
public:
 
	bool checked;
 

	
 
	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
 
	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)
 
	{
 
		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(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;
 
	}
 

	
 
	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);
 
		this->SetString(GetString(STR_COMPANY_NAME_COMPANY_NUM));
 
	}
 
};
 

	
 
@@ -208,7 +127,7 @@ static void PopupMainToolbarMenu(Window 
 
	int i = 0;
 
	for (StringID string : strings) {
 
		if (string == STR_NULL) {
 
			list.push_back(std::make_unique<DropDownListItem>(-1, false));
 
			list.push_back(std::make_unique<DropDownListDividerItem>(-1, false));
 
		} else {
 
			list.push_back(std::make_unique<DropDownListStringItem>(string, i, false));
 
			i++;
 
@@ -254,7 +173,7 @@ static void PopupMainCompanyToolbMenu(Wi
 

	
 
	for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 
		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);
 
@@ -341,16 +260,16 @@ static CallBackFunction ToolbarOptionsCl
 
	}
 
	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<DropDownListItem>(-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>(STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false, HasBit(_display_opt, DO_SHOW_STATION_NAMES)));
 
	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>(STR_SETTINGS_MENU_SIGNS_DISPLAYED,         OME_SHOW_SIGNS, false, HasBit(_display_opt, DO_SHOW_SIGNS)));
 
	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>(STR_SETTINGS_MENU_FULL_ANIMATION,          OME_FULL_ANIMATION, false, HasBit(_display_opt, DO_FULL_ANIMATION)));
 
	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>(STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS,   OME_TRANSPARENTBUILDINGS, false, IsTransparencySet(TO_HOUSES)));
 
	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<DropDownListDividerItem>(-1, false));
 
	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>(HasBit(_display_opt, DO_SHOW_STATION_NAMES), STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false));
 
	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>(HasBit(_display_opt, DO_SHOW_SIGNS), STR_SETTINGS_MENU_SIGNS_DISPLAYED, OME_SHOW_SIGNS, false));
 
	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>(HasBit(_display_opt, DO_FULL_ANIMATION), STR_SETTINGS_MENU_FULL_ANIMATION, OME_FULL_ANIMATION, false));
 
	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>(IsTransparencySet(TO_HOUSES), STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS, false));
 
	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);
 
	if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
src/widgets/dropdown.cpp
Show inline comments
 
@@ -22,82 +22,6 @@
 
#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[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		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 (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_index == item->result);
 
				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;
 
		}
 
@@ -343,6 +265,7 @@ Dimension GetDropDownListDimension(const
 
		dim.height += item->Height();
 
		dim.width = std::max(dim.width, item->Width());
 
	}
 
	dim.width += WidgetDimensions::scaled.dropdowntext.Horizontal();
 
	return dim;
 
}
 

	
src/widgets/dropdown_type.h
Show inline comments
 
@@ -12,61 +12,200 @@
 

	
 
#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"
 

	
 
/**
 
 * 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
 
	int result; ///< Result value to return to window on selection.
 
	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 bool Selectable() const { return false; }
 
	virtual uint Height() const { return GetCharacterHeight(FS_NORMAL); }
 
	virtual bool Selectable() const { return true; }
 
	virtual uint Height() 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;
 
	}
 
};
 

	
 
/**
 
 * Drop down divider component.
 
 * @tparam TBase Base component.
 
 * @tparam TFs Font size -- used to determine height.
 
 */
 
template<class TBase, FontSize TFs = FS_NORMAL>
 
class DropDownDivider : public TBase {
 
public:
 
	template <typename... Args>
 
	explicit DropDownDivider(Args&&... args) : TBase(std::forward<Args>(args)...) {}
 

	
 
	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);
 
	}
 
};
 

	
 
/**
 
 * Common string list item.
 
 * 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 DropDownListStringItem : public DropDownListItem {
 
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:
 
	std::string string; ///< String of item
 
	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));
 
	}
 

	
 
	DropDownListStringItem(StringID string, int result, bool masked);
 
	DropDownListStringItem(const std::string &string, int result, bool masked);
 
	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(); }
 

	
 
	bool Selectable() const override { return true; }
 
	uint Width() const override;
 
	void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
 
	virtual const std::string &String() const { return this->string; }
 
	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);
 
	}
 

	
 
	static bool NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second);
 
	/**
 
	* 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;
 
	}
 
};
 

	
 
/**
 
 * List item with icon and string.
 
 * Drop down icon component.
 
 * @tparam TBase Base component.
 
 * @tparam TEnd Position icon at end if true, or start if false.
 
 */
 
class DropDownListIconItem : public DropDownListStringItem {
 
	SpriteID sprite;
 
	PaletteID pal;
 
	Dimension dim;
 
	uint sprite_y;
 
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:
 
	DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked);
 
	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;
 
	}
 
};
 

	
 
	uint Height() const override;
 
	uint Width() const override;
 
	void Draw(const Rect &r, bool sel, Colours bg_colour) const override;
 
	void SetDimension(Dimension d);
 
/**
 
 * 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.
 
 */
0 comments (0 inline, 0 general)