1
0
mirror of https://github.com/OpenTTD/OpenTTD.git synced 2025-08-22 14:09:10 +00:00

Compare commits

..

6 Commits

Author SHA1 Message Date
07a8bd21e4 Fix: Make station list filters use per-window state instead of global state.
Changing filters with multiple windows open would have unexpected effects leading to inconsistent state.

Now state is loaded and saved when the window is opened and closed, so state is still persistent.
2023-12-04 08:24:39 +00:00
dcf730f1f6 Codechange: Optionally allow passing state to GUIList sorter function.
GUIList sorter functions can currently only use global state, which makes per-window-instance sorting difficult.
2023-12-04 08:24:39 +00:00
4d9f335f36 Change: Add outline of sprite to sprite aligner. 2023-12-04 08:14:02 +00:00
f7380a4d2d Change: Make NewGRF Sprite Aligner window resizeable. 2023-12-04 08:14:02 +00:00
0434c1b474 Feature: (-tte) Add zoom level buttons to sprite aligner.
This allows for offsets to be adjusted a different zoom level than currently in use, and offset adjustment and display is also more convenient.
2023-12-04 08:14:02 +00:00
4e5700939d Change: Use sparse padding for sprite aligner. 2023-12-04 08:14:02 +00:00
15 changed files with 189 additions and 119 deletions

View File

@@ -28,7 +28,7 @@ struct GUIEngineListItem {
bool operator == (const EngineID &other) const { return this->engine_id == other; }
};
typedef GUIList<GUIEngineListItem, CargoID> GUIEngineList;
typedef GUIList<GUIEngineListItem, std::nullptr_t, CargoID> GUIEngineList;
typedef bool EngList_SortTypeFunction(const GUIEngineListItem&, const GUIEngineListItem&); ///< argument type for #EngList_Sort.
void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare);

View File

@@ -1245,7 +1245,7 @@ static const NWidgetPart _nested_industry_directory_widgets[] = {
EndContainer(),
};
typedef GUIList<const Industry *, const std::pair<CargoID, CargoID> &> GUIIndustryList;
typedef GUIList<const Industry *, const CargoID &, const std::pair<CargoID, CargoID> &> GUIIndustryList;
/** Special cargo filter criteria */
enum CargoFilterSpecialType {
@@ -1315,7 +1315,7 @@ protected:
static const StringID sorter_names[];
static GUIIndustryList::SortFunction * const sorter_funcs[];
GUIIndustryList industries;
GUIIndustryList industries{IndustryDirectoryWindow::produced_cargo_filter};
Scrollbar *vscroll;
Scrollbar *hscroll;
@@ -1484,7 +1484,7 @@ protected:
}
/** Sort industries by name */
static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b)
static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b, const CargoID &)
{
int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
if (r == 0) return a->index < b->index;
@@ -1492,21 +1492,20 @@ protected:
}
/** Sort industries by type and name */
static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b)
static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
{
int it_a = 0;
while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
int it_b = 0;
while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
int r = it_a - it_b;
return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
}
/** Sort industries by production and name */
static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b)
static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
{
CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
if (filter == CF_NONE) return IndustryTypeSorter(a, b);
if (filter == CF_NONE) return IndustryTypeSorter(a, b, filter);
uint prod_a = 0, prod_b = 0;
if (filter == CF_ANY) {
@@ -1522,14 +1521,14 @@ protected:
}
int r = prod_a - prod_b;
return (r == 0) ? IndustryTypeSorter(a, b) : r < 0;
return (r == 0) ? IndustryTypeSorter(a, b, filter) : r < 0;
}
/** Sort industries by transported cargo and name */
static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b)
static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
{
int r = GetCargoTransportedSortValue(a) - GetCargoTransportedSortValue(b);
return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
}
/**

View File

@@ -326,7 +326,7 @@ enum ContentListFilterCriteria {
/** Window that lists the content that's at the content server */
class NetworkContentListWindow : public Window, ContentCallback {
/** List with content infos. */
typedef GUIList<const ContentInfo *, ContentListFilterData &> GUIContentList;
typedef GUIList<const ContentInfo *, std::nullptr_t, ContentListFilterData &> GUIContentList;
static const uint EDITBOX_MAX_SIZE = 50; ///< Maximum size of the editbox in characters.

View File

@@ -79,7 +79,7 @@ static DropDownList BuildVisibilityDropDownList()
return list;
}
typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList;
typedef GUIList<NetworkGameList*, std::nullptr_t, StringFilter&> GUIGameServerList;
typedef int ServerListPosition;
static const ServerListPosition SLP_INVALID = -1;

