diff --git a/source/main/Application.h b/source/main/Application.h index ff3b816694..312704c3ed 100644 --- a/source/main/Application.h +++ b/source/main/Application.h @@ -144,6 +144,7 @@ enum MsgType MSG_GUI_DOWNLOAD_PROGRESS, MSG_GUI_DOWNLOAD_FINISHED, MSG_GUI_REFRESH_TUNING_MENU_REQUESTED, + MSG_GUI_SHOW_CHATBOX_REQUESTED, //!< Description = message or server command to pre-fill in the chatbox (deleting whatever was there previously) // Editing MSG_EDI_MODIFY_GROUNDMODEL_REQUESTED, //!< Payload = RoR::ground_model_t* (weak) MSG_EDI_ENTER_TERRN_EDITOR_REQUESTED, diff --git a/source/main/gui/GUIUtils.cpp b/source/main/gui/GUIUtils.cpp index 3d90c3cc48..d422736121 100644 --- a/source/main/gui/GUIUtils.cpp +++ b/source/main/gui/GUIUtils.cpp @@ -515,6 +515,28 @@ bool RoR::ImButtonHoldToConfirm(const std::string& btn_idstr, const bool smallbu return false; } +bool RoR::ImMoveTextInputCursorToEnd(const char* label) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImGuiID id = window->GetID(label); + ImGuiContext& g = *GImGui; + + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiInputTextState* state = NULL; + if (g.InputTextState.ID != id) + { + return false; + } + + state = &g.InputTextState; + // based on `ImGuiInputTextState::CursorClamp()` + state->Stb.cursor = state->CurLenW; + state->Stb.select_start = 0; + state->Stb.select_end = 0; + + return true; +} + bool RoR::GetScreenPosFromWorldPos(Ogre::Vector3 const& world_pos, ImVec2& out_screen) { ImVec2 screen_size = ImGui::GetIO().DisplaySize; diff --git a/source/main/gui/GUIUtils.h b/source/main/gui/GUIUtils.h index 081a987e00..708f9113f6 100644 --- a/source/main/gui/GUIUtils.h +++ b/source/main/gui/GUIUtils.h @@ -91,4 +91,7 @@ ImVec2 ImCalcEventHighlightedSize(events input_event); // Draws button which must be held for a period to report "clicked" - shows a tooltip with countdown progressbar. bool ImButtonHoldToConfirm(const std::string& btn_idstr, const bool smallbutton, const float time_limit); +// Returns true if succeeded (needs the text box to have focus). +bool ImMoveTextInputCursorToEnd(const char* label); + } // namespace RoR diff --git a/source/main/gui/panels/GUI_GameChatBox.cpp b/source/main/gui/panels/GUI_GameChatBox.cpp index d0ac0daf9d..2d0f961dc2 100644 --- a/source/main/gui/panels/GUI_GameChatBox.cpp +++ b/source/main/gui/panels/GUI_GameChatBox.cpp @@ -26,6 +26,7 @@ #include "ChatSystem.h" #include "Console.h" #include "GUIManager.h" +#include "GUIUtils.h" #include "Language.h" #include "InputEngine.h" @@ -148,8 +149,12 @@ void GameChatBox::Draw() ImGui::SetKeyboardFocusHere(); m_kb_focused = true; } + if (m_scheduled_move_textcursor_to_end && ImMoveTextInputCursorToEnd("##chatbox")) + { + m_scheduled_move_textcursor_to_end = false; + } const ImGuiInputTextFlags cmd_flags = ImGuiInputTextFlags_EnterReturnsTrue; - if (ImGui::InputText("", m_msg_buffer.GetBuffer(), m_msg_buffer.GetCapacity(), cmd_flags)) + if (ImGui::InputText("##chatbox", m_msg_buffer.GetBuffer(), m_msg_buffer.GetCapacity(), cmd_flags)) { if (App::mp_state->getEnum() == MpState::CONNECTED) { diff --git a/source/main/gui/panels/GUI_GameChatBox.h b/source/main/gui/panels/GUI_GameChatBox.h index 78b816801c..f5cf8a2801 100644 --- a/source/main/gui/panels/GUI_GameChatBox.h +++ b/source/main/gui/panels/GUI_GameChatBox.h @@ -47,6 +47,7 @@ class GameChatBox void Draw(); ConsoleView& GetConsoleView() { return m_console_view; } + void AssignBuffer(const std::string& buffer) { m_msg_buffer = buffer; m_scheduled_move_textcursor_to_end = true; } private: void SubmitMessage(); //!< Flush the user input box @@ -57,6 +58,7 @@ class GameChatBox ConsoleView m_console_view; bool initialized = true; bool init_scroll = false; + bool m_scheduled_move_textcursor_to_end = false; }; } // namespace GUI diff --git a/source/main/gui/panels/GUI_MultiplayerClientList.cpp b/source/main/gui/panels/GUI_MultiplayerClientList.cpp index 90c63c19d4..9a76f1f676 100644 --- a/source/main/gui/panels/GUI_MultiplayerClientList.cpp +++ b/source/main/gui/panels/GUI_MultiplayerClientList.cpp @@ -111,7 +111,11 @@ void MpClientList::Draw() const ImVec2 hover_tl = ImGui::GetCursorScreenPos(); // PeerOptions popup menu - if (ImGui::Button(" < ")) + if (user.uniqueid == local_user.uniqueid) + { + ImGui::Dummy(ImVec2(ImGui::CalcTextSize(" < ").x + ImGui::GetStyle().FramePadding.x*2 , ImGui::GetTextLineHeight())); + } + else if (ImGui::Button(" < ")) { if (m_peeropts_menu_active_user_vectorpos == vectorpos) { @@ -296,6 +300,15 @@ void MpClientList::DrawPeerOptCheckbox(const BitMask_t flag, const std::string& } } +void RoR::GUI::MpClientList::DrawServerCommandBtn(const std::string& cmdfmt, const std::string& label) +{ + std::string chatmsg = fmt::format(cmdfmt, m_users[m_peeropts_menu_active_user_vectorpos].uniqueid); + if (ImGui::Button(label.c_str())) + { + App::GetGameContext()->PushMessage(Message(MSG_GUI_SHOW_CHATBOX_REQUESTED, chatmsg)); + } +} + void MpClientList::DrawPeerOptionsMenu() { if (m_peeropts_menu_active_user_vectorpos == -1) @@ -313,11 +326,19 @@ void MpClientList::DrawPeerOptionsMenu() const int flags = ImGuiWindowFlags_NoDecoration; if (ImGui::Begin("PeerOptions", nullptr, flags)) { - ImGui::TextDisabled("%s", _LC("MultiplayerClientList", "Peer options")); - ImGui::Separator(); + ImGui::TextDisabled("%s", _LC("MultiplayerClientList", "Local actions")); this->DrawPeerOptCheckbox(RoRnet::PEEROPT_MUTE_CHAT, _LC("MultiplayerClientList", "Mute chat")); this->DrawPeerOptCheckbox(RoRnet::PEEROPT_MUTE_ACTORS, _LC("MultiplayerClientList", "Mute actors")); this->DrawPeerOptCheckbox(RoRnet::PEEROPT_HIDE_ACTORS, _LC("MultiplayerClientList", "Hide actors")); + ImGui::Separator(); + ImGui::TextDisabled("%s", _LC("MultiplayerClientList", "Server commands")); + this->DrawServerCommandBtn("!report {} Please enter reason: ", _LC("MultiplayerClientList", "Report")); + const int32_t authstatus = App::GetNetwork()->GetLocalUserData().authstatus; + if (BITMASK_IS_1(authstatus, RoRnet::AUTH_ADMIN) || BITMASK_IS_1(authstatus, RoRnet::AUTH_MOD)) + { + this->DrawServerCommandBtn("!kick {}", _LC("MultiplayerClientList", "Kick")); + this->DrawServerCommandBtn("!ban {}", _LC("MultiplayerClientList", "Ban")); + } m_peeropts_menu_corner_br = ImGui::GetCursorScreenPos(); ImGui::End(); diff --git a/source/main/gui/panels/GUI_MultiplayerClientList.h b/source/main/gui/panels/GUI_MultiplayerClientList.h index 5b7225252d..c9f7ad87ee 100644 --- a/source/main/gui/panels/GUI_MultiplayerClientList.h +++ b/source/main/gui/panels/GUI_MultiplayerClientList.h @@ -51,8 +51,9 @@ class MpClientList // Peer options menu - opened by [<] button, closes when mouse cursor leaves. void DrawPeerOptionsMenu(); void DrawPeerOptCheckbox(const BitMask_t flag, const std::string& label); - const int PEEROPTS_MENU_WIDTH = 125; - const int PEEROPTS_HOVER_PAD = 150; + void DrawServerCommandBtn(const std::string& cmdfmt, const std::string& label); + const int PEEROPTS_MENU_WIDTH = 150; + const int PEEROPTS_HOVER_PAD = 170; int m_peeropts_menu_active_user_vectorpos = -1; ImVec2 m_peeropts_menu_corner_tl = ImVec2(0, 0); ImVec2 m_peeropts_menu_corner_br = ImVec2(0, 0); diff --git a/source/main/main.cpp b/source/main/main.cpp index 39eb5625cc..e1bc450672 100644 --- a/source/main/main.cpp +++ b/source/main/main.cpp @@ -1515,6 +1515,23 @@ int main(int argc, char *argv[]) break; } + case MSG_GUI_SHOW_CHATBOX_REQUESTED: + { + try + { + App::GetGuiManager()->ChatBox.SetVisible(true); + if (m.description != "") + { + App::GetGuiManager()->ChatBox.AssignBuffer(m.description); + } + } + catch (...) + { + HandleMsgQueueException(m.type); + } + break; + } + // -- Editing events -- case MSG_EDI_MODIFY_GROUNDMODEL_REQUESTED: