Changeset - r28814:02b90af3b0cc
[Not reviewed]
master
0 19 0
Peter Nelson - 11 months ago 2023-12-28 10:04:55
peter1138@openttd.org
Codechange: Give ColourShade values names instead of numbers.
19 files changed with 55 insertions and 55 deletions:
0 comments (0 inline, 0 general)
src/build_vehicle_gui.cpp
Show inline comments
 
@@ -972,97 +972,97 @@ int DrawVehiclePurchaseInfo(int left, in
 
		y += GetCharacterHeight(FS_NORMAL);
 

	
 
		/* Reliability */
 
		SetDParam(0, ToPercent16(e->reliability));
 
		DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY);
 
		y += GetCharacterHeight(FS_NORMAL);
 
	}
 

	
 
	if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
 

	
 
	/* Additional text from NewGRF */
 
	y = ShowAdditionalText(left, right, y, engine_number);
 

	
 
	/* The NewGRF's name which the vehicle comes from */
 
	const GRFConfig *config = GetGRFConfig(e->GetGRFID());
 
	if (_settings_client.gui.show_newgrf_name && config != nullptr)
 
	{
 
		DrawString(left, right, y, config->GetName(), TC_BLACK);
 
		y += GetCharacterHeight(FS_NORMAL);
 
	}
 

	
 
	return y;
 
}
 

	
 
/**
 
 * Engine drawing loop
 
 * @param type Type of vehicle (VEH_*)
 
 * @param r The Rect of the list
 
 * @param eng_list What engines to draw
 
 * @param min where to start in the list
 
 * @param max where in the list to end
 
 * @param selected_id what engine to highlight as selected, if any
 
 * @param show_count Whether to show the amount of engines or not
 
 * @param selected_group the group to list the engines of
 
 */
 
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16_t min, uint16_t max, EngineID selected_id, bool show_count, GroupID selected_group)
 
{
 
	static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
 

	
 
	/* Obligatory sanity checks! */
 
	assert(max <= eng_list.size());
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	int step_size = GetEngineListHeight(type);
 
	int sprite_left  = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
 
	int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
 
	int sprite_width = sprite_left + sprite_right;
 
	int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
 
	int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_4);
 
	int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
 

	
 
	Rect ir      = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
 
	int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
 

	
 
	Dimension replace_icon = {0, 0};
 
	int count_width = 0;
 
	if (show_count) {
 
		replace_icon = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
 

	
 
		uint biggest_num_engines = 0;
 
		for (auto i = min; i < max; i++) {
 
			const auto &item = eng_list[i];
 
			const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
 
			biggest_num_engines = std::max(biggest_num_engines, num_engines);
 
		}
 

	
 
		SetDParam(0, biggest_num_engines);
 
		count_width = GetStringBoundingBox(STR_JUST_COMMA, FS_SMALL).width;
 
	}
 

	
 
	Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
 
	Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl);  // Count position
 
	Rect rr = tr.WithWidth(replace_icon.width, !rtl);                                                                 // Replace icon position
 
	if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
 

	
 
	int normal_text_y_offset = (ir.Height() - GetCharacterHeight(FS_NORMAL)) / 2;
 
	int small_text_y_offset  = ir.Height() - GetCharacterHeight(FS_SMALL);
 
	int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
 

	
 
	int y = ir.top;
 
	for (; min < max; min++, y += step_size) {
 
		const auto &item = eng_list[min];
 
		uint indent       = item.indent * WidgetDimensions::scaled.hsep_indent;
 
		bool has_variants = (item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None;
 
		bool is_folded    = (item.flags & EngineDisplayFlags::IsFolded)    != EngineDisplayFlags::None;
 
		bool shaded       = (item.flags & EngineDisplayFlags::Shaded)      != EngineDisplayFlags::None;
 
		/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
 
		const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
 

	
 
		const Engine *e = Engine::Get(item.engine_id);
 
		bool hidden = HasBit(e->company_hidden, _local_company);
 
		StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
 
		TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
 

	
 
		if (show_count) {
 
			/* relies on show_count to find 'Vehicle in use' panel of autoreplace window */
 
			SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::AutoreplaceVehicleInUse, item.indent));
 
		} else {
src/company_cmd.cpp
Show inline comments
 
@@ -101,98 +101,98 @@ void Company::PostDestructor(size_t inde
 
 * @return the max loan amount.
 
 */
 
Money Company::GetMaxLoan() const
 
{
 
	if (this->max_loan == COMPANY_MAX_LOAN_DEFAULT) return _economy.max_loan;
 
	return this->max_loan;
 
}
 

	
 
/**
 
 * Sets the local company and updates the settings that are set on a
 
 * per-company basis to reflect the core's state in the GUI.
 
 * @param new_company the new company
 
 * @pre Company::IsValidID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE
 
 */
 
void SetLocalCompany(CompanyID new_company)
 
{
 
	/* company could also be COMPANY_SPECTATOR or OWNER_NONE */
 
	assert(Company::IsValidID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE);
 

	
 
	/* If actually changing to another company, several windows need closing */
 
	bool switching_company = _local_company != new_company;
 

	
 
	/* Delete the chat window, if you were team chatting. */
 
	if (switching_company) InvalidateWindowData(WC_SEND_NETWORK_MSG, DESTTYPE_TEAM, _local_company);
 

	
 
	assert(IsLocalCompany());
 

	
 
	_current_company = _local_company = new_company;
 

	
 
	if (switching_company) {
 
		InvalidateWindowClassesData(WC_COMPANY);
 
		/* Delete any construction windows... */
 
		CloseConstructionWindows();
 
	}
 

	
 
	/* ... and redraw the whole screen. */
 
	MarkWholeScreenDirty();
 
	InvalidateWindowClassesData(WC_SIGN_LIST, -1);
 
	InvalidateWindowClassesData(WC_GOALS_LIST);
 
}
 

	
 
/**
 
 * Get the colour for DrawString-subroutines which matches the colour of the company.
 
 * @param company Company to get the colour of.
 
 * @return Colour of \a company.
 
 */
 
TextColour GetDrawStringCompanyColour(CompanyID company)
 
{
 
	if (!Company::IsValidID(company)) return (TextColour)GetColourGradient(COLOUR_WHITE, SHADE_4) | TC_IS_PALETTE_COLOUR;
 
	return (TextColour)GetColourGradient(_company_colours[company], SHADE_4) | TC_IS_PALETTE_COLOUR;
 
	if (!Company::IsValidID(company)) return (TextColour)GetColourGradient(COLOUR_WHITE, SHADE_NORMAL) | TC_IS_PALETTE_COLOUR;
 
	return (TextColour)GetColourGradient(_company_colours[company], SHADE_NORMAL) | TC_IS_PALETTE_COLOUR;
 
}
 

	
 
/**
 
 * Draw the icon of a company.
 
 * @param c Company that needs its icon drawn.
 
 * @param x Horizontal coordinate of the icon.
 
 * @param y Vertical coordinate of the icon.
 
 */
 
void DrawCompanyIcon(CompanyID c, int x, int y)
 
{
 
	DrawSprite(SPR_COMPANY_ICON, COMPANY_SPRITE_COLOUR(c), x, y);
 
}
 

	
 
/**
 
 * Checks whether a company manager's face is a valid encoding.
 
 * Unused bits are not enforced to be 0.
 
 * @param cmf the fact to check
 
 * @return true if and only if the face is valid
 
 */
 
static bool IsValidCompanyManagerFace(CompanyManagerFace cmf)
 
{
 
	if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_GEN_ETHN, GE_WM)) return false;
 

	
 
	GenderEthnicity ge   = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM);
 
	bool has_moustache   = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE,   ge) != 0;
 
	bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0;
 
	bool has_glasses     = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0;
 

	
 
	if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_EYE_COLOUR, ge)) return false;
 
	for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
 
		switch (cmfv) {
 
			case CMFV_MOUSTACHE:   if (!has_moustache)   continue; break;
 
			case CMFV_LIPS:
 
			case CMFV_NOSE:        if (has_moustache)    continue; break;
 
			case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
 
			case CMFV_GLASSES:     if (!has_glasses)     continue; break;
 
			default: break;
 
		}
 
		if (!AreCompanyManagerFaceBitsValid(cmf, cmfv, ge)) return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Refresh all windows owned by a company.
 
 * @param company Company that changed, and needs its windows refreshed.
 
 */
src/console_gui.cpp
Show inline comments
 