View File

@@ -819,11 +819,15 @@ struct SpriteAlignerWindow : Window {
Scrollbar *vscroll;
std::map<SpriteID, XyOffs> offs_start_map; ///< Mapping of starting offsets for the sprites which have been aligned in the sprite aligner window.
static inline ZoomLevel zoom = ZOOM_LVL_END;
static bool centre;
static bool crosshair;
SpriteAlignerWindow(WindowDesc *desc, WindowNumber wno) : Window(desc)
{
/* On first opening, set initial zoom to current zoom level. */
if (SpriteAlignerWindow::zoom == ZOOM_LVL_END) SpriteAlignerWindow::zoom = _gui_zoom;
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_SA_SCROLLBAR);
this->vscroll->SetCount(_newgrf_debug_sprite_picker.sprites.size());
@@ -834,6 +838,8 @@ struct SpriteAlignerWindow : Window {
/* Oh yes, we assume there is at least one normal sprite! */
while (GetSpriteType(this->current_sprite) != SpriteType::Normal) this->current_sprite++;
this->InvalidateData(0, true);
}
void SetStringParameters(int widget) const override
@@ -846,8 +852,8 @@ struct SpriteAlignerWindow : Window {
break;
case WID_SA_OFFSETS_ABS:
SetDParam(0, spr->x_offs);
SetDParam(1, spr->y_offs);
SetDParam(0, UnScaleByZoom(spr->x_offs, SpriteAlignerWindow::zoom));
SetDParam(1, UnScaleByZoom(spr->y_offs, SpriteAlignerWindow::zoom));
break;
case WID_SA_OFFSETS_REL: {
@@ -856,8 +862,8 @@ struct SpriteAlignerWindow : Window {
*/
const auto key_offs_pair = this->offs_start_map.find(this->current_sprite);
if (key_offs_pair != this->offs_start_map.end()) {
SetDParam(0, spr->x_offs - key_offs_pair->second.first);
SetDParam(1, spr->y_offs - key_offs_pair->second.second);
SetDParam(0, UnScaleByZoom(spr->x_offs - key_offs_pair->second.first, SpriteAlignerWindow::zoom));
SetDParam(1, UnScaleByZoom(spr->y_offs - key_offs_pair->second.second, SpriteAlignerWindow::zoom));
} else {
SetDParam(0, 0);
SetDParam(1, 0);
@@ -898,8 +904,8 @@ struct SpriteAlignerWindow : Window {
int x;
int y;
if (SpriteAlignerWindow::centre) {
x = -UnScaleGUI(spr->x_offs) + (ir.Width() - UnScaleGUI(spr->width)) / 2;
y = -UnScaleGUI(spr->y_offs) + (ir.Height() - UnScaleGUI(spr->height)) / 2;
x = -UnScaleByZoom(spr->x_offs, SpriteAlignerWindow::zoom) + (ir.Width() - UnScaleByZoom(spr->width, SpriteAlignerWindow::zoom)) / 2;
y = -UnScaleByZoom(spr->y_offs, SpriteAlignerWindow::zoom) + (ir.Height() - UnScaleByZoom(spr->height, SpriteAlignerWindow::zoom)) / 2;
} else {
x = ir.Width() / 2;
y = ir.Height() / 2;
@@ -909,8 +915,13 @@ struct SpriteAlignerWindow : Window {
if (!FillDrawPixelInfo(&new_dpi, ir.left, ir.top, ir.Width(), ir.Height())) break;
AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
DrawSprite(this->current_sprite, PAL_NONE, x, y, nullptr, ZOOM_LVL_GUI);
if (this->crosshair) {
DrawSprite(this->current_sprite, PAL_NONE, x, y, nullptr, SpriteAlignerWindow::zoom);
Rect outline = {0, 0, UnScaleByZoom(spr->width, SpriteAlignerWindow::zoom) - 1, UnScaleByZoom(spr->height, SpriteAlignerWindow::zoom) - 1};
outline = outline.Translate(x + UnScaleByZoom(spr->x_offs, SpriteAlignerWindow::zoom),y + UnScaleByZoom(spr->y_offs, SpriteAlignerWindow::zoom));
DrawRectOutline(outline.Expand(1), PC_LIGHT_BLUE, 1, 1);
if (SpriteAlignerWindow::crosshair) {
GfxDrawLine(x, 0, x, ir.Height() - 1, PC_WHITE, 1, 1);
GfxDrawLine(0, y, ir.Width() - 1, y, PC_WHITE, 1, 1);
}
@@ -927,7 +938,7 @@ struct SpriteAlignerWindow : Window {
Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
for (int i = this->vscroll->GetPosition(); i < max; i++) {
SetDParam(0, list[i]);
DrawString(ir, STR_JUST_COMMA, TC_BLACK, SA_RIGHT | SA_FORCE);
DrawString(ir, STR_JUST_COMMA, list[i] == this->current_sprite ? TC_WHITE : TC_BLACK, SA_RIGHT | SA_FORCE);
ir.top += step_size;
}
break;
@@ -995,12 +1006,13 @@ struct SpriteAlignerWindow : Window {
if (this->offs_start_map.count(this->current_sprite) == 0) {
this->offs_start_map[this->current_sprite] = XyOffs(spr->x_offs, spr->y_offs);
}
int amt = ScaleByZoom(_ctrl_pressed ? 8 : 1, SpriteAlignerWindow::zoom);
switch (widget) {
/* Move eight units at a time if ctrl is pressed. */
case WID_SA_UP: spr->y_offs -= _ctrl_pressed ? 8 : 1; break;
case WID_SA_DOWN: spr->y_offs += _ctrl_pressed ? 8 : 1; break;
case WID_SA_LEFT: spr->x_offs -= _ctrl_pressed ? 8 : 1; break;
case WID_SA_RIGHT: spr->x_offs += _ctrl_pressed ? 8 : 1; break;
case WID_SA_UP: spr->y_offs -= amt; break;
case WID_SA_DOWN: spr->y_offs += amt; break;
case WID_SA_LEFT: spr->x_offs -= amt; break;
case WID_SA_RIGHT: spr->x_offs += amt; break;
}
/* Of course, we need to redraw the sprite, but where is it used?
* Everywhere is a safe bet. */
@@ -1025,6 +1037,13 @@ struct SpriteAlignerWindow : Window {
this->SetWidgetLoweredState(widget, SpriteAlignerWindow::crosshair);
this->SetDirty();
break;
default:
if (IsInsideBS(widget, WID_SA_ZOOM, ZOOM_LVL_END)) {
SpriteAlignerWindow::zoom = ZoomLevel(widget - WID_SA_ZOOM);
this->InvalidateData(0, true);
}
break;
}
}
@@ -1053,6 +1072,10 @@ struct SpriteAlignerWindow : Window {
this->RaiseWidget(WID_SA_PICKER);
this->vscroll->SetCount(_newgrf_debug_sprite_picker.sprites.size());
}
for (ZoomLevel z = ZOOM_LVL_NORMAL; z < ZOOM_LVL_END; z++) {
this->SetWidgetsLoweredState(SpriteAlignerWindow::zoom == z, WID_SA_ZOOM + z);
}
}
void OnResize() override
@@ -1072,53 +1095,65 @@ static const NWidgetPart _nested_sprite_aligner_widgets[] = {
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_HORIZONTAL), SetPIP(0, 0, 10),
NWidget(NWID_VERTICAL), SetPIP(10, 5, 10),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 5, 10),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_PREVIOUS), SetDataTip(STR_SPRITE_ALIGNER_PREVIOUS_BUTTON, STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_GOTO), SetDataTip(STR_SPRITE_ALIGNER_GOTO_BUTTON, STR_SPRITE_ALIGNER_GOTO_TOOLTIP), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_NEXT), SetDataTip(STR_SPRITE_ALIGNER_NEXT_BUTTON, STR_SPRITE_ALIGNER_NEXT_TOOLTIP), SetFill(1, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), SetPadding(WidgetDimensions::unscaled.sparse_resize),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_PREVIOUS), SetDataTip(STR_SPRITE_ALIGNER_PREVIOUS_BUTTON, STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_GOTO), SetDataTip(STR_SPRITE_ALIGNER_GOTO_BUTTON, STR_SPRITE_ALIGNER_GOTO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_NEXT), SetDataTip(STR_SPRITE_ALIGNER_NEXT_BUTTON, STR_SPRITE_ALIGNER_NEXT_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_UP), SetDataTip(SPR_ARROW_UP, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), SetMinimalSize(11, 11),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL_LTR), SetPIP(10, 5, 10),
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(NWID_VERTICAL),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_LEFT), SetDataTip(SPR_ARROW_LEFT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), SetMinimalSize(11, 11),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(0, 1),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_DARK_BLUE, WID_SA_SPRITE), SetDataTip(STR_NULL, STR_SPRITE_ALIGNER_SPRITE_TOOLTIP),
NWidget(WWT_PANEL, COLOUR_DARK_BLUE, WID_SA_SPRITE), SetDataTip(STR_NULL, STR_SPRITE_ALIGNER_SPRITE_TOOLTIP), SetResize(1, 1), SetFill(1, 1),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_RIGHT), SetDataTip(SPR_ARROW_RIGHT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), SetMinimalSize(11, 11),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(0, 1),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), SetMinimalSize(11, 11),
NWidget(NWID_SPACER), SetFill(1, 1),
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 0),
EndContainer(),
NWidget(WWT_LABEL, COLOUR_GREY, WID_SA_OFFSETS_ABS), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_ABS, STR_NULL), SetFill(1, 0), SetPadding(0, 10, 0, 10),
NWidget(WWT_LABEL, COLOUR_GREY, WID_SA_OFFSETS_REL), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_REL, STR_NULL), SetFill(1, 0), SetPadding(0, 10, 0, 10),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 5, 10),
NWidget(WWT_TEXTBTN_2, COLOUR_GREY, WID_SA_CENTRE), SetDataTip(STR_SPRITE_ALIGNER_CENTRE_OFFSET, STR_NULL), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_RESET_REL), SetDataTip(STR_SPRITE_ALIGNER_RESET_BUTTON, STR_SPRITE_ALIGNER_RESET_TOOLTIP), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_CROSSHAIR), SetDataTip(STR_SPRITE_ALIGNER_CROSSHAIR, STR_NULL), SetFill(1, 0),
NWidget(WWT_LABEL, COLOUR_GREY, WID_SA_OFFSETS_ABS), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_ABS, STR_NULL), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_LABEL, COLOUR_GREY, WID_SA_OFFSETS_REL), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_REL, STR_NULL), SetFill(1, 0), SetResize(1, 0),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
NWidget(WWT_TEXTBTN_2, COLOUR_GREY, WID_SA_CENTRE), SetDataTip(STR_SPRITE_ALIGNER_CENTRE_OFFSET, STR_NULL), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_RESET_REL), SetDataTip(STR_SPRITE_ALIGNER_RESET_BUTTON, STR_SPRITE_ALIGNER_RESET_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_CROSSHAIR), SetDataTip(STR_SPRITE_ALIGNER_CROSSHAIR, STR_NULL), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
EndContainer(),
NWidget(NWID_VERTICAL), SetPIP(10, 5, 10),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_PICKER), SetDataTip(STR_SPRITE_ALIGNER_PICKER_BUTTON, STR_SPRITE_ALIGNER_PICKER_TOOLTIP), SetFill(1, 0),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, WID_SA_LIST), SetResize(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetFill(1, 1), SetScrollbar(WID_SA_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SA_SCROLLBAR),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_NORMAL), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_MIN, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_OUT_2X), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_IN_2X, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_OUT_4X), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_NORMAL, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_OUT_8X), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_OUT_16X), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_ZOOM + ZOOM_LVL_OUT_32X), SetDataTip(STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X, STR_NULL), SetFill(1, 0),
EndContainer(),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
EndContainer(),
};

