diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index fc6b0d53a2..2c8186f294 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -125,6 +125,10 @@ class ReplaceVehicleWindow : public Window { /* 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); } } @@ -617,7 +621,22 @@ public: uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget); size_t engine_count = this->engines[click_side].size(); - EngineID e = engine_count > i ? this->engines[click_side][i].engine_id : INVALID_ENGINE; + EngineID e = INVALID_ENGINE; + if (i < engine_count) { + const auto &item = this->engines[click_side][i]; + const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); + if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) { + /* toggle folded flag on engine */ + assert(item.variant_id != INVALID_ENGINE); + Engine *engine = Engine::Get(item.variant_id); + engine->display_flags ^= EngineDisplayFlags::IsFolded; + + InvalidateWindowData(WC_REPLACE_VEHICLE, (VehicleType)this->window_number, 0); // Update the autoreplace window + InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well + return; + } + if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id; + } /* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing. * This is most common when we have finished autoreplacing the engine and want to remove it from the list. */ diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 26deec82e5..e9b7e90c34 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -974,9 +974,10 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li 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 = _colour_gradient[COLOUR_ORANGE][4]; Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix); - int sprite_x = ir.WithWidth(sprite_width, rtl).left + sprite_left; int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2; Dimension replace_icon = {0, 0}; @@ -987,7 +988,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li count_width = GetStringBoundingBox(STR_TINY_BLACK_COMA).width; } - Rect tr = ir.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position + 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); @@ -998,22 +999,40 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li int y = ir.top; for (; min < max; min++, y += step_size) { - const EngineID engine = eng_list[min].engine_id; + 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, engine); + const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id); - const Engine *e = Engine::Get(engine); + 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 = (engine == selected_id) ? TC_WHITE : (TC_NO_SHADE | (hidden ? TC_GREY : TC_BLACK)); + TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : (TC_NO_SHADE | ((hidden | shaded) ? TC_GREY : TC_BLACK)); - SetDParam(0, engine); - DrawString(tr.left, tr.right, y + normal_text_y_offset, str, tc); - DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE); + SetDParam(0, item.engine_id); + Rect itr = tr.Indent(indent, rtl); + DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc); + int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left; + DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE); if (show_count) { SetDParam(0, num_engines); DrawString(cr.left, cr.right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE); - if (EngineHasReplacementForCompany(Company::Get(_local_company), engine, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset); + if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset); + } + if (has_variants) { + 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}, false, 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 + FONT_HEIGHT_NORMAL / 2; + bool continues = (min + 1U) < eng_list.size() && eng_list[min + 1].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); } } } @@ -1090,6 +1109,10 @@ struct BuildVehicleWindow : Window { /* 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); } } @@ -1487,7 +1510,23 @@ struct BuildVehicleWindow : Window { case WID_BV_LIST: { uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST); size_t num_items = this->eng_list.size(); - this->SelectEngine((i < num_items) ? this->eng_list[i].engine_id : INVALID_ENGINE); + EngineID e = INVALID_ENGINE; + if (i < num_items) { + const auto &item = this->eng_list[i]; + const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); + if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) { + /* toggle folded flag on engine */ + assert(item.variant_id != INVALID_ENGINE); + Engine *engine = Engine::Get(item.variant_id); + engine->display_flags ^= EngineDisplayFlags::IsFolded; + + InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window + InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well + return; + } + if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id; + } + this->SelectEngine(e); this->SetDirty(); if (_ctrl_pressed) { this->OnClick(pt, WID_BV_SHOW_HIDE, 1); @@ -1523,6 +1562,20 @@ struct BuildVehicleWindow : Window { } else { Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); } + + /* Update last used variant and refresh if necessary. */ + bool refresh = false; + int recursion = 10; /* In case of infinite loop */ + for (Engine *e = Engine::Get(sel_eng); recursion > 0; e = Engine::Get(e->info.variant_id), --recursion) { + refresh |= (e->display_last_variant != sel_eng); + e->display_last_variant = sel_eng; + if (e->info.variant_id == INVALID_ENGINE) break; + } + if (refresh) { + InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window + InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well + return; + } } break; } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 16eb7696bb..71f7abbd25 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -9001,7 +9001,7 @@ static void FinaliseEngineArray() /* Set appropriate flags on variant engine */ if (e->info.variant_id != INVALID_ENGINE) { - Engine::Get(e->info.variant_id)->display_flags |= EngineDisplayFlags::HasVariants; + Engine::Get(e->info.variant_id)->display_flags |= EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded; } /* Skip wagons, there livery is defined via the engine */