mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-09 23:50:25 +00:00
Fix: Draw continuation lines for engine variant hierarchy tree. (#12434)
This commit is contained in:
parent
de4e00c93f
commit
dc7c2bb30d
@ -34,8 +34,6 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group);
|
||||
|
||||
static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
return Engine::Get(a.engine_id)->list_position < Engine::Get(b.engine_id)->list_position;
|
||||
@ -114,27 +112,6 @@ class ReplaceVehicleWindow : public Window {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddChildren(const GUIEngineList &source, GUIEngineList &target, EngineID parent, int indent, int side)
|
||||
{
|
||||
for (const auto &item : source) {
|
||||
if (item.variant_id != parent || item.engine_id == parent) continue;
|
||||
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
EngineDisplayFlags flags = item.flags;
|
||||
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
|
||||
target.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
target.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, target, item.engine_id, indent + 1, side);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an engines list
|
||||
* @param draw_left true if generating the left list, otherwise false
|
||||
@ -208,7 +185,7 @@ class ReplaceVehicleWindow : public Window {
|
||||
|
||||
this->engines[side].clear();
|
||||
if (side == 1) {
|
||||
AddChildren(list, this->engines[side], INVALID_ENGINE, 0, side);
|
||||
GUIEngineListAddChildren(this->engines[side], list);
|
||||
} else {
|
||||
this->engines[side].swap(list);
|
||||
}
|
||||
|
@ -1045,6 +1045,9 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
int small_text_y_offset = ir.Height() - GetCharacterHeight(FS_SMALL);
|
||||
int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
|
||||
|
||||
const int offset = (rtl ? -circle_width : circle_width) / 2;
|
||||
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
|
||||
|
||||
int y = ir.top;
|
||||
for (auto it = first; it != last; ++it) {
|
||||
const auto &item = *it;
|
||||
@ -1052,6 +1055,21 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
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;
|
||||
|
||||
if (item.indent > 0) {
|
||||
/* Draw tree continuation lines. */
|
||||
int tx = (rtl ? ir.right : ir.left) + offset;
|
||||
int ty = y - WidgetDimensions::scaled.matrix.top;
|
||||
for (uint lvl = 1; lvl <= item.indent; ++lvl) {
|
||||
if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ty, tx, ty + step_size - 1, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
if (lvl < item.indent) tx += level_width;
|
||||
}
|
||||
/* Draw our node in the tree. */
|
||||
int ycentre = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2 - 1;
|
||||
if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ty, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -1079,14 +1097,6 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
|
||||
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, SA_CENTER);
|
||||
}
|
||||
if (indent > 0) {
|
||||
/* Draw tree lines */
|
||||
Rect fr = ir.Indent(indent - WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(circle_width, rtl);
|
||||
int ycenter = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2;
|
||||
bool continues = std::next(it) != std::end(eng_list) && std::next(it)->indent == item.indent;
|
||||
GfxDrawLine(fr.left + circle_width / 2, y - WidgetDimensions::scaled.matrix.top, fr.left + circle_width / 2, continues ? y - WidgetDimensions::scaled.matrix.top + step_size - 1 : ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
GfxDrawLine(fr.left + circle_width / 2, ycenter, fr.right, ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
}
|
||||
y += step_size;
|
||||
}
|
||||
}
|
||||
@ -1114,6 +1124,44 @@ void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selecte
|
||||
ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add children to GUI engine list to build a hierarchical tree.
|
||||
* @param dst Destination list.
|
||||
* @param src Source list.
|
||||
* @param parent Current tree parent (set by self with recursion).
|
||||
* @param indent Current tree indentation level (set by self with recursion).
|
||||
*/
|
||||
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
|
||||
{
|
||||
for (const auto &item : src) {
|
||||
if (item.variant_id != parent || item.engine_id == parent) continue;
|
||||
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
EngineDisplayFlags flags = item.flags;
|
||||
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
|
||||
dst.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (indent > 0 || dst.empty()) return;
|
||||
|
||||
/* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
|
||||
uint16_t level_mask = 0;
|
||||
for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
|
||||
auto next_it = std::next(it);
|
||||
SB(level_mask, it->indent, 1, it->indent <= next_it->indent);
|
||||
next_it->level_mask = level_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/** Enum referring to the Hotkeys in the build vehicle window */
|
||||
enum BuildVehicleHotkeys {
|
||||
BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
@ -1157,27 +1205,6 @@ struct BuildVehicleWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
void AddChildren(const GUIEngineList &source, EngineID parent, int indent)
|
||||
{
|
||||
for (const auto &item : source) {
|
||||
if (item.variant_id != parent || item.engine_id == parent) continue;
|
||||
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
EngineDisplayFlags flags = item.flags;
|
||||
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
|
||||
this->eng_list.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
this->eng_list.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, item.engine_id, indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
|
||||
{
|
||||
this->vehicle_type = type;
|
||||
@ -1518,7 +1545,7 @@ struct BuildVehicleWindow : Window {
|
||||
default: NOT_REACHED();
|
||||
case VEH_TRAIN:
|
||||
this->GenerateBuildTrainList(list);
|
||||
AddChildren(list, INVALID_ENGINE, 0);
|
||||
GUIEngineListAddChildren(this->eng_list, list);
|
||||
this->eng_list.shrink_to_fit();
|
||||
this->eng_list.RebuildDone();
|
||||
return;
|
||||
@ -1556,7 +1583,7 @@ struct BuildVehicleWindow : Window {
|
||||
EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
|
||||
|
||||
this->eng_list.swap(list);
|
||||
AddChildren(list, INVALID_ENGINE, 0);
|
||||
GUIEngineListAddChildren(this->eng_list, list, INVALID_ENGINE, 0);
|
||||
this->eng_list.shrink_to_fit();
|
||||
this->eng_list.RebuildDone();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define ENGINE_GUI_H
|
||||
|
||||
#include "engine_type.h"
|
||||
#include "group_type.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "gfx_type.h"
|
||||
#include "vehicle_type.h"
|
||||
@ -20,9 +21,10 @@ struct GUIEngineListItem {
|
||||
EngineID engine_id; ///< Engine to display in build purchase list
|
||||
EngineID variant_id; ///< Variant group of the engine.
|
||||
EngineDisplayFlags flags; ///< Flags for toggling/drawing (un)folded status and controlling indentation.
|
||||
int8_t indent; ///< Display indentation level.
|
||||
uint8_t indent; ///< Display indentation level.
|
||||
uint16_t level_mask; ///< Mask of level continuations.
|
||||
|
||||
GUIEngineListItem(EngineID engine_id, EngineID variant_id, EngineDisplayFlags flags, int indent) : engine_id(engine_id), variant_id(variant_id), flags(flags), indent(indent) {}
|
||||
GUIEngineListItem(EngineID engine_id, EngineID variant_id, EngineDisplayFlags flags, uint8_t indent) : engine_id(engine_id), variant_id(variant_id), flags(flags), indent(indent), level_mask(0) {}
|
||||
|
||||
/* Used when searching list only by engine_id. */
|
||||
bool operator == (const EngineID &other) const { return this->engine_id == other; }
|
||||
@ -50,7 +52,10 @@ extern bool _engine_sort_show_hidden_engines[];
|
||||
extern const StringID _engine_sort_listing[][12];
|
||||
extern EngList_SortTypeFunction * const _engine_sort_functions[][11];
|
||||
|
||||
/* Functions in build_vehicle_gui.cpp */
|
||||
uint GetEngineListHeight(VehicleType type);
|
||||
void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button);
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group);
|
||||
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent = INVALID_ENGINE, uint8_t indent = 0);
|
||||
|
||||
#endif /* ENGINE_GUI_H */
|
||||
|
Loading…
Reference in New Issue
Block a user