Merge branch 'master' into jgrpp

# Conflicts:
#	.github/workflows/release-windows.yml
#	src/autoreplace_gui.cpp
#	src/cargotype.cpp
#	src/company_base.h
#	src/company_cmd.cpp
#	src/company_gui.cpp
#	src/currency.h
#	src/date_gui.cpp
#	src/dropdown.cpp
#	src/dropdown_func.h
#	src/dropdown_type.h
#	src/game/game_gui.cpp
#	src/genworld.cpp
#	src/genworld_gui.cpp
#	src/ground_vehicle.hpp
#	src/group_gui.cpp
#	src/house.h
#	src/industry_gui.cpp
#	src/network/network_client.cpp
#	src/network/network_server.cpp
#	src/network/network_type.h
#	src/newgrf_class_func.h
#	src/newgrf_house.cpp
#	src/newgrf_roadstop.h
#	src/openttd.cpp
#	src/order_gui.cpp
#	src/saveload/saveload.cpp
#	src/saveload/saveload.h
#	src/screenshot_gui.cpp
#	src/settings_gui.cpp
#	src/settings_type.h
#	src/slider.cpp
#	src/smallmap_gui.cpp
#	src/station_cmd.cpp
#	src/stdafx.h
#	src/survey.cpp
#	src/tile_map.h
#	src/town_cmd.cpp
#	src/town_gui.cpp
#	src/vehicle.cpp
#	src/vehicle_gui.cpp
#	src/vehicle_gui_base.h
This commit is contained in:
Jonathan G Rennison
2024-05-28 19:48:40 +01:00
173 changed files with 2504 additions and 1971 deletions

View File