View File

@@ -602,7 +602,7 @@ static void ShowSavePresetWindow(const char *initial_text);
* Window for showing NewGRF files
*/
struct NewGRFWindow : public Window, NewGRFScanCallback {
typedef GUIList<const GRFConfig *, StringFilter &> GUIGRFConfigList;
typedef GUIList<const GRFConfig *, std::nullptr_t, StringFilter &> GUIGRFConfigList;
static const uint EDITBOX_MAX_SIZE = 50;

View File

@@ -45,7 +45,7 @@ enum BuildObjectHotkeys {
/** The window used for building objects. */
class BuildObjectWindow : public Window {
typedef GUIList<ObjectClassID, StringFilter &> GUIObjectClassList; ///< Type definition for the list to hold available object classes.
typedef GUIList<ObjectClassID, std::nullptr_t, StringFilter &> GUIObjectClassList; ///< Type definition for the list to hold available object classes.
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.

View File

@@ -903,7 +903,7 @@ private:
Scrollbar *vscroll; ///< Vertical scrollbar of the new station list.
Scrollbar *vscroll2; ///< Vertical scrollbar of the matrix with new stations.
typedef GUIList<StationClassID, StringFilter &> GUIStationClassList; ///< Type definition for the list to hold available station classes.
typedef GUIList<StationClassID, std::nullptr_t, StringFilter &> GUIStationClassList; ///< Type definition for the list to hold available station classes.
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.

View File

@@ -1101,7 +1101,7 @@ private:
Scrollbar *vscrollList; ///< Vertical scrollbar of the new station list.
Scrollbar *vscrollMatrix; ///< Vertical scrollbar of the station picker matrix.
typedef GUIList<RoadStopClassID, StringFilter &> GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes.
typedef GUIList<RoadStopClassID, std::nullptr_t, StringFilter &> GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes.
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.

View File

@@ -41,7 +41,7 @@ struct SignList {
/**
* A GUIList contains signs and uses a StringFilter for filtering.
*/
typedef GUIList<const Sign *, StringFilter &> GUISignList;
typedef GUIList<const Sign *, std::nullptr_t, StringFilter &> GUISignList;
GUISignList signs;

View File

@@ -40,12 +40,13 @@ struct Filtering {
/**
* List template of 'things' \p T to sort in a GUI.
* @tparam T Type of data stored in the list to represent each item.
* @tparam P Tyoe of data passed as additional parameter to the sort function.
* @tparam F Type of data fed as additional value to the filter function. @see FilterFunction
*/
template <typename T, typename F = const char*>
template <typename T, typename P = std::nullptr_t, typename F = const char*>
class GUIList : public std::vector<T> {
public:
typedef bool SortFunction(const T&, const T&); ///< Signature of sort function.
using SortFunction = std::conditional_t<std::is_same_v<P, std::nullptr_t>, bool (const T&, const T&), bool (const T&, const T&, const P)>; ///< Signature of sort function.
typedef bool CDECL FilterFunction(const T*, F); ///< Signature of filter function.
protected:
@@ -56,6 +57,11 @@ protected:
uint8_t filter_type; ///< what criteria to filter on
uint16_t resort_timer; ///< resort list after a given amount of ticks if set
/* If sort parameters are used then params must be a reference, however if not then params cannot be a reference as
* it will not be able to reference anything. */
using SortParameterReference = std::conditional_t<std::is_same_v<P, std::nullptr_t>, P, P&>;
const SortParameterReference params;
/**
* Check if the list is sortable
*
@@ -76,13 +82,28 @@ protected:
}
public:
/* If sort parameters are not used then we don't require a reference to the params. */
template <typename T_ = T, typename P_ = P, typename _F = F, std::enable_if_t<std::is_same_v<P_, std::nullptr_t>>* = nullptr>
GUIList() :
sort_func_list(nullptr),
filter_func_list(nullptr),
flags(VL_NONE),
sort_type(0),
filter_type(0),
resort_timer(1)
resort_timer(1),
params(nullptr)
{};
/* If sort parameters are used then we require a reference to the params. */
template <typename T_ = T, typename P_ = P, typename _F = F, std::enable_if_t<!std::is_same_v<P_, std::nullptr_t>>* = nullptr>
GUIList(const P& params) :
sort_func_list(nullptr),
filter_func_list(nullptr),
flags(VL_NONE),
sort_type(0),
filter_type(0),
resort_timer(1),
params(params)
{};
/**
@@ -258,7 +279,11 @@ public:
const bool desc = (this->flags & VL_DESC) != 0;
std::sort(std::vector<T>::begin(), std::vector<T>::end(), [&](const T &a, const T &b) { return desc ? compare(b, a) : compare(a, b); });
if constexpr (std::is_same_v<P, std::nullptr_t>) {
std::sort(std::vector<T>::begin(), std::vector<T>::end(), [&](const T &a, const T &b) { return desc ? compare(b, a) : compare(a, b); });
} else {
std::sort(std::vector<T>::begin(), std::vector<T>::end(), [&](const T &a, const T &b) { return desc ? compare(b, a, params) : compare(a, b, params); });
}
return true;
}

View File

@@ -209,7 +209,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ
if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN);
}
typedef GUIList<const Station*> GUIStationList;
typedef GUIList<const Station*, const CargoTypes &> GUIStationList;
/**
* The list of stations per company.
@@ -218,17 +218,26 @@ class CompanyStationsWindow : public Window
{
protected:
/* Runtime saved values */
static Listing last_sorting;
static byte facilities; // types of stations of interest
static bool include_empty; // whether we should include stations without waiting cargo
static const CargoTypes cargo_filter_max;
static CargoTypes cargo_filter; // bitmap of cargo types to include
struct FilterState {
Listing last_sorting;
byte facilities; ///< types of stations of interest
bool include_empty; ///< whether we should include stations without waiting cargo
CargoTypes cargoes; ///< bitmap of cargo types to include
};
static inline FilterState initial_state = {
{false, 0},
FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK,
true,
ALL_CARGOTYPES,
};
/* Constants for sorting stations */
static const StringID sorter_names[];
static GUIStationList::SortFunction * const sorter_funcs[];
GUIStationList stations;
FilterState filter;
GUIStationList stations{filter.cargoes};
Scrollbar *vscroll;
uint rating_width;
@@ -247,19 +256,19 @@ protected:
for (const Station *st : Station::Iterate()) {
if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
if (this->facilities & st->facilities) { // only stations with selected facilities
if (this->filter.facilities & st->facilities) { // only stations with selected facilities
int num_waiting_cargo = 0;
for (CargoID j = 0; j < NUM_CARGO; j++) {
if (st->goods[j].HasRating()) {
num_waiting_cargo++; // count number of waiting cargo
if (HasBit(this->cargo_filter, j)) {
if (HasBit(this->filter.cargoes, j)) {
this->stations.push_back(st);
break;
}
}
}
/* stations without waiting cargo */
if (num_waiting_cargo == 0 && this->include_empty) {
if (num_waiting_cargo == 0 && this->filter.include_empty) {
this->stations.push_back(st);
}
}
@@ -273,7 +282,7 @@ protected:
}
/** Sort stations by their name */
static bool StationNameSorter(const Station * const &a, const Station * const &b)
static bool StationNameSorter(const Station * const &a, const Station * const &b, const CargoTypes &)
{
int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
if (r == 0) return a->index < b->index;
@@ -281,13 +290,13 @@ protected:
}
/** Sort stations by their type */
static bool StationTypeSorter(const Station * const &a, const Station * const &b)
static bool StationTypeSorter(const Station * const &a, const Station * const &b, const CargoTypes &)
{
return a->facilities < b->facilities;
}
/** Sort stations by their waiting cargo */
static bool StationWaitingTotalSorter(const Station * const &a, const Station * const &b)
static bool StationWaitingTotalSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
{
int diff = 0;
@@ -299,7 +308,7 @@ protected:
}
/** Sort stations by their available waiting cargo */
static bool StationWaitingAvailableSorter(const Station * const &a, const Station * const &b)
static bool StationWaitingAvailableSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
{
int diff = 0;
@@ -311,7 +320,7 @@ protected:
}
/** Sort stations by their rating */
static bool StationRatingMaxSorter(const Station * const &a, const Station * const &b)
static bool StationRatingMaxSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
{
byte maxr1 = 0;
byte maxr2 = 0;
@@ -325,7 +334,7 @@ protected:
}
/** Sort stations by their rating */
static bool StationRatingMinSorter(const Station * const &a, const Station * const &b)
static bool StationRatingMinSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
{
byte minr1 = 255;
byte minr2 = 255;
@@ -350,8 +359,12 @@ protected:
public:
CompanyStationsWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
this->stations.SetListing(this->last_sorting);
this->stations.SetSortFuncs(this->sorter_funcs);
/* Load initial filter state. */
this->filter = CompanyStationsWindow::initial_state;
if (this->filter.cargoes == ALL_CARGOTYPES) this->filter.cargoes = _cargo_mask;
this->stations.SetListing(this->filter.last_sorting);
this->stations.SetSortFuncs(CompanyStationsWindow::sorter_funcs);
this->stations.ForceRebuild();
this->stations.NeedResort();
this->SortStationsList();
@@ -363,25 +376,27 @@ public:
uint8_t index = 0;
for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
if (HasBit(this->cargo_filter, cs->Index())) {
if (HasBit(this->filter.cargoes, cs->Index())) {
this->LowerWidget(WID_STL_CARGOSTART + index);
}
index++;
}
if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
if (this->filter.cargoes == ALL_CARGOTYPES) this->filter.cargoes = _cargo_mask;
for (uint i = 0; i < 5; i++) {
if (HasBit(this->facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
if (HasBit(this->filter.facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
}
this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->include_empty);
this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->filter.include_empty);
this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
}
~CompanyStationsWindow()
{
this->last_sorting = this->stations.GetListing();
/* Save filter state. */
this->filter.last_sorting = this->stations.GetListing();
CompanyStationsWindow::initial_state = this->filter;
}
void UpdateWidgetSize(int widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
@@ -397,8 +412,8 @@ public:
case WID_STL_SORTDROPBTN: {
Dimension d = {0, 0};
for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
for (int i = 0; CompanyStationsWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
d = maxdim(d, GetStringBoundingBox(CompanyStationsWindow::sorter_names[i]));
}
d.width += padding.width;
d.height += padding.height;
@@ -502,7 +517,7 @@ public:
if (widget >= WID_STL_CARGOSTART) {
Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? WidgetDimensions::scaled.pressed : 0;
int cg_ofst = HasBit(this->filter.cargoes, cs->Index()) ? WidgetDimensions::scaled.pressed : 0;
br = br.Translate(cg_ofst, cg_ofst);
GfxFillRect(br, cs->rating_colour);
TextColour tc = GetContrastColour(cs->rating_colour);
@@ -545,13 +560,13 @@ public:
case WID_STL_AIRPLANE:
case WID_STL_SHIP:
if (_ctrl_pressed) {
ToggleBit(this->facilities, widget - WID_STL_TRAIN);
ToggleBit(this->filter.facilities, widget - WID_STL_TRAIN);
this->ToggleWidgetLoweredState(widget);
} else {
for (uint i : SetBitIterator(this->facilities)) {
for (uint i : SetBitIterator(this->filter.facilities)) {
this->RaiseWidget(i + WID_STL_TRAIN);
}
this->facilities = 1 << (widget - WID_STL_TRAIN);
this->filter.facilities = 1 << (widget - WID_STL_TRAIN);
this->LowerWidget(widget);
}
this->stations.ForceRebuild();
@@ -563,7 +578,7 @@ public:
this->LowerWidget(i);
}
this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
this->filter.facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
this->stations.ForceRebuild();
this->SetDirty();
break;
@@ -574,8 +589,8 @@ public:
}
this->LowerWidget(WID_STL_NOCARGOWAITING);
this->cargo_filter = _cargo_mask;
this->include_empty = true;
this->filter.cargoes = _cargo_mask;
this->filter.include_empty = true;
this->stations.ForceRebuild();
this->SetDirty();
break;
@@ -592,15 +607,15 @@ public:
case WID_STL_NOCARGOWAITING:
if (_ctrl_pressed) {
this->include_empty = !this->include_empty;
this->filter.include_empty = !this->filter.include_empty;
this->ToggleWidgetLoweredState(WID_STL_NOCARGOWAITING);
} else {
for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) {
this->RaiseWidget(WID_STL_CARGOSTART + i);
}
this->cargo_filter = 0;
this->include_empty = true;
this->filter.cargoes = 0;
this->filter.include_empty = true;
this->LowerWidget(WID_STL_NOCARGOWAITING);
}
@@ -614,7 +629,7 @@ public:
const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
if (_ctrl_pressed) {
ToggleBit(this->cargo_filter, cs->Index());
ToggleBit(this->filter.cargoes, cs->Index());
this->ToggleWidgetLoweredState(widget);
} else {
for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) {
@@ -622,10 +637,10 @@ public:
}
this->RaiseWidget(WID_STL_NOCARGOWAITING);
this->cargo_filter = 0;
this->include_empty = false;
this->filter.cargoes = 0;
this->filter.include_empty = false;
SetBit(this->cargo_filter, cs->Index());
SetBit(this->filter.cargoes, cs->Index());
this->LowerWidget(widget);
}
this->stations.ForceRebuild();
@@ -642,7 +657,7 @@ public:
this->stations.SetSortType(index);
/* Display the current sort variant */
this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = CompanyStationsWindow::sorter_names[this->stations.SortType()];
this->SetDirty();
}
@@ -678,12 +693,6 @@ public:
}
};
Listing CompanyStationsWindow::last_sorting = {false, 0};
byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
bool CompanyStationsWindow::include_empty = true;
const CargoTypes CompanyStationsWindow::cargo_filter_max = ALL_CARGOTYPES;
CargoTypes CompanyStationsWindow::cargo_filter = ALL_CARGOTYPES;
/* Available station sorting functions */
GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
&StationNameSorter,

View File

@@ -47,7 +47,7 @@
TownKdtree _town_local_authority_kdtree(&Kdtree_TownXYFunc);
typedef GUIList<const Town*> GUITownList;
typedef GUIList<const Town*, const bool &> GUITownList;
static const NWidgetPart _nested_town_authority_widgets[] = {
NWidget(NWID_HORIZONTAL),
@@ -707,7 +707,7 @@ private:
StringFilter string_filter; ///< Filter for towns
QueryString townname_editbox; ///< Filter editbox
GUITownList towns;
GUITownList towns{TownDirectoryWindow::last_sorting.order};
Scrollbar *vscroll;
@@ -736,31 +736,31 @@ private:
}
/** Sort by town name */
static bool TownNameSorter(const Town * const &a, const Town * const &b)
static bool TownNameSorter(const Town * const &a, const Town * const &b, const bool &)
{
return StrNaturalCompare(a->GetCachedName(), b->GetCachedName()) < 0; // Sort by name (natural sorting).
}
/** Sort by population (default descending, as big towns are of the most interest). */
static bool TownPopulationSorter(const Town * const &a, const Town * const &b)
static bool TownPopulationSorter(const Town * const &a, const Town * const &b, const bool &order)
{
uint32_t a_population = a->cache.population;
uint32_t b_population = b->cache.population;
if (a_population == b_population) return TownDirectoryWindow::TownNameSorter(a, b);
if (a_population == b_population) return TownDirectoryWindow::TownNameSorter(a, b, order);
return a_population < b_population;
}
/** Sort by town rating */
static bool TownRatingSorter(const Town * const &a, const Town * const &b)
static bool TownRatingSorter(const Town * const &a, const Town * const &b, const bool &order)
{
bool before = !TownDirectoryWindow::last_sorting.order; // Value to get 'a' before 'b'.
bool before = !order; // Value to get 'a' before 'b'.
/* Towns without rating are always after towns with rating. */
if (HasBit(a->have_ratings, _local_company)) {
if (HasBit(b->have_ratings, _local_company)) {
int16_t a_rating = a->ratings[_local_company];
int16_t b_rating = b->ratings[_local_company];
if (a_rating == b_rating) return TownDirectoryWindow::TownNameSorter(a, b);
if (a_rating == b_rating) return TownDirectoryWindow::TownNameSorter(a, b, order);
return a_rating < b_rating;
}
return before;
@@ -768,8 +768,8 @@ private:
if (HasBit(b->have_ratings, _local_company)) return !before;
/* Sort unrated towns always on ascending town name. */
if (before) return TownDirectoryWindow::TownNameSorter(a, b);
return TownDirectoryWindow::TownNameSorter(b, a);
if (before) return TownDirectoryWindow::TownNameSorter(a, b, order);
return TownDirectoryWindow::TownNameSorter(b, a, order);
}
public:

View File

@@ -19,7 +19,7 @@
#include "window_gui.h"
#include "widgets/dropdown_type.h"
typedef GUIList<const Vehicle*, CargoID> GUIVehicleList;
typedef GUIList<const Vehicle*, std::nullptr_t, CargoID> GUIVehicleList;
struct GUIVehicleGroup {
VehicleList::const_iterator vehicles_begin; ///< Pointer to beginning element of this vehicle group.
@@ -62,7 +62,7 @@ struct GUIVehicleGroup {
}
};
typedef GUIList<GUIVehicleGroup, CargoID> GUIVehicleGroupList;
typedef GUIList<GUIVehicleGroup, std::nullptr_t, CargoID> GUIVehicleGroupList;
struct BaseVehicleListWindow : public Window {

View File

@@ -37,6 +37,8 @@ enum SpriteAlignerWidgets {
WID_SA_PICKER, ///< Sprite picker.
WID_SA_LIST, ///< Queried sprite list.
WID_SA_SCROLLBAR, ///< Scrollbar for sprite list.
WID_SA_ZOOM, ///< Zoom level buttons (from ZOOM_LVL_BEGIN to ZOOM_LVL_END).
WID_SA_ZOOM_LAST = WID_SA_ZOOM + ZOOM_LVL_END - 1, ///< Marker for last zoom level button.
WID_SA_RESET_REL, ///< Reset relative sprite offset
WID_SA_CENTRE, ///< Toggle centre sprite.
WID_SA_CROSSHAIR, ///< Toggle crosshair.