@@ -449,53 +449,53 @@ void IConsoleGUIPrint(TextColour colour_
 
{
 
	_iconsole_buffer.push_front(IConsoleLine(str, colour_code));
 
	SetWindowDirty(WC_CONSOLE, 0);
 
}
 

	
 
/**
 
 * Remove old lines from the backlog buffer.
 
 * The buffer is limited by a maximum size and a minimum age. Every time truncation runs,
 
 * all lines in the buffer are aged by one. When a line exceeds both the maximum position
 
 * and also the maximum age, it gets removed.
 
 * @return true if any lines were removed
 
 */
 
static bool TruncateBuffer()
 
{
 
	bool need_truncation = false;
 
	size_t count = 0;
 
	for (IConsoleLine &line : _iconsole_buffer) {
 
		count++;
 
		line.time++;
 
		if (line.time > _settings_client.gui.console_backlog_timeout && count > _settings_client.gui.console_backlog_length) {
 
			/* Any messages after this are older and need to be truncated */
 
			need_truncation = true;
 
			break;
 
		}
 
	}
 

	
 
	if (need_truncation) {
 
		_iconsole_buffer.resize(count - 1);
 
	}
 

	
 
	return need_truncation;
 
}
 

	
 

	
 
/**
 
 * Check whether the given TextColour is valid for console usage.
 
 * @param c The text colour to compare to.
 
 * @return true iff the TextColour is valid for console usage.
 
 */
 
bool IsValidConsoleColour(TextColour c)
 
{
 
	/* A normal text colour is used. */
 
	if (!(c & TC_IS_PALETTE_COLOUR)) return TC_BEGIN <= c && c < TC_END;
 

	
 
	/* A text colour from the palette is used; must be the company
 
	 * colour gradient, so it must be one of those. */
 
	c &= ~TC_IS_PALETTE_COLOUR;
 
	for (Colours i = COLOUR_BEGIN; i < COLOUR_END; i++) {
 
		if (GetColourGradient(i, SHADE_4) == c) return true;
 
		if (GetColourGradient(i, SHADE_NORMAL) == c) return true;
 
	}
 

	
 
	return false;
 
}
src/depot_gui.cpp
Show inline comments
 
@@ -339,97 +339,97 @@ struct DepotWindow : Window {
 
			case VEH_AIRCRAFT: DrawAircraftImage(v, image, this->sel, EIT_IN_DEPOT); break;
 
			default: NOT_REACHED();
 
		}
 

	
 
		uint diff_x, diff_y;
 
		if (v->IsGroundVehicle()) {
 
			/* Arrange unitnumber and flag horizontally */
 
			diff_x = this->flag_size.width + WidgetDimensions::scaled.hsep_normal;
 
			diff_y = WidgetDimensions::scaled.matrix.top;
 
		} else {
 
			/* Arrange unitnumber and flag vertically */
 
			diff_x = 0;
 
			diff_y = WidgetDimensions::scaled.matrix.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
 
		}
 

	
 
		text = text.WithWidth(this->header_width - WidgetDimensions::scaled.hsep_normal, rtl).WithHeight(GetCharacterHeight(FS_NORMAL)).Indent(diff_x, rtl);
 
		if (free_wagon) {
 
			DrawString(text, STR_DEPOT_NO_ENGINE);
 
		} else {
 
			Rect flag = r.WithWidth(this->flag_size.width, rtl).WithHeight(this->flag_size.height).Translate(0, diff_y);
 
			DrawSpriteIgnorePadding((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, flag, SA_CENTER);
 

	
 
			SetDParam(0, v->unitnumber);
 
			DrawString(text, STR_JUST_COMMA, (v->max_age - CalendarTime::DAYS_IN_LEAP_YEAR) >= v->age ? TC_BLACK : TC_RED);
 
		}
 
	}
 

	
 
	void DrawWidget(const Rect &r, WidgetID widget) const override
 
	{
 
		if (widget != WID_D_MATRIX) return;
 

	
 
		bool rtl = _current_text_dir == TD_RTL;
 

	
 
		/* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */
 
		const NWidgetCore *wid = this->GetWidget<NWidgetCore>(WID_D_MATRIX);
 

	
 
		/* Set up rect for each cell */
 
		Rect ir = r.WithHeight(this->resize.step_height);
 
		if (this->num_columns != 1) ir = ir.WithWidth(this->resize.step_width, rtl);
 
		ir = ir.Shrink(WidgetDimensions::scaled.framerect, RectPadding::zero);
 

	
 
		/* Draw vertical separators at whole tiles.
 
		 * This only works in two cases:
 
		 *  - All vehicles use VEHICLEINFO_FULL_VEHICLE_WIDTH as reference width.
 
		 *  - All vehicles are 8/8. This cannot be checked for NewGRF, so instead we check for "all vehicles are original vehicles".
 
		 */
 
		if (this->type == VEH_TRAIN && _consistent_train_width != 0) {
 
			int w = ScaleSpriteTrad(2 * _consistent_train_width);
 
			int col = GetColourGradient(wid->colour, SHADE_4);
 
			int col = GetColourGradient(wid->colour, SHADE_NORMAL);
 
			Rect image = ir.Indent(this->header_width, rtl).Indent(this->count_width, !rtl);
 
			int first_line = w + (-this->hscroll->GetPosition()) % w;
 
			if (rtl) {
 
				for (int x = image.right - first_line; x >= image.left; x -= w) {
 
					GfxDrawLine(x, r.top, x, r.bottom, col, ScaleGUITrad(1), ScaleGUITrad(3));
 
				}
 
			} else {
 
				for (int x = image.left + first_line; x <= image.right; x += w) {
 
					GfxDrawLine(x, r.top, x, r.bottom, col, ScaleGUITrad(1), ScaleGUITrad(3));
 
				}
 
			}
 
		}
 

	
 
		uint16_t rows_in_display = wid->current_y / wid->resize_y;
 

	
 
		uint num = this->vscroll->GetPosition() * this->num_columns;
 
		uint maxval = static_cast<uint>(std::min<size_t>(this->vehicle_list.size(), num + (rows_in_display * this->num_columns)));
 
		for (; num < maxval; ir = ir.Translate(0, this->resize.step_height)) { // Draw the rows
 
			Rect cell = ir; /* Keep track of horizontal cells */
 
			for (uint i = 0; i < this->num_columns && num < maxval; i++, num++) {
 
				/* Draw all vehicles in the current row */
 
				const Vehicle *v = this->vehicle_list[num];
 
				this->DrawVehicleInDepot(v, cell);
 
				cell = cell.Translate(rtl ? -(int)this->resize.step_width : (int)this->resize.step_width, 0);
 
			}
 
		}
 

	
 
		maxval = static_cast<uint>(std::min<size_t>(this->vehicle_list.size() + this->wagon_list.size(), (this->vscroll->GetPosition() * this->num_columns) + (rows_in_display * this->num_columns)));
 

	
 
		/* Draw the train wagons without an engine in front. */
 
		for (; num < maxval; num++, ir = ir.Translate(0, this->resize.step_height)) {
 
			const Vehicle *v = this->wagon_list[num - this->vehicle_list.size()];
 
			this->DrawVehicleInDepot(v, ir);
 
		}
 
	}
 

	
 
	void SetStringParameters(WidgetID widget) const override
 
	{
 
		if (widget != WID_D_CAPTION) return;
 

	
 
		SetDParam(0, this->type);
 
		SetDParam(1, this->GetDepotIndex());
 
	}
 

	
 
	struct GetDepotVehiclePtData {
 
		const Vehicle *head;
 
		const Vehicle *wagon;
 
	};
src/graph_gui.cpp
Show inline comments
 
@@ -582,97 +582,97 @@ public:
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
 
	{
 
		if (!gui_scope) return;
 
		this->UpdateStatistics(true);
 
	}
 

	
 
	/**
 
	 * Update the statistics.
 
	 * @param initialize Initialize the data structure.
 
	 */
 
	void UpdateStatistics(bool initialize)
 
	{
 
		CompanyMask excluded_companies = _legend_excluded_companies;
 

	
 
		/* Exclude the companies which aren't valid */
 
		for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 
			if (!Company::IsValidID(c)) SetBit(excluded_companies, c);
 
		}
 

	
 
		byte nums = 0;
 
		for (const Company *c : Company::Iterate()) {
 
			nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
 
		}
 

	
 
		int mo = (TimerGameEconomy::month / 3 - nums) * 3;
 
		auto yr = TimerGameEconomy::year;
 
		while (mo < 0) {
 
			yr--;
 
			mo += 12;
 
		}
 

	
 
		if (!initialize && this->excluded_data == excluded_companies && this->num_on_x_axis == nums &&
 
				this->year == yr && this->month == mo) {
 
			/* There's no reason to get new stats */
 
			return;
 
		}
 

	
 
		this->excluded_data = excluded_companies;
 
		this->num_on_x_axis = nums;
 
		this->year = yr;
 
		this->month = mo;
 

	
 
		int numd = 0;
 
		for (CompanyID k = COMPANY_FIRST; k < MAX_COMPANIES; k++) {
 
			const Company *c = Company::GetIfValid(k);
 
			if (c != nullptr) {
 
				this->colours[numd] = GetColourGradient(c->colour, SHADE_6);
 
				this->colours[numd] = GetColourGradient(c->colour, SHADE_LIGHTER);
 
				for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
 
					if (j >= c->num_valid_stat_ent) {
 
						this->cost[numd][i] = INVALID_DATAPOINT;
 
					} else {
 
						/* Ensure we never assign INVALID_DATAPOINT, as that has another meaning.
 
						 * Instead, use the value just under it. Hopefully nobody will notice. */
 
						this->cost[numd][i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
 
					}
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		this->num_dataset = numd;
 
	}
 
};
 

	
 

	
 
/********************/
 
/* OPERATING PROFIT */
 
/********************/
 

	
 
struct OperatingProfitGraphWindow : BaseGraphWindow {
 
	OperatingProfitGraphWindow(WindowDesc *desc, WindowNumber window_number) :
 
			BaseGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
 
	{
 
		this->num_on_x_axis = GRAPH_NUM_MONTHS;
 
		this->num_vert_lines = GRAPH_NUM_MONTHS;
 
		this->x_values_start = ECONOMY_QUARTER_MINUTES;
 
		this->x_values_increment = ECONOMY_QUARTER_MINUTES;
 
		this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
 

	
 
		this->InitializeWindow(window_number);
 
	}
 

	
 
	OverflowSafeInt64 GetGraphData(const Company *c, int j) override
 
	{
 
		return c->old_economy[j].income + c->old_economy[j].expenses;
 
	}
 
};
 

	
 
static constexpr NWidgetPart _nested_operating_profit_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
 
		NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
 
		NWidget(WWT_SHADEBOX, COLOUR_BROWN),
 
@@ -1249,98 +1249,98 @@ struct PerformanceRatingDetailWindow : W
 
				 * is used, which can produce quite short renderings of very large
 
				 * values. Otherwise the calculated width could be too narrow.
 
				 * Note that it doesn't work if there was a currency with an exchange
 
				 * rate greater than max.
 
				 * When the currency rate is more than 1000, the 999 999 k becomes at
 
				 * least 999 999 M which roughly is equally long. Furthermore if the
 
				 * exchange rate is that high, 999 999 k is usually not enough anymore
 
				 * to show the different currency numbers. */
 
				if (_currency->rate < 1000) max /= _currency->rate;
 
				SetDParam(0, max);
 
				SetDParam(1, max);
 
				uint score_detail_width = GetStringBoundingBox(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY).width;
 

	
 
				size->width = WidgetDimensions::scaled.frametext.Horizontal() + score_info_width + WidgetDimensions::scaled.hsep_wide + this->bar_width + WidgetDimensions::scaled.hsep_wide + score_detail_width;
 
				uint left  = WidgetDimensions::scaled.frametext.left;
 
				uint right = size->width - WidgetDimensions::scaled.frametext.right;
 

	
 
				bool rtl = _current_text_dir == TD_RTL;
 
				this->score_info_left  = rtl ? right - score_info_width : left;
 
				this->score_info_right = rtl ? right : left + score_info_width;
 

	
 
				this->score_detail_left  = rtl ? left : right - score_detail_width;
 
				this->score_detail_right = rtl ? left + score_detail_width : right;
 

	
 
				this->bar_left  = left + (rtl ? score_detail_width : score_info_width) + WidgetDimensions::scaled.hsep_wide;
 
				this->bar_right = this->bar_left + this->bar_width - 1;
 
				break;
 
		}
 
	}
 

	
 
	void DrawWidget(const Rect &r, WidgetID widget) const override
 
	{
 
		/* No need to draw when there's nothing to draw */
 
		if (this->company == INVALID_COMPANY) return;
 

	
 
		if (IsInsideMM(widget, WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST + 1)) {
 
			if (this->IsWidgetDisabled(widget)) return;
 
			CompanyID cid = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
 
			Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
 
			DrawCompanyIcon(cid, CenterBounds(r.left, r.right, sprite_size.width), CenterBounds(r.top, r.bottom, sprite_size.height));
 
			return;
 
		}
 

	
 
		if (!IsInsideMM(widget, WID_PRD_SCORE_FIRST, WID_PRD_SCORE_LAST + 1)) return;
 

	
 
		ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
 

	
 
		/* The colours used to show how the progress is going */
 
		int colour_done = GetColourGradient(COLOUR_GREEN, SHADE_4);
 
		int colour_notdone = GetColourGradient(COLOUR_RED, SHADE_4);
 
		int colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
 
		int colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
 

	
 
		/* Draw all the score parts */
 
		int64_t val    = _score_part[company][score_type];
 
		int64_t needed = _score_info[score_type].needed;
 
		int   score  = _score_info[score_type].score;
 

	
 
		/* SCORE_TOTAL has its own rules ;) */
 
		if (score_type == SCORE_TOTAL) {
 
			for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) score += _score_info[i].score;
 
			needed = SCORE_MAX;
 
		}
 

	
 
		uint bar_top  = CenterBounds(r.top, r.bottom, this->bar_height);
 
		uint text_top = CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
 

	
 
		DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
 

	
 
		/* Draw the score */
 
		SetDParam(0, score);
 
		DrawString(this->score_info_left, this->score_info_right, text_top, STR_JUST_COMMA, TC_BLACK, SA_RIGHT);
 

	
 
		/* Calculate the %-bar */
 
		uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
 
		bool rtl = _current_text_dir == TD_RTL;
 
		if (rtl) {
 
			x = this->bar_right - x;
 
		} else {
 
			x = this->bar_left + x;
 
		}
 

	
 
		/* Draw the bar */
 
		if (x != this->bar_left)  GfxFillRect(this->bar_left, bar_top, x,               bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
 
		if (x != this->bar_right) GfxFillRect(x,              bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
 

	
 
		/* Draw it */
 
		SetDParam(0, Clamp<int64_t>(val, 0, needed) * 100 / needed);
 
		DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING, SA_HOR_CENTER);
 

	
 
		/* SCORE_LOAN is inversed */
 
		if (score_type == SCORE_LOAN) val = needed - val;
 

	
 
		/* Draw the amount we have against what is needed
 
		 * For some of them it is in currency format */
 
		SetDParam(0, val);
 
		SetDParam(1, needed);
 
		switch (score_type) {
 
			case SCORE_MIN_PROFIT:
 
			case SCORE_MIN_INCOME:
src/group_gui.cpp
Show inline comments
 
@@ -217,97 +217,97 @@ private:
 

	
 
		this->column_size[VGC_PROTECT] = GetSpriteSize(SPR_GROUP_REPLACE_PROTECT);
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_PROTECT].height);
 

	
 
		this->column_size[VGC_AUTOREPLACE] = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_AUTOREPLACE].height);
 

	
 
		this->column_size[VGC_PROFIT].width = 0;
 
		this->column_size[VGC_PROFIT].height = 0;
 
		static const SpriteID profit_sprites[] = {SPR_PROFIT_NA, SPR_PROFIT_NEGATIVE, SPR_PROFIT_SOME, SPR_PROFIT_LOT};
 
		for (uint i = 0; i < lengthof(profit_sprites); i++) {
 
			Dimension d = GetSpriteSize(profit_sprites[i]);
 
			this->column_size[VGC_PROFIT] = maxdim(this->column_size[VGC_PROFIT], d);
 
		}
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_PROFIT].height);
 

	
 
		int num_vehicle = GetGroupNumVehicle(this->vli.company, ALL_GROUP, this->vli.vtype);
 
		SetDParamMaxValue(0, num_vehicle, 3, FS_SMALL);
 
		SetDParamMaxValue(1, num_vehicle, 3, FS_SMALL);
 
		this->column_size[VGC_NUMBER] = GetStringBoundingBox(STR_GROUP_COUNT_WITH_SUBGROUP);
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_NUMBER].height);
 

	
 
		this->tiny_step_height += WidgetDimensions::scaled.framerect.Vertical();
 

	
 
		return WidgetDimensions::scaled.framerect.left +
 
			this->column_size[VGC_FOLD].width + WidgetDimensions::scaled.hsep_normal +
 
			this->column_size[VGC_NAME].width + WidgetDimensions::scaled.hsep_wide +
 
			this->column_size[VGC_PROTECT].width + WidgetDimensions::scaled.hsep_normal +
 
			this->column_size[VGC_AUTOREPLACE].width + WidgetDimensions::scaled.hsep_normal +
 
			this->column_size[VGC_PROFIT].width + WidgetDimensions::scaled.hsep_normal +
 
			this->column_size[VGC_NUMBER].width +
 
			WidgetDimensions::scaled.framerect.right;
 
	}
 

	
 
	/**
 
	 * Draw a row in the group list.
 
	 * @param y Top of the row.
 
	 * @param left Left of the row.
 
	 * @param right Right of the row.
 
	 * @param g_id Group to list.
 
	 * @param indent Indentation level.
 
	 * @param protection Whether autoreplace protection is set.
 
	 * @param has_children Whether the group has children and should have a fold / unfold button.
 
	 */
 
	void DrawGroupInfo(int y, int left, int right, GroupID g_id, int indent = 0, bool protection = false, bool has_children = false) const
 
	{
 
		/* Highlight the group if a vehicle is dragged over it */
 
		if (g_id == this->group_over) {
 
			GfxFillRect(left + WidgetDimensions::scaled.bevel.left, y + WidgetDimensions::scaled.framerect.top, right - WidgetDimensions::scaled.bevel.right, y + this->tiny_step_height - 1 - WidgetDimensions::scaled.framerect.bottom, GetColourGradient(COLOUR_GREY, SHADE_7));
 
			GfxFillRect(left + WidgetDimensions::scaled.bevel.left, y + WidgetDimensions::scaled.framerect.top, right - WidgetDimensions::scaled.bevel.right, y + this->tiny_step_height - 1 - WidgetDimensions::scaled.framerect.bottom, GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST));
 
		}
 

	
 
		if (g_id == NEW_GROUP) return;
 

	
 
		/* draw the selected group in white, else we draw it in black */
 
		TextColour colour = g_id == this->vli.index ? TC_WHITE : TC_BLACK;
 
		const GroupStatistics &stats = GroupStatistics::Get(this->vli.company, g_id, this->vli.vtype);
 
		bool rtl = _current_text_dir == TD_RTL;
 

	
 
		/* draw fold / unfold button */
 
		int x = rtl ? right - WidgetDimensions::scaled.framerect.right - this->column_size[VGC_FOLD].width + 1 : left + WidgetDimensions::scaled.framerect.left;
 
		if (has_children) {
 
			DrawSprite(Group::Get(g_id)->folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, rtl ? x - indent : x + indent, y + (this->tiny_step_height - this->column_size[VGC_FOLD].height) / 2);
 
		}
 

	
 
		/* draw group name */
 
		StringID str;
 
		if (IsAllGroupID(g_id)) {
 
			str = STR_GROUP_ALL_TRAINS + this->vli.vtype;
 
		} else if (IsDefaultGroupID(g_id)) {
 
			str = STR_GROUP_DEFAULT_TRAINS + this->vli.vtype;
 
		} else {
 
			SetDParam(0, g_id);
 
			str = STR_GROUP_NAME;
 
		}
 
		x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_NAME].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_FOLD].width;
 
		DrawString(x + (rtl ? 0 : indent), x + this->column_size[VGC_NAME].width - 1 - (rtl ? indent : 0), y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour);
 

	
 
		/* draw autoreplace protection */
 
		x = rtl ? x - WidgetDimensions::scaled.hsep_wide - this->column_size[VGC_PROTECT].width : x + WidgetDimensions::scaled.hsep_wide + this->column_size[VGC_NAME].width;
 
		if (protection) DrawSprite(SPR_GROUP_REPLACE_PROTECT, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROTECT].height) / 2);
 

	
 
		/* draw autoreplace status */
 
		x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_AUTOREPLACE].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_PROTECT].width;
 
		if (stats.autoreplace_defined) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, stats.autoreplace_finished ? PALETTE_CRASH : PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_AUTOREPLACE].height) / 2);
 

	
 
		/* draw the profit icon */
 
		x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_PROFIT].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_AUTOREPLACE].width;
 
		SpriteID spr;
 
		uint num_vehicle_min_age = GetGroupNumVehicleMinAge(this->vli.company, g_id, this->vli.vtype);
 
		Money profit_last_year_min_age = GetGroupProfitLastYearMinAge(this->vli.company, g_id, this->vli.vtype);
 
		if (num_vehicle_min_age == 0) {
 
			spr = SPR_PROFIT_NA;
 
		} else if (profit_last_year_min_age < 0) {
 
			spr = SPR_PROFIT_NEGATIVE;
 
		} else if (profit_last_year_min_age < VEHICLE_PROFIT_THRESHOLD * num_vehicle_min_age) {
 
			spr = SPR_PROFIT_SOME;
 
		} else {
 
@@ -585,97 +585,97 @@ public:
 
				DrawString(tr, STR_JUST_CURRENCY_LONG, TC_BLACK, SA_RIGHT);
 

	
 
				tr.top += GetCharacterHeight(FS_NORMAL);
 
				DrawString(tr, TimerGameEconomy::UsingWallclockUnits() ? STR_GROUP_PROFIT_LAST_PERIOD : STR_GROUP_PROFIT_LAST_YEAR, TC_BLACK);
 
				SetDParam(0, last_year);
 
				DrawString(tr, STR_JUST_CURRENCY_LONG, TC_BLACK, SA_RIGHT);
 

	
 
				tr.top += GetCharacterHeight(FS_NORMAL);
 
				DrawString(tr, STR_GROUP_OCCUPANCY, TC_BLACK);
 
				const size_t vehicle_count = this->vehicles.size();
 
				if (vehicle_count > 0) {
 
					SetDParam(0, occupancy / vehicle_count);
 
					DrawString(tr, STR_GROUP_OCCUPANCY_VALUE, TC_BLACK, SA_RIGHT);
 
				}
 

	
 
				break;
 
			}
 

	
 
			case WID_GL_LIST_GROUP: {
 
				int y1 = r.top;
 
				size_t max = std::min<size_t>(this->group_sb->GetPosition() + this->group_sb->GetCapacity(), this->groups.size());
 
				for (size_t i = this->group_sb->GetPosition(); i < max; ++i) {
 
					const Group *g = this->groups[i];
 

	
 
					assert(g->owner == this->owner);
 

	
 
					DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * WidgetDimensions::scaled.hsep_indent, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (i + 1 < this->groups.size() && indents[i + 1] > this->indents[i]));
 

	
 
					y1 += this->tiny_step_height;
 
				}
 
				if ((uint)this->group_sb->GetPosition() + this->group_sb->GetCapacity() > this->groups.size()) {
 
					DrawGroupInfo(y1, r.left, r.right, NEW_GROUP);
 
				}
 
				break;
 
			}
 

	
 
			case WID_GL_SORT_BY_ORDER:
 
				this->DrawSortButtonState(WID_GL_SORT_BY_ORDER, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
 
				break;
 

	
 
			case WID_GL_LIST_VEHICLE:
 
				if (this->vli.index != ALL_GROUP && this->grouping == GB_NONE) {
 
					/* Mark vehicles which are in sub-groups (only if we are not using shared order coalescing) */
 
					Rect mr = r.WithHeight(this->resize.step_height);
 
					size_t max = std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehgroups.size());
 
					for (size_t i = this->vscroll->GetPosition(); i < max; ++i) {
 
						const Vehicle *v = this->vehgroups[i].GetSingleVehicle();
 
						if (v->group_id != this->vli.index) {
 
							GfxFillRect(mr.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_GREY, SHADE_3), FILLRECT_CHECKER);
 
							GfxFillRect(mr.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_GREY, SHADE_DARK), FILLRECT_CHECKER);
 
						}
 
						mr = mr.Translate(0, this->resize.step_height);
 
					}
 
				}
 

	
 
				this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
 
				break;
 
		}
 
	}
 

	
 
	static void DeleteGroupCallback(Window *win, bool confirmed)
 
	{
 
		if (confirmed) {
 
			VehicleGroupWindow *w = (VehicleGroupWindow*)win;
 
			w->vli.index = ALL_GROUP;
 
			Command<CMD_DELETE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_DELETE, w->group_confirm);
 
		}
 
	}
 

	
 
	void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_GL_SORT_BY_ORDER: // Flip sorting method ascending/descending
 
				this->vehgroups.ToggleSortOrder();
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_GL_GROUP_BY_DROPDOWN: // Select grouping option dropdown menu
 
				ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_GL_GROUP_BY_DROPDOWN, 0, 0);
 
				return;
 

	
 
			case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
 
				ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(),  WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
 
				return;
 

	
 
			case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu
 
				ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget);
 
				break;
 

	
 
			case WID_GL_ALL_VEHICLES: // All vehicles button
 
				if (!IsAllGroupID(this->vli.index)) {
 
					this->vli.index = ALL_GROUP;
 
					this->vehgroups.ForceRebuild();
 
					this->SetDirty();
 
				}
 
				break;
 

	
 
			case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles button
