From 6c2abde5c99a236453b795abaa6d7d70105e31f7 Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 26 Dec 2025 14:50:33 +0500 Subject: Just a big Ctrl+H refactoring --- src/game/client/gui/CMakeLists.txt | 8 +- src/game/client/gui/background.cc | 14 +- src/game/client/gui/background.hh | 4 +- src/game/client/gui/bother.cc | 12 +- src/game/client/gui/bother.hh | 7 +- src/game/client/gui/chat.cc | 30 +-- src/game/client/gui/chat.hh | 8 +- src/game/client/gui/crosshair.cc | 6 +- src/game/client/gui/crosshair.hh | 4 +- src/game/client/gui/direct_connection.cc | 20 +- src/game/client/gui/direct_connection.hh | 4 +- src/game/client/gui/hotbar.cc | 54 ++-- src/game/client/gui/hotbar.hh | 17 +- src/game/client/gui/imdraw_ext.cc | 34 --- src/game/client/gui/imdraw_ext.hh | 17 -- src/game/client/gui/imutils_button.cc | 95 +++++++ src/game/client/gui/imutils_button.hh | 14 + src/game/client/gui/imutils_popup.cc | 56 ++++ src/game/client/gui/imutils_popup.hh | 11 + src/game/client/gui/imutils_text.cc | 46 ++++ src/game/client/gui/imutils_text.hh | 9 + src/game/client/gui/language.cc | 38 +-- src/game/client/gui/language.hh | 19 +- src/game/client/gui/main_menu.cc | 190 ++++++-------- src/game/client/gui/main_menu.hh | 4 +- src/game/client/gui/message_box.cc | 20 +- src/game/client/gui/message_box.hh | 11 +- src/game/client/gui/metrics.cc | 28 +- src/game/client/gui/metrics.hh | 4 +- src/game/client/gui/play_menu.cc | 65 +++-- src/game/client/gui/play_menu.hh | 4 +- src/game/client/gui/progress_bar.cc | 16 +- src/game/client/gui/progress_bar.hh | 11 +- src/game/client/gui/scoreboard.cc | 4 +- src/game/client/gui/scoreboard.hh | 4 +- src/game/client/gui/settings.cc | 434 +++++++++++++++---------------- src/game/client/gui/splash.cc | 32 +-- src/game/client/gui/splash.hh | 4 +- src/game/client/gui/status_lines.cc | 38 +-- src/game/client/gui/status_lines.hh | 11 +- src/game/client/gui/window_title.cc | 2 +- src/game/client/gui/window_title.hh | 4 +- 42 files changed, 763 insertions(+), 650 deletions(-) delete mode 100644 src/game/client/gui/imdraw_ext.cc delete mode 100644 src/game/client/gui/imdraw_ext.hh create mode 100644 src/game/client/gui/imutils_button.cc create mode 100644 src/game/client/gui/imutils_button.hh create mode 100644 src/game/client/gui/imutils_popup.cc create mode 100644 src/game/client/gui/imutils_popup.hh create mode 100644 src/game/client/gui/imutils_text.cc create mode 100644 src/game/client/gui/imutils_text.hh (limited to 'src/game/client/gui') diff --git a/src/game/client/gui/CMakeLists.txt b/src/game/client/gui/CMakeLists.txt index 46d64a1..e3d8b7a 100644 --- a/src/game/client/gui/CMakeLists.txt +++ b/src/game/client/gui/CMakeLists.txt @@ -12,8 +12,12 @@ target_sources(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/gui_screen.hh" "${CMAKE_CURRENT_LIST_DIR}/hotbar.cc" "${CMAKE_CURRENT_LIST_DIR}/hotbar.hh" - "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.cc" - "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.hh" + "${CMAKE_CURRENT_LIST_DIR}/imutils_button.cc" + "${CMAKE_CURRENT_LIST_DIR}/imutils_button.hh" + "${CMAKE_CURRENT_LIST_DIR}/imutils_popup.cc" + "${CMAKE_CURRENT_LIST_DIR}/imutils_popup.hh" + "${CMAKE_CURRENT_LIST_DIR}/imutils_text.cc" + "${CMAKE_CURRENT_LIST_DIR}/imutils_text.hh" "${CMAKE_CURRENT_LIST_DIR}/language.cc" "${CMAKE_CURRENT_LIST_DIR}/language.hh" "${CMAKE_CURRENT_LIST_DIR}/main_menu.cc" diff --git a/src/game/client/gui/background.cc b/src/game/client/gui/background.cc index 50fef01..81fb764 100644 --- a/src/game/client/gui/background.cc +++ b/src/game/client/gui/background.cc @@ -12,9 +12,9 @@ static resource_ptr texture; -void gui::background::init(void) +void background::init(void) { - texture = resource::load("textures/gui/background.png", TEXTURE_GUI_LOAD_VFLIP); + texture = resource::load("textures/gui/background.png"); if(texture == nullptr) { spdlog::critical("background: texture load failed"); @@ -22,18 +22,14 @@ void gui::background::init(void) } } -void gui::background::shutdown(void) +void background::shutdown(void) { texture = nullptr; } -void gui::background::layout(void) +void background::layout(void) { auto viewport = ImGui::GetMainViewport(); auto draw_list = ImGui::GetBackgroundDrawList(); - - auto scaled_width = 0.75f * static_cast(globals::width / globals::gui_scale); - auto scaled_height = 0.75f * static_cast(globals::height / globals::gui_scale); - auto scale_uv = ImVec2(scaled_width / static_cast(texture->size.x), scaled_height / static_cast(texture->size.y)); - draw_list->AddImage(texture->handle, ImVec2(0.0f, 0.0f), viewport->Size, ImVec2(0.0f, 0.0f), scale_uv); + draw_list->AddImage(texture->handle, {}, viewport->Size); } diff --git a/src/game/client/gui/background.hh b/src/game/client/gui/background.hh index 5c72a3f..51497d1 100644 --- a/src/game/client/gui/background.hh +++ b/src/game/client/gui/background.hh @@ -1,8 +1,8 @@ #pragma once -namespace gui::background +namespace background { void init(void); void shutdown(void); void layout(void); -} // namespace gui::background +} // namespace background diff --git a/src/game/client/gui/bother.cc b/src/game/client/gui/bother.cc index a045284..7309bbc 100644 --- a/src/game/client/gui/bother.cc +++ b/src/game/client/gui/bother.cc @@ -28,7 +28,7 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) bother_set.erase(identity); - gui::BotherResponseEvent event; + BotherResponseEvent event; event.identity = identity; event.is_server_unreachable = false; event.num_players = packet.num_players; @@ -42,7 +42,7 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) enet_peer_disconnect(packet.peer, protocol::CHANNEL); } -void gui::bother::init(void) +void bother::init(void) { bother_host = enet_host_create(nullptr, BOTHER_PEERS, 1, 0, 0); bother_dispatcher.clear(); @@ -51,14 +51,14 @@ void gui::bother::init(void) bother_dispatcher.sink().connect<&on_status_response_packet>(); } -void gui::bother::shutdown(void) +void bother::shutdown(void) { enet_host_destroy(bother_host); bother_dispatcher.clear(); bother_set.clear(); } -void gui::bother::update_late(void) +void bother::update_late(void) { unsigned int free_peers = 0U; @@ -121,7 +121,7 @@ void gui::bother::update_late(void) } } -void gui::bother::ping(unsigned int identity, std::string_view host, std::uint16_t port) +void bother::ping(unsigned int identity, std::string_view host, std::uint16_t port) { if(bother_set.count(identity)) { // Already in the process @@ -143,7 +143,7 @@ void gui::bother::ping(unsigned int identity, std::string_view host, std::uint16 bother_queue.push_back(item); } -void gui::bother::cancel(unsigned int identity) +void bother::cancel(unsigned int identity) { bother_set.erase(identity); diff --git a/src/game/client/gui/bother.hh b/src/game/client/gui/bother.hh index b0355ca..ed4f84f 100644 --- a/src/game/client/gui/bother.hh +++ b/src/game/client/gui/bother.hh @@ -1,7 +1,5 @@ #pragma once -namespace gui -{ struct BotherResponseEvent final { unsigned int identity; bool is_server_unreachable; @@ -12,13 +10,12 @@ struct BotherResponseEvent final { std::uint32_t game_version_patch; std::string motd; }; -} // namespace gui -namespace gui::bother +namespace bother { void init(void); void shutdown(void); void update_late(void); void ping(unsigned int identity, std::string_view host, std::uint16_t port); void cancel(unsigned int identity); -} // namespace gui::bother +} // namespace bother diff --git a/src/game/client/gui/chat.cc b/src/game/client/gui/chat.cc index 70a1668..76260af 100644 --- a/src/game/client/gui/chat.cc +++ b/src/game/client/gui/chat.cc @@ -16,7 +16,7 @@ #include "client/config/keybind.hh" #include "client/gui/gui_screen.hh" -#include "client/gui/imdraw_ext.hh" +#include "client/gui/imutils_text.hh" #include "client/gui/language.hh" #include "client/gui/settings.hh" @@ -65,7 +65,7 @@ static void append_player_join(const std::string& sender) { GuiChatMessage message; message.spawn = globals::curtime; - message.text = std::format("{} {}", sender, gui::language::resolve("chat.client_join")); + message.text = std::format("{} {}", sender, language::resolve("chat.client_join")); message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); history.push_back(message); @@ -78,7 +78,7 @@ static void append_player_leave(const std::string& sender, const std::string& re { GuiChatMessage message; message.spawn = globals::curtime; - message.text = std::format("{} {} ({})", sender, gui::language::resolve("chat.client_left"), gui::language::resolve(reason.c_str())); + message.text = std::format("{} {} ({})", sender, language::resolve("chat.client_left"), language::resolve(reason.c_str())); message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); history.push_back(message); @@ -105,7 +105,7 @@ static void on_chat_message_packet(const protocol::ChatMessage& packet) } } -static void on_glfw_key(const io::GlfwKeyEvent& event) +static void on_glfw_key(const GlfwKeyEvent& event) { if(event.action == GLFW_PRESS) { if((event.key == GLFW_KEY_ENTER) && (globals::gui_screen == GUI_CHAT)) { @@ -138,7 +138,7 @@ static void on_glfw_key(const io::GlfwKeyEvent& event) } } -void gui::client_chat::init(void) +void client_chat::init(void) { globals::client_config.add_value("chat.key", key_chat); globals::client_config.add_value("chat.history_size", history_size); @@ -147,28 +147,28 @@ void gui::client_chat::init(void) settings::add_slider(1, history_size, settings_location::VIDEO_GUI, "chat.history_size", false); globals::dispatcher.sink().connect<&on_chat_message_packet>(); - globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_glfw_key>(); sfx_chat_message = resource::load("sounds/ui/chat_message.wav"); } -void gui::client_chat::init_late(void) +void client_chat::init_late(void) { } -void gui::client_chat::shutdown(void) +void client_chat::shutdown(void) { sfx_chat_message = nullptr; } -void gui::client_chat::update(void) +void client_chat::update(void) { while(history.size() > history_size.get_value()) { history.pop_front(); } } -void gui::client_chat::layout(void) +void client_chat::layout(void) { auto viewport = ImGui::GetMainViewport(); auto window_start = ImVec2(0.0f, 0.0f); @@ -177,7 +177,7 @@ void gui::client_chat::layout(void) ImGui::SetNextWindowPos(window_start); ImGui::SetNextWindowSize(window_size); - ImGui::PushFont(globals::font_unscii16, 8.0f); + ImGui::PushFont(globals::font_unscii16, 16.0f); if(!ImGui::Begin("###chat", nullptr, WINDOW_FLAGS)) { ImGui::End(); @@ -235,7 +235,7 @@ void gui::client_chat::layout(void) draw_list->AddRectFilled(rect_pos, rect_end, rect_col); - imdraw_ext::text_shadow_w(it->text, text_pos, text_col, shadow_col, font, draw_list, 8.0f, window_size.x); + imutils::text_wr(draw_list, it->text, text_pos, text_col, shadow_col, font, window_size.x, ImGui::GetFontSize()); ypos -= rect_size.y; } @@ -245,12 +245,12 @@ void gui::client_chat::layout(void) ImGui::PopFont(); } -void gui::client_chat::clear(void) +void client_chat::clear(void) { history.clear(); } -void gui::client_chat::refresh_timings(void) +void client_chat::refresh_timings(void) { for(auto it = history.begin(); it < history.end(); ++it) { // Reset the spawn time so the fadeout timer @@ -259,7 +259,7 @@ void gui::client_chat::refresh_timings(void) } } -void gui::client_chat::print(const std::string& text) +void client_chat::print(const std::string& text) { GuiChatMessage message = {}; message.spawn = globals::curtime; diff --git a/src/game/client/gui/chat.hh b/src/game/client/gui/chat.hh index 6a3ea33..91c3740 100644 --- a/src/game/client/gui/chat.hh +++ b/src/game/client/gui/chat.hh @@ -1,17 +1,17 @@ #pragma once -namespace gui::client_chat +namespace client_chat { void init(void); void init_late(void); void shutdown(void); void update(void); void layout(void); -} // namespace gui::client_chat +} // namespace client_chat -namespace gui::client_chat +namespace client_chat { void clear(void); void refresh_timings(void); void print(const std::string& string); -} // namespace gui::client_chat +} // namespace client_chat diff --git a/src/game/client/gui/crosshair.cc b/src/game/client/gui/crosshair.cc index 649602f..758a10b 100644 --- a/src/game/client/gui/crosshair.cc +++ b/src/game/client/gui/crosshair.cc @@ -13,7 +13,7 @@ static resource_ptr texture; -void gui::crosshair::init(void) +void crosshair::init(void) { texture = resource::load("textures/gui/hud_crosshair.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T | TEXTURE_GUI_LOAD_VFLIP); @@ -24,12 +24,12 @@ void gui::crosshair::init(void) } } -void gui::crosshair::shutdown(void) +void crosshair::shutdown(void) { texture = nullptr; } -void gui::crosshair::layout(void) +void crosshair::layout(void) { auto viewport = ImGui::GetMainViewport(); auto draw_list = ImGui::GetForegroundDrawList(); diff --git a/src/game/client/gui/crosshair.hh b/src/game/client/gui/crosshair.hh index 589727e..6789e1e 100644 --- a/src/game/client/gui/crosshair.hh +++ b/src/game/client/gui/crosshair.hh @@ -1,8 +1,8 @@ #pragma once -namespace gui::crosshair +namespace crosshair { void init(void); void shutdown(void); void layout(void); -} // namespace gui::crosshair +} // namespace crosshair diff --git a/src/game/client/gui/direct_connection.cc b/src/game/client/gui/direct_connection.cc index 39ea2b5..d7cea64 100644 --- a/src/game/client/gui/direct_connection.cc +++ b/src/game/client/gui/direct_connection.cc @@ -29,7 +29,7 @@ static std::string str_password; static std::string direct_hostname; static std::string direct_password; -static void on_glfw_key(const io::GlfwKeyEvent& event) +static void on_glfw_key(const GlfwKeyEvent& event) { if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { if(globals::gui_screen == GUI_DIRECT_CONNECTION) { @@ -39,14 +39,14 @@ static void on_glfw_key(const io::GlfwKeyEvent& event) } } -static void on_language_set(const gui::LanguageSetEvent& event) +static void on_language_set(const LanguageSetEvent& event) { - str_title = gui::language::resolve("direct_connection.title"); - str_connect = gui::language::resolve_gui("direct_connection.connect"); - str_cancel = gui::language::resolve_gui("direct_connection.cancel"); + str_title = language::resolve("direct_connection.title"); + str_connect = language::resolve_gui("direct_connection.connect"); + str_cancel = language::resolve_gui("direct_connection.cancel"); - str_hostname = gui::language::resolve("direct_connection.hostname"); - str_password = gui::language::resolve("direct_connection.password"); + str_hostname = language::resolve("direct_connection.hostname"); + str_password = language::resolve("direct_connection.password"); } static void connect_to_server(void) @@ -72,13 +72,13 @@ static void connect_to_server(void) session::connect(parsed_hostname.c_str(), parsed_port, direct_password.c_str()); } -void gui::direct_connection::init(void) +void direct_connection::init(void) { - globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_glfw_key>(); globals::dispatcher.sink().connect<&on_language_set>(); } -void gui::direct_connection::layout(void) +void direct_connection::layout(void) { auto viewport = ImGui::GetMainViewport(); auto window_start = ImVec2(0.25f * viewport->Size.x, 0.20f * viewport->Size.y); diff --git a/src/game/client/gui/direct_connection.hh b/src/game/client/gui/direct_connection.hh index aa02d7c..0a09a3f 100644 --- a/src/game/client/gui/direct_connection.hh +++ b/src/game/client/gui/direct_connection.hh @@ -1,7 +1,7 @@ #pragma once -namespace gui::direct_connection +namespace direct_connection { void init(void); void layout(void); -} // namespace gui::direct_connection +} // namespace direct_connection diff --git a/src/game/client/gui/hotbar.cc b/src/game/client/gui/hotbar.cc index 663f263..b38c368 100644 --- a/src/game/client/gui/hotbar.cc +++ b/src/game/client/gui/hotbar.cc @@ -19,13 +19,13 @@ #include "client/globals.hh" -constexpr static float ITEM_SIZE = 20.0f; -constexpr static float ITEM_PADDING = 2.0f; -constexpr static float SELECTOR_PADDING = 1.0f; -constexpr static float HOTBAR_PADDING = 2.0f; +constexpr static float ITEM_SIZE = 40.0f; +constexpr static float ITEM_PADDING = 4.0f; +constexpr static float SELECTOR_PADDING = 2.0f; +constexpr static float HOTBAR_PADDING = 4.0f; -unsigned int gui::hotbar::active_slot = 0U; -std::array gui::hotbar::slots = {}; +unsigned int hotbar::active_slot = 0U; +std::array hotbar::slots = {}; static config::KeyBind hotbar_keys[HOTBAR_SIZE]; @@ -40,22 +40,22 @@ static ImU32 get_color_alpha(ImGuiCol style_color, float alpha) static void update_hotbar_item(void) { - auto current_item = gui::hotbar::slots[gui::hotbar::active_slot]; + auto current_item = hotbar::slots[hotbar::active_slot]; if(current_item == nullptr) { - gui::status_lines::unset(gui::STATUS_HOTBAR); + status_lines::unset(STATUS_HOTBAR); } else { - gui::status_lines::set(gui::STATUS_HOTBAR, current_item->get_name(), ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); + status_lines::set(STATUS_HOTBAR, current_item->get_name(), ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); } } -static void on_glfw_key(const io::GlfwKeyEvent& event) +static void on_glfw_key(const GlfwKeyEvent& event) { if((event.action == GLFW_PRESS) && !globals::gui_screen) { for(unsigned int i = 0U; i < HOTBAR_SIZE; ++i) { if(hotbar_keys[i].equals(event.key)) { - gui::hotbar::active_slot = i; + hotbar::active_slot = i; update_hotbar_item(); break; } @@ -63,22 +63,22 @@ static void on_glfw_key(const io::GlfwKeyEvent& event) } } -static void on_glfw_scroll(const io::GlfwScrollEvent& event) +static void on_glfw_scroll(const GlfwScrollEvent& event) { if(!globals::gui_screen) { if(event.dy < 0.0) { - gui::hotbar::next_slot(); + hotbar::next_slot(); return; } if(event.dy > 0.0) { - gui::hotbar::prev_slot(); + hotbar::prev_slot(); return; } } } -void gui::hotbar::init(void) +void hotbar::init(void) { hotbar_keys[0].set_key(GLFW_KEY_1); hotbar_keys[1].set_key(GLFW_KEY_2); @@ -113,17 +113,17 @@ void gui::hotbar::init(void) hotbar_background = resource::load("textures/gui/hud_hotbar.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); hotbar_selector = resource::load("textures/gui/hud_selector.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - globals::dispatcher.sink().connect<&on_glfw_key>(); - globals::dispatcher.sink().connect<&on_glfw_scroll>(); + globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_glfw_scroll>(); } -void gui::hotbar::shutdown(void) +void hotbar::shutdown(void) { hotbar_background = nullptr; hotbar_selector = nullptr; } -void gui::hotbar::layout(void) +void hotbar::layout(void) { auto& style = ImGui::GetStyle(); @@ -142,7 +142,7 @@ void gui::hotbar::layout(void) // Draw the hotbar selector image auto selector_padding_a = SELECTOR_PADDING * globals::gui_scale; auto selector_padding_b = SELECTOR_PADDING * globals::gui_scale * 2.0f; - auto selector_start = ImVec2(background_start.x + gui::hotbar::active_slot * item_size - selector_padding_a, + auto selector_start = ImVec2(background_start.x + hotbar::active_slot * item_size - selector_padding_a, background_start.y - selector_padding_a); auto selector_end = ImVec2(selector_start.x + item_size + selector_padding_b, selector_start.y + item_size + selector_padding_b); draw_list->AddImage(hotbar_selector->handle, selector_start, selector_end); @@ -153,7 +153,7 @@ void gui::hotbar::layout(void) // Draw individual item textures in the hotbar for(std::size_t i = 0; i < HOTBAR_SIZE; ++i) { - auto item = gui::hotbar::slots[i]; + auto item = hotbar::slots[i]; if((item == nullptr) || (item->get_cached_texture() == nullptr)) { // There's either no item in the slot @@ -167,16 +167,16 @@ void gui::hotbar::layout(void) } } -void gui::hotbar::next_slot(void) +void hotbar::next_slot(void) { - gui::hotbar::active_slot += 1U; - gui::hotbar::active_slot %= HOTBAR_SIZE; + hotbar::active_slot += 1U; + hotbar::active_slot %= HOTBAR_SIZE; update_hotbar_item(); } -void gui::hotbar::prev_slot(void) +void hotbar::prev_slot(void) { - gui::hotbar::active_slot += HOTBAR_SIZE - 1U; - gui::hotbar::active_slot %= HOTBAR_SIZE; + hotbar::active_slot += HOTBAR_SIZE - 1U; + hotbar::active_slot %= HOTBAR_SIZE; update_hotbar_item(); } diff --git a/src/game/client/gui/hotbar.hh b/src/game/client/gui/hotbar.hh index c529230..bba5e63 100644 --- a/src/game/client/gui/hotbar.hh +++ b/src/game/client/gui/hotbar.hh @@ -3,28 +3,25 @@ // TODO: design an inventory system and an item // registry and integrate the hotbar into that system -namespace world -{ class Item; -} // namespace world constexpr static unsigned int HOTBAR_SIZE = 9U; -namespace gui::hotbar +namespace hotbar { extern unsigned int active_slot; -extern std::array slots; -} // namespace gui::hotbar +extern std::array slots; +} // namespace hotbar -namespace gui::hotbar +namespace hotbar { void init(void); void shutdown(void); void layout(void); -} // namespace gui::hotbar +} // namespace hotbar -namespace gui::hotbar +namespace hotbar { void next_slot(void); void prev_slot(void); -} // namespace gui::hotbar +} // namespace hotbar diff --git a/src/game/client/gui/imdraw_ext.cc b/src/game/client/gui/imdraw_ext.cc deleted file mode 100644 index 4b44d5f..0000000 --- a/src/game/client/gui/imdraw_ext.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "client/pch.hh" - -#include "client/gui/imdraw_ext.hh" - -#include "client/globals.hh" - -void gui::imdraw_ext::text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list) -{ - imdraw_ext::text_shadow(text, position, text_color, shadow_color, font, draw_list, font->LegacySize); -} - -void gui::imdraw_ext::text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size) -{ - const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); - draw_list->AddText(font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size()); - draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size()); -} - -void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float wrap_width) -{ - imdraw_ext::text_shadow_w(text, position, text_color, shadow_color, font, draw_list, font->LegacySize, wrap_width); -} - -void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size, float wrap_width) -{ - const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); - draw_list->AddText(font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size(), - wrap_width); - draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size(), wrap_width); -} diff --git a/src/game/client/gui/imdraw_ext.hh b/src/game/client/gui/imdraw_ext.hh deleted file mode 100644 index a7e1503..0000000 --- a/src/game/client/gui/imdraw_ext.hh +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace gui::imdraw_ext -{ -void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list); -void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list, - float font_size); -} // namespace gui::imdraw_ext - -namespace gui::imdraw_ext -{ -void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float wrap_width); -void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size, float wrap_width); -} // namespace gui::imdraw_ext diff --git a/src/game/client/gui/imutils_button.cc b/src/game/client/gui/imutils_button.cc new file mode 100644 index 0000000..2017e33 --- /dev/null +++ b/src/game/client/gui/imutils_button.cc @@ -0,0 +1,95 @@ +#include "client/pch.hh" + +#include "client/gui/imutils_button.hh" + +void imutils::button(const char* title, const ImVec2& size, void (*callback)(void)) +{ + assert(title); + assert(callback); + + if(ImGui::Button(title, size)) { + callback(); + } +} + +void imutils::button(const char* title, void (*callback)(void)) +{ + assert(title); + assert(callback); + + if(ImGui::Button(title)) { + callback(); + } +} + +bool imutils::selectable_button(const char* label, const ImVec2& size, bool value) +{ + assert(label); + + auto value_changed = false; + + if(value) { + ImVec4 button_color(0.750f, 0.750f, 0.750f, 1.000f); + ImGui::PushStyleColor(ImGuiCol_Button, button_color); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_color); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_color); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); + } + else { + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); + } + + if(ImGui::Button(label, size)) { + value_changed = true; + } + + ImGui::PopStyleColor(value ? 4 : 1); + + return value_changed; +} + +bool imutils::toggle_button(const char* label, const ImVec2& size, bool& value) +{ + assert(label); + + auto use_custom_color = !value; + auto value_changed = false; + + if(use_custom_color) { + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); + } + + if(ImGui::Button(label, size)) { + value_changed = true; + value = !value; + } + + if(use_custom_color) { + ImGui::PopStyleColor(); + } + + return value_changed; +} + +bool imutils::toggle_button(const char* label, bool& value) +{ + assert(label); + + auto use_custom_color = !value; + auto value_changed = false; + + if(use_custom_color) { + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); + } + + if(ImGui::Button(label)) { + value_changed = true; + value = !value; + } + + if(use_custom_color) { + ImGui::PopStyleColor(); + } + + return value_changed; +} diff --git a/src/game/client/gui/imutils_button.hh b/src/game/client/gui/imutils_button.hh new file mode 100644 index 0000000..0c80694 --- /dev/null +++ b/src/game/client/gui/imutils_button.hh @@ -0,0 +1,14 @@ +#pragma once + +namespace imutils +{ +void button(const char* title, const ImVec2& size, void (*callback)(void)); +void button(const char* title, void (*callback)(void)); +} // namespace imutils + +namespace imutils +{ +bool selectable_button(const char* label, const ImVec2& size, bool value); +bool toggle_button(const char* label, const ImVec2& size, bool& value); +bool toggle_button(const char* label, bool& value); +} // namespace imutils diff --git a/src/game/client/gui/imutils_popup.cc b/src/game/client/gui/imutils_popup.cc new file mode 100644 index 0000000..f97facb --- /dev/null +++ b/src/game/client/gui/imutils_popup.cc @@ -0,0 +1,56 @@ +#include "client/pch.hh" + +#include "client/gui/imutils_popup.hh" + +#include "client/globals.hh" + +int imutils::popup(const std::string& title, const std::string& question, const std::string* choices, std::size_t num_choices, + float font_scale) +{ + assert(choices); + assert(num_choices); + + int result = POPUP_WAIT; + + ImGui::PushFont(globals::font_unscii16, globals::font_unscii16->LegacySize); + + if(ImGui::BeginPopupModal(title.c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize)) { + const auto viewport = ImGui::GetMainViewport(); + const auto& viewport_size = viewport->Size; + + ImVec2 popup_size(ImGui::GetWindowSize()); + ImVec2 popup_pos(viewport_size.x * 0.5f - popup_size.x * 0.5f, viewport_size.y * 0.5f - popup_size.y * 0.5f); + + ImGui::PushTextWrapPos(popup_size.x); + ImGui::TextUnformatted(question.c_str()); + ImGui::PopTextWrapPos(); + + ImGui::NewLine(); + + auto& style = ImGui::GetStyle(); + auto& spacing = style.ItemSpacing; + ImVec2 button_size(0.5f * (ImGui::CalcItemWidth() - spacing.x), 0.0f); + + for(std::size_t i = 0; i < num_choices; ++i) { + if(ImGui::Button(choices[i].c_str(), button_size)) { + result = static_cast(i); + ImGui::CloseCurrentPopup(); + } + + if((i + 1) % 2 == 1) { + ImGui::SameLine(); + } + else if(i + 1 < num_choices) { + ImGui::Dummy(ImVec2(0.0, spacing.y)); + } + } + + ImGui::SetWindowPos(popup_pos, ImGuiCond_Always); + + ImGui::EndPopup(); + } + + ImGui::PopFont(); + + return result; +} diff --git a/src/game/client/gui/imutils_popup.hh b/src/game/client/gui/imutils_popup.hh new file mode 100644 index 0000000..1678f87 --- /dev/null +++ b/src/game/client/gui/imutils_popup.hh @@ -0,0 +1,11 @@ +#pragma once + +/// Returned by popup::show whenever the popup is opened +/// but there is no user action yet, so the UI should wait +constexpr static int POPUP_WAIT = -1; + +namespace imutils +{ +int popup(const std::string& title, const std::string& question, const std::string* choices, std::size_t num_choices, + float font_scale = 1.75f); +} // namespace imutils diff --git a/src/game/client/gui/imutils_text.cc b/src/game/client/gui/imutils_text.cc new file mode 100644 index 0000000..1058c7f --- /dev/null +++ b/src/game/client/gui/imutils_text.cc @@ -0,0 +1,46 @@ +#include "client/pch.hh" + +#include "client/gui/imutils_text.hh" + +#include "client/globals.hh" + +void imutils::text_nw(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font) +{ + assert(list); + assert(font); + + imutils::text_nw(list, text, pos, color, shadow, font, font->LegacySize); +} + +void imutils::text_nw(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float size) +{ + assert(list); + assert(font); + + auto shift = static_cast(globals::gui_scale); + auto shadow_pos = ImVec2(pos.x + shift, pos.y + shift); + + list->AddText(font, size, shadow_pos, shadow, text.data(), text.data() + text.size()); + list->AddText(font, size, pos, color, text.data(), text.data() + text.size()); +} + +void imutils::text_wr(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float wrap) +{ + assert(list); + assert(font); + + imutils::text_wr(list, text, pos, color, shadow, font, wrap, font->LegacySize); +} + +void imutils::text_wr(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float wrap, + float size) +{ + assert(list); + assert(font); + + auto shift = static_cast(globals::gui_scale); + auto shadow_pos = ImVec2(pos.x + shift, pos.y + shift); + + list->AddText(font, size, shadow_pos, shadow, text.data(), text.data() + text.size(), wrap); + list->AddText(font, size, pos, color, text.data(), text.data() + text.size(), wrap); +} diff --git a/src/game/client/gui/imutils_text.hh b/src/game/client/gui/imutils_text.hh new file mode 100644 index 0000000..64d06a4 --- /dev/null +++ b/src/game/client/gui/imutils_text.hh @@ -0,0 +1,9 @@ +#pragma once + +namespace imutils +{ +void text_nw(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font); +void text_nw(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float size); +void text_wr(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float wrap); +void text_wr(ImDrawList* list, std::string_view text, const ImVec2& pos, ImU32 color, ImU32 shadow, ImFont* font, float wrap, float size); +} // namespace imutils diff --git a/src/game/client/gui/language.cc b/src/game/client/gui/language.cc index 0109ae6..8a2d8d1 100644 --- a/src/game/client/gui/language.cc +++ b/src/game/client/gui/language.cc @@ -19,20 +19,20 @@ constexpr static std::string_view DEFAULT_LANGUAGE = "en_US"; // system knows what language it can load and will act accordingly constexpr static std::string_view MANIFEST_PATH = "lang/manifest.json"; -static gui::LanguageManifest manifest; -static gui::LanguageIterator current_language; +static LanguageManifest manifest; +static LanguageIterator current_language; static std::unordered_map language_map; -static std::unordered_map ietf_map; +static std::unordered_map ietf_map; static config::String config_language(DEFAULT_LANGUAGE); -static void send_language_event(gui::LanguageIterator new_language) +static void send_language_event(LanguageIterator new_language) { - gui::LanguageSetEvent event; + LanguageSetEvent event; event.new_language = new_language; globals::dispatcher.trigger(event); } -void gui::language::init(void) +void language::init(void) { globals::client_config.add_value("language", config_language); @@ -41,7 +41,7 @@ void gui::language::init(void) auto file = PHYSFS_openRead(std::string(MANIFEST_PATH).c_str()); if(file == nullptr) { - spdlog::critical("language: {}: {}", MANIFEST_PATH, io::physfs_error()); + spdlog::critical("language: {}: {}", MANIFEST_PATH, physfs_error()); std::terminate(); } @@ -85,19 +85,19 @@ void gui::language::init(void) current_language = manifest.cend(); } -void gui::language::init_late(void) +void language::init_late(void) { auto user_language = ietf_map.find(config_language.get_value()); if(user_language != ietf_map.cend()) { - gui::language::set(user_language->second); + language::set(user_language->second); return; } auto fallback = ietf_map.find(std::string(DEFAULT_LANGUAGE)); if(fallback != ietf_map.cend()) { - gui::language::set(fallback->second); + language::set(fallback->second); return; } @@ -106,7 +106,7 @@ void gui::language::init_late(void) std::terminate(); } -void gui::language::set(LanguageIterator new_language) +void language::set(LanguageIterator new_language) { if(new_language != manifest.cend()) { auto path = std::format("lang/lang.{}.json", new_language->ietf); @@ -114,7 +114,7 @@ void gui::language::set(LanguageIterator new_language) auto file = PHYSFS_openRead(path.c_str()); if(file == nullptr) { - spdlog::warn("language: {}: {}", path, io::physfs_error()); + spdlog::warn("language: {}: {}", path, physfs_error()); send_language_event(new_language); return; } @@ -156,12 +156,12 @@ void gui::language::set(LanguageIterator new_language) send_language_event(new_language); } -gui::LanguageIterator gui::language::get_current(void) +LanguageIterator language::get_current(void) { return current_language; } -gui::LanguageIterator gui::language::find(std::string_view ietf) +LanguageIterator language::find(std::string_view ietf) { const auto it = ietf_map.find(std::string(ietf)); if(it != ietf_map.cend()) { @@ -172,17 +172,17 @@ gui::LanguageIterator gui::language::find(std::string_view ietf) } } -gui::LanguageIterator gui::language::cbegin(void) +LanguageIterator language::cbegin(void) { return manifest.cbegin(); } -gui::LanguageIterator gui::language::cend(void) +LanguageIterator language::cend(void) { return manifest.cend(); } -std::string_view gui::language::resolve(std::string_view key) +std::string_view language::resolve(std::string_view key) { const auto it = language_map.find(std::string(key)); @@ -193,10 +193,10 @@ std::string_view gui::language::resolve(std::string_view key) return key; } -std::string gui::language::resolve_gui(std::string_view key) +std::string language::resolve_gui(std::string_view key) { // We need window tags to retain their hierarchy when a language // dynamically changes; ImGui allows to provide hidden unique identifiers // to GUI primitives that have their name change dynamically, so we're using this - return std::format("{}###{}", gui::language::resolve(key), key); + return std::format("{}###{}", language::resolve(key), key); } diff --git a/src/game/client/gui/language.hh b/src/game/client/gui/language.hh index 90132d7..20c7520 100644 --- a/src/game/client/gui/language.hh +++ b/src/game/client/gui/language.hh @@ -1,7 +1,5 @@ #pragma once -namespace gui -{ struct LanguageInfo final { std::string endonym; // Language's self-name std::string display; // Display for the settings GUI @@ -14,29 +12,28 @@ using LanguageIterator = LanguageManifest::const_iterator; struct LanguageSetEvent final { LanguageIterator new_language; }; -} // namespace gui -namespace gui::language +namespace language { void init(void); void init_late(void); -} // namespace gui::language +} // namespace language -namespace gui::language +namespace language { void set(LanguageIterator new_language); -} // namespace gui::language +} // namespace language -namespace gui::language +namespace language { LanguageIterator get_current(void); LanguageIterator find(std::string_view ietf); LanguageIterator cbegin(void); LanguageIterator cend(void); -} // namespace gui::language +} // namespace language -namespace gui::language +namespace language { std::string_view resolve(std::string_view key); std::string resolve_gui(std::string_view key); -} // namespace gui::language +} // namespace language diff --git a/src/game/client/gui/main_menu.cc b/src/game/client/gui/main_menu.cc index d60a507..d764449 100644 --- a/src/game/client/gui/main_menu.cc +++ b/src/game/client/gui/main_menu.cc @@ -9,6 +9,8 @@ #include "core/version.hh" #include "client/gui/gui_screen.hh" +#include "client/gui/imutils_button.hh" +#include "client/gui/imutils_popup.hh" #include "client/gui/language.hh" #include "client/gui/window_title.hh" @@ -19,18 +21,17 @@ #include "client/globals.hh" #include "client/session.hh" -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; +static std::string str_button_play; +static std::string str_button_resume; +static std::string str_button_settings; +static std::string str_button_disconnect; +static std::string str_button_quit; -static std::string str_play; -static std::string str_resume; -static std::string str_settings; -static std::string str_leave; -static std::string str_quit; +static std::string str_quit_popup_title; +static std::string str_quit_popup_question; +static std::string str_quit_popup_choices[2]; -static resource_ptr title; -static float title_aspect; - -static void on_glfw_key(const io::GlfwKeyEvent& event) +static void on_glfw_key(const GlfwKeyEvent& event) { if(session::is_ingame() && (event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { if(globals::gui_screen == GUI_SCREEN_NONE) { @@ -45,127 +46,106 @@ static void on_glfw_key(const io::GlfwKeyEvent& event) } } -static void on_language_set(const gui::LanguageSetEvent& event) +static void on_language_set(const LanguageSetEvent& event) { - str_play = gui::language::resolve_gui("main_menu.play"); - str_resume = gui::language::resolve_gui("main_menu.resume"); - str_settings = gui::language::resolve("main_menu.settings"); - str_leave = gui::language::resolve("main_menu.leave"); - str_quit = gui::language::resolve("main_menu.quit"); + str_button_play = language::resolve_gui("main_menu.button.play"); + str_button_resume = language::resolve_gui("main_menu.button.resume"); + str_button_settings = language::resolve("main_menu.button.settings"); + str_button_disconnect = language::resolve("main_menu.button.disconnect"); + str_button_quit = language::resolve("main_menu.button.quit"); + + str_quit_popup_title = language::resolve_gui("main_menu.quit_popup.title"); + str_quit_popup_question = language::resolve("main_menu.quit_popup.question"); + str_quit_popup_choices[0] = language::resolve_gui("main_menu.quit_popup.choice.yes"); + str_quit_popup_choices[1] = language::resolve_gui("main_menu.quit_popup.choice.no"); } -void gui::main_menu::init(void) +void main_menu::init(void) { - title = resource::load("textures/gui/menu_title.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - - if(title == nullptr) { - spdlog::critical("main_menu: texture load failed"); - std::terminate(); - } - - if(title->size.x > title->size.y) { - title_aspect = static_cast(title->size.x) / static_cast(title->size.y); - } - else { - title_aspect = static_cast(title->size.y) / static_cast(title->size.x); - } - - globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_glfw_key>(); globals::dispatcher.sink().connect<&on_language_set>(); } -void gui::main_menu::shutdown(void) +void main_menu::shutdown(void) { - title = nullptr; + // empty } -void gui::main_menu::layout(void) +void main_menu::layout(void) { const auto viewport = ImGui::GetMainViewport(); - const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.15f); - const auto window_size = ImVec2(viewport->Size.x, viewport->Size.y); + const auto& viewport_size = viewport->Size; - ImGui::SetNextWindowPos(window_start); - ImGui::SetNextWindowSize(window_size); + ImVec2 margin(globals::gui_scale * 8.0f, globals::gui_scale * 8.0f); + ImVec2 control(globals::gui_scale * 256.0f, globals::gui_scale * 32.0f); - if(ImGui::Begin("###main_menu", nullptr, WINDOW_FLAGS)) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 2.0f * globals::gui_scale)); + ImGui::SetNextWindowPos({}); + ImGui::SetNextWindowSize(viewport_size); - if(session::is_ingame()) { - ImGui::Dummy(ImVec2(0.0f, 32.0f * globals::gui_scale)); - } - else { - auto reference_height = 0.225f * window_size.y; - auto image_width = glm::min(window_size.x, reference_height * title_aspect); - auto image_height = image_width / title_aspect; - ImGui::SetCursorPosX(0.5f * (window_size.x - image_width)); - ImGui::Image(title->handle, ImVec2(image_width, image_height)); - } - - ImGui::Dummy(ImVec2(0.0f, 24.0f * globals::gui_scale)); - - const float button_width = 240.0f * globals::gui_scale; - const float button_xpos = 0.5f * (window_size.x - button_width); - - if(session::is_ingame()) { - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_resume.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_SCREEN_NONE; - } - - ImGui::Spacing(); - } - else { - ImGui::SetCursorPosX(button_xpos); + ImGuiWindowFlags flags = 0U; + flags |= ImGuiWindowFlags_NoBackground; + flags |= ImGuiWindowFlags_NoDecoration; - if(ImGui::Button(str_play.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_PLAY_MENU; - } - - ImGui::Spacing(); - } + if(!ImGui::Begin("###gui::main_menu", nullptr, flags)) { + ImGui::End(); + return; + } - ImGui::SetCursorPosX(button_xpos); + ImGui::PushFont(globals::font_unscii16, 16.0f); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.000f, 0.000f, 0.000f, 0.000f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.500f, 0.500f, 0.500f, 0.125f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.500f, 0.500f, 0.500f, 0.500f)); + ImGui::PushStyleVarX(ImGuiStyleVar_FramePadding, 16.0f * globals::gui_scale); + ImGui::PushStyleVarX(ImGuiStyleVar_ButtonTextAlign, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - if(ImGui::Button(str_settings.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_SETTINGS; - } + ImVec2 cursor_pos(margin.x, viewport_size.y - margin.y); - ImGui::Spacing(); + cursor_pos.y -= control.y * 1.5f; + ImGui::SetCursorPos(cursor_pos); - if(session::is_ingame()) { - ImGui::SetCursorPosX(button_xpos); + if(session::is_ingame()) { + imutils::button(str_button_disconnect.c_str(), control, [] { + session::disconnect("protocol.client_disconnect"); + }); + } + else { + imutils::button(str_button_quit.c_str(), control, [] { + ImGui::OpenPopup(str_quit_popup_title.c_str()); + }); + } - if(ImGui::Button(str_leave.c_str(), ImVec2(button_width, 0.0f))) { - session::disconnect("protocol.client_disconnect"); - globals::gui_screen = GUI_PLAY_MENU; - gui::window_title::update(); - } + cursor_pos.y -= control.y; + ImGui::SetCursorPos(cursor_pos); - ImGui::Spacing(); - } - else { - ImGui::SetCursorPosX(button_xpos); + imutils::button(str_button_settings.c_str(), control, [] { + globals::gui_screen = GUI_SETTINGS; + }); - if(ImGui::Button(str_quit.c_str(), ImVec2(button_width, 0.0f))) { - glfwSetWindowShouldClose(globals::window, true); - } + cursor_pos.y -= control.y * 1.5f; + ImGui::SetCursorPos(cursor_pos); - ImGui::Spacing(); - } - - if(!session::is_ingame()) { - const auto& padding = ImGui::GetStyle().FramePadding; - const auto& spacing = ImGui::GetStyle().ItemSpacing; + if(session::is_ingame()) { + imutils::button(str_button_resume.c_str(), control, [] { + globals::gui_screen = GUI_SCREEN_NONE; + }); + } + else { + imutils::button(str_button_play.c_str(), control, [] { + globals::gui_screen = GUI_PLAY_MENU; + }); + } - ImGui::PushFont(globals::font_unscii8, 4.0f); - ImGui::SetCursorScreenPos(ImVec2(padding.x + spacing.x, window_size.y - ImGui::GetFontSize() - padding.y - spacing.y)); - ImGui::Text("Voxelius %*s", version::full.size(), version::full.data()); // string_view is not always null-terminated - ImGui::PopFont(); - } + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(3); + ImGui::PopFont(); - ImGui::PopStyleVar(); + if(0 == imutils::popup(str_quit_popup_title, str_quit_popup_question, str_quit_popup_choices, 2)) { + // We don't really have a good way to + // pass "i want to quit" boolean to the main loop, + // so instead we just raise an external interrupt signal + // which handler latches an internal flag in the main loop + std::raise(SIGINT); } ImGui::End(); diff --git a/src/game/client/gui/main_menu.hh b/src/game/client/gui/main_menu.hh index 205f078..b5333d4 100644 --- a/src/game/client/gui/main_menu.hh +++ b/src/game/client/gui/main_menu.hh @@ -1,8 +1,8 @@ #pragma once -namespace gui::main_menu +namespace main_menu { void init(void); void shutdown(void); void layout(void); -} // namespace gui::main_menu +} // namespace main_menu diff --git a/src/game/client/gui/message_box.cc b/src/game/client/gui/message_box.cc index 59e2d33..d97267a 100644 --- a/src/game/client/gui/message_box.cc +++ b/src/game/client/gui/message_box.cc @@ -10,7 +10,7 @@ constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; struct Button final { - gui::message_box_action action; + message_box_action action; std::string str_title; }; @@ -18,14 +18,14 @@ static std::string str_title; static std::string str_subtitle; static std::vector