@@ -18,7 +18,7 @@
#include "vehicle_func.h"
#include "autoreplace_gui.h"
#include "company_func.h"
#include "widgets/dropdown_func.h"
#include "dropdown_func.h"
#include "tilehighlight_func.h"
#include "vehicle_gui_base.h"
#include "core/geometry_func.hpp"
@@ -29,6 +29,7 @@
#include "gfx_func.h"
#include "tbtr_template_gui_main.h"
#include "newgrf_debug.h"
#include "group_gui.h"
#include "group_gui_list.h"
#include "widgets/group_widget.h"
@@ -116,7 +117,74 @@ static constexpr NWidgetPart _nested_group_widgets[] = {
EndContainer(),
};
void SortGUIGroupList(GUIGroupList &list)
/**
* Add children to GUI group list to build a hierarchical tree.
* @param dst Destination list.
* @param src Source list.
* @param fold Whether to handle group folding/hiding.
* @param parent Current tree parent (set by self with recursion).
* @param indent Current tree indentation level (set by self with recursion).
*/
static void GuiGroupListAddChildren(GUIGroupList &dst, const GUIGroupList &src, bool fold, GroupID parent, int indent)
{
for (const auto &item : src) {
if (item.group->parent != parent) continue;
dst.emplace_back(item.group, indent);
if (fold && item.group->folded) {
/* Test if this group has children at all. If not, the folded flag should be cleared to avoid lingering unfold buttons in the list. */
GroupID groupid = item.group->index;
bool has_children = std::any_of(src.begin(), src.end(), [groupid](const auto &child) { return child.group->parent == groupid; });
Group::Get(item.group->index)->folded = has_children;
} else {
GuiGroupListAddChildren(dst, src, fold, item.group->index, indent + 1);
}
}
}
/**
* Build GUI group list, a sorted hierarchical list of groups for owner and vehicle type.
* @param dst Destination list, owned by the caller.
* @param fold Whether to handle group folding/hiding.
* @param owner Owner of groups.
* @param veh_type Vehicle type of groups.
*/
void BuildGuiGroupList(GUIGroupList &dst, bool fold, Owner owner, VehicleType veh_type)
{
GUIGroupList list;
for (const Group *g : Group::Iterate()) {
if (g->owner == owner && g->vehicle_type == veh_type) {
list.emplace_back(g, 0);
}
}
list.ForceResort();
/* Sort the groups by their name */
std::array<std::pair<const Group *, std::string>, 2> last_group{};
list.Sort([&last_group](const GUIGroupListItem &a, const GUIGroupListItem &b) -> bool {
if (a.group != last_group[0].first) {
SetDParam(0, a.group->index);
last_group[0] = {a.group, GetString(STR_GROUP_NAME)};
}
if (b.group != last_group[1].first) {
SetDParam(0, b.group->index);
last_group[1] = {b.group, GetString(STR_GROUP_NAME)};
}
int r = StrNaturalCompare(last_group[0].second, last_group[1].second); // Sort by name (natural sorting).
if (r == 0) return a.group->index < b.group->index;
return r < 0;
});
GuiGroupListAddChildren(dst, list, fold, INVALID_GROUP, 0);
}
void SortGUIGroupOnlyList(GUIGroupOnlyList &list)
{
/* Sort the groups by their name */
const Group *last_group[2] = { nullptr, nullptr };
@@ -162,30 +230,12 @@ private:
uint tiny_step_height; ///< Step height for the group list
Scrollbar *group_sb;
std::vector<int> indents; ///< Indentation levels
Dimension column_size[VGC_END]; ///< Size of the columns in the group list.
Money money_this_year;
Money money_last_year;
uint32_t occupancy_ratio;
void AddChildren(GUIGroupList &source, GroupID parent, int indent)
{
for (const Group *g : source) {
if (g->parent != parent) continue;
this->groups.push_back(g);
this->indents.push_back(indent);
if (g->folded) {
/* Test if this group has children at all. If not, the folded flag should be cleared to avoid lingering unfold buttons in the list. */
bool has_children = std::any_of(source.begin(), source.end(), [g](const Group *child){ return child->parent == g->index; });
Group::Get(g->index)->folded = has_children;
} else {
AddChildren(source, g->index, indent + 1);
}
}
}
/**
* (Re)Build the group list.
*
@@ -196,22 +246,16 @@ private:
if (!this->groups.NeedRebuild()) return;
this->groups.clear();
this->indents.clear();
GUIGroupList list;
bool enable_expand_all = false;
bool enable_collapse_all = false;
for (const Group *g : Group::Iterate()) {
if (g->owner == owner && g->vehicle_type == this->vli.vtype) {
list.push_back(g);
if (g->parent != INVALID_GROUP) {
if (Group::Get(g->parent)->folded) {
enable_expand_all = true;
} else {
enable_collapse_all = true;
}
if (g->owner == owner && g->vehicle_type == this->vli.vtype && g->parent != INVALID_GROUP) {
if (Group::Get(g->parent)->folded) {
enable_expand_all = true;
} else {
enable_collapse_all = true;
}
}
}
@@ -219,10 +263,7 @@ private:
this->SetWidgetDisabledState(WID_GL_EXPAND_ALL_GROUPS, !enable_expand_all);
this->SetWidgetDisabledState(WID_GL_COLLAPSE_ALL_GROUPS, !enable_collapse_all);
list.ForceResort();
SortGUIGroupList(list);
AddChildren(list, INVALID_GROUP, 0);
BuildGuiGroupList(this->groups, true, owner, this->vli.vtype);
this->groups.shrink_to_fit();
this->groups.RebuildDone();
@@ -658,14 +699,14 @@ public:
}
case WID_GL_LIST_GROUP: {
int y1 = r.top + WidgetDimensions::scaled.framerect.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];
int y1 = r.top;
auto [first, last] = this->group_sb->GetVisibleRangeIterators(this->groups);
for (auto it = first; it != last; ++it) {
const Group *g = it->group;
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]));
DrawGroupInfo(y1, r.left, r.right, g->index, it->indent * WidgetDimensions::scaled.hsep_indent, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (std::next(it) != std::end(this->groups) && std::next(it)->indent > it->indent));
y1 += this->tiny_step_height;
}
@@ -748,27 +789,26 @@ public:
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
if (it == this->groups.end()) return;
size_t id_g = it - this->groups.begin();
if ((*it)->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) {
if (it->group->folded || (std::next(it) != std::end(this->groups) && std::next(it)->indent > it->indent)) {
/* The group has children, check if the user clicked the fold / unfold button. */
NWidgetCore *group_display = this->GetWidget<NWidgetCore>(widget);
int x = _current_text_dir == TD_RTL ?
group_display->pos_x + group_display->current_x - WidgetDimensions::scaled.framerect.right - this->indents[id_g] * WidgetDimensions::scaled.hsep_indent - this->column_size[VGC_FOLD].width :
group_display->pos_x + WidgetDimensions::scaled.framerect.left + this->indents[id_g] * WidgetDimensions::scaled.hsep_indent;
group_display->pos_x + group_display->current_x - WidgetDimensions::scaled.framerect.right - it->indent * WidgetDimensions::scaled.hsep_indent - this->column_size[VGC_FOLD].width :
group_display->pos_x + WidgetDimensions::scaled.framerect.left + it->indent * WidgetDimensions::scaled.hsep_indent;
if (click_count > 1 || (pt.x >= x && pt.x < (int)(x + this->column_size[VGC_FOLD].width))) {
GroupID g = this->vli.index;
if (!IsAllGroupID(g) && !IsDefaultGroupID(g)) {
do {
g = Group::Get(g)->parent;
if (g == groups[id_g]->index) {
if (g == it->group->index) {
this->vli.index = g;
break;
}
} while (g != INVALID_GROUP);
}
Group::Get(groups[id_g]->index)->folded = !groups[id_g]->folded;
Group::Get(it->group->index)->folded = !it->group->folded;
this->groups.ForceRebuild();
this->SetDirty();
@@ -776,7 +816,7 @@ public:
}
}
this->group_sel = this->vli.index = this->groups[id_g]->index;
this->group_sel = this->vli.index = it->group->index;
SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
@@ -923,7 +963,7 @@ public:
case WID_GL_LIST_GROUP: { // Matrix group
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = it == this->groups.end() ? INVALID_GROUP : (*it)->index;
GroupID new_g = it == this->groups.end() ? INVALID_GROUP : it->group->index;
if (this->group_sel != new_g && g->parent != new_g) {
DoCommandP(0, this->group_sel | (1 << 16), new_g, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_SET_PARENT));
@@ -956,7 +996,7 @@ public:
this->SetDirty();
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = it == this->groups.end() ? NEW_GROUP : (*it)->index;
GroupID new_g = it == this->groups.end() ? NEW_GROUP : it->group->index;
DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr);
break;
@@ -1162,7 +1202,7 @@ public:
case WID_GL_LIST_GROUP: { // ... the list of custom groups.
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
new_group_over = it == this->groups.end() ? NEW_GROUP : (*it)->index;
new_group_over = it == this->groups.end() ? NEW_GROUP : it->group->index;
break;
}
@@ -1224,18 +1264,19 @@ public:
this->vli.index = g_id;
if (g_id != ALL_GROUP && g_id != DEFAULT_GROUP) {
const Group *g = Group::Get(g_id);
int id_g = find_index(this->groups, g);
// The group's branch is maybe collapsed, so try to expand it
if (id_g == -1) {
auto found = std::find_if(std::begin(this->groups), std::end(this->groups), [g](const auto &item) { return item.group == g; });
if (found == std::end(this->groups)) {
/* The group's branch is maybe collapsed, so try to expand it. */
for (auto pg = Group::GetIfValid(g->parent); pg != nullptr; pg = Group::GetIfValid(pg->parent)) {
pg->folded = false;
}
this->groups.ForceRebuild();
this->BuildGroupList(this->owner);
this->group_sb->SetCount(this->groups.size());
id_g = find_index(this->groups, g);
found = std::find_if(std::begin(this->groups), std::end(this->groups), [g](const auto &item) { return item.group == g; });
}
this->group_sb->ScrollTowards(id_g);
if (found != std::end(this->groups)) this->group_sb->ScrollTowards(std::distance(std::begin(this->groups), found));
}
this->vehgroups.ForceRebuild();
this->SetDirty();
@@ -1265,7 +1306,7 @@ static WindowDesc _train_group_desc(__FILE__, __LINE__,
* @param group The group to be selected. Defaults to INVALID_GROUP.
* @param need_existing_window Whether the existing window is needed. Defaults to false.
*/
void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type, GroupID group = INVALID_GROUP, bool need_existing_window = false)
void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type, GroupID group, bool need_existing_window)
{
if (!Company::IsValidID(company)) return;