src/linkgraph/linkgraph_gui.cpp
Show inline comments
 
@@ -267,118 +267,118 @@ void LinkGraphOverlay::Draw(const DrawPi
 
	}
 
	this->DrawLinks(dpi);
 
	this->DrawStationDots(dpi);
 
}
 

	
 
/**
 
 * Draw the cached links or part of them into the given area.
 
 * @param dpi Area to be drawn to.
 
 */
 
void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const
 
{
 
	int width = ScaleGUITrad(this->scale);
 
	for (const auto &i : this->cached_links) {
 
		if (!Station::IsValidID(i.first)) continue;
 
		Point pta = this->GetStationMiddle(Station::Get(i.first));
 
		for (const auto &j : i.second) {
 
			if (!Station::IsValidID(j.first)) continue;
 
			Point ptb = this->GetStationMiddle(Station::Get(j.first));
 
			if (!this->IsLinkVisible(pta, ptb, dpi, width + 2)) continue;
 
			this->DrawContent(pta, ptb, j.second);
 
		}
 
	}
 
}
 

	
 
/**
 
 * Draw one specific link.
 
 * @param pta Source of the link.
 
 * @param ptb Destination of the link.
 
 * @param cargo Properties of the link.
 
 */
 
void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
 
{
 
	uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage());
 
	int colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)];
 
	int width = ScaleGUITrad(this->scale);
 
	int dash = cargo.shared ? width * 4 : 0;
 

	
 
	/* Move line a bit 90° against its dominant direction to prevent it from
 
	 * being hidden below the grey line. */
 
	int side = _settings_game.vehicle.road_side ? 1 : -1;
 
	if (abs(pta.x - ptb.x) < abs(pta.y - ptb.y)) {
 
		int offset_x = (pta.y > ptb.y ? 1 : -1) * side * width;
 
		GfxDrawLine(pta.x + offset_x, pta.y, ptb.x + offset_x, ptb.y, colour, width, dash);
 
	} else {
 
		int offset_y = (pta.x < ptb.x ? 1 : -1) * side * width;
 
		GfxDrawLine(pta.x, pta.y + offset_y, ptb.x, ptb.y + offset_y, colour, width, dash);
 
	}
 

	
 
	GfxDrawLine(pta.x, pta.y, ptb.x, ptb.y, GetColourGradient(COLOUR_GREY, SHADE_1), width);
 
	GfxDrawLine(pta.x, pta.y, ptb.x, ptb.y, GetColourGradient(COLOUR_GREY, SHADE_DARKEST), width);
 
}
 

	
 
/**
 
 * Draw dots for stations into the smallmap. The dots' sizes are determined by the amount of
 
 * cargo produced there, their colours by the type of cargo produced.
 
 */
 
void LinkGraphOverlay::DrawStationDots(const DrawPixelInfo *dpi) const
 
{
 
	int width = ScaleGUITrad(this->scale);
 
	for (const auto &i : this->cached_stations) {
 
		const Station *st = Station::GetIfValid(i.first);
 
		if (st == nullptr) continue;
 
		Point pt = this->GetStationMiddle(st);
 
		if (!this->IsPointVisible(pt, dpi, 3 * width)) continue;
 

	
 
		uint r = width * 2 + width * 2 * std::min(200U, i.second) / 200;
 

	
 
		LinkGraphOverlay::DrawVertex(pt.x, pt.y, r,
 
				GetColourGradient(st->owner != OWNER_NONE ?
 
						Company::Get(st->owner)->colour : COLOUR_GREY, SHADE_5),
 
				GetColourGradient(COLOUR_GREY, SHADE_1));
 
						Company::Get(st->owner)->colour : COLOUR_GREY, SHADE_LIGHT),
 
				GetColourGradient(COLOUR_GREY, SHADE_DARKEST));
 
	}
 
}
 

	
 
/**
 
 * Draw a square symbolizing a producer of cargo.
 
 * @param x X coordinate of the middle of the vertex.
 
 * @param y Y coordinate of the middle of the vertex.
 
 * @param size x and y extent of the vertex.
 
 * @param colour Colour with which the vertex will be filled.
 
 * @param border_colour Colour for the border of the vertex.
 
 */
 
/* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, int colour, int border_colour)
 
{
 
	size--;
 
	int w1 = size / 2;
 
	int w2 = size / 2 + size % 2;
 
	int borderwidth = ScaleGUITrad(1);
 

	
 
	GfxFillRect(x - w1 - borderwidth, y - w1 - borderwidth, x + w2 + borderwidth, y + w2 + borderwidth, border_colour);
 
	GfxFillRect(x - w1, y - w1, x + w2, y + w2, colour);
 
}
 

	
 
bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
 
{
 
	for (auto i(this->cached_links.crbegin()); i != this->cached_links.crend(); ++i) {
 
		if (!Station::IsValidID(i->first)) continue;
 
		Point pta = this->GetStationMiddle(Station::Get(i->first));
 
		for (auto j(i->second.crbegin()); j != i->second.crend(); ++j) {
 
			if (!Station::IsValidID(j->first)) continue;
 
			if (i->first == j->first) continue;
 

	
 
			/* Check the distance from the cursor to the line defined by the two stations. */
 
			Point ptb = this->GetStationMiddle(Station::Get(j->first));
 
			float dist = std::abs((int64_t)(ptb.x - pta.x) * (int64_t)(pta.y - pt.y) - (int64_t)(pta.x - pt.x) * (int64_t)(ptb.y - pta.y)) /
 
				std::sqrt((int64_t)(ptb.x - pta.x) * (int64_t)(ptb.x - pta.x) + (int64_t)(ptb.y - pta.y) * (int64_t)(ptb.y - pta.y));
 
			const auto &link = j->second;
 
			if (dist <= 4 && link.Usage() > 0 &&
 
					pt.x + 2 >= std::min(pta.x, ptb.x) &&
 
					pt.x - 2 <= std::max(pta.x, ptb.x) &&
 
					pt.y + 2 >= std::min(pta.y, ptb.y) &&
 
					pt.y - 2 <= std::max(pta.y, ptb.y)) {
 
				static std::string tooltip_extension;
 
				tooltip_extension.clear();
 
				/* Fill buf with more information if this is a bidirectional link. */
 
				uint32_t back_time = 0;
 
				auto k = this->cached_links[j->first].find(i->first);
 
				if (k != this->cached_links[j->first].end()) {
 
					const auto &back = k->second;
src/misc_gui.cpp
Show inline comments
 
@@ -749,97 +749,97 @@ struct TooltipsWindow : public Window
 

	
 
/**
 
 * Shows a tooltip
 
 * @param parent The window this tooltip is related to.
 
 * @param str String to be displayed
 
 * @param close_tooltip the condition under which the tooltip closes
 
 * @param paramcount number of params to deal with
 
 */
 
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
 
{
 
	CloseWindowById(WC_TOOLTIPS, 0);
 

	
 
	if (str == STR_NULL || !_cursor.in_window) return;
 

	
 
	new TooltipsWindow(parent, str, paramcount, close_tooltip);
 
}
 

	
 
void QueryString::HandleEditBox(Window *w, WidgetID wid)
 
{
 
	if (w->IsWidgetGloballyFocused(wid) && this->text.HandleCaret()) {
 
		w->SetWidgetDirty(wid);
 

	
 
		/* For the OSK also invalidate the parent window */
 
		if (w->window_class == WC_OSK) w->InvalidateData();
 
	}
 
}
 

	
 
static int GetCaretWidth()
 
{
 
	return GetCharacterWidth(FS_NORMAL, '_');
 
}
 

	
 
void QueryString::DrawEditBox(const Window *w, WidgetID wid) const
 
{
 
	const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
 

	
 
	assert((wi->type & WWT_MASK) == WWT_EDITBOX);
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	Dimension sprite_size = GetScaledSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
 
	int clearbtn_width = sprite_size.width + WidgetDimensions::scaled.imgbtn.Horizontal();
 

	
 
	Rect r = wi->GetCurrentRect();
 
	Rect cr = r.WithWidth(clearbtn_width, !rtl);
 
	Rect fr = r.Indent(clearbtn_width, !rtl);
 

	
 
	DrawFrameRect(cr, wi->colour, wi->IsLowered() ? FR_LOWERED : FR_NONE);
 
	DrawSpriteIgnorePadding(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT, PAL_NONE, cr, SA_CENTER);
 
	if (this->text.bytes == 1) GfxFillRect(cr.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(wi->colour, SHADE_2), FILLRECT_CHECKER);
 
	if (this->text.bytes == 1) GfxFillRect(cr.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(wi->colour, SHADE_DARKER), FILLRECT_CHECKER);
 

	
 
	DrawFrameRect(fr, wi->colour, FR_LOWERED | FR_DARKENED);
 
	GfxFillRect(fr.Shrink(WidgetDimensions::scaled.bevel), PC_BLACK);
 

	
 
	fr = fr.Shrink(WidgetDimensions::scaled.framerect);
 
	/* Limit the drawing of the string inside the widget boundaries */
 
	DrawPixelInfo dpi;
 
	if (!FillDrawPixelInfo(&dpi, fr)) return;
 

	
 
	AutoRestoreBackup dpi_backup(_cur_dpi, &dpi);
 

	
 
	/* We will take the current widget length as maximum width, with a small
 
	 * space reserved at the end for the caret to show */
 
	const Textbuf *tb = &this->text;
 
	int delta = std::min(0, (fr.right - fr.left) - tb->pixels - GetCaretWidth());
 

	
 
	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
 

	
 
	/* If we have a marked area, draw a background highlight. */
 
	if (tb->marklength != 0) GfxFillRect(delta + tb->markxoffs, 0, delta + tb->markxoffs + tb->marklength - 1, fr.bottom - fr.top, PC_GREY);
 

	
 
	DrawString(delta, tb->pixels, 0, tb->buf, TC_YELLOW);
 
	bool focussed = w->IsWidgetGloballyFocused(wid) || IsOSKOpenedFor(w, wid);
 
	if (focussed && tb->caret) {
 
		int caret_width = GetStringBoundingBox("_").width;
 
		DrawString(tb->caretxoffs + delta, tb->caretxoffs + delta + caret_width, 0, "_", TC_WHITE);
 
	}
 
}
 

	
 
/**
 
 * Get the current caret position.
 
 * @param w Window the edit box is in.
 
 * @param wid Widget index.
 
 * @return Top-left location of the caret, relative to the window.
 
 */
 
Point QueryString::GetCaretPosition(const Window *w, WidgetID wid) const
 
{
 
	const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
 

	
 
	assert((wi->type & WWT_MASK) == WWT_EDITBOX);
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	Dimension sprite_size = GetScaledSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
 
	int clearbtn_width = sprite_size.width + WidgetDimensions::scaled.imgbtn.Horizontal();
 

	
 
	Rect r = wi->GetCurrentRect().Indent(clearbtn_width, !rtl).Shrink(WidgetDimensions::scaled.framerect);
 

	
 
	/* Clamp caret position to be inside out current width. */
src/network/network_gui.cpp
Show inline comments
 
@@ -1901,97 +1901,97 @@ public:
 
			default: NOT_REACHED();
 

	
 
			case WID_CL_SERVER_NAME_EDIT: {
 
				if (!_network_server) break;
 

	
 
				SetSettingValue(GetSettingFromName("network.server_name")->AsStringSetting(), str);
 
				this->InvalidateData();
 
				break;
 
			}
 

	
 
			case WID_CL_CLIENT_NAME_EDIT: {
 
				SetSettingValue(GetSettingFromName("network.client_name")->AsStringSetting(), str);
 
				this->InvalidateData();
 
				break;
 
			}
 

	
 
			case WID_CL_COMPANY_JOIN:
 
				NetworkClientRequestMove(this->join_company, str);
 
				break;
 
		}
 
	}
 

	
 
	/**
 
	 * Draw the buttons for a single line in the matrix.
 
	 *
 
	 * The x-position in RTL is the most left or otherwise the most right pixel
 
	 * we can draw the buttons from.
 
	 *
 
	 * @param x The x-position to start with the buttons. Updated during this function.
 
	 * @param y The y-position to start with the buttons.
 
	 * @param buttons The buttons to draw.
 
	 */
 
	void DrawButtons(int &x, uint y, const std::vector<std::unique_ptr<ButtonCommon>> &buttons) const
 
	{
 
		Rect r;
 

	
 
		for (auto &button : buttons) {
 
			bool rtl = _current_text_dir == TD_RTL;
 

	
 
			int offset = (this->line_height - button->height) / 2;
 
			r.left = rtl ? x : x - button->width + 1;
 
			r.right = rtl ? x + button->width - 1 : x;
 
			r.top = y + offset;
 
			r.bottom = r.top + button->height - 1;
 

	
 
			DrawFrameRect(r, button->colour, FR_NONE);
 
			DrawSprite(button->sprite, PAL_NONE, r.left + WidgetDimensions::scaled.framerect.left, r.top + WidgetDimensions::scaled.framerect.top);
 
			if (button->disabled) {
 
				GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(button->colour, SHADE_2), FILLRECT_CHECKER);
 
				GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(button->colour, SHADE_DARKER), FILLRECT_CHECKER);
 
			}
 

	
 
			int width = button->width + WidgetDimensions::scaled.hsep_normal;
 
			x += rtl ? width : -width;
 
		}
 
	}
 

	
 
	/**
 
	 * Draw a company and its clients on the matrix.
 
	 * @param company_id The company to draw.
 
	 * @param r The rect to draw within.
 
	 * @param line The Nth line we are drawing. Updated during this function.
 
	 */
 
	void DrawCompany(CompanyID company_id, const Rect &r, uint &line) const
 
	{
 
		bool rtl = _current_text_dir == TD_RTL;
 
		int text_y_offset = CenterBounds(0, this->line_height, GetCharacterHeight(FS_NORMAL));
 

	
 
		Dimension d = GetSpriteSize(SPR_COMPANY_ICON);
 
		int offset = CenterBounds(0, this->line_height, d.height);
 

	
 
		uint line_start = this->vscroll->GetPosition();
 
		uint line_end = line_start + this->vscroll->GetCapacity();
 

	
 
		uint y = r.top + (this->line_height * (line - line_start));
 

	
 
		/* Draw the company line (if in range of scrollbar). */
 
		if (IsInsideMM(line, line_start, line_end)) {
 
			int icon_left = r.WithWidth(d.width, rtl).left;
 
			Rect tr = r.Indent(d.width + WidgetDimensions::scaled.hsep_normal, rtl);
 
			int &x = rtl ? tr.left : tr.right;
 

	
 
			/* If there are buttons for this company, draw them. */
 
			auto button_find = this->buttons.find(line);
 
			if (button_find != this->buttons.end()) {
 
				this->DrawButtons(x, y, button_find->second);
 
			}
 

	
 
			if (company_id == COMPANY_SPECTATOR) {
 
				DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, icon_left, y + offset);
 
				DrawString(tr.left, tr.right, y + text_y_offset, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER);
 
			} else if (company_id == COMPANY_NEW_COMPANY) {
 
				DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, icon_left, y + offset);
 
				DrawString(tr.left, tr.right, y + text_y_offset, STR_NETWORK_CLIENT_LIST_NEW_COMPANY, TC_WHITE);
 
			} else {
 
				DrawCompanyIcon(company_id, icon_left, y + offset);
 

	
 
				SetDParam(0, company_id);
src/order_gui.cpp
Show inline comments
 
@@ -1079,97 +1079,97 @@ public:
 
					this->DisableWidget(WID_O_UNLOAD);
 
					this->DisableWidget(WID_O_REFIT_DROPDOWN);
 
					break;
 
			}
 
		}
 

	
 
		/* Disable list of vehicles with the same shared orders if there is no list */
 
		this->SetWidgetDisabledState(WID_O_SHARED_ORDER_LIST, !shared_orders);
 

	
 
		this->SetDirty();
 
	}
 

	
 
	void OnPaint() override
 
	{
 
		if (this->vehicle->owner != _local_company) {
 
			this->selected_order = -1; // Disable selection any selected row at a competitor order window.
 
		} else {
 
			this->SetWidgetLoweredState(WID_O_GOTO, this->goto_type != OPOS_NONE);
 
		}
 
		this->DrawWidgets();
 
	}
 

	
 
	void DrawWidget(const Rect &r, WidgetID widget) const override
 
	{
 
		if (widget != WID_O_ORDER_LIST) return;
 

	
 
		Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect);
 
		bool rtl = _current_text_dir == TD_RTL;
 
		SetDParamMaxValue(0, this->vehicle->GetNumOrders(), 2);
 
		int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + WidgetDimensions::scaled.hsep_normal;
 
		int middle = rtl ? ir.right - index_column_width : ir.left + index_column_width;
 

	
 
		int y = ir.top;
 
		int line_height = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST)->resize_y;
 

	
 
		int i = this->vscroll->GetPosition();
 
		const Order *order = this->vehicle->GetOrder(i);
 
		/* First draw the highlighting underground if it exists. */
 
		if (this->order_over != INVALID_VEH_ORDER_ID) {
 
			while (order != nullptr) {
 
				/* Don't draw anything if it extends past the end of the window. */
 
				if (!this->vscroll->IsVisible(i)) break;
 

	
 
				if (i != this->selected_order && i == this->order_over) {
 
					/* Highlight dragged order destination. */
 
					int top = (this->order_over < this->selected_order ? y : y + line_height) - WidgetDimensions::scaled.framerect.top;
 
					int bottom = std::min(top + 2, ir.bottom);
 
					top = std::max(top - 3, ir.top);
 
					GfxFillRect(ir.left, top, ir.right, bottom, GetColourGradient(COLOUR_GREY, SHADE_7));
 
					GfxFillRect(ir.left, top, ir.right, bottom, GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST));
 
					break;
 
				}
 
				y += line_height;
 

	
 
				i++;
 
				order = order->next;
 
			}
 

	
 
			/* Reset counters for drawing the orders. */
 
			y = ir.top;
 
			i = this->vscroll->GetPosition();
 
			order = this->vehicle->GetOrder(i);
 
		}
 

	
 
		/* Draw the orders. */
 
		while (order != nullptr) {
 
			/* Don't draw anything if it extends past the end of the window. */
 
			if (!this->vscroll->IsVisible(i)) break;
 

	
 
			DrawOrderString(this->vehicle, order, i, y, i == this->selected_order, false, ir.left, middle, ir.right);
 
			y += line_height;
 

	
 
			i++;
 
			order = order->next;
 
		}
 

	
 
		if (this->vscroll->IsVisible(i)) {
 
			StringID str = this->vehicle->IsOrderListShared() ? STR_ORDERS_END_OF_SHARED_ORDERS : STR_ORDERS_END_OF_ORDERS;
 
			DrawString(rtl ? ir.left : middle, rtl ? middle : ir.right, y, str, (i == this->selected_order) ? TC_WHITE : TC_BLACK);
 
		}
 
	}
 

	
 
	void SetStringParameters(WidgetID widget) const override
 
	{
 
		switch (widget) {
 
			case WID_O_COND_VALUE: {
 
				VehicleOrderID sel = this->OrderGetSel();
 
				const Order *order = this->vehicle->GetOrder(sel);
 

	
 
				if (order != nullptr && order->IsType(OT_CONDITIONAL)) {
 
					uint value = order->GetConditionValue();
 
					if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value, this->vehicle->type);
 
					SetDParam(0, value);
 
				}
 
				break;
 
			}
 

	
 
			case WID_O_CAPTION:
src/palette_func.h
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file palette_func.h Functions related to palettes. */
 

	
 
#ifndef PALETTE_FUNC_H
 
#define PALETTE_FUNC_H
 

	
 
#include "core/enum_type.hpp"
 
#include "gfx_type.h"
 
#include "strings_type.h"
 
#include "string_type.h"
 

	
 
extern Palette _cur_palette; ///< Current palette
 

	
 
bool CopyPalette(Palette &local_palette, bool force_copy = false);
 
void GfxInitPalettes();
 

	
 
uint8_t GetNearestColourIndex(uint8_t r, uint8_t g, uint8_t b);
 

	
 
inline uint8_t GetNearestColourIndex(const Colour colour)
 
{
 
	return GetNearestColourIndex(colour.r, colour.g, colour.b);
 
}
 

	
 
/**
 
 * Checks if a Colours value is valid.
 
 *
 
 * @param colours The value to check
 
 * @return true if the given value is a valid Colours.
 
 */
 
inline bool IsValidColours(Colours colours)
 
{
 
	return colours < COLOUR_END;
 
}
 

	
 
TextColour GetContrastColour(uint8_t background, uint8_t threshold = 128);
 

	
 
enum ColourShade : uint8_t {
 
	SHADE_BEGIN = 0,
 
	SHADE_1,
 
	SHADE_2,
 
	SHADE_3,
 
	SHADE_4,
 
	SHADE_5,
 
	SHADE_6,
 
	SHADE_7,
 
	SHADE_DARKEST,
 
	SHADE_DARKER,
 
	SHADE_DARK,
 
	SHADE_NORMAL,
 
	SHADE_LIGHT,
 
	SHADE_LIGHTER,
 
	SHADE_LIGHTEST,
 
	SHADE_END,
 
};
 
DECLARE_POSTFIX_INCREMENT(ColourShade)
 

	
 
byte GetColourGradient(Colours colour, ColourShade shade);
 
void SetColourGradient(Colours colour, ColourShade shade, byte palette_colour);
 

	
 
/**
 
 * Return the colour for a particular greyscale level.
 
 * @param level Intensity, 0 = black, 15 = white
 
 * @return colour
 
 */
 
#define GREY_SCALE(level) (level)
 

	
 
static const uint8_t PC_BLACK              = GREY_SCALE(1);  ///< Black palette colour.
 
static const uint8_t PC_DARK_GREY          = GREY_SCALE(6);  ///< Dark grey palette colour.
 
static const uint8_t PC_GREY               = GREY_SCALE(10); ///< Grey palette colour.
 
static const uint8_t PC_WHITE              = GREY_SCALE(15); ///< White palette colour.
 

	
 
static const uint8_t PC_VERY_DARK_RED      = 0xB2;           ///< Almost-black red palette colour.
 
static const uint8_t PC_DARK_RED           = 0xB4;           ///< Dark red palette colour.
 
static const uint8_t PC_RED                = 0xB8;           ///< Red palette colour.
 

	
 
static const uint8_t PC_VERY_DARK_BROWN    = 0x56;           ///< Almost-black brown palette colour.
 

	
 
static const uint8_t PC_ORANGE             = 0xC2;           ///< Orange palette colour.
 

	
 
static const uint8_t PC_YELLOW             = 0xBF;           ///< Yellow palette colour.
 
static const uint8_t PC_LIGHT_YELLOW       = 0x44;           ///< Light yellow palette colour.
 
static const uint8_t PC_VERY_LIGHT_YELLOW  = 0x45;           ///< Almost-white yellow palette colour.
 

	
 
static const uint8_t PC_GREEN              = 0xD0;           ///< Green palette colour.
 

	
 
static const uint8_t PC_VERY_DARK_BLUE     = 0x9A;           ///< Almost-black blue palette colour.
 
static const uint8_t PC_DARK_BLUE          = 0x9D;           ///< Dark blue palette colour.
 
static const uint8_t PC_LIGHT_BLUE         = 0x98;           ///< Light blue palette colour.
 

	
 
static const uint8_t PC_ROUGH_LAND         = 0x52;           ///< Dark green palette colour for rough land.
 
static const uint8_t PC_GRASS_LAND         = 0x54;           ///< Dark green palette colour for grass land.
 
static const uint8_t PC_BARE_LAND          = 0x37;           ///< Brown palette colour for bare land.
 
static const uint8_t PC_RAINFOREST         = 0x5C;           ///< Pale green palette colour for rainforest.
 
static const uint8_t PC_FIELDS             = 0x25;           ///< Light brown palette colour for fields.
 
static const uint8_t PC_TREES              = 0x57;           ///< Green palette colour for trees.
 
static const uint8_t PC_WATER              = 0xC9;           ///< Dark blue palette colour for water.
 

	
 
#endif /* PALETTE_FUNC_H */
src/settings_gui.cpp
Show inline comments
 
@@ -1369,97 +1369,97 @@ bool BaseSettingEntry::IsVisible(const B
 
BaseSettingEntry *BaseSettingEntry::FindEntry(uint row_num, uint *cur_row)
 
{
 
	if (this->IsFiltered()) return nullptr;
 
	if (row_num == *cur_row) return this;
 
	(*cur_row)++;
 
	return nullptr;
 
}
 

	
 
/**
 
 * Draw a row in the settings panel.
 
 *
 
 * The scrollbar uses rows of the page, while the page data structure is a tree of #SettingsPage and #SettingEntry objects.
 
 * As a result, the drawing routing traverses the tree from top to bottom, counting rows in \a cur_row until it reaches \a first_row.
 
 * Then it enables drawing rows while traversing until \a max_row is reached, at which point drawing is terminated.
 
 *
 
 * The \a parent_last parameter ensures that the vertical lines at the left are
 
 * only drawn when another entry follows, that it prevents output like
 
 * \verbatim
 
 *  |-- setting
 
 *  |-- (-) - Title
 
 *  |    |-- setting
 
 *  |    |-- setting
 
 * \endverbatim
 
 * The left-most vertical line is not wanted. It is prevented by setting the
 
 * appropriate bit in the \a parent_last parameter.
 
 *
 
 * @param settings_ptr Pointer to current values of all settings
 
 * @param left         Left-most position in window/panel to start drawing \a first_row
 
 * @param right        Right-most x position to draw strings at.
 
 * @param y            Upper-most position in window/panel to start drawing \a first_row
 
 * @param first_row    First row number to draw
 
 * @param max_row      Row-number to stop drawing (the row-number of the row below the last row to draw)
 
 * @param selected     Selected entry by the user.
 
 * @param cur_row      Current row number (internal variable)
 
 * @param parent_last  Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
 
 * @return Row number of the next row to draw
 
 */
 
uint BaseSettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
 
{
 
	if (this->IsFiltered()) return cur_row;
 
	if (cur_row >= max_row) return cur_row;
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	int offset = (rtl ? -(int)_circle_size.width : (int)_circle_size.width) / 2;
 
	int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
 

	
 
	int x = rtl ? right : left;
 
	if (cur_row >= first_row) {
 
		int colour = GetColourGradient(COLOUR_ORANGE, SHADE_4);
 
		int colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
 
		y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
 

	
 
		/* Draw vertical for parent nesting levels */
 
		for (uint lvl = 0; lvl < this->level; lvl++) {
 
			if (!HasBit(parent_last, lvl)) GfxDrawLine(x + offset, y, x + offset, y + SETTING_HEIGHT - 1, colour);
 
			x += level_width;
 
		}
 
		/* draw own |- prefix */
 
		int halfway_y = y + SETTING_HEIGHT / 2;
 
		int bottom_y = (flags & SEF_LAST_FIELD) ? halfway_y : y + SETTING_HEIGHT - 1;
 
		GfxDrawLine(x + offset, y, x + offset, bottom_y, colour);
 
		/* Small horizontal line from the last vertical line */
 
		GfxDrawLine(x + offset, halfway_y, x + level_width - (rtl ? -WidgetDimensions::scaled.hsep_normal : WidgetDimensions::scaled.hsep_normal), halfway_y, colour);
 
		x += level_width;
 

	
 
		this->DrawSetting(settings_ptr, rtl ? left : x, rtl ? x : right, y, this == selected);
 
	}
 
	cur_row++;
 

	
 
	return cur_row;
 
}
 

	
 
/* == SettingEntry methods == */
 

	
 
/**
 
 * Constructor for a single setting in the 'advanced settings' window
 
 * @param name Name of the setting in the setting table
 
 */
 
SettingEntry::SettingEntry(const char *name)
 
{
 
	this->name = name;
 
	this->setting = nullptr;
 
}
 

	
 
/**
 
 * Initialization of a setting entry
 
 * @param level      Page nesting level of this entry
 
 */
 
void SettingEntry::Init(byte level)
 
{
 
	BaseSettingEntry::Init(level);
 
	this->setting = GetSettingFromName(this->name)->AsIntSetting();
 
}
 

	
 
/* Sets the given setting entry to its default value */
 
void SettingEntry::ResetAll()
 
{
 
	SetSettingValue(this->setting, this->setting->def);
 
@@ -2864,128 +2864,128 @@ static constexpr NWidgetPart _nested_set
 
				NWidget(WWT_EDITBOX, COLOUR_MAUVE, WID_GS_FILTER), SetMinimalSize(50, 12), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
 
			EndContainer(),
 
		EndContainer(),
 
	EndContainer(),
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_PANEL, COLOUR_MAUVE, WID_GS_OPTIONSPANEL), SetMinimalSize(400, 174), SetScrollbar(WID_GS_SCROLLBAR), EndContainer(),
 
		NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_GS_SCROLLBAR),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_MAUVE),
 
		NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GS_HELP_TEXT), SetMinimalSize(300, 25), SetFill(1, 1), SetResize(1, 0),
 
				SetPadding(WidgetDimensions::unscaled.frametext),
 
	EndContainer(),
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_EXPAND_ALL), SetDataTip(STR_CONFIG_SETTING_EXPAND_ALL, STR_NULL),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_COLLAPSE_ALL), SetDataTip(STR_CONFIG_SETTING_COLLAPSE_ALL, STR_NULL),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_RESET_ALL), SetDataTip(STR_CONFIG_SETTING_RESET_ALL, STR_NULL),
 
		NWidget(WWT_PANEL, COLOUR_MAUVE), SetFill(1, 0), SetResize(1, 0),
 
		EndContainer(),
 
		NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
 
	EndContainer(),
 
};
 

	
 
static WindowDesc _settings_selection_desc(__FILE__, __LINE__,
 
	WDP_CENTER, "settings", 510, 450,
 
	WC_GAME_OPTIONS, WC_NONE,
 
	0,
 
	std::begin(_nested_settings_selection_widgets), std::end(_nested_settings_selection_widgets)
 
);
 

	
 
/** Open advanced settings window. */
 
void ShowGameSettings()
 
{
 
	CloseWindowByClass(WC_GAME_OPTIONS);
 
	new GameSettingsWindow(&_settings_selection_desc);
 
}
 

	
 

	
 
