Skip to content

Commit

Permalink
🎮 Made WalkieTalkie/Resetmode keyhints clickable.
Browse files Browse the repository at this point in the history
Features:
* InputEngine: added `setEventSimulatedValue()` allowing simulated input from UI or other programmatic source.
* GUIUtils: Extended `ImDrawEventHighlighted()` global helper to draw key hints as buttons which simulate input.
* GUIManager: added `RequestStaticMenusBlocking()` so that ad-hoc windows like the WalkieTalkie can block static menus like TopMenubar when hovered by mouse.
  • Loading branch information
ohlidalp committed Jul 9, 2023
1 parent fa36c62 commit f907ddd
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 49 deletions.
79 changes: 46 additions & 33 deletions source/main/gfx/GfxScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,12 @@ void GfxScene::CacheWalkietalkieCommandButtons(const ActorPtr& actor)
{
desc = actor->ar_command_key[i].description.c_str();
}
std::string text = fmt::format("{}: {} {}",
App::GetInputEngine()->getEventCommandTrimmed(eventID),
std::string text = fmt::format("{} {}",
cmdbeam.cmb_is_contraction ? "Retract" : "Extend",
desc);

auto inserted_pair = m_walkietalkie_commandkeys_cache.insert(std::make_pair(eventID, text));
auto inserted_pair = m_walkietalkie_commandkeys_cache.insert(
std::make_pair(eventID, text));
// Only increase the size if we added a new commandkey - there may be multiple commandbeams for the same key.
if (inserted_pair.second)
{
Expand All @@ -442,6 +442,8 @@ void GfxScene::CacheWalkietalkieCommandButtons(const ActorPtr& actor)

void GfxScene::DrawWalkieTalkieLabel(Ogre::Vector3 scene_pos, float cam_dist, const ActorPtr& actor)
{
// Draw a label with icon and usable command keys
// ----------------------------------------------

ImVec2 screen_size = ImGui::GetIO().DisplaySize;
World2ScreenConverter world2screen(
Expand All @@ -459,10 +461,11 @@ void GfxScene::DrawWalkieTalkieLabel(Ogre::Vector3 scene_pos, float cam_dist, co
Ogre::TexturePtr icon = FetchIcon("walkie_talkie.png", "IconsRG");
ImVec2 icon_size(icon->getWidth(), icon->getHeight());

const float ICON_PAD_RIGHT = 2.f;
const float ICON_PAD_RIGHT = 5.f;
ImVec2 total_size = icon_size;

std::string caption;
std::string id_str = "walkie talkie for character";
if (actor)
{
// Cache info
Expand All @@ -473,45 +476,55 @@ void GfxScene::DrawWalkieTalkieLabel(Ogre::Vector3 scene_pos, float cam_dist, co
ImVec2 namesize = ImGui::CalcTextSize(caption.c_str());
total_size.x += ICON_PAD_RIGHT + std::max(namesize.x, m_walkietalkie_commandkeys_screensize.x);
total_size.y = std::max(total_size.y, m_walkietalkie_commandkeys_screensize.y);
}

ImDrawList* drawlist = GetImDummyFullscreenWindow();
ImGuiContext* g = ImGui::GetCurrentContext();
GUIManager::GuiTheme const& theme = App::GetGuiManager()->GetTheme();
// Account for space between event-button and description
total_size + ImGui::GetStyle().ItemSpacing * ImVec2(2.f, 1.f);

// Draw background rectangle
const float PADDING = 4.f;
const ImVec2 PAD2(PADDING, PADDING);
ImVec2 rect_min = (pos - total_size/2) - PAD2;
ImVec2 rect_max = (pos + total_size/2) + PAD2;
drawlist->AddRectFilled(
rect_min, rect_max,
ImColor(theme.semitransparent_window_bg),
ImGui::GetStyle().WindowRounding);
ImVec2 cursor = rect_min + PAD2;

// Draw icon
if (icon)
{
drawlist->AddImage(reinterpret_cast<ImTextureID>(icon->getHandle()), cursor, cursor + icon_size);
cursor.x += icon_size.x + ICON_PAD_RIGHT;
id_str = fmt::format("walkie talkie for actor {}", actor->ar_instance_id).c_str();
}

// Because we want buttons in the label, it must be a window, not a fullscreen dummy for drawing
const float PADDING = 4.f;
const ImVec2 BOXPAD2(PADDING, PADDING);
const ImVec2 BTNPAD2(2.f, 0.f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, BOXPAD2);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, BTNPAD2);
ImVec2 rect_min = (pos - total_size/2) - BOXPAD2;
ImGui::SetNextWindowPos(rect_min);
ImGui::SetNextWindowSize(total_size + BOXPAD2*2);
int window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
ImGui::Begin(id_str.c_str(), nullptr, window_flags);
GUIManager::GuiTheme const& theme = App::GetGuiManager()->GetTheme();

if (actor)
{
// Draw actor name (light text)
drawlist->AddText(g->Font, g->FontSize, cursor, ImColor(ImGui::GetStyle().Colors[ImGuiCol_Text]), caption.c_str());
cursor.y += ImGui::GetTextLineHeight();
// Draw actor name (dark text)
ImGui::SetCursorPosX(icon_size.x + ICON_PAD_RIGHT);
ImGui::TextDisabled(caption.c_str());
ImGui::Separator();

// Draw the commands (darker text, except where active)
const ImU32 color_lo = ImColor(ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
const ImU32 color_hi = ImColor(theme.highlight_text_color);
// Draw the commands (light text with activity highlight)

for (auto& pair: m_walkietalkie_commandkeys_cache)
{
const ImU32 color = (actor->ar_command_key[pair.first].playerInputValue != 0) ? color_hi : color_lo;
drawlist->AddText(g->Font, g->FontSize, cursor, color, pair.second.c_str());
cursor.y += ImGui::GetTextLineHeight();
const bool force_active = (actor->ar_command_key[pair.first].playerInputValue != 0);
ImGui::SetCursorPosX(icon_size.x + ICON_PAD_RIGHT);
ImDrawEventHighlighted(events(pair.first), force_active);
ImGui::SameLine();
ImGui::Text(pair.second.c_str());
}
}

// Draw icon last
if (icon)
{
ImGui::SetCursorPos(BOXPAD2);
ImGui::Image(reinterpret_cast<ImTextureID>(icon->getHandle()), icon_size);
}

App::GetGuiManager()->RequestStaticMenusBlocking(ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows));
ImGui::End();
ImGui::PopStyleVar(); //ImGuiStyleVar_WindowPadding, BOXPAD2);
ImGui::PopStyleVar(); //ImGuiStyleVar_FramePadding, BTNPAD2);
}
2 changes: 1 addition & 1 deletion source/main/gfx/GfxScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class GfxScene
SkidmarkConfig m_skidmark_conf;

// Walkie talkie icon (forwardcommands / importcommands)
std::unordered_map<int, std::string> m_walkietalkie_commandkeys_cache;
std::unordered_map<int, std::string> m_walkietalkie_commandkeys_cache; // eventID -> description
ActorPtr m_walkietalkie_commandkeys_cache_actor;
ImVec2 m_walkietalkie_commandkeys_screensize;
};
Expand Down
10 changes: 9 additions & 1 deletion source/main/gui/GUIManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,16 @@ void GUIManager::ShutdownMyGUI()
}
}

void GUIManager::ApplyGuiCaptureKeyboard()
void GUIManager::ApplyQueuedGuiRequests()
{
m_gui_kb_capture_requested = m_gui_kb_capture_queued;
m_staticmenus_blocking_requested = m_staticmenus_blocking_queued;
};

bool GUIManager::AreStaticMenusAllowed() //!< i.e. top menubar / vehicle UI buttons
{
return (App::GetCameraManager()->GetCurrentBehavior() != CameraManager::CAMERA_BEHAVIOR_FREE &&
!m_staticmenus_blocking_requested && // For ad-hoc windows like walkie-talkie label
!this->ConsoleWindow.IsHovered() &&
!this->GameControls.IsHovered() &&
!this->FrictionSettings.IsHovered() &&
Expand Down Expand Up @@ -317,6 +319,7 @@ void GUIManager::NewImGuiFrame(float dt)

// Reset state
m_gui_kb_capture_queued = false;
m_staticmenus_blocking_queued = false;
}

void GUIManager::SetupImGui()
Expand Down Expand Up @@ -450,6 +453,11 @@ void GUIManager::RequestGuiCaptureKeyboard(bool val)
m_gui_kb_capture_queued = m_gui_kb_capture_queued || val;
}

void GUIManager::RequestStaticMenusBlocking(bool val)
{
m_staticmenus_blocking_queued = m_staticmenus_blocking_queued || val;
}

void GUIManager::WakeUpGUI()
{
m_last_mousemove_time.reset();
Expand Down
6 changes: 4 additions & 2 deletions source/main/gui/GUIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ class GUIManager
void ShowMessageBox(const char* title, const char* text, bool allow_close = true, const char* btn1_text = "OK", const char* btn2_text = nullptr);
void ShowMessageBox(GUI::MessageBoxConfig const& conf);
void RequestGuiCaptureKeyboard(bool val); //!< Pass true during frame to prevent input passing to application
void RequestStaticMenusBlocking(bool val); //!< Pass true during frame to prevent static menus (i.e. top menubar) from displaying
bool IsGuiCaptureKeyboardRequested() const { return m_gui_kb_capture_requested; }
void ApplyGuiCaptureKeyboard(); //!< Call after rendered frame to apply queued value
void ApplyQueuedGuiRequests(); //!< Call after rendered frame to apply queued value
bool AreStaticMenusAllowed(); //!< i.e. top menubar / vehicle UI buttons

void NewImGuiFrame(float dt);
void DrawMainMenuGui();
void DrawSimulationGui(float dt); //!< Touches live data; must be called in sync with sim. thread
Expand Down Expand Up @@ -173,6 +173,8 @@ class GUIManager
GuiTheme m_theme;
bool m_gui_kb_capture_queued = false; //!< Resets and accumulates every frame
bool m_gui_kb_capture_requested = false; //!< Effective value, persistent
bool m_staticmenus_blocking_queued = false; //!< Resets and accumulates every frame
bool m_staticmenus_blocking_requested = false; //!< Effective value, persistent
Ogre::Timer m_last_mousemove_time;
bool m_is_cursor_supressed = false; //!< True if cursor was manually hidden.
};
Expand Down
17 changes: 10 additions & 7 deletions source/main/gui/GUIUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,21 +402,24 @@ void RoR::ImTerminateComboboxString(std::string& target)
target.resize(prev_size + 2, '\0');
}

