diff --git a/src/lang/english.txt b/src/lang/english.txt index 33b9f155b7..b72538af7f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3695,6 +3695,7 @@ STR_EDIT_SIGN_CAPTION :{WHITE}Edit sig STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}Centre the main view on sign location. Ctrl+Click to open a new viewport on sign location STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}Go to next sign STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Go to previous sign +STR_EDIT_SIGN_TEXT_COLOUR_TOOLTIP :{BLACK}Colour of the sign's text STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Enter a name for the sign diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 961bb7e10f..589b549ba8 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -412,6 +412,7 @@ enum SaveLoadVersion : uint16_t { SLV_DOCKS_UNDER_BRIDGES, ///< 360 PR#14594 Allow docks under bridges. SLV_LOCKS_UNDER_BRIDGES, ///< 361 PR#14595 Allow locks under bridges. SLV_ENGINE_MULTI_RAILTYPE, ///< 362 PR#14357 v15.0 Train engines can have multiple railtypes. + SLV_SIGN_TEXT_COLOURS, ///< 363 PR#14743 Configurable sign text colors in scenario editor. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/signs_sl.cpp b/src/saveload/signs_sl.cpp index 1f3b36b98b..f0486ad5e9 100644 --- a/src/saveload/signs_sl.cpp +++ b/src/saveload/signs_sl.cpp @@ -19,15 +19,16 @@ /** Description of a sign within the savegame. */ static const SaveLoad _sign_desc[] = { - SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84), + SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84), SLE_CONDSSTR(Sign, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), - SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), - SLE_CONDVAR(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION), + SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), + SLE_CONDVAR(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION), + SLE_CONDVAR(Sign, text_colour, SLE_UINT8, SLV_SIGN_TEXT_COLOURS, SL_MAX_VERSION), }; struct SIGNChunkHandler : ChunkHandler { diff --git a/src/script/api/script_sign.cpp b/src/script/api/script_sign.cpp index e95511f1fe..3a3730378e 100644 --- a/src/script/api/script_sign.cpp +++ b/src/script/api/script_sign.cpp @@ -45,7 +45,7 @@ EnforcePreconditionEncodedText(false, text); EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_SIGN_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG); - return ScriptObject::Command::Do(sign_id, text); + return ScriptObject::Command::Do(sign_id, text, INVALID_COLOUR); } /* static */ std::optional ScriptSign::GetName(SignID sign_id) @@ -67,7 +67,7 @@ { EnforceDeityOrCompanyModeValid(false); EnforcePrecondition(false, IsValidSign(sign_id)); - return ScriptObject::Command::Do(sign_id, ""); + return ScriptObject::Command::Do(sign_id, "", INVALID_COLOUR); } /* static */ SignID ScriptSign::BuildSign(TileIndex location, Text *name) diff --git a/src/signs_base.h b/src/signs_base.h index 57d5e34a7c..195d56225b 100644 --- a/src/signs_base.h +++ b/src/signs_base.h @@ -24,7 +24,8 @@ struct Sign : SignPool::PoolItem<&_sign_pool> { int32_t x = 0; int32_t y = 0; int32_t z = 0; - Owner owner = INVALID_OWNER; // placed by this company. Anyone can delete them though. OWNER_NONE for gray signs from old games. + Owner owner = INVALID_OWNER; // Placed by this company. Anyone can delete them though. OWNER_NONE for gray signs from old games. + Colours text_colour = COLOUR_WHITE; // Colour of the sign's text. Only relevant for OWNER_DEITY. Sign() {} Sign(Owner owner, int32_t x, int32_t y, int32_t z, const std::string &name) : name(name), x(x), y(y), z(z), owner(owner) {} diff --git a/src/signs_cmd.cpp b/src/signs_cmd.cpp index 3f93e29726..f8b4fb1965 100644 --- a/src/signs_cmd.cpp +++ b/src/signs_cmd.cpp @@ -62,9 +62,10 @@ std::tuple CmdPlaceSign(DoCommandFlags flags, TileIndex til * @param flags type of operation * @param sign_id index of the sign to be renamed/removed * @param text the new name or an empty string when resetting to the default + * @param text_colour colour of the sign's text. Only relevant for OWNER_DEITY. Use INVALID_COLOUR to keep the current colour. * @return the cost of this operation or an error */ -CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::string &text) +CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::string &text, Colours text_colour) { Sign *si = Sign::GetIfValid(sign_id); if (si == nullptr) return CMD_ERROR; @@ -77,6 +78,7 @@ CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::strin if (flags.Test(DoCommandFlag::Execute)) { /* Assign the new one */ si->name = text; + if (text_colour != INVALID_COLOUR) si->text_colour = text_colour; if (_game_mode != GM_EDITOR) si->owner = _current_company; si->UpdateVirtCoord(); diff --git a/src/signs_cmd.h b/src/signs_cmd.h index 75f66b0044..4faa7e5b31 100644 --- a/src/signs_cmd.h +++ b/src/signs_cmd.h @@ -12,9 +12,10 @@ #include "command_type.h" #include "signs_type.h" +#include "gfx_type.h" std::tuple CmdPlaceSign(DoCommandFlags flags, TileIndex tile, const std::string &text); -CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::string &text); +CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::string &text, Colours text_colour); CommandCost CmdMoveSign(DoCommandFlags flags, SignID sign_id, TileIndex tile); DEF_CMD_TRAIT(CMD_PLACE_SIGN, CmdPlaceSign, CommandFlag::Deity, CommandType::OtherManagement) diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index b0b2c94e55..b675bf72aa 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -30,6 +30,8 @@ #include "signs_cmd.h" #include "timer/timer.h" #include "timer/timer_window.h" +#include "dropdown_common_type.h" +#include "dropdown_func.h" #include "widgets/sign_widget.h" @@ -379,12 +381,13 @@ Window *ShowSignList() * Actually rename the sign. * @param index the sign to rename. * @param text the new name. + * @param text_colour Colour of the text if the sign is owned by OWNER_DEITY. * @return true if the window will already be removed after returning. */ -static bool RenameSign(SignID index, std::string_view text) +static bool RenameSign(SignID index, std::string_view text, Colours text_colour) { bool remove = text.empty(); - Command::Post(remove ? STR_ERROR_CAN_T_DELETE_SIGN : STR_ERROR_CAN_T_CHANGE_SIGN_NAME, index, std::string{text}); + Command::Post(remove ? STR_ERROR_CAN_T_DELETE_SIGN : STR_ERROR_CAN_T_CHANGE_SIGN_NAME, index, std::string{text}, text_colour); return remove; } @@ -402,6 +405,7 @@ struct SignWindow : Window, SignList { QueryString name_editbox; SignID cur_sign{}; WidgetID last_user_action = INVALID_WIDGET; ///< Last started user action. + std::optional new_colour; ///< New colour selected by the user. Will be assigned when the OK button is clicked. SignWindow(WindowDesc &desc, const Sign *si) : Window(desc), name_editbox(MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS) { @@ -412,6 +416,11 @@ struct SignWindow : Window, SignList { this->InitNested(WN_QUERY_STRING_SIGN); + if (_game_mode != GameMode::GM_EDITOR) { + this->GetWidget(WID_QES_COLOUR_PANE)->SetDisplayedPlane(SZSP_VERTICAL); + this->ReInit(); + } + UpdateSignEditWindow(si); this->SetFocusedWidget(WID_QES_TEXT); } @@ -426,8 +435,10 @@ struct SignWindow : Window, SignList { } this->cur_sign = si->index; + this->new_colour.reset(); this->SetWidgetDirty(WID_QES_TEXT); + this->SetWidgetDirty(WID_QES_COLOUR); this->SetFocusedWidget(WID_QES_TEXT); } @@ -464,14 +475,47 @@ struct SignWindow : Window, SignList { case WID_QES_CAPTION: return GetString(this->name_editbox.caption); + case WID_QES_COLOUR: + return GetString(STR_COLOUR_DARK_BLUE + this->new_colour.value_or(Sign::Get(this->cur_sign)->text_colour)); + default: return this->Window::GetWidgetString(widget, stringid); } } + void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override + { + if (widget == WID_QES_COLOUR) { + const Dimension square_size = GetSpriteSize(SPR_SQUARE); + const uint string_padding = square_size.width + WidgetDimensions::scaled.hsep_normal + padding.width; + for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; ++colour) { + size.width = std::max(size.width, GetStringBoundingBox(STR_COLOUR_DARK_BLUE + colour).width + string_padding); + } + size.width = std::max(size.width, GetStringBoundingBox(STR_COLOUR_DEFAULT).width + string_padding); + return; + } + + Window::UpdateWidgetSize(widget, size, padding, fill, resize); + } + + void ShowColourDropDownMenu() + { + DropDownList list; + for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; ++colour) { + list.emplace_back(MakeDropDownListIconItem(SPR_SQUARE, GetColourPalette(colour), STR_COLOUR_DARK_BLUE + colour, colour)); + } + const int selected = this->new_colour.value_or(Sign::Get(this->cur_sign)->text_colour); + ShowDropDownList(this, std::move(list), selected, WID_QES_COLOUR); + } + void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { + case WID_QES_COLOUR: { + ShowColourDropDownMenu(); + break; + } + case WID_QES_LOCATION: { const Sign *si = Sign::Get(this->cur_sign); TileIndex tile = TileVirtXY(si->x, si->y); @@ -500,7 +544,7 @@ struct SignWindow : Window, SignList { } case WID_QES_OK: - if (RenameSign(this->cur_sign, this->name_editbox.text.GetText())) break; + if (RenameSign(this->cur_sign, this->name_editbox.text.GetText(), this->new_colour.value_or(INVALID_COLOUR))) break; [[fallthrough]]; case WID_QES_CANCEL: @@ -509,7 +553,7 @@ struct SignWindow : Window, SignList { case WID_QES_DELETE: /* Only need to set the buffer to null, the rest is handled as the OK button */ - RenameSign(this->cur_sign, ""); + RenameSign(this->cur_sign, "", INVALID_COLOUR); /* don't delete this, we are deleted in Sign::~Sign() -> DeleteRenameSignWindow() */ break; @@ -524,7 +568,7 @@ struct SignWindow : Window, SignList { { switch (this->last_user_action) { case WID_QES_MOVE: // Place sign button - RenameSign(this->cur_sign, this->name_editbox.text.GetText()); + RenameSign(this->cur_sign, this->name_editbox.text.GetText(), this->new_colour.value_or(INVALID_COLOUR)); MoveSign(this->cur_sign, tile); this->Close(); break; @@ -537,6 +581,11 @@ struct SignWindow : Window, SignList { { this->RaiseButtons(); } + + void OnDropdownSelect(WidgetID widget, int index, int) override + { + if (widget == WID_QES_COLOUR) this->new_colour = static_cast(index); + } }; static constexpr std::initializer_list _nested_query_sign_edit_widgets = { @@ -553,6 +602,10 @@ static constexpr std::initializer_list _nested_query_sign_edit_widg NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_CANCEL), SetMinimalSize(60, 12), SetStringTip(STR_BUTTON_CANCEL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_DELETE), SetMinimalSize(60, 12), SetStringTip(STR_TOWN_VIEW_DELETE_BUTTON), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QES_MOVE), SetMinimalSize(60, 12), SetStringTip(STR_BUTTON_MOVE), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_QES_COLOUR_PANE), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_QES_COLOUR), SetMinimalSize(60, 12), SetToolTip(STR_EDIT_SIGN_TEXT_COLOUR_TOOLTIP), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_PREVIOUS), SetMinimalSize(11, 12), SetArrowWidgetTypeTip(AWV_DECREASE, STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_NEXT), SetMinimalSize(11, 12), SetArrowWidgetTypeTip(AWV_INCREASE, STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP), EndContainer(), @@ -575,7 +628,7 @@ void HandleClickOnSign(const Sign *si) if (!CompanyCanEditSign(si)) return; if (_ctrl_pressed && (si->owner == _local_company || (si->owner == OWNER_DEITY && _game_mode == GM_EDITOR))) { - RenameSign(si->index, ""); + RenameSign(si->index, "", INVALID_COLOUR); return; } diff --git a/src/viewport.cpp b/src/viewport.cpp index 6427a1465e..fac0227625 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1404,12 +1404,18 @@ static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vectortext_colour == COLOUR_WHITE ? INVALID_COLOUR : si->text_colour; + std::string *str = ViewportAddString(dpi, &si->sign, (si->owner == OWNER_DEITY) ? deity_flags : flags, - (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner])); + (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? deity_colour : _company_colours[si->owner])); if (str == nullptr) continue; *str = GetString(STR_SIGN_NAME, si->index); diff --git a/src/widgets/sign_widget.h b/src/widgets/sign_widget.h index e8ffa0c131..9e7c328dfa 100644 --- a/src/widgets/sign_widget.h +++ b/src/widgets/sign_widget.h @@ -22,15 +22,17 @@ enum SignListWidgets : WidgetID { /** Widgets of the #SignWindow class. */ enum QueryEditSignWidgets : WidgetID { - WID_QES_CAPTION, ///< Caption of the window. - WID_QES_LOCATION, ///< Scroll to sign location. - WID_QES_TEXT, ///< Text of the query. - WID_QES_OK, ///< OK button. - WID_QES_CANCEL, ///< Cancel button. - WID_QES_DELETE, ///< Delete button. - WID_QES_MOVE, ///< Move Sign button. - WID_QES_PREVIOUS, ///< Previous button. - WID_QES_NEXT, ///< Next button. + WID_QES_CAPTION, ///< Caption of the window. + WID_QES_LOCATION, ///< Scroll to sign location. + WID_QES_TEXT, ///< Text of the query. + WID_QES_OK, ///< OK button. + WID_QES_CANCEL, ///< Cancel button. + WID_QES_DELETE, ///< Delete button. + WID_QES_COLOUR_PANE, ///< Pane to show/hide the color dropdown. + WID_QES_COLOUR, ///< Colour selection dropdown. + WID_QES_MOVE, ///< Move Sign button. + WID_QES_PREVIOUS, ///< Previous button. + WID_QES_NEXT, ///< Next button. }; #endif /* SIGN_WIDGET_H */