/**
 
 * Draw [<][>] boxes.
 
 * @param x the x position to draw
 
 * @param y the y position to draw
 
 * @param button_colour the colour of the button
 
 * @param state 0 = none clicked, 1 = first clicked, 2 = second clicked
 
 * @param clickable_left is the left button clickable?
 
 * @param clickable_right is the right button clickable?
 
 */
 
void DrawArrowButtons(int x, int y, Colours button_colour, byte state, bool clickable_left, bool clickable_right)
 
{
 
	int colour = GetColourGradient(button_colour, SHADE_2);
 
	int colour = GetColourGradient(button_colour, SHADE_DARKER);
 
	Dimension dim = NWidgetScrollbar::GetHorizontalDimension();
 

	
 
	Rect lr = {x,                  y, x + (int)dim.width     - 1, y + (int)dim.height - 1};
 
	Rect rr = {x + (int)dim.width, y, x + (int)dim.width * 2 - 1, y + (int)dim.height - 1};
 

	
 
	DrawFrameRect(lr, button_colour, (state == 1) ? FR_LOWERED : FR_NONE);
 
	DrawFrameRect(rr, button_colour, (state == 2) ? FR_LOWERED : FR_NONE);
 
	DrawSpriteIgnorePadding(SPR_ARROW_LEFT,  PAL_NONE, lr, SA_CENTER);
 
	DrawSpriteIgnorePadding(SPR_ARROW_RIGHT, PAL_NONE, rr, SA_CENTER);
 

	
 
	/* Grey out the buttons that aren't clickable */
 
	bool rtl = _current_text_dir == TD_RTL;
 
	if (rtl ? !clickable_right : !clickable_left) {
 
		GfxFillRect(lr.Shrink(WidgetDimensions::scaled.bevel), colour, FILLRECT_CHECKER);
 
	}
 
	if (rtl ? !clickable_left : !clickable_right) {
 
		GfxFillRect(rr.Shrink(WidgetDimensions::scaled.bevel), colour, FILLRECT_CHECKER);
 
	}
 
}
 

	
 
/**
 
 * Draw a dropdown button.
 
 * @param x the x position to draw
 
 * @param y the y position to draw
 
 * @param button_colour the colour of the button
 
 * @param state true = lowered
 
 * @param clickable is the button clickable?
 
 */
 
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
 
{
 
	int colour = GetColourGradient(button_colour, SHADE_2);
 
	int colour = GetColourGradient(button_colour, SHADE_DARKER);
 

	
 
	Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
 

	
 
	DrawFrameRect(r, button_colour, state ? FR_LOWERED : FR_NONE);
 
	DrawSpriteIgnorePadding(SPR_ARROW_DOWN, PAL_NONE, r, SA_CENTER);
 

	
 
	if (!clickable) {
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), colour, FILLRECT_CHECKER);
 
	}
 
}
 

	
 
/**
 
 * Draw a toggle button.
 
 * @param x the x position to draw
 
 * @param y the y position to draw
 
 * @param state true = lowered
 
 * @param clickable is the button clickable?
 
 */
 
void DrawBoolButton(int x, int y, bool state, bool clickable)
 
{
 
	static const Colours _bool_ctabs[2][2] = {{COLOUR_CREAM, COLOUR_RED}, {COLOUR_DARK_GREEN, COLOUR_GREEN}};
 

	
 
	Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
 
	DrawFrameRect(r, _bool_ctabs[state][clickable], state ? FR_LOWERED : FR_NONE);
 
}
 

	
 
struct CustomCurrencyWindow : Window {
 
	int query_widget;
 

	
 
	CustomCurrencyWindow(WindowDesc *desc) : Window(desc)
 
	{
 
		this->InitNested();
 

	
 
		SetButtonState();
 
	}
 

	
 
	void SetButtonState()
 
	{
 
		this->SetWidgetDisabledState(WID_CC_RATE_DOWN, _custom_currency.rate == 1);
 
		this->SetWidgetDisabledState(WID_CC_RATE_UP, _custom_currency.rate == UINT16_MAX);
 
		this->SetWidgetDisabledState(WID_CC_YEAR_DOWN, _custom_currency.to_euro == CF_NOEURO);
 
		this->SetWidgetDisabledState(WID_CC_YEAR_UP, _custom_currency.to_euro == CalendarTime::MAX_YEAR);
 
	}
 

	
 
	void SetStringParameters(WidgetID widget) const override
 
