mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-22 22:19:08 +00:00
Compare commits
9 Commits
2dcb00a79a
...
c0ea0589b4
Author | SHA1 | Date | |
---|---|---|---|
c0ea0589b4 | |||
10f94fb0dd | |||
9f853c10b0 | |||
cb53fed229 | |||
a05d6ee404 | |||
54d45a6047 | |||
a29766d6cc | |||
f1cceb43a1 | |||
52b121942b |
@@ -1475,7 +1475,7 @@ static IntervalTimer<TimerGameRealtime> _autosave_interval({std::chrono::millise
|
||||
*/
|
||||
void ChangeAutosaveFrequency(bool reset)
|
||||
{
|
||||
_autosave_interval.SetInterval({_settings_client.gui.autosave_interval, TimerGameRealtime::AUTOSAVE}, reset);
|
||||
_autosave_interval.SetInterval({std::chrono::minutes(_settings_client.gui.autosave_interval), TimerGameRealtime::AUTOSAVE}, reset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1513,54 +1513,8 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the variable size of the variable in the saveload configuration
|
||||
* matches with the actual variable size.
|
||||
* @param sld The saveload configuration to test.
|
||||
*/
|
||||
[[maybe_unused]] static bool IsVariableSizeRight(const SaveLoad &sld)
|
||||
{
|
||||
if (GetVarMemType(sld.conv) == SLE_VAR_NULL) return true;
|
||||
|
||||
switch (sld.cmd) {
|
||||
case SL_VAR:
|
||||
switch (GetVarMemType(sld.conv)) {
|
||||
case SLE_VAR_BL:
|
||||
return sld.size == sizeof(bool);
|
||||
case SLE_VAR_I8:
|
||||
case SLE_VAR_U8:
|
||||
return sld.size == sizeof(int8_t);
|
||||
case SLE_VAR_I16:
|
||||
case SLE_VAR_U16:
|
||||
return sld.size == sizeof(int16_t);
|
||||
case SLE_VAR_I32:
|
||||
case SLE_VAR_U32:
|
||||
return sld.size == sizeof(int32_t);
|
||||
case SLE_VAR_I64:
|
||||
case SLE_VAR_U64:
|
||||
return sld.size == sizeof(int64_t);
|
||||
case SLE_VAR_NAME:
|
||||
return sld.size == sizeof(std::string);
|
||||
default:
|
||||
return sld.size == sizeof(void *);
|
||||
}
|
||||
case SL_REF:
|
||||
/* These should all be pointer sized. */
|
||||
return sld.size == sizeof(void *);
|
||||
|
||||
case SL_STDSTR:
|
||||
/* These should be all pointers to std::string. */
|
||||
return sld.size == sizeof(std::string);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool SlObjectMember(void *object, const SaveLoad &sld)
|
||||
{
|
||||
assert(IsVariableSizeRight(sld));
|
||||
|
||||
if (!SlIsObjectValidInSavegame(sld)) return false;
|
||||
|
||||
VarType conv = GB(sld.conv, 0, 8);
|
||||
|
@@ -709,6 +709,86 @@ struct SaveLoadCompat {
|
||||
SaveLoadVersion version_to; ///< Save/load the variable before this savegame version.
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the NumberType of a setting. This describes the integer type
|
||||
* as it is represented in memory
|
||||
* @param type VarType holding information about the variable-type
|
||||
* @return the SLE_VAR_* part of a variable-type description
|
||||
*/
|
||||
static inline constexpr VarType GetVarMemType(VarType type)
|
||||
{
|
||||
return GB(type, 4, 4) << 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FileType of a setting. This describes the integer type
|
||||
* as it is represented in a savegame/file
|
||||
* @param type VarType holding information about the file-type
|
||||
* @return the SLE_FILE_* part of a variable-type description
|
||||
*/
|
||||
static inline constexpr VarType GetVarFileType(VarType type)
|
||||
{
|
||||
return GB(type, 0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given saveload type is a numeric type.
|
||||
* @param conv the type to check
|
||||
* @return True if it's a numeric type.
|
||||
*/
|
||||
static inline constexpr bool IsNumericType(VarType conv)
|
||||
{
|
||||
return GetVarMemType(conv) <= SLE_VAR_U64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return expect size in bytes of a VarType
|
||||
* @param type VarType to get size of.
|
||||
* @return size of type in bytes.
|
||||
*/
|
||||
static inline constexpr size_t SlVarSize(VarType type)
|
||||
{
|
||||
switch (GetVarMemType(type)) {
|
||||
case SLE_VAR_BL: return sizeof(bool);
|
||||
case SLE_VAR_I8: return sizeof(int8_t);
|
||||
case SLE_VAR_U8: return sizeof(uint8_t);
|
||||
case SLE_VAR_I16: return sizeof(int16_t);
|
||||
case SLE_VAR_U16: return sizeof(uint16_t);
|
||||
case SLE_VAR_I32: return sizeof(int32_t);
|
||||
case SLE_VAR_U32: return sizeof(uint32_t);
|
||||
case SLE_VAR_I64: return sizeof(int64_t);
|
||||
case SLE_VAR_U64: return sizeof(uint64_t);
|
||||
case SLE_VAR_NULL: return sizeof(void *);
|
||||
case SLE_VAR_STR: return sizeof(std::string);
|
||||
case SLE_VAR_STRQ: return sizeof(std::string);
|
||||
case SLE_VAR_NAME: return sizeof(std::string);
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a saveload cmd/type/length entry matches the size of the variable.
|
||||
* @param cmd SaveLoadType of entry.
|
||||
* @param type VarType of entry.
|
||||
* @param length Array length of entry.
|
||||
* @param size Actual size of variable.
|
||||
* @return true iff the sizes match.
|
||||
*/
|
||||
static inline constexpr bool SlCheckVarSize(SaveLoadType cmd, VarType type, size_t length, size_t size)
|
||||
{
|
||||
switch (cmd) {
|
||||
case SL_VAR: return SlVarSize(type) == size;
|
||||
case SL_REF: return sizeof(void *) == size;
|
||||
case SL_STDSTR: return SlVarSize(type) == size;
|
||||
case SL_ARR: return SlVarSize(type) * length <= size; // Partial load of array is permitted.
|
||||
case SL_DEQUE: return sizeof(std::deque<void *>) == size;
|
||||
case SL_VECTOR: return sizeof(std::vector<void *>) == size;
|
||||
case SL_REFLIST: return sizeof(std::list<void *>) == size;
|
||||
case SL_SAVEBYTE: return true;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage of simple variables, references (pointers), and arrays.
|
||||
* @param cmd Load/save type. @see SaveLoadType
|
||||
@@ -716,12 +796,18 @@ struct SaveLoadCompat {
|
||||
* @param base Name of the class or struct containing the variable.
|
||||
* @param variable Name of the variable in the class or struct referenced by \a base.
|
||||
* @param type Storage of the data in memory and in the savegame.
|
||||
* @param length Number of elements in the array.
|
||||
* @param from First savegame version that has the field.
|
||||
* @param to Last savegame version that has the field.
|
||||
* @param extra Extra data to pass to the address callback function.
|
||||
* @note In general, it is better to use one of the SLE_* macros below.
|
||||
*/
|
||||
#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr}
|
||||
#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) \
|
||||
SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { \
|
||||
static_assert(SlCheckVarSize(cmd, type, length, sizeof(static_cast<base *>(b)->variable))); \
|
||||
assert(b != nullptr); \
|
||||
return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); \
|
||||
}, extra, nullptr}
|
||||
|
||||
/**
|
||||
* Storage of simple variables, references (pointers), and arrays with a custom name.
|
||||
@@ -729,6 +815,7 @@ struct SaveLoadCompat {
|
||||
* @param base Name of the class or struct containing the variable.
|
||||
* @param variable Name of the variable in the class or struct referenced by \a base.
|
||||
* @param type Storage of the data in memory and in the savegame.
|
||||
* @param length Number of elements in the array.
|
||||
* @param from First savegame version that has the field.
|
||||
* @param to Last savegame version that has the field.
|
||||
* @param extra Extra data to pass to the address callback function.
|
||||
@@ -934,7 +1021,10 @@ struct SaveLoadCompat {
|
||||
* @param extra Extra data to pass to the address callback function.
|
||||
* @note In general, it is better to use one of the SLEG_* macros below.
|
||||
*/
|
||||
#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra, nullptr}
|
||||
#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) \
|
||||
SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { \
|
||||
static_assert(SlCheckVarSize(cmd, type, length, sizeof(variable))); \
|
||||
return static_cast<void *>(std::addressof(variable)); }, extra, nullptr}
|
||||
|
||||
/**
|
||||
* Storage of a global variable in some savegame versions.
|
||||
@@ -1133,38 +1223,6 @@ static inline bool SlIsObjectCurrentlyValid(SaveLoadVersion version_from, SaveLo
|
||||
return version_from <= SAVEGAME_VERSION && SAVEGAME_VERSION < version_to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NumberType of a setting. This describes the integer type
|
||||
* as it is represented in memory
|
||||
* @param type VarType holding information about the variable-type
|
||||
* @return the SLE_VAR_* part of a variable-type description
|
||||
*/
|
||||
static inline VarType GetVarMemType(VarType type)
|
||||
{
|
||||
return GB(type, 4, 4) << 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FileType of a setting. This describes the integer type
|
||||
* as it is represented in a savegame/file
|
||||
* @param type VarType holding information about the file-type
|
||||
* @return the SLE_FILE_* part of a variable-type description
|
||||
*/
|
||||
static inline VarType GetVarFileType(VarType type)
|
||||
{
|
||||
return GB(type, 0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given saveload type is a numeric type.
|
||||
* @param conv the type to check
|
||||
* @return True if it's a numeric type.
|
||||
*/
|
||||
static inline bool IsNumericType(VarType conv)
|
||||
{
|
||||
return GetVarMemType(conv) <= SLE_VAR_U64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address of the variable. Null-variables don't have an address,
|
||||
* everything else has a callback function that returns the address based
|
||||
|
@@ -139,7 +139,7 @@
|
||||
if (_settings_game.economy.station_noise_level) {
|
||||
AirportTileTableIterator it(as->table[0], tile);
|
||||
uint dist;
|
||||
AirportGetNearestTown(as, it, dist);
|
||||
AirportGetNearestTown(as, tile, it, dist);
|
||||
return GetAirportNoiseLevelForDistance(as, dist);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
if (!as->IsWithinMapBounds(0, tile)) return INVALID_TOWN;
|
||||
|
||||
uint dist;
|
||||
return AirportGetNearestTown(as, AirportTileTableIterator(as->table[0], tile), dist)->index;
|
||||
return AirportGetNearestTown(as, tile, AirportTileTableIterator(as->table[0], tile), dist)->index;
|
||||
}
|
||||
|
||||
/* static */ SQInteger ScriptAirport::GetMaintenanceCostFactor(AirportType type)
|
||||
|
@@ -1397,11 +1397,11 @@ void LoadFromConfig(bool startup)
|
||||
auto old_value = OneOfManySettingDesc::ParseSingleValue(old_item->value->c_str(), old_item->value->size(), _old_autosave_interval);
|
||||
|
||||
switch (old_value) {
|
||||
case 0: _settings_client.gui.autosave_interval = std::chrono::minutes::zero(); break;
|
||||
case 1: _settings_client.gui.autosave_interval = std::chrono::minutes(10); break;
|
||||
case 2: _settings_client.gui.autosave_interval = std::chrono::minutes(30); break;
|
||||
case 3: _settings_client.gui.autosave_interval = std::chrono::minutes(60); break;
|
||||
case 4: _settings_client.gui.autosave_interval = std::chrono::minutes(120); break;
|
||||
case 0: _settings_client.gui.autosave_interval = 0; break;
|
||||
case 1: _settings_client.gui.autosave_interval = 10; break;
|
||||
case 2: _settings_client.gui.autosave_interval = 30; break;
|
||||
case 3: _settings_client.gui.autosave_interval = 60; break;
|
||||
case 4: _settings_client.gui.autosave_interval = 120; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@@ -60,12 +60,12 @@ static const StringID _autosave_dropdown[] = {
|
||||
};
|
||||
|
||||
/** Available settings for autosave intervals. */
|
||||
static const std::chrono::minutes _autosave_dropdown_to_minutes[] = {
|
||||
std::chrono::minutes::zero(), ///< never
|
||||
std::chrono::minutes(10),
|
||||
std::chrono::minutes(30),
|
||||
std::chrono::minutes(60),
|
||||
std::chrono::minutes(120),
|
||||
static const uint32_t _autosave_dropdown_to_minutes[] = {
|
||||
0, ///< never
|
||||
10,
|
||||
30,
|
||||
60,
|
||||
120,
|
||||
};
|
||||
|
||||
static Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window.
|
||||
|
@@ -64,14 +64,14 @@ enum IndustryDensity {
|
||||
};
|
||||
|
||||
/** Possible values for "use_relay_service" setting. */
|
||||
enum UseRelayService {
|
||||
enum UseRelayService : uint8_t {
|
||||
URS_NEVER = 0,
|
||||
URS_ASK,
|
||||
URS_ALLOW,
|
||||
};
|
||||
|
||||
/** Possible values for "participate_survey" setting. */
|
||||
enum ParticipateSurvey {
|
||||
enum ParticipateSurvey : uint8_t {
|
||||
PS_ASK = 0,
|
||||
PS_NO,
|
||||
PS_YES,
|
||||
@@ -149,7 +149,7 @@ struct GUISettings {
|
||||
ZoomLevel zoom_min; ///< minimum zoom out level
|
||||
ZoomLevel zoom_max; ///< maximum zoom out level
|
||||
ZoomLevel sprite_zoom_min; ///< maximum zoom level at which higher-resolution alternative sprites will be used (if available) instead of scaling a lower resolution sprite
|
||||
std::chrono::minutes autosave_interval; ///< how often should we do autosaves?
|
||||
uint32_t autosave_interval; ///< how often should we do autosaves?
|
||||
bool threaded_saves; ///< should we do threaded saves?
|
||||
bool keep_all_autosave; ///< name the autosave in a different way
|
||||
bool autosave_on_exit; ///< save an autosave when you quit the game, but do not ask "Do you really want to quit?"
|
||||
|
@@ -2303,18 +2303,19 @@ uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
|
||||
* Finds the town nearest to given airport. Based on minimal manhattan distance to any airport's tile.
|
||||
* If two towns have the same distance, town with lower index is returned.
|
||||
* @param as airport's description
|
||||
* @param tile origin tile (top corner of the airport)
|
||||
* @param it An iterator over all airport tiles
|
||||
* @param[out] mindist Minimum distance to town
|
||||
* @return nearest town to airport
|
||||
*/
|
||||
Town *AirportGetNearestTown(const AirportSpec *as, const TileIterator &it, uint &mindist)
|
||||
Town *AirportGetNearestTown(const AirportSpec *as, TileIndex tile, const TileIterator &it, uint &mindist)
|
||||
{
|
||||
assert(Town::GetNumItems() > 0);
|
||||
|
||||
Town *nearest = nullptr;
|
||||
|
||||
uint perimeter_min_x = TileX(it);
|
||||
uint perimeter_min_y = TileY(it);
|
||||
uint perimeter_min_x = TileX(tile);
|
||||
uint perimeter_min_y = TileY(tile);
|
||||
uint perimeter_max_x = perimeter_min_x + as->size_x - 1;
|
||||
uint perimeter_max_y = perimeter_min_y + as->size_y - 1;
|
||||
|
||||
@@ -2322,6 +2323,8 @@ Town *AirportGetNearestTown(const AirportSpec *as, const TileIterator &it, uint
|
||||
|
||||
std::unique_ptr<TileIterator> copy(it.Clone());
|
||||
for (TileIndex cur_tile = *copy; cur_tile != INVALID_TILE; cur_tile = ++*copy) {
|
||||
assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, as->size_x));
|
||||
assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, as->size_y));
|
||||
if (TileX(cur_tile) == perimeter_min_x || TileX(cur_tile) == perimeter_max_x || TileY(cur_tile) == perimeter_min_y || TileY(cur_tile) == perimeter_max_y) {
|
||||
Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
|
||||
if (t == nullptr) continue;
|
||||
@@ -2349,7 +2352,7 @@ void UpdateAirportsNoise()
|
||||
const AirportSpec *as = st->airport.GetSpec();
|
||||
AirportTileIterator it(st);
|
||||
uint dist;
|
||||
Town *nearest = AirportGetNearestTown(as, it, dist);
|
||||
Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist);
|
||||
nearest->noise_reached += GetAirportNoiseLevelForDistance(as, dist);
|
||||
}
|
||||
}
|
||||
@@ -2399,7 +2402,7 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty
|
||||
|
||||
/* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
|
||||
uint dist;
|
||||
Town *nearest = AirportGetNearestTown(as, tile_iter, dist);
|
||||
Town *nearest = AirportGetNearestTown(as, tile, tile_iter, dist);
|
||||
uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
|
||||
|
||||
/* Check if local auth would allow a new airport */
|
||||
@@ -2527,7 +2530,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
|
||||
* need of recalculation */
|
||||
AirportTileIterator it(st);
|
||||
uint dist;
|
||||
Town *nearest = AirportGetNearestTown(as, it, dist);
|
||||
Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist);
|
||||
nearest->noise_reached -= GetAirportNoiseLevelForDistance(as, dist);
|
||||
|
||||
if (_settings_game.economy.station_noise_level) {
|
||||
|
@@ -16,7 +16,7 @@
|
||||
enum StationClassID : byte;
|
||||
enum RoadStopClassID : byte;
|
||||
|
||||
extern Town *AirportGetNearestTown(const struct AirportSpec *as, const TileIterator &it, uint &mindist);
|
||||
extern Town *AirportGetNearestTown(const struct AirportSpec *as, TileIndex tile, const TileIterator &it, uint &mindist);
|
||||
extern uint8_t GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance);
|
||||
|
||||
CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_type, byte layout, StationID station_to_join, bool allow_adjacent);
|
||||
|
@@ -24,7 +24,7 @@
|
||||
|
||||
static const NWidgetPart _nested_dropdown_menu_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_END, WID_DM_ITEMS), SetMinimalSize(1, 1), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_END, WID_DM_ITEMS), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_DM_SHOW_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_END, WID_DM_SCROLL),
|
||||
EndContainer(),
|
||||
@@ -41,67 +41,49 @@ static WindowDesc _dropdown_desc(__FILE__, __LINE__,
|
||||
/** Drop-down menu window */
|
||||
struct DropdownWindow : Window {
|
||||
int parent_button; ///< Parent widget number where the window is dropped from.
|
||||
Rect wi_rect; ///< Rect of the button that opened the dropdown.
|
||||
const DropDownList list; ///< List with dropdown menu items.
|
||||
int selected_index; ///< Index of the selected item in the list.
|
||||
byte click_delay; ///< Timer to delay selection.
|
||||
bool drag_mode;
|
||||
int selected_result; ///< Result value of the selected item in the list.
|
||||
byte click_delay = 0; ///< Timer to delay selection.
|
||||
bool drag_mode = true;
|
||||
bool instant_close; ///< Close the window when the mouse button is raised.
|
||||
int scrolling; ///< If non-zero, auto-scroll the item list (one time).
|
||||
int scrolling = 0; ///< If non-zero, auto-scroll the item list (one time).
|
||||
Point position; ///< Position of the topleft corner of the window.
|
||||
Scrollbar *vscroll;
|
||||
|
||||
Dimension items_dim; ///< Calculated cropped and padded dimension for the items widget.
|
||||
|
||||
/**
|
||||
* Create a dropdown menu.
|
||||
* @param parent Parent window.
|
||||
* @param list Dropdown item list.
|
||||
* @param selected Index of the selected item in the list.
|
||||
* @param selected Initial selected result of the list.
|
||||
* @param button Widget of the parent window doing the dropdown.
|
||||
* @param wi_rect Rect of the button that opened the dropdown.
|
||||
* @param instant_close Close the window when the mouse button is raised.
|
||||
* @param position Topleft position of the dropdown menu window.
|
||||
* @param size Size of the dropdown menu window.
|
||||
* @param wi_colour Colour of the parent widget.
|
||||
* @param scroll Dropdown menu has a scrollbar.
|
||||
*/
|
||||
DropdownWindow(Window *parent, DropDownList &&list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll)
|
||||
: Window(&_dropdown_desc), list(std::move(list))
|
||||
DropdownWindow(Window *parent, DropDownList &&list, int selected, int button, const Rect wi_rect, bool instant_close, Colours wi_colour)
|
||||
: Window(&_dropdown_desc)
|
||||
, parent_button(button)
|
||||
, wi_rect(wi_rect)
|
||||
, list(std::move(list))
|
||||
, selected_result(selected)
|
||||
, instant_close(instant_close)
|
||||
{
|
||||
assert(!this->list.empty());
|
||||
|
||||
this->position = position;
|
||||
this->parent = parent;
|
||||
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->GetWidget<NWidgetCore>(WID_DM_ITEMS)->colour = wi_colour;
|
||||
this->GetWidget<NWidgetCore>(WID_DM_SCROLL)->colour = wi_colour;
|
||||
this->vscroll = this->GetScrollbar(WID_DM_SCROLL);
|
||||
|
||||
uint items_width = size.width - (scroll ? NWidgetScrollbar::GetVerticalDimension().width : 0);
|
||||
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_DM_ITEMS);
|
||||
nwi->SetMinimalSizeAbsolute(items_width, size.height + WidgetDimensions::scaled.dropdownlist.Vertical());
|
||||
nwi->colour = wi_colour;
|
||||
|
||||
nwi = this->GetWidget<NWidgetCore>(WID_DM_SCROLL);
|
||||
nwi->colour = wi_colour;
|
||||
|
||||
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(scroll ? 0 : SZSP_NONE);
|
||||
this->UpdateSizeAndPosition();
|
||||
|
||||
this->FinishInitNested(0);
|
||||
CLRBITS(this->flags, WF_WHITE_BORDER);
|
||||
|
||||
/* Total length of list */
|
||||
int list_height = 0;
|
||||
for (const auto &item : this->list) {
|
||||
list_height += item->Height();
|
||||
}
|
||||
|
||||
/* Capacity is the average number of items visible */
|
||||
this->vscroll->SetCapacity(size.height * this->list.size() / list_height);
|
||||
this->vscroll->SetCount(this->list.size());
|
||||
|
||||
this->parent = parent;
|
||||
this->parent_button = button;
|
||||
this->selected_index = selected;
|
||||
this->click_delay = 0;
|
||||
this->drag_mode = true;
|
||||
this->instant_close = instant_close;
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
@@ -113,7 +95,7 @@ struct DropdownWindow : Window {
|
||||
Point pt = _cursor.pos;
|
||||
pt.x -= this->parent->left;
|
||||
pt.y -= this->parent->top;
|
||||
this->parent->OnDropdownClose(pt, this->parent_button, this->selected_index, this->instant_close);
|
||||
this->parent->OnDropdownClose(pt, this->parent_button, this->selected_result, this->instant_close);
|
||||
|
||||
/* Set flag on parent widget to indicate that we have just closed. */
|
||||
NWidgetCore *nwc = this->parent->GetWidget<NWidgetCore>(this->parent_button);
|
||||
@@ -128,6 +110,73 @@ struct DropdownWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fit dropdown list into available height, rounding to average item size. Width is adjusted if scrollbar is present.
|
||||
* @param[in,out] desired Desired dimensions of dropdown list.
|
||||
* @param list Dimensions of the list itself, without padding or cropping.
|
||||
* @param available_height Available height to fit list within.
|
||||
*/
|
||||
void FitAvailableHeight(Dimension &desired, const Dimension &list, uint available_height)
|
||||
{
|
||||
if (desired.height < available_height) return;
|
||||
|
||||
/* If the dropdown doesn't fully fit, we a need a dropdown. */
|
||||
uint avg_height = list.height / (uint)this->list.size();
|
||||
uint rows = std::max((available_height - WidgetDimensions::scaled.dropdownlist.Vertical()) / avg_height, 1U);
|
||||
|
||||
desired.width = std::max(list.width, desired.width - NWidgetScrollbar::GetVerticalDimension().width);
|
||||
desired.height = rows * avg_height + WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update size and position of window to fit dropdown list into available space.
|
||||
*/
|
||||
void UpdateSizeAndPosition()
|
||||
{
|
||||
Rect button_rect = this->wi_rect.Translate(this->parent->left, this->parent->top);
|
||||
|
||||
/* Get the dimensions required for the list. */
|
||||
Dimension list_dim = GetDropDownListDimension(this->list);
|
||||
|
||||
/* Set up dimensions for the items widget. */
|
||||
Dimension widget_dim = list_dim;
|
||||
widget_dim.width += WidgetDimensions::scaled.dropdownlist.Horizontal();
|
||||
widget_dim.height += WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
|
||||
/* Width should match at least the width of the parent widget. */
|
||||
widget_dim.width = std::max<uint>(widget_dim.width, button_rect.Width());
|
||||
|
||||
/* Available height below (or above, if the dropdown is placed above the widget). */
|
||||
uint available_height_below = std::max(GetMainViewBottom() - button_rect.bottom - 1, 0);
|
||||
uint available_height_above = std::max(button_rect.top - 1 - GetMainViewTop(), 0);
|
||||
|
||||
/* Is it better to place the dropdown above the widget? */
|
||||
if (widget_dim.height > available_height_below && available_height_above > available_height_below) {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_above);
|
||||
this->position.y = button_rect.top - widget_dim.height;
|
||||
} else {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_below);
|
||||
this->position.y = button_rect.bottom + 1;
|
||||
}
|
||||
|
||||
this->position.x = (_current_text_dir == TD_RTL) ? button_rect.right + 1 - (int)widget_dim.width : button_rect.left;
|
||||
|
||||
this->items_dim = widget_dim;
|
||||
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(list_dim.height > widget_dim.height ? 0 : SZSP_NONE);
|
||||
|
||||
/* Capacity is the average number of items visible */
|
||||
this->vscroll->SetCapacity((widget_dim.height - WidgetDimensions::scaled.dropdownlist.Vertical()) * this->list.size() / list_dim.height);
|
||||
this->vscroll->SetCount(this->list.size());
|
||||
|
||||
/* If the dropdown is positioned above the parent widget, start selection at the bottom. */
|
||||
if (this->position.y < button_rect.top && list_dim.height > widget_dim.height) this->vscroll->UpdatePosition(INT_MAX);
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(int widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
|
||||
{
|
||||
if (widget == WID_DM_ITEMS) *size = this->items_dim;
|
||||
}
|
||||
|
||||
Point OnInitialPosition([[maybe_unused]] int16_t sm_width, [[maybe_unused]] int16_t sm_height, [[maybe_unused]] int window_number) override
|
||||
{
|
||||
return this->position;
|
||||
@@ -182,7 +231,7 @@ struct DropdownWindow : Window {
|
||||
if (y + item_height - 1 <= ir.bottom) {
|
||||
Rect full{ir.left, y, ir.right, y + item_height - 1};
|
||||
|
||||
bool selected = (this->selected_index == item->result) && item->Selectable();
|
||||
bool selected = (this->selected_result == item->result) && item->Selectable();
|
||||
if (selected) GfxFillRect(full, PC_BLACK);
|
||||
|
||||
item->Draw(full, full.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero), selected, colour);
|
||||
@@ -197,7 +246,7 @@ struct DropdownWindow : Window {
|
||||
int item;
|
||||
if (this->GetDropDownItem(item)) {
|
||||
this->click_delay = 4;
|
||||
this->selected_index = item;
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
@@ -217,7 +266,7 @@ struct DropdownWindow : Window {
|
||||
/* Close the dropdown, so it doesn't affect new window placement.
|
||||
* Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */
|
||||
this->Close();
|
||||
this->parent->OnDropdownSelect(this->parent_button, this->selected_index);
|
||||
this->parent->OnDropdownSelect(this->parent_button, this->selected_result);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -245,8 +294,8 @@ struct DropdownWindow : Window {
|
||||
if (!this->GetDropDownItem(item)) return;
|
||||
}
|
||||
|
||||
if (this->selected_index != item) {
|
||||
this->selected_index = item;
|
||||
if (this->selected_result != item) {
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
@@ -283,65 +332,7 @@ Dimension GetDropDownListDimension(const DropDownList &list)
|
||||
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, int button, Rect wi_rect, Colours wi_colour, bool instant_close)
|
||||
{
|
||||
CloseWindowByClass(WC_DROPDOWN_MENU);
|
||||
|
||||
/* The preferred position is just below the dropdown calling widget */
|
||||
int top = w->top + wi_rect.bottom + 1;
|
||||
|
||||
/* The preferred width equals the calling widget */
|
||||
uint width = wi_rect.Width();
|
||||
|
||||
/* Get the height and width required for the list. */
|
||||
Dimension dim = GetDropDownListDimension(list);
|
||||
dim.width += WidgetDimensions::scaled.dropdownlist.Horizontal();
|
||||
|
||||
/* Scrollbar needed? */
|
||||
bool scroll = false;
|
||||
|
||||
/* Is it better to place the dropdown above the widget? */
|
||||
bool above = false;
|
||||
|
||||
/* Available height below (or above, if the dropdown is placed above the widget). */
|
||||
uint available_height = std::max(GetMainViewBottom() - top - (int)WidgetDimensions::scaled.dropdownlist.Vertical(), 0);
|
||||
|
||||
/* If the dropdown doesn't fully fit below the widget... */
|
||||
if (dim.height > available_height) {
|
||||
|
||||
uint available_height_above = std::max(w->top + wi_rect.top - GetMainViewTop() - (int)WidgetDimensions::scaled.dropdownlist.Vertical(), 0);
|
||||
|
||||
/* Put the dropdown above if there is more available space. */
|
||||
if (available_height_above > available_height) {
|
||||
above = true;
|
||||
available_height = available_height_above;
|
||||
}
|
||||
|
||||
/* If the dropdown doesn't fully fit, we need a dropdown. */
|
||||
if (dim.height > available_height) {
|
||||
scroll = true;
|
||||
uint avg_height = dim.height / (uint)list.size();
|
||||
|
||||
/* Fit the list; create at least one row, even if there is no height available. */
|
||||
uint rows = std::max<uint>(available_height / avg_height, 1);
|
||||
dim.height = rows * avg_height;
|
||||
|
||||
/* Add space for the scrollbar. */
|
||||
dim.width += NWidgetScrollbar::GetVerticalDimension().width;
|
||||
}
|
||||
|
||||
/* Set the top position if needed. */
|
||||
if (above) {
|
||||
top = w->top + wi_rect.top - dim.height - WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
}
|
||||
}
|
||||
|
||||
dim.width = std::max(width, dim.width);
|
||||
|
||||
Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - (int)width : wi_rect.left), top};
|
||||
DropdownWindow *dropdown = new DropdownWindow(w, std::move(list), selected, button, instant_close, dw_pos, dim, wi_colour, scroll);
|
||||
|
||||
/* The dropdown starts scrolling downwards when opening it towards
|
||||
* the top and holding down the mouse button. It can be fooled by
|
||||
* opening the dropdown scrolled to the very bottom. */
|
||||
if (above && scroll) dropdown->vscroll->UpdatePosition(INT_MAX);
|
||||
new DropdownWindow(w, std::move(list), selected, button, wi_rect, instant_close, wi_colour);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user