void RoR::ImDrawEventHighlighted(events input_event)
void RoR::ImDrawEventHighlighted(events input_event, bool force_active /*=false*/)
{
ImVec4 col = ImGui::GetStyle().Colors[ImGuiCol_Text];
if (App::GetInputEngine()->getEventValue(input_event))
if (force_active || App::GetInputEngine()->getEventValue(input_event))
{
col = App::GetGuiManager()->GetTheme().highlight_text_color;
}
std::string text = App::GetInputEngine()->getKeyForCommand(input_event);
const ImVec2 PAD = ImVec2(2.f, 0.f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, PAD);
ImGui::BeginChildFrame(ImGuiID(input_event), ImGui::CalcTextSize(text.c_str()) + PAD*2);
ImGui::TextColored(col, "%s", text.c_str());
ImGui::EndChildFrame();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, PAD);
ImGui::PushStyleColor(ImGuiCol_Text, col);
ImGui::PushID(ImGuiID(input_event));
ImGui::Button(text.c_str());
// `Button()` only returns `true` the moment when pressed, we need continuous input from `IsItemActive()`
App::GetInputEngine()->setEventSimulatedValue(input_event, static_cast<float>(ImGui::IsItemActive()));
ImGui::PopID(); //ImGuiID(input_event)
ImGui::PopStyleColor(); // Text
ImGui::PopStyleVar(); // FramePadding

}

void RoR::ImDrawModifierKeyHighlighted(OIS::KeyCode key)
Expand Down
2 changes: 1 addition & 1 deletion source/main/gui/GUIUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void ImAddItemToComboboxString(std::string& target, std::string const& item);
void ImTerminateComboboxString(std::string& target);

// Input engine helpers
void ImDrawEventHighlighted(events input_event);
void ImDrawEventHighlighted(events input_event, bool force_active=false); ///!< Draws button displaying configured key combo and simulating the event when pressed.
void ImDrawModifierKeyHighlighted(OIS::KeyCode key);

} // namespace RoR
2 changes: 1 addition & 1 deletion source/main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ int main(int argc, char *argv[])
}
} // Render block

App::GetGuiManager()->ApplyGuiCaptureKeyboard();
App::GetGuiManager()->ApplyQueuedGuiRequests();

} // End of main rendering/input loop

Expand Down
20 changes: 17 additions & 3 deletions source/main/utils/InputEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,11 @@ void InputEngine::resetKeys()
}
}

void InputEngine::setEventSimulatedValue(RoR::events eventID, float value)
{
event_values_simulated[eventID] = value;
}

bool InputEngine::getEventBoolValue(int eventID)
{
return (getEventValue(eventID) > 0.5f);
Expand Down Expand Up @@ -900,6 +905,10 @@ bool InputEngine::isEventAnalog(int eventID)

float InputEngine::getEventValue(int eventID, bool pure, InputSourceType valueSource /*= InputSourceType::IST_ANY*/)
{
const float simulatedValue = event_values_simulated[eventID];
if (simulatedValue != 0.f)
return simulatedValue;

float returnValue = 0;
std::vector<event_trigger_t> t_vec = events[eventID];
float value = 0;
Expand Down Expand Up @@ -1174,6 +1183,12 @@ const char* InputEngine::getEventTypeName(eventtypes type)
}

void InputEngine::addEvent(int eventID, event_trigger_t& t)
{
this->addEvent(eventID);
events[eventID].push_back(t);
}

void InputEngine::addEvent(int eventID)
{
uniqueCounter++;

Expand All @@ -1183,9 +1198,8 @@ void InputEngine::addEvent(int eventID, event_trigger_t& t)
if (events.find(eventID) == events.end())
{
events[eventID] = std::vector<event_trigger_t>();
events[eventID].clear();
event_values_simulated[eventID] = false;
}
events[eventID].push_back(t);
}

void InputEngine::addEventDefault(int eventID, int deviceID /*= -1*/)
Expand Down Expand Up @@ -1495,7 +1509,7 @@ bool InputEngine::processLine(const char* line, int deviceID)
if (eventID == -1)
return false;
// Insert event with no trigger
events.insert(std::make_pair(eventID, std::vector<event_trigger_t>()));
addEvent(eventID);
return true;
}
case ET_MouseButton:
Expand Down
3 changes: 3 additions & 0 deletions source/main/utils/InputEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ class InputEngine
void ProcessKeyRelease(const OIS::KeyEvent& arg);
void ProcessJoystickEvent(const OIS::JoyStickEvent& arg);
void resetKeys();
void setEventSimulatedValue(events eventID, float value);

// Event info

Expand Down Expand Up @@ -513,6 +514,7 @@ class InputEngine
// Event management

void addEvent(int eventID, event_trigger_t& t); //!< Registers new trigger for this event.
void addEvent(int eventID); //!< Registers new event without trigger.
void addEventDefault(int eventID, int deviceID = -1); //!< Adds a new trigger with builtin value for this event.
void updateEvent(int eventID, const event_trigger_t& t);
void eraseEvent(int eventID, const event_trigger_t* t);
Expand Down Expand Up @@ -573,6 +575,7 @@ class InputEngine

// define event aliases
std::map<int, std::vector<event_trigger_t>> events;
std::map<int, float> event_values_simulated;
std::map<int, float> event_times;
std::string m_loaded_configs[MAX_JOYSTICKS];
bool loadMapping(Ogre::String fileName, int deviceID);
Expand Down

0 comments on commit f907ddd

Please sign in to comment.