	{
 
		switch (widget) {
 
			case WID_CC_RATE:      SetDParam(0, 1); SetDParam(1, 1);            break;
src/smallmap_gui.cpp
Show inline comments
 
@@ -301,97 +301,97 @@ void BuildLandLegend()
 
	 *
 
	 * The table below defines up to which height level a particular delta in the legend should be
 
	 * used. One could opt for just dividing the maximum height and use that as delta, but that
 
	 * creates many "ugly" legend labels, e.g. once every 950 meter. As a result, this table will
 
	 * reduce the number of deltas to 7: every 100m, 200m, 300m, 500m, 750m, 1000m and 1250m. The
 
	 * deltas are closer together at the lower numbers because going from 12 entries to just 4, as
 
	 * would happen when replacing 200m and 300m by 250m, would mean the legend would be short and
 
	 * that might not be considered appropriate.
 
	 *
 
	 * The current method yields at least 7 legend entries and at most 12. It can be increased to
 
	 * 8 by adding a 150m and 400m option, but especially 150m creates ugly heights.
 
	 *
 
	 * It tries to evenly space the legend items over the two columns that are there for the legend.
 
	 */
 

	
 
	/* Table for delta; if max_height is less than the first column, use the second column as value. */
 
	uint deltas[][2] = { { 24, 2 }, { 48, 4 }, { 72, 6 }, { 120, 10 }, { 180, 15 }, { 240, 20 }, { MAX_TILE_HEIGHT + 1, 25 }};
 
	uint i = 0;
 
	for (; _settings_game.construction.map_height_limit >= deltas[i][0]; i++) {
 
		/* Nothing to do here. */
 
	}
 
	uint delta = deltas[i][1];
 

	
 
	int total_entries = (_settings_game.construction.map_height_limit / delta) + 1;
 
	int rows = CeilDiv(total_entries, 2);
 
	int j = 0;
 

	
 
	for (i = 0; i < lengthof(_legend_land_contours) - 1 && j < total_entries; i++) {
 
		if (_legend_land_contours[i].legend != STR_TINY_BLACK_HEIGHT) continue;
 

	
 
		_legend_land_contours[i].col_break = j % rows == 0;
 
		_legend_land_contours[i].end = false;
 
		_legend_land_contours[i].height = j * delta;
 
		_legend_land_contours[i].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[j * delta];
 
		j++;
 
	}
 
	_legend_land_contours[i].end = true;
 
}
 

	
 
/**
 
 * Completes the array for the owned property legend.
 
 */
 
void BuildOwnerLegend()
 
{
 
	_legend_land_owners[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
 

	
 
	int i = NUM_NO_COMPANY_ENTRIES;
 
	for (const Company *c : Company::Iterate()) {
 
		_legend_land_owners[i].colour = GetColourGradient(c->colour, SHADE_5);
 
		_legend_land_owners[i].colour = GetColourGradient(c->colour, SHADE_LIGHT);
 
		_legend_land_owners[i].company = c->index;
 
		_legend_land_owners[i].show_on_map = true;
 
		_legend_land_owners[i].col_break = false;
 
		_legend_land_owners[i].end = false;
 
		_company_to_list_pos[c->index] = i;
 
		i++;
 
	}
 

	
 
	/* Terminate the list */
 
	_legend_land_owners[i].end = true;
 

	
 
	/* Store maximum amount of owner legend entries. */
 
	_smallmap_company_count = i;
 
}
 

	
 
struct AndOr {
 
	uint32_t mor;
 
	uint32_t mand;
 
};
 

	
 
static inline uint32_t ApplyMask(uint32_t colour, const AndOr *mask)
 
{
 
	return (colour & mask->mand) | mask->mor;
 
}
 

	
 

	
 
/** Colour masks for "Contour" and "Routes" modes. */
 
static const AndOr _smallmap_contours_andor[] = {
 
	{MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_CLEAR
 
	{MKCOLOUR_0XX0(PC_GREY      ), MKCOLOUR_F00F}, // MP_RAILWAY
 
	{MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_ROAD
 
	{MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_HOUSE
 
	{MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TREES
 
	{MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000}, // MP_STATION
 
	{MKCOLOUR_XXXX(PC_WATER     ), MKCOLOUR_0000}, // MP_WATER
 
	{MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_VOID
 
	{MKCOLOUR_XXXX(PC_DARK_RED  ), MKCOLOUR_0000}, // MP_INDUSTRY
 
	{MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE
 
	{MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_OBJECT
 
	{MKCOLOUR_0XX0(PC_GREY      ), MKCOLOUR_F00F},
 
};
 

	
 
/** Colour masks for "Vehicles", "Industry", and "Vegetation" modes. */
 
static const AndOr _smallmap_vehicles_andor[] = {
 
	{MKCOLOUR_0000               , MKCOLOUR_FFFF}, // MP_CLEAR
 
	{MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_RAILWAY
 
	{MKCOLOUR_0XX0(PC_BLACK     ), MKCOLOUR_F00F}, // MP_ROAD
 
	{MKCOLOUR_0XX0(PC_DARK_RED  ), MKCOLOUR_F00F}, // MP_HOUSE
src/train_gui.cpp
Show inline comments
 
@@ -31,97 +31,97 @@ void CcBuildWagon(Commands, const Comman
 
	if (result.Failed()) return;
 

	
 
	/* find a locomotive in the depot. */
 
	const Vehicle *found = nullptr;
 
	for (const Train *t : Train::Iterate()) {
 
		if (t->IsFrontEngine() && t->tile == tile && t->IsStoppedInDepot()) {
 
			if (found != nullptr) return; // must be exactly one.
 
			found = t;
 
		}
 
	}
 

	
 
	/* if we found a loco, */
 
	if (found != nullptr) {
 
		found = found->Last();
 
		/* put the new wagon at the end of the loco. */
 
		Command<CMD_MOVE_RAIL_VEHICLE>::Post(found->tile, new_veh_id, found->index, false);
 
		InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 
	}
 
}
 

	
 
/**
 
 * Highlight the position where a rail vehicle is dragged over by drawing a light gray background.
 
 * @param px        The current x position to draw from.
 
 * @param max_width The maximum space available to draw.
 
 * @param y         The vertical centre position to draw from.
 
 * @param selection Selected vehicle that is dragged.
 
 * @param chain     Whether a whole chain is dragged.
 
 * @return The width of the highlight mark.
 
 */
 
static int HighlightDragPosition(int px, int max_width, int y, VehicleID selection, bool chain)
 
{
 
	bool rtl = _current_text_dir == TD_RTL;
 

	
 
	assert(selection != INVALID_VEHICLE);
 
	int dragged_width = 0;
 
	for (Train *t = Train::Get(selection); t != nullptr; t = chain ? t->Next() : (t->HasArticulatedPart() ? t->GetNextArticulatedPart() : nullptr)) {
 
		dragged_width += t->GetDisplayImageWidth(nullptr);
 
	}
 

	
 
	int drag_hlight_left = rtl ? std::max(px - dragged_width + 1, 0) : px;
 
	int drag_hlight_right = rtl ? px : std::min(px + dragged_width, max_width) - 1;
 
	int drag_hlight_width = std::max(drag_hlight_right - drag_hlight_left + 1, 0);
 

	
 
	if (drag_hlight_width > 0) {
 
		int height = ScaleSpriteTrad(12);
 
		int top = y - height / 2;
 
		Rect r = {drag_hlight_left, top, drag_hlight_right, top + height - 1};
 
		/* Sprite-scaling is used here as the area is from sprite size */
 
		GfxFillRect(r.Shrink(ScaleSpriteTrad(1)), GetColourGradient(COLOUR_GREY, SHADE_7));
 
		GfxFillRect(r.Shrink(ScaleSpriteTrad(1)), GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST));
 
	}
 

	
 
	return drag_hlight_width;
 
}
 

	
 
/**
 
 * Draws an image of a whole train
 
 * @param v         Front vehicle
 
 * @param r         Rect to draw at
 
 * @param selection Selected vehicle to draw a frame around
 
 * @param skip      Number of pixels to skip at the front (for scrolling)
 
 * @param drag_dest The vehicle another one is dragged over, \c INVALID_VEHICLE if none.
 
 */
 
void DrawTrainImage(const Train *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip, VehicleID drag_dest)
 
{
 
	bool rtl = _current_text_dir == TD_RTL;
 
	Direction dir = rtl ? DIR_E : DIR_W;
 

	
 
	DrawPixelInfo tmp_dpi;
 
	/* Position of highlight box */
 
	int highlight_l = 0;
 
	int highlight_r = 0;
 
	int max_width = r.Width();
 

	
 
	if (!FillDrawPixelInfo(&tmp_dpi, r)) return;
 

	
 
	{
 
		AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
 

	
 
		int px = rtl ? max_width + skip : -skip;
 
		int y = r.Height() / 2;
 
		bool sel_articulated = false;
 
		bool dragging = (drag_dest != INVALID_VEHICLE);
 
		bool drag_at_end_of_train = (drag_dest == v->index); // Head index is used to mark dragging at end of train.
 
		for (; v != nullptr && (rtl ? px > 0 : px < max_width); v = v->Next()) {
 
			if (dragging && !drag_at_end_of_train && drag_dest == v->index) {
 
				/* Highlight the drag-and-drop destination inside the train. */
 
				int drag_hlight_width = HighlightDragPosition(px, max_width, y, selection, _cursor.vehchain);
 
				px += rtl ? -drag_hlight_width : drag_hlight_width;
 
			}
 

	
 
			Point offset;
 
			int width = Train::From(v)->GetDisplayImageWidth(&offset);
 

	
 
			if (rtl ? px + width > 0 : px - width < max_width) {
 
				PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
				VehicleSpriteSeq seq;
 
				v->GetImage(dir, image_type, &seq);
 
@@ -354,97 +354,97 @@ void DrawTrainDetails(const Train *v, co
 
	bool rtl = _current_text_dir == TD_RTL;
 
	int line_height = r.Height();
 
	int sprite_y_offset = line_height / 2;
 
	int text_y_offset = (line_height - GetCharacterHeight(FS_NORMAL)) / 2;
 

	
 
	/* draw the first 3 details tabs */
 
	if (det_tab != TDW_TAB_TOTALS) {
 
		Direction dir = rtl ? DIR_E : DIR_W;
 
		int x = rtl ? r.right : r.left;
 
		for (; v != nullptr && vscroll_pos > -vscroll_cap; v = v->GetNextVehicle()) {
 
			GetCargoSummaryOfArticulatedVehicle(v, _cargo_summary);
 

	
 
			/* Draw sprites */
 
			uint dx = 0;
 
			int px = x;
 
			const Train *u = v;
 
			do {
 
				Point offset;
 
				int width = u->GetDisplayImageWidth(&offset);
 
				if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
 
					int pitch = 0;
 
					const Engine *e = Engine::Get(v->engine_type);
 
					if (e->GetGRF() != nullptr) {
 
						pitch = ScaleSpriteTrad(e->GetGRF()->traininfo_vehicle_pitch);
 
					}
 
					PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(u);
 
					VehicleSpriteSeq seq;
 
					u->GetImage(dir, EIT_IN_DETAILS, &seq);
 
					seq.Draw(px + (rtl ? -offset.x : offset.x), r.top - line_height * vscroll_pos + sprite_y_offset + pitch, pal, (v->vehstatus & VS_CRASHED) != 0);
 
				}
 
				px += rtl ? -width : width;
 
				dx += width;
 
				u = u->Next();
 
			} while (u != nullptr && u->IsArticulatedPart());
 

	
 
			bool separate_sprite_row = (dx > (uint)ScaleSpriteTrad(TRAIN_DETAILS_MAX_INDENT));
 
			if (separate_sprite_row) {
 
				vscroll_pos--;
 
				dx = 0;
 
			}
 

	
 
			int sprite_width = std::max<int>(dx, ScaleSpriteTrad(TRAIN_DETAILS_MIN_INDENT)) + WidgetDimensions::scaled.hsep_normal;
 
			Rect dr = r.Indent(sprite_width, rtl);
 
			uint num_lines = std::max(1u, (unsigned)_cargo_summary.size());
 
			for (uint i = 0; i < num_lines; i++) {
 
				if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
 
					int py = r.top - line_height * vscroll_pos + text_y_offset;
 
					if (i > 0 || separate_sprite_row) {
 
						if (vscroll_pos != 0) GfxFillRect(r.left, py - WidgetDimensions::scaled.matrix.top - 1, r.right, py - WidgetDimensions::scaled.matrix.top, GetColourGradient(COLOUR_GREY, SHADE_5));
 
						if (vscroll_pos != 0) GfxFillRect(r.left, py - WidgetDimensions::scaled.matrix.top - 1, r.right, py - WidgetDimensions::scaled.matrix.top, GetColourGradient(COLOUR_GREY, SHADE_LIGHT));
 
					}
 
					switch (det_tab) {
 
						case TDW_TAB_CARGO:
 
							if (i < _cargo_summary.size()) {
 
								TrainDetailsCargoTab(&_cargo_summary[i], dr.left, dr.right, py);
 
							} else {
 
								DrawString(dr.left, dr.right, py, STR_QUANTITY_N_A, TC_LIGHT_BLUE);
 
							}
 
							break;
 

	
 
						case TDW_TAB_INFO:
 
							if (i == 0) TrainDetailsInfoTab(v, dr.left, dr.right, py);
 
							break;
 

	
 
						case TDW_TAB_CAPACITY:
 
							if (i < _cargo_summary.size()) {
 
								TrainDetailsCapacityTab(&_cargo_summary[i], dr.left, dr.right, py);
 
							} else {
 
								SetDParam(0, STR_EMPTY);
 
								DrawString(dr.left, dr.right, py, STR_VEHICLE_INFO_NO_CAPACITY);
 
							}
 
							break;
 

	
 
						default: NOT_REACHED();
 
					}
 
				}
 
				vscroll_pos--;
 
			}
 
		}
 
	} else {
 
		int y = r.top;
 
		CargoArray act_cargo{};
 
		CargoArray max_cargo{};
 
		Money feeder_share = 0;
 

	
 
		for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
 
			act_cargo[u->cargo_type] += u->cargo.StoredCount();
 
			max_cargo[u->cargo_type] += u->cargo_cap;
 
			feeder_share             += u->cargo.GetFeederShare();
 
		}
 

	
 
		/* draw total cargo tab */
 
		DrawString(r.left, r.right, y + text_y_offset, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT);
 
		y += line_height;
 

	
 
		/* Indent the total cargo capacity details */
 
		Rect ir = r.Indent(WidgetDimensions::scaled.hsep_indent, rtl);
 
		for (const CargoSpec *cs : _sorted_cargo_specs) {
src/vehicle_gui.cpp
Show inline comments
 
@@ -579,97 +579,97 @@ byte GetBestFittingSubType(Vehicle *v_fr
 
}
 

	
 
/** Option to refit a vehicle chain */
 
struct RefitOption {
 
	CargoID cargo;    ///< Cargo to refit to
 
	byte subtype;     ///< Subcargo to use
 
	StringID string;  ///< GRF-local String to display for the cargo
 

	
 
	/**
 
	 * Inequality operator for #RefitOption.
 
	 * @param other Compare to this #RefitOption.
 
	 * @return True if both #RefitOption are different.
 
	 */
 
	inline bool operator != (const RefitOption &other) const
 
	{
 
		return other.cargo != this->cargo || other.string != this->string;
 
	}
 

	
 
	/**
 
	 * Equality operator for #RefitOption.
 
	 * @param other Compare to this #RefitOption.
 
	 * @return True if both #RefitOption are equal.
 
	 */
 
	inline bool operator == (const RefitOption &other) const
 
	{
 
		return other.cargo == this->cargo && other.string == this->string;
 
	}
 
};
 

	
 
using RefitOptions = std::map<CargoID, std::vector<RefitOption>, CargoIDComparator>; ///< Available refit options (subtype and string) associated with each cargo type.
 

	
 
/**
 
 * Draw the list of available refit options for a consist and highlight the selected refit option (if any).
 
 * @param refits Available refit options for each (sorted) cargo.
 
 * @param sel   Selected refit option in the window
 
 * @param pos   Position of the selected item in caller widow
 
 * @param rows  Number of rows(capacity) in caller window
 
 * @param delta Step height in caller window
 
 * @param r     Rectangle of the matrix widget.
 
 */
 
static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
 
{
 
	Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
 
	uint current = 0;
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
 
	uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
 
	int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_4);
 
	int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
 

	
 
	int iconleft   = rtl ? ir.right - iconwidth     : ir.left;
 
	int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
 
	int iconinner  = rtl ? ir.right - iconwidth     : ir.left + iconwidth;
 

	
 
	Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
 

	
 
	/* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
 
	for (const auto &pair : refits) {
 
		bool has_subtypes = pair.second.size() > 1;
 
		for (const RefitOption &refit : pair.second) {
 
			if (current >= pos + rows) break;
 

	
 
			/* Hide subtypes if selected cargo type does not match */
 
			if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
 

	
 
			/* Refit options with a position smaller than pos don't have to be drawn. */
 
			if (current < pos) {
 
				current++;
 
				continue;
 
			}
 

	
 
			if (has_subtypes) {
 
				if (refit.subtype != UINT8_MAX) {
 
					/* Draw tree lines */
 
					int ycenter = tr.top + GetCharacterHeight(FS_NORMAL) / 2;
 
					GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
 
					GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
 
				} else {
 
					/* Draw expand/collapse icon */
 
					DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (GetCharacterHeight(FS_NORMAL) - iconheight) / 2);
 
				}
 
			}
 

	
 
			TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
 
			/* Get the cargo name. */
 
			SetDParam(0, CargoSpec::Get(refit.cargo)->name);
 
			SetDParam(1, refit.string);
 
			DrawString(tr, STR_JUST_STRING_STRING, colour);
 

	
 
			tr.top += delta;
 
			current++;
 
		}
 
	}
 
}
 

	
 
/** Refit cargo window. */
 
struct RefitWindow : public Window {
src/viewport.cpp
Show inline comments
 
@@ -1671,97 +1671,97 @@ static void ViewportDrawBoundingBoxes(co
 
		Point pt2 = RemapCoords(ps->xmin    , ps->ymax + 1, ps->zmax + 1); // top left corner
 
		Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin    , ps->zmax + 1); // top right corner
 
		Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin    ); // bottom front corner
 

	
 
		DrawBox(        pt1.x,         pt1.y,
 
		        pt2.x - pt1.x, pt2.y - pt1.y,
 
		        pt3.x - pt1.x, pt3.y - pt1.y,
 
		        pt4.x - pt1.x, pt4.y - pt1.y);
 
	}
 
}
 

	
 
/**
 
 * Draw/colour the blocks that have been redrawn.
 
 */
 
static void ViewportDrawDirtyBlocks()
 
{
 
	Blitter *blitter = BlitterFactory::GetCurrentBlitter();
 
	const DrawPixelInfo *dpi = _cur_dpi;
 
	void *dst;
 
	int right =  UnScaleByZoom(dpi->width,  dpi->zoom);
 
	int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
 

	
 
	int colour = _string_colourmap[_dirty_block_colour & 0xF];
 

	
 
	dst = dpi->dst_ptr;
 

	
 
	byte bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
 
	do {
 
		for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
 
		dst = blitter->MoveTo(dst, 0, 1);
 
	} while (--bottom > 0);
 
}
 

	
 
static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
 
{
 
	for (const StringSpriteToDraw &ss : *sstdv) {
 
		TextColour colour = TC_BLACK;
 
		bool small = HasBit(ss.width, 15);
 
		int w = GB(ss.width, 0, 15);
 
		int x = UnScaleByZoom(ss.x, zoom);
 
		int y = UnScaleByZoom(ss.y, zoom);
 
		int h = WidgetDimensions::scaled.fullbevel.top + (small ? GetCharacterHeight(FS_SMALL) : GetCharacterHeight(FS_NORMAL)) + WidgetDimensions::scaled.fullbevel.bottom;
 

	
 
		if (ss.colour != INVALID_COLOUR) {
 
			if (IsTransparencySet(TO_SIGNS) && ss.string_id != STR_WHITE_SIGN) {
 
				/* Don't draw the rectangle.
 
				 * Real colours need the TC_IS_PALETTE_COLOUR flag.
 
				 * Otherwise colours from _string_colourmap are assumed. */
 
				colour = (TextColour)GetColourGradient(ss.colour, SHADE_6) | TC_IS_PALETTE_COLOUR;
 
				colour = (TextColour)GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR;
 
			} else {
 
				/* Draw the rectangle if 'transparent station signs' is off,
 
				 * or if we are drawing a general text sign (STR_WHITE_SIGN). */
 
				DrawFrameRect(
 
					x, y, x + w - 1, y + h - 1, ss.colour,
 
					IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
 
				);
 
			}
 
		}
 

	
 
		DrawString(x + WidgetDimensions::scaled.fullbevel.left, x + w - 1 - WidgetDimensions::scaled.fullbevel.right, y + WidgetDimensions::scaled.fullbevel.top, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
 
	}
 
}
 

	
 
void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom)
 
{
 
	_vd.dpi.zoom = vp->zoom;
 
	int mask = ScaleByZoom(-1, vp->zoom);
 

	
 
	_vd.combine_sprites = SPRITE_COMBINE_NONE;
 

	
 
	_vd.dpi.width = (right - left) & mask;
 
	_vd.dpi.height = (bottom - top) & mask;
 
	_vd.dpi.left = left & mask;
 
	_vd.dpi.top = top & mask;
 
	_vd.dpi.pitch = _cur_dpi->pitch;
 
	_vd.last_child = nullptr;
 

	
 
	int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
 
	int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
 

	
 
	_vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_cur_dpi->dst_ptr, x - _cur_dpi->left, y - _cur_dpi->top);
 
	AutoRestoreBackup dpi_backup(_cur_dpi, &_vd.dpi);
 

	
 
	ViewportAddLandscape();
 
	ViewportAddVehicles(&_vd.dpi);
 

	
 
	ViewportAddKdtreeSigns(&_vd.dpi);
 

	
 
	DrawTextEffects(&_vd.dpi);
 

	
 
	if (!_vd.tile_sprites_to_draw.empty()) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
 

	
 
	for (auto &psd : _vd.parent_sprites_to_draw) {
 
		_vd.parent_sprites_to_sort.push_back(&psd);
 
	}
 

	
 
	_vp_sprite_sorter(&_vd.parent_sprites_to_sort);
src/widget.cpp
Show inline comments
 
@@ -237,100 +237,100 @@ static void ScrollbarClickPositioning(Wi
 
 * @param w Window on which a scroll was performed.
 
 * @param nw Pointer to the scrollbar widget.
 
 * @param x The X coordinate of the mouse click.
 
 * @param y The Y coordinate of the mouse click.
 
 */
 
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
 
{
 
	int mi, ma;
 

	
 
	if (nw->type == NWID_HSCROLLBAR) {
 
		mi = nw->pos_x;
 
		ma = nw->pos_x + nw->current_x;
 
	} else {
 
		mi = nw->pos_y;
 
		ma = nw->pos_y + nw->current_y;
 
	}
 
	NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
 
	assert(scrollbar != nullptr);
 
	ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
 
}
 

	
 
/**
 
 * Returns the index for the widget located at the given position
 
 * relative to the window. It includes all widget-corner pixels as well.
 
 * @param *w Window to look inside
 
 * @param  x The Window client X coordinate
 
 * @param  y The Window client y coordinate
 
 * @return A widget index, or -1 if no widget was found.
 
 */
 
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
 
{
 
	NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
 
	return (nw != nullptr) ? nw->index : -1;
 
}
 

	
 
/**
 
 * Draw frame rectangle.
 
 * @param left   Left edge of the frame
 
 * @param top    Top edge of the frame
 
 * @param right  Right edge of the frame
 
 * @param bottom Bottom edge of the frame
 
 * @param colour Colour table to use. @see Colours
 
 * @param flags  Flags controlling how to draw the frame. @see FrameFlags
 
 */
 
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
 
{
 
	assert(colour < COLOUR_END);
 

	
 
	uint dark         = GetColourGradient(colour, SHADE_3);
 
	uint medium_dark  = GetColourGradient(colour, SHADE_5);
 
	uint medium_light = GetColourGradient(colour, SHADE_6);
 
	uint light        = GetColourGradient(colour, SHADE_7);
 
	uint dark         = GetColourGradient(colour, SHADE_DARK);
 
	uint medium_dark  = GetColourGradient(colour, SHADE_LIGHT);
 
	uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
 
	uint light        = GetColourGradient(colour, SHADE_LIGHTEST);
 

	
 
	if (flags & FR_TRANSPARENT) {
 
		GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
 
	} else {
 
		uint interior;
 

	
 
		Rect outer = {left, top, right, bottom};                   // Outside rectangle
 
		Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
 

	
 
		if (flags & FR_LOWERED) {
 
			GfxFillRect(outer.left,      outer.top,        inner.left - 1,  outer.bottom, dark);   // Left
 
			GfxFillRect(inner.left,      outer.top,        outer.right,     inner.top - 1, dark);  // Top
 
			GfxFillRect(inner.right + 1, inner.top,        outer.right,     inner.bottom,  light); // Right
 
			GfxFillRect(inner.left,      inner.bottom + 1, outer.right,     outer.bottom, light);  // Bottom
 
			interior = (flags & FR_DARKENED ? medium_dark : medium_light);
 
		} else {
 
			GfxFillRect(outer.left,      outer.top,        inner.left - 1, inner.bottom,  light); // Left
 
			GfxFillRect(inner.left,      outer.top,        inner.right,    inner.top - 1, light); // Top
 
			GfxFillRect(inner.right + 1, outer.top,        outer.right,    inner.bottom,  dark);  // Right
 
			GfxFillRect(outer.left,      inner.bottom + 1, outer.right,    outer.bottom, dark);   // Bottom
 
			interior = medium_dark;
 
		}
 
		if (!(flags & FR_BORDERONLY)) {
 
			GfxFillRect(inner.left,  inner.top, inner.right, inner.bottom, interior); // Inner
 
		}
 
	}
 
}
 

	
 
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
 
{
 
	Point offset;
 
	Dimension d = GetSpriteSize(img, &offset);
 
	d.width  -= offset.x;
 
	d.height -= offset.y;
 

	
 
	Point p = GetAlignedPosition(r, d, align);
 
	DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
 
}
 

	
 
/**
 
 * Draw an image button.
 
 * @param r       Rectangle of the button.
 
 * @param type    Widget type (#WWT_IMGBTN or #WWT_IMGBTN_2).
 
 * @param colour  Colour of the button.
 
 * @param clicked Button is clicked.
 
 * @param img     Sprite to draw.
 
 * @param align   Alignment of the sprite.
 
 */
 
@@ -378,359 +378,359 @@ static inline void DrawText(const Rect &
 
}
 

	
 
/**
 
 * Draw an inset widget.
 
 * @param r           Rectangle of the background.
 
 * @param colour      Colour of the inset.
 
 * @param text_colour Colour of the text.
 
 * @param str         Text to draw.
 
 * @param align       Alignment of the text.
 
 * @param fs          Font size of the text.
 
 */
 
static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
 
{
 
	DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED);
 
	if (str != STR_NULL) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
 
}
 

	
 
/**
 
 * Draw a matrix widget.
 
 * @param r       Rectangle of the matrix background.
 
 * @param colour  Colour of the background.
 
 * @param clicked Matrix is rendered lowered.
 
 * @param data    Data of the widget, number of rows and columns of the widget.
 
 * @param resize_x Matrix resize unit size.
 
 * @param resize_y Matrix resize unit size.
 
 */
 
static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16_t data, uint resize_x, uint resize_y)
 
{
 
	DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
 

	
 
	int num_columns = GB(data, MAT_COL_START, MAT_COL_BITS);  // Lower 8 bits of the widget data: Number of columns in the matrix.
 
	int column_width; // Width of a single column in the matrix.
 
	if (num_columns == 0) {
 
		column_width = resize_x;
 
		num_columns = r.Width() / column_width;
 
	} else {
 
		column_width = r.Width() / num_columns;
 
	}
 

	
 
	int num_rows = GB(data, MAT_ROW_START, MAT_ROW_BITS); // Upper 8 bits of the widget data: Number of rows in the matrix.
 
	int row_height; // Height of a single row in the matrix.
 
	if (num_rows == 0) {
 
		row_height = resize_y;
 
		num_rows = r.Height() / row_height;
 
	} else {
 
		row_height = r.Height() / num_rows;
 
	}
 

	
 
	int col = GetColourGradient(colour, SHADE_6);
 
	int col = GetColourGradient(colour, SHADE_LIGHTER);
 

	
 
	int x = r.left;
 
	for (int ctr = num_columns; ctr > 1; ctr--) {
 
		x += column_width;
 
		GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
 
	}
 

	
 
	x = r.top;
 
	for (int ctr = num_rows; ctr > 1; ctr--) {
 
		x += row_height;
 
		GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x, r.right - WidgetDimensions::scaled.bevel.right, x + WidgetDimensions::scaled.bevel.top - 1, col);
 
	}
 

	
 
	col = GetColourGradient(colour, SHADE_4);
 
	col = GetColourGradient(colour, SHADE_NORMAL);
 

	
 
	x = r.left - 1;
 
	for (int ctr = num_columns; ctr > 1; ctr--) {
 
		x += column_width;
 
		GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
 
	}
 

	
 
	x = r.top - 1;
 
	for (int ctr = num_rows; ctr > 1; ctr--) {
 
		x += row_height;
 
		GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
 
	}
 
}
 

	
 
/**
 
 * Draw a vertical scrollbar.
 
 * @param r            Rectangle of the scrollbar widget.
 
 * @param colour       Colour of the scrollbar widget.
 
 * @param up_clicked   Up-arrow is clicked.
 
 * @param bar_dragged  Bar is dragged.
 
 * @param down_clicked Down-arrow is clicked.
 
 * @param scrollbar    Scrollbar size, offset, and capacity information.
 
 */
 
static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
 
{
 
	int height = NWidgetScrollbar::GetVerticalDimension().height;
 

	
 
	/* draw up/down buttons */
 
	DrawImageButtons(r.WithHeight(height, false),  NWID_VSCROLLBAR, colour, up_clicked,   SPR_ARROW_UP,   SA_CENTER);
 
	DrawImageButtons(r.WithHeight(height, true),   NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
 

	
 
	int c1 = GetColourGradient(colour, SHADE_3);
 
	int c2 = GetColourGradient(colour, SHADE_7);
 
	int c1 = GetColourGradient(colour, SHADE_DARK);
 
	int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
 

	
 
	/* draw "shaded" background */
 
	GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
 
	GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c1, FILLRECT_CHECKER);
 

	
 
	/* track positions. These fractions are based on original 1x dimensions, but scale better. */
 
	int left  = r.left + r.Width() * 3 / 11; /*  left track is positioned 3/11ths from the left */
 
	int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
 
	const uint8_t bl = WidgetDimensions::scaled.bevel.left;
 
	const uint8_t br = WidgetDimensions::scaled.bevel.right;
 

	
 
	/* draw shaded lines */
 
	GfxFillRect(left - bl,  r.top + height, left       - 1, r.bottom - height, c1);
 
	GfxFillRect(left,       r.top + height, left + br  - 1, r.bottom - height, c2);
 
	GfxFillRect(right - bl, r.top + height, right      - 1, r.bottom - height, c1);
 
	GfxFillRect(right,      r.top + height, right + br - 1, r.bottom - height, c2);
 

	
 
	Point pt = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
 
	DrawFrameRect(r.left, pt.x, r.right, pt.y, colour, bar_dragged ? FR_LOWERED : FR_NONE);
 
}
 

	
 
/**
 
 * Draw a horizontal scrollbar.
 
 * @param r             Rectangle of the scrollbar widget.
 
 * @param colour        Colour of the scrollbar widget.
 
 * @param left_clicked  Left-arrow is clicked.
 
 * @param bar_dragged   Bar is dragged.
 
 * @param right_clicked Right-arrow is clicked.
 
 * @param scrollbar     Scrollbar size, offset, and capacity information.
 
 */
 
static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
 
{
 
	int width = NWidgetScrollbar::GetHorizontalDimension().width;
 

	
 
	DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked,  SPR_ARROW_LEFT,  SA_CENTER);
 
	DrawImageButtons(r.WithWidth(width, true),  NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
 

	
 
	int c1 = GetColourGradient(colour, SHADE_3);
 
	int c2 = GetColourGradient(colour, SHADE_7);
 
	int c1 = GetColourGradient(colour, SHADE_DARK);
 
	int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
 

	
 
	/* draw "shaded" background */
 
	GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
 
	GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c1, FILLRECT_CHECKER);
 

	
 
	/* track positions. These fractions are based on original 1x dimensions, but scale better. */
 
	int top    = r.top + r.Height() * 3 / 11; /*    top track is positioned 3/11ths from the top */
 
	int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
 
	const uint8_t bt = WidgetDimensions::scaled.bevel.top;
 
	const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
 

	
 
	/* draw shaded lines */
 
	GfxFillRect(r.left + width, top - bt,    r.right - width, top         - 1, c1);
 
	GfxFillRect(r.left + width, top,         r.right - width, top + bb    - 1, c2);
 
	GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom      - 1, c1);
 
	GfxFillRect(r.left + width, bottom,      r.right - width, bottom + bb - 1, c2);
 

	
 
	/* draw actual scrollbar */
 
	Point pt = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
 
	DrawFrameRect(pt.x, r.top, pt.y, r.bottom, colour, bar_dragged ? FR_LOWERED : FR_NONE);
 
}
 

	
 
/**
 
 * Draw a frame widget.
 
 * @param r           Rectangle of the frame.
 
 * @param colour      Colour of the frame.
 
 * @param text_colour Colour of the text.
 
 * @param str         Text of the frame.
 
 * @param align       Alignment of the text in the frame.
 
 * @param fs          Font size of the text.
 
 */
 
static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
 
{
 
	int x2 = r.left; // by default the left side is the left side of the widget
 

	
 
	if (str != STR_NULL) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
 

	
 
	int c1 = GetColourGradient(colour, SHADE_3);
 
	int c2 = GetColourGradient(colour, SHADE_7);
 
	int c1 = GetColourGradient(colour, SHADE_DARK);
 
	int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
 

	
 
	/* If the frame has text, adjust the top bar to fit half-way through */
 
	Rect inner = r.Shrink(ScaleGUITrad(1));
 
	if (str != STR_NULL) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
 

	
 
	Rect outer  = inner.Expand(WidgetDimensions::scaled.bevel);
 
	Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
 

	
 
	if (_current_text_dir == TD_LTR) {
 
		/* Line from upper left corner to start of text */
 
		GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top  - 1, c1);
 
		GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
 

	
 
		/* Line from end of text to upper right corner */
 
		GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right,  inner.top  - 1, c1);
 
		GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
 
	} else {
 
		/* Line from upper left corner to start of text */
 
		GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top  - 1, c1);
 
		GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
 

	
 
		/* Line from end of text to upper right corner */
 
		GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right,  inner.top  - 1, c1);
 
		GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
 
	}
 

	
 
	/* Line from upper left corner to bottom left corner */
 
	GfxFillRect(outer.left, inner.top,  inner.left  - 1, inner.bottom,  c1);
 
	GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
 

	
 
	/* Line from upper right corner to bottom right corner */
 
	GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
 
	GfxFillRect(inner.right  + 1, outer.top, outer.right, inner.bottom,  c2);
 

	
 
	/* Line from bottom left corner to bottom right corner */
 
	GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
 
	GfxFillRect(outer.left, inner.bottom  + 1, outer.right, outer.bottom, c2);
 
}
 

	
 
/**
 
 * Draw a shade box.
 
 * @param r       Rectangle of the box.
 
 * @param colour  Colour of the shade box.
 
 * @param clicked Box is lowered.
 
 */
 
static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
 
{
 
	DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
 
}
 

	
 
/**
 
 * Draw a sticky box.
 
 * @param r       Rectangle of the box.
 
 * @param colour  Colour of the sticky box.
 
 * @param clicked Box is lowered.
 
 */
 
static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
 
{
 
	DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
 
}
 

	
 
/**
 
 * Draw a defsize box.
 
 * @param r       Rectangle of the box.
 
 * @param colour  Colour of the defsize box.
 
 * @param clicked Box is lowered.
 
 */
 
static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
 
{
 
	DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
 
}
 

	
 
/**
 
 * Draw a NewGRF debug box.
 
 * @param r       Rectangle of the box.
 
 * @param colour  Colour of the debug box.
 
 * @param clicked Box is lowered.
 
 */
 
static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
 
{
 
	DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
 
}
 

	
 
/**
 
 * Draw a resize box.
 
 * @param r       Rectangle of the box.
 
 * @param colour  Colour of the resize box.
 
 * @param at_left Resize box is at left-side of the window,
 
 * @param clicked Box is lowered.
 
 * @param bevel   Draw bevel iff set.
 
 */
 
static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
 
{
 
	if (bevel) {
 
		DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
 
	} else if (clicked) {
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(colour, SHADE_6));
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(colour, SHADE_LIGHTER));
 
	}
 
	DrawSpriteIgnorePadding(at_left ? SPR_WINDOW_RESIZE_LEFT : SPR_WINDOW_RESIZE_RIGHT, PAL_NONE, r.Shrink(ScaleGUITrad(2)), at_left ? (SA_LEFT | SA_BOTTOM | SA_FORCE) : (SA_RIGHT | SA_BOTTOM | SA_FORCE));
 
}
 

	
 
/**
 
 * Draw a close box.
 
 * @param r      Rectangle of the box.`
 
 * @param colour Colour of the close box.
 
 */
 
static inline void DrawCloseBox(const Rect &r, Colours colour)
 
{
 
	if (colour != COLOUR_WHITE) DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
 
	Point offset;
 
	Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
 
	d.width  -= offset.x;
 
	d.height -= offset.y;
 
	int s = ScaleSpriteTrad(1); /* Offset to account for shadow of SPR_CLOSEBOX */
 
	DrawSprite(SPR_CLOSEBOX, (colour != COLOUR_WHITE ? TC_BLACK : TC_SILVER) | (1U << PALETTE_TEXT_RECOLOUR), CenterBounds(r.left, r.right, d.width - s) - offset.x, CenterBounds(r.top, r.bottom, d.height - s) - offset.y);
 
}
 

	
 
/**
 
 * Draw a caption bar.
 
 * @param r           Rectangle of the bar.
 
 * @param colour      Colour of the window.
 
 * @param owner       'Owner' of the window.
 
 * @param text_colour Colour of the text.
 
 * @param str         Text to draw in the bar.
 
 * @param align       Alignment of the text.
 
 * @param fs          Font size of the text.
 
 */
 
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
 
{
 
	bool company_owned = owner < MAX_COMPANIES;
 

	
 
	DrawFrameRect(r, colour, FR_BORDERONLY);
 
	Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
 
	DrawFrameRect(ir, colour, company_owned ? FR_LOWERED | FR_DARKENED | FR_BORDERONLY : FR_LOWERED | FR_DARKENED);
 

	
 
	if (company_owned) {
 
		GfxFillRect(ir.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(_company_colours[owner], SHADE_4));
 
		GfxFillRect(ir.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(_company_colours[owner], SHADE_NORMAL));
 
	}
 

	
 
	if (str != STR_NULL) {
 
		Dimension d = GetStringBoundingBox(str);
 
		Point p = GetAlignedPosition(r, d, align);
 
		DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
 
	}
 
}
 

	
 
/**
 
 * Draw a button with a dropdown (#WWT_DROPDOWN and #NWID_BUTTON_DROPDOWN).
 
 * @param r                Rectangle containing the widget.
 
 * @param colour           Background colour of the widget.
 
 * @param clicked_button   The button-part is clicked.
 
 * @param clicked_dropdown The drop-down part is clicked.
 
 * @param str              Text of the button.
 
 * @param align            Alignment of the text within the dropdown.
 
 *
 
 * @note Magic constants are also used in #NWidgetLeaf::ButtonHit.
 
 */
 
static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
 
{
 
	int dd_width  = NWidgetLeaf::dropdown_dimension.width;
 

	
 
	if (_current_text_dir == TD_LTR) {
 
		DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
 
		DrawImageButtons(r.WithWidth(dd_width, true), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
 
		if (str != STR_NULL) {
 
			DrawString(r.left + WidgetDimensions::scaled.dropdowntext.left, r.right - dd_width - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
 
		}
 
	} else {
 
		DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
 
		DrawImageButtons(r.WithWidth(dd_width, false), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
 
		if (str != STR_NULL) {
 
			DrawString(r.left + dd_width + WidgetDimensions::scaled.dropdowntext.left, r.right - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
 
		}
 
	}
 
}
 

	
 
/**
 
 * Paint all widgets of a window.
 
 */
 
void Window::DrawWidgets() const
 
{
 
	this->nested_root->Draw(this);
 

	
 
	if (this->flags & WF_WHITE_BORDER) {
 
		DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FR_BORDERONLY);
 
@@ -1861,97 +1861,97 @@ void NWidgetMatrix::AssignSizePosition(S
 
	this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
 
	this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
 

	
 
	/* When resizing, update the scrollbar's count. E.g. with a vertical
 
	 * scrollbar becoming wider or narrower means the amount of rows in
 
	 * the scrollbar becomes respectively smaller or higher. */
 
	this->SetCount(this->count);
 
}
 

	
 
void NWidgetMatrix::FillWidgetLookup(WidgetLookup &widget_lookup)
 
{
 
	if (this->index >= 0) widget_lookup[this->index] = this;
 
	NWidgetContainer::FillWidgetLookup(widget_lookup);
 
}
 

	
 
NWidgetCore *NWidgetMatrix::GetWidgetFromPos(int x, int y)
 
{
 
	/* Falls outside of the matrix widget. */
 
	if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
 

	
 
	int start_x, start_y, base_offs_x, base_offs_y;
 
	this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 

	
 
	int widget_col = (rtl ?
 
				-x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
 
				 x - (int)this->pip_pre  - this->pos_x - base_offs_x
 
			) / this->widget_w;
 

	
 
	int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
 

	
 
	this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
 
	if (this->current_element >= this->count) return nullptr;
 

	
 
	NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
 
	assert(child != nullptr);
 
	child->AssignSizePosition(ST_RESIZE,
 
			this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
 
			this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
 
			child->smallest_x, child->smallest_y, rtl);
 

	
 
	return child->GetWidgetFromPos(x, y);
 
}
 

	
 
/* virtual */ void NWidgetMatrix::Draw(const Window *w)
 
{
 
	/* Fill the background. */
 
	GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_5));
 
	GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
 

	
 
	/* Set up a clipping area for the previews. */
 
	bool rtl = _current_text_dir == TD_RTL;
 
	DrawPixelInfo tmp_dpi;
 
	if (!FillDrawPixelInfo(&tmp_dpi, this->pos_x + (rtl ? this->pip_post : this->pip_pre), this->pos_y + this->pip_pre, this->current_x - this->pip_pre - this->pip_post, this->current_y - this->pip_pre - this->pip_post)) return;
 

	
 
	{
 
		AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
 

	
 
		/* Get the appropriate offsets so we can draw the right widgets. */
 
		NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
 
		assert(child != nullptr);
 
		int start_x, start_y, base_offs_x, base_offs_y;
 
		this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
 

	
 
		int offs_y = base_offs_y;
 
		for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
 
			/* Are we within bounds? */
 
			if (offs_y + child->smallest_y <= 0) continue;
 
			if (offs_y >= (int)this->current_y) break;
 

	
 
			/* We've passed our amount of widgets. */
 
			if (y * this->widgets_x >= this->count) break;
 

	
 
			int offs_x = base_offs_x;
 
			for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
 
				/* Are we within bounds? */
 
				if (offs_x + child->smallest_x <= 0) continue;
 
				if (offs_x >= (int)this->current_x) continue;
 

	
 
				/* Do we have this many widgets? */
 
				this->current_element = y * this->widgets_x + x;
 
				if (this->current_element >= this->count) break;
 

	
 
				child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
 
				child->SetLowered(this->clicked == this->current_element);
 
				child->Draw(w);
 
			}
 
		}
 
	}
 

	
 
	DrawOutline(w, this);
 
}
 

	
 
/**
 
 * Get the different offsets that are influenced by scrolling.
 
 * @param[out] start_x     The start position in columns (index of the left-most column, swapped in RTL).
 
 * @param[out] start_y     The start position in rows.
 
@@ -2126,97 +2126,97 @@ void NWidgetBackground::AssignSizePositi
 

	
 
	if (this->child != nullptr) {
 
		uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
 
		uint width = given_width - this->child->padding.Horizontal();
 
		uint height = given_height - this->child->padding.Vertical();
 
		this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
 
	}
 
}
 

	
 
void NWidgetBackground::FillWidgetLookup(WidgetLookup &widget_lookup)
 
{
 
	if (this->index >= 0) widget_lookup[this->index] = this;
 
	if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
 
}
 

	
 
void NWidgetBackground::Draw(const Window *w)
 
{
 
	if (this->current_x == 0 || this->current_y == 0) return;
 

	
 
	Rect r = this->GetCurrentRect();
 

	
 
	const DrawPixelInfo *dpi = _cur_dpi;
 
	if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
 

	
 
	switch (this->type) {
 
		case WWT_PANEL:
 
			assert(this->widget_data == 0);
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FR_LOWERED : FR_NONE);
 
			break;
 

	
 
		case WWT_FRAME:
 
			if (this->index >= 0) w->SetStringParameters(this->index);
 
			DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
 
			break;
 

	
 
		case WWT_INSET:
 
			if (this->index >= 0) w->SetStringParameters(this->index);
 
			DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
 
			break;
 

	
 
		default:
 
			NOT_REACHED();
 
	}
 

	
 
	if (this->index >= 0) w->DrawWidget(r, this->index);
 
	if (this->child != nullptr) this->child->Draw(w);
 

	
 
	if (this->IsDisabled()) {
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_2), FILLRECT_CHECKER);
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
 
	}
 

	
 
	DrawOutline(w, this);
 
}
 

	
 
NWidgetCore *NWidgetBackground::GetWidgetFromPos(int x, int y)
 
{
 
	NWidgetCore *nwid = nullptr;
 
	if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
 
		if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
 
		if (nwid == nullptr) nwid = this;
 
	}
 
	return nwid;
 
}
 

	
 
NWidgetBase *NWidgetBackground::GetWidgetOfType(WidgetType tp)
 
{
 
	NWidgetBase *nwid = nullptr;
 
	if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
 
	if (nwid == nullptr && this->type == tp) nwid = this;
 
	return nwid;
 
}
 

	
 
NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, 0x0, STR_NULL)
 
{
 
}
 

	
 
void NWidgetViewport::SetupSmallestSize(Window *)
 
{
 
	this->smallest_x = this->min_x;
 
	this->smallest_y = this->min_y;
 
}
 

	
 
void NWidgetViewport::Draw(const Window *w)
 
{
 
	if (this->current_x == 0 || this->current_y == 0) return;
 

	
 
	if (this->disp_flags & ND_NO_TRANSPARENCY) {
 
		TransparencyOptionBits to_backup = _transparency_opt;
 
		_transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
 
		w->DrawViewport();
 
		_transparency_opt = to_backup;
 
	} else {
 
		w->DrawViewport();
 
	}
 

	
 
	/* Optionally shade the viewport. */
 
	if (this->disp_flags & (ND_SHADE_GREY | ND_SHADE_DIMMED)) {
 
@@ -2371,97 +2371,97 @@ NWidgetScrollbar::NWidgetScrollbar(Widge
 
			this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
void NWidgetScrollbar::SetupSmallestSize(Window *)
 
{
 
	this->min_x = 0;
 
	this->min_y = 0;
 

	
 
	switch (this->type) {
 
		case NWID_HSCROLLBAR:
 
			this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
 
			break;
 

	
 
		case NWID_VSCROLLBAR:
 
			this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 

	
 
	this->smallest_x = this->min_x;
 
	this->smallest_y = this->min_y;
 
}
 

	
 
void NWidgetScrollbar::Draw(const Window *w)
 
{
 
	if (this->current_x == 0 || this->current_y == 0) return;
 

	
 
	Rect r = this->GetCurrentRect();
 

	
 
	const DrawPixelInfo *dpi = _cur_dpi;
 
	if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
 

	
 
	bool up_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_UP);
 
	bool down_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_DOWN);
 
	bool middle_lowered = !(this->disp_flags & ND_SCROLLBAR_BTN) && w->mouse_capture_widget == this->index;
 

	
 
	if (this->type == NWID_HSCROLLBAR) {
 
		DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
 
	} else {
 
		DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
 
	}
 

	
 
	if (this->IsDisabled()) {
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_2), FILLRECT_CHECKER);
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
 
	}
 

	
 
	DrawOutline(w, this);
 
}
 

	
 
/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
 
{
 
	vertical_dimension.width   = vertical_dimension.height   = 0;
 
	horizontal_dimension.width = horizontal_dimension.height = 0;
 
}
 

	
 
/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
 
{
 
	if (vertical_dimension.width == 0) {
 
		vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
 
		vertical_dimension.width += WidgetDimensions::scaled.vscrollbar.Horizontal();
 
		vertical_dimension.height += WidgetDimensions::scaled.vscrollbar.Vertical();
 
	}
 
	return vertical_dimension;
 
}
 

	
 
/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
 
{
 
	if (horizontal_dimension.width == 0) {
 
		horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
 
		horizontal_dimension.width += WidgetDimensions::scaled.hscrollbar.Horizontal();
 
		horizontal_dimension.height += WidgetDimensions::scaled.hscrollbar.Vertical();
 
	}
 
	return horizontal_dimension;
 
}
 

	
 
Dimension NWidgetScrollbar::vertical_dimension = {0, 0};
 
Dimension NWidgetScrollbar::horizontal_dimension = {0, 0};
 

	
 
/** Reset the cached dimensions. */
 
/* static */ void NWidgetLeaf::InvalidateDimensionCache()
 
{
 
	shadebox_dimension.width   = shadebox_dimension.height   = 0;
 
	debugbox_dimension.width   = debugbox_dimension.height   = 0;
 
	defsizebox_dimension.width = defsizebox_dimension.height = 0;
 
	stickybox_dimension.width  = stickybox_dimension.height  = 0;
 
	resizebox_dimension.width  = resizebox_dimension.height  = 0;
 
	closebox_dimension.width   = closebox_dimension.height   = 0;
 
	dropdown_dimension.width   = dropdown_dimension.height   = 0;
 
}
 

	
 
Dimension NWidgetLeaf::shadebox_dimension   = {0, 0};
 
Dimension NWidgetLeaf::debugbox_dimension   = {0, 0};
 
@@ -2822,97 +2822,97 @@ void NWidgetLeaf::Draw(const Window *w)
 
			if (this->index >= 0) w->SetStringParameters(this->index);
 
			DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align, this->text_size);
 
			break;
 

	
 
		case WWT_SHADEBOX:
 
			assert(this->widget_data == 0);
 
			DrawShadeBox(r, this->colour, w->IsShaded());
 
			break;
 

	
 
		case WWT_DEBUGBOX:
 
			DrawDebugBox(r, this->colour, clicked);
 
			break;
 

	
 
		case WWT_STICKYBOX:
 
			assert(this->widget_data == 0);
 
			DrawStickyBox(r, this->colour, !!(w->flags & WF_STICKY));
 
			break;
 

	
 
		case WWT_DEFSIZEBOX:
 
			assert(this->widget_data == 0);
 
			DrawDefSizeBox(r, this->colour, clicked);
 
			break;
 

	
 
		case WWT_RESIZEBOX:
 
			DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), !!(w->flags & WF_SIZING), this->widget_data == 0);
 
			break;
 

	
 
		case WWT_CLOSEBOX:
 
			DrawCloseBox(r, this->colour);
 
			break;
 

	
 
		case WWT_DROPDOWN:
 
			if (this->index >= 0) w->SetStringParameters(this->index);
 
			DrawButtonDropdown(r, this->colour, false, clicked, this->widget_data, this->align);
 
			break;
 

	
 
		case NWID_BUTTON_DROPDOWN:
 
		case NWID_PUSHBUTTON_DROPDOWN:
 
			if (this->index >= 0) w->SetStringParameters(this->index);
 
			DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align);
 
			break;
 

	
 
		default:
 
			NOT_REACHED();
 
	}
 
	if (this->index >= 0) w->DrawWidget(r, this->index);
 

	
 
	if (this->IsDisabled()) {
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_2), FILLRECT_CHECKER);
 
		GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
 
	}
 

	
 
	DrawOutline(w, this);
 
}
 

	
 
/**
 
 * For a #NWID_BUTTON_DROPDOWN, test whether \a pt refers to the button or to the drop-down.
 
 * @param pt Point in the widget.
 
 * @return The point refers to the button.
 
 *
 
 * @note The magic constants are also used at #DrawButtonDropdown.
 
 */
 
bool NWidgetLeaf::ButtonHit(const Point &pt)
 
{
 
	if (_current_text_dir == TD_LTR) {
 
		int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
 
		return pt.x < button_width;
 
	} else {
 
		int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
 
		return pt.x >= button_left;
 
	}
 
}
 

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

	
 
/**
 
 * Construct a single nested widget in \a *dest from its parts.
 
 *
 
 * Construct a NWidgetBase object from a #NWidget function, and apply all
 
 * settings that follow it, until encountering a #EndContainer, another
 
 * #NWidget, or the end of the parts array.
 
 *
 
 * @param nwid_begin Pointer to beginning of nested widget parts.
 
 * @param nwid_end Pointer to ending of nested widget parts.
 
 * @param dest  Address of pointer to use for returning the composed widget.
 
 * @param fill_dest Fill the composed widget with child widgets.
 
 * @return Pointer to remaining nested widget parts.
 
 */
 
static const NWidgetPart *MakeNWidget(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end, std::unique_ptr<NWidgetBase> &dest, bool *fill_dest)
 
{
 
	dest = nullptr;
 
	*fill_dest = false;
 

	
 
	while (nwid_begin < nwid_end) {
 
		switch (nwid_begin->type) {
 
			case NWID_SPACER:
 
				if (dest != nullptr) return nwid_begin;
 
				dest = std::make_unique<NWidgetSpacer>(0, 0);
src/widgets/dropdown_type.h
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file dropdown_type.h Types related to the drop down widget. */
 

	
 
#ifndef WIDGETS_DROPDOWN_TYPE_H
 
#define WIDGETS_DROPDOWN_TYPE_H
 

	
 
#include "../window_type.h"
 
#include "../gfx_func.h"
 
#include "../gfx_type.h"
 
#include "../palette_func.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.
 
 */
 
class DropDownListItem {
 
public:
 
	int result; ///< Result value to return to window on selection.
 
	bool masked; ///< Masked and unselectable item.
 
	bool shaded; ///< Shaded item, affects text colour.
 

	
 
	explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
 
	virtual ~DropDownListItem() = default;
 

	
 
	virtual bool Selectable() const { return true; }
 
	virtual uint Height() const { return 0; }
 
	virtual uint Width() const { return 0; }
 

	
 
	virtual void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const
 
	{
 
		if (this->masked) GfxFillRect(full, GetColourGradient(bg_colour, SHADE_5), FILLRECT_CHECKER);
 
		if (this->masked) GfxFillRect(full, GetColourGradient(bg_colour, SHADE_LIGHT), 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 = GetColourGradient(bg_colour, SHADE_3);
 
		uint8_t c2 = GetColourGradient(bg_colour, SHADE_7);
 
		uint8_t c1 = GetColourGradient(bg_colour, SHADE_DARK);
 
		uint8_t c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST);
 

	
 
		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);
src/widgets/slider.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file slider.cpp Implementation of the horizontal slider widget. */
 

	
 
#include "../stdafx.h"
 
#include "../palette_func.h"
 
#include "../window_gui.h"
 
#include "../window_func.h"
 
#include "../strings_func.h"
 
#include "../zoom_func.h"
 
#include "slider_func.h"
 

	
 
#include "../safeguards.h"
 

	
 
static const int SLIDER_WIDTH = 3;
 

	
 
/**
 
 * Draw a slider widget with knob at given value
 
 * @param r Rectangle to draw the widget in
 
 * @param min_value Minimum value of slider
 
 * @param max_value Maximum value of slider
 
 * @param value Value to put the slider at
 
 * @param labels List of positions and labels to draw along the slider.
 
 */
 
void DrawSliderWidget(Rect r, int min_value, int max_value, int value, const std::map<int, StringID> &labels)
 
{
 
	/* Allow space for labels. We assume they are in the small font. */
 
	if (!labels.empty()) r.bottom -= GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.hsep_normal;
 

	
 
	max_value -= min_value;
 

	
 
	/* Draw a wedge indicating low to high value. */
 
	const int ha = (r.bottom - r.top) / 5;
 
	const int sw = ScaleGUITrad(SLIDER_WIDTH);
 
	const int t = WidgetDimensions::scaled.bevel.top; /* Thickness of lines */
 
	int wx1 = r.left  + sw / 2;
 
	int wx2 = r.right - sw / 2;
 
	if (_current_text_dir == TD_RTL) std::swap(wx1, wx2);
 
	const uint shadow = GetColourGradient(COLOUR_GREY, SHADE_3);
 
	const uint fill = GetColourGradient(COLOUR_GREY, SHADE_6);
 
	const uint light = GetColourGradient(COLOUR_GREY, SHADE_7);
 
	const uint shadow = GetColourGradient(COLOUR_GREY, SHADE_DARK);
 
	const uint fill = GetColourGradient(COLOUR_GREY, SHADE_LIGHTER);
 
	const uint light = GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST);
 
	const std::vector<Point> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} };
 
	GfxFillPolygon(wedge, fill);
 
	GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t);
 
	GfxDrawLine(wedge[1].x, wedge[1].y, wedge[2].x, wedge[2].y, _current_text_dir == TD_RTL ? shadow : light, t);
 
	GfxDrawLine(wedge[0].x, wedge[0].y, wedge[1].x, wedge[1].y, shadow, t);
 

	
 
	int x;
 
	for (auto label : labels) {
 
		x = label.first - min_value;
 
		if (_current_text_dir == TD_RTL) x = max_value - x;
 
		x = r.left + (x * (r.right - r.left - sw) / max_value) + sw / 2;
 
		GfxDrawLine(x, r.bottom - ha + 1, x, r.bottom + (label.second == STR_NULL ? 0 : WidgetDimensions::scaled.hsep_normal), shadow, t);
 
		if (label.second != STR_NULL) {
 
			Dimension d = GetStringBoundingBox(label.second, FS_SMALL);
 
			x = Clamp(x - d.width / 2, r.left, r.right - d.width);
 
			DrawString(x, x + d.width, r.bottom + 1 + WidgetDimensions::scaled.hsep_normal, label.second, TC_BLACK, SA_CENTER, false, FS_SMALL);
 
		}
 
	}
 

	
 
	/* Draw a slider handle indicating current value. */
 
	value -= min_value;
 
	if (_current_text_dir == TD_RTL) value = max_value - value;
 
	x = r.left + (value * (r.right - r.left - sw) / max_value);
 
	DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE);
 
}
 

	
 
/**
 
 * Handle click on a slider widget to change the value
 
 * @param r      Rectangle of the widget
 
 * @param pt     Clicked point
 
 * @param value[in,out] Value to modify
 
 * @return       True if the value setting was modified
 
 */
 
bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, int &value)
 
{
 
	max_value -= min_value;
 

	
 
	const int sw = ScaleGUITrad(SLIDER_WIDTH);
 
	int new_value = Clamp((pt.x - r.left - sw / 2) * max_value / (r.right - r.left - sw), 0, max_value);
 
	if (_current_text_dir == TD_RTL) new_value = max_value - new_value;
 
	new_value += min_value;
 

	
 
	if (new_value != value) {
 
		value = new_value;
 
		return true;
 
	}
 

	
 
	return false;
0 comments (0 inline, 0 general)