diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index f81a7927c..9cd1a70fb 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -84,6 +84,7 @@ public: using ImGuiFullscreen::ChoiceDialogOptions; using ImGuiFullscreen::FocusResetType; +using ImGuiFullscreen::LAYOUT_CENTER_ALIGN_TEXT; using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT; using ImGuiFullscreen::LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE; using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE; @@ -2961,7 +2962,7 @@ void FullscreenUI::DrawIntRangeSetting(SettingsInterface* bsi, std::string_view ImGui::PopStyleVar(2); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) { CloseFixedPopupDialog(); } @@ -3016,7 +3017,7 @@ void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, std::string_vie ImGui::PopStyleVar(2); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -3136,7 +3137,7 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, std::string_v SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -3249,7 +3250,7 @@ bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, std::string_view t ImGui::PopStyleVar(2); - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -3368,7 +3369,7 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, std::string_vie SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -5106,7 +5107,8 @@ void FullscreenUI::DrawControllerSettingsPage() } ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -5983,7 +5985,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, - ImVec2(0.5f, 0.0f))) + LAYOUT_CENTER_ALIGN_TEXT)) { CloseFixedPopupDialog(); } @@ -6052,7 +6054,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, - ImVec2(0.5f, 0.0f))) + LAYOUT_CENTER_ALIGN_TEXT)) { CloseFixedPopupDialog(); } @@ -6466,7 +6468,8 @@ void FullscreenUI::DrawAchievementsLoginWindow() const bool login_enabled = (std::strlen(username) > 0 && std::strlen(password) > 0 && !is_logging_in); - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_KEY, "Login"), login_enabled)) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_KEY, "Login"), login_enabled, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + LAYOUT_CENTER_ALIGN_TEXT)) { ImGuiFullscreen::OpenBackgroundProgressDialog(LOGIN_PROGRESS_NAME, FSUI_STR("Logging in to RetroAchievements..."), 0, 0, 0); @@ -6490,8 +6493,11 @@ void FullscreenUI::DrawAchievementsLoginWindow() }); } - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_TIMES, "Cancel"), !is_logging_in)) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_TIMES, "Cancel"), !is_logging_in, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) + { CloseFixedPopupDialog(); + } EndMenuButtons(); @@ -6740,7 +6746,8 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(FSUI_VSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + LAYOUT_CENTER_ALIGN_TEXT)) CloseFixedPopupDialog(); EndMenuButtons(); @@ -7557,21 +7564,23 @@ bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* ent void FullscreenUI::DrawResumeStateSelector() { - if (!BeginFixedPopupDialog(LayoutScale(30.0f), LayoutScale(40.0f), LayoutScale(820.0f, 645.0f))) + if (!BeginFixedPopupDialog(LayoutScale(30.0f), LayoutScale(40.0f), ImVec2(LayoutScale(550.0f), 0.0f))) { ClearSaveStateEntryList(); return; } SaveStateListEntry& entry = s_state.save_state_selector_slots.front(); - SmallString time; - TimeToPrintableString(&time, entry.timestamp); - ImGui::TextWrapped( - FSUI_CSTR("A resume save state created at %s was found.\n\nDo you want to load this save and continue?"), - time.c_str()); + + SmallString sick; + sick.format(FSUI_FSTR("Do you want to continue from the automatic save created at {:%c}?"), + fmt::localtime(entry.timestamp)); + ImGui::PushFontWeight(UIStyle.BoldFontWeight); + ImGuiFullscreen::TextAlignedMultiLine(0.5f, IMSTR_START_END(sick)); + ImGui::PopFontWeight(); const GPUTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get(); - const float image_height = LayoutScale(250.0f); + const float image_height = LayoutScale(280.0f); const float image_width = image_height * (static_cast(image->GetWidth()) / static_cast(image->GetHeight())); const ImVec2 pos(ImGui::GetCursorScreenPos() + @@ -7587,7 +7596,8 @@ void FullscreenUI::DrawResumeStateSelector() ResetFocusHere(); BeginMenuButtons(); - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_PLAY, "Load State"))) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_PLAY, "Load State"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + LAYOUT_CENTER_ALIGN_TEXT)) { std::string game_path = std::move(entry.game_path); std::string state_path = std::move(entry.state_path); @@ -7596,7 +7606,8 @@ void FullscreenUI::DrawResumeStateSelector() DoStartPath(std::move(game_path), std::move(state_path)); } - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_LIGHTBULB, "Clean Boot"))) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_LIGHTBULB, "Clean Boot"), true, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) { std::string game_path = std::move(entry.game_path); ClearSaveStateEntryList(); @@ -7604,7 +7615,8 @@ void FullscreenUI::DrawResumeStateSelector() DoStartPath(std::move(game_path)); } - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_FOLDER_MINUS, "Delete State"))) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_FOLDER_MINUS, "Delete State"), true, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) { if (FileSystem::DeleteFile(entry.state_path.c_str())) { @@ -7619,10 +7631,13 @@ void FullscreenUI::DrawResumeStateSelector() } } - if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_WINDOW_CLOSE, "Cancel"))) + if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_FA_WINDOW_CLOSE, "Cancel"), true, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) + { CloseFixedPopupDialog(); + } - EndMenuButtons(); + ImGuiFullscreen::EndHorizontalMenuButtons(); SetStandardSelectionFooterText(false); @@ -8309,12 +8324,14 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) // ellipise title, remove one character to make room draw_title.format("{}...", std::string_view(entry->title).substr(0, unclipped_size - 1)); RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, title_bb.Min, - title_bb.Max, text_color, draw_title, nullptr, ImVec2(0.5f, 0.0f), 0.0f, &title_bb); + title_bb.Max, text_color, draw_title, nullptr, LAYOUT_CENTER_ALIGN_TEXT, 0.0f, + &title_bb); } else { RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, title_bb.Min, - title_bb.Max, text_color, entry->title, nullptr, ImVec2(0.5f, 0.0f), 0.0f, &title_bb); + title_bb.Max, text_color, entry->title, nullptr, LAYOUT_CENTER_ALIGN_TEXT, 0.0f, + &title_bb); } if (pressed) @@ -9190,7 +9207,6 @@ TRANSLATE_NOOP("FullscreenUI", "9x"); TRANSLATE_NOOP("FullscreenUI", "9x (18x Speed)"); TRANSLATE_NOOP("FullscreenUI", "9x (for 4K)"); TRANSLATE_NOOP("FullscreenUI", "A cover already exists for this game. Are you sure that you want to overwrite it?"); -TRANSLATE_NOOP("FullscreenUI", "A resume save state created at %s was found.\n\nDo you want to load this save and continue?"); TRANSLATE_NOOP("FullscreenUI", "AMOLED"); TRANSLATE_NOOP("FullscreenUI", "About"); TRANSLATE_NOOP("FullscreenUI", "Account"); @@ -9364,6 +9380,7 @@ TRANSLATE_NOOP("FullscreenUI", "Displays DualShock/DualSense button icons in the TRANSLATE_NOOP("FullscreenUI", "Displays popup messages on events such as achievement unlocks and leaderboard submissions."); TRANSLATE_NOOP("FullscreenUI", "Displays popup messages when starting, submitting, or failing a leaderboard challenge."); TRANSLATE_NOOP("FullscreenUI", "Dithering"); +TRANSLATE_NOOP("FullscreenUI", "Do you want to continue from the automatic save created at {:%c}?"); TRANSLATE_NOOP("FullscreenUI", "Double-Click Toggles Fullscreen"); TRANSLATE_NOOP("FullscreenUI", "Download Covers"); TRANSLATE_NOOP("FullscreenUI", "Downloads covers from a user-specified URL template."); diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 264273be3..f093649bb 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -207,12 +207,14 @@ struct ALIGN_TO_CACHE_LINE UIState { std::recursive_mutex shared_state_mutex; - u32 menu_button_index = 0; CloseButtonState close_button_state = CloseButtonState::None; ImGuiDir has_pending_nav_move = ImGuiDir_None; FocusResetType focus_reset_queued = FocusResetType::None; bool initialized = false; + u32 menu_button_index = 0; + ImVec2 horizontal_menu_button_size = {}; + LRUCache> texture_cache{128, true}; std::shared_ptr placeholder_texture; std::deque> texture_upload_queue; @@ -1554,6 +1556,101 @@ void ImGuiFullscreen::RenderShadowedTextClipped(ImFont* font, float font_size, f text_size_if_known, align, wrap_width, clip_rect); } +void ImGuiFullscreen::TextAlignedMultiLine(float align_x, const char* text, const char* text_end, float wrap_width) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(text != NULL); + + if (text_end == NULL) + text_end = text + strlen(text); + + const ImVec2 text_pos = window->DC.CursorPos; + const ImRect clip_rect = window->ClipRect; + wrap_width = (wrap_width < 0.0f) ? ImGui::GetContentRegionAvail().x : wrap_width; + + ImVec2 pos = text_pos; + const char* text_remaining = text; + + while (text_remaining < text_end) + { + // Find the end of the current wrapped line + const char* line_end = text_remaining; + float line_width = 0.0f; + + // Process text word by word to find natural line breaks + while (line_end < text_end) + { + const char* word_start = line_end; + const char* word_end = word_start; + + // Handle explicit newlines + if (*line_end == '\n') + { + line_end++; + break; + } + + // Find end of current word (including spaces) + while (word_end < text_end && *word_end != ' ' && *word_end != '\n') + word_end++; + + // Include trailing space if present + if (word_end < text_end && *word_end == ' ') + word_end++; + + // Calculate width if we add this word + ImVec2 word_size = ImGui::CalcTextSize(text_remaining, word_end, false, -1.0f); + + // If adding this word would exceed wrap width, break here + if (word_size.x > wrap_width && line_end > text_remaining) + break; + + line_end = word_end; + line_width = word_size.x; + } + + // If we didn't advance at all, force at least one character to prevent infinite loop + if (line_end == text_remaining && line_end < text_end) + line_end++; + + // Calculate actual line size for the determined line segment + ImVec2 line_size = ImGui::CalcTextSize(text_remaining, line_end, false, -1.0f); + + // Calculate aligned position for this line + ImVec2 line_pos = pos; + if (align_x > 0.0f) + { + float offset_x = (wrap_width - line_size.x) * align_x; + line_pos.x += offset_x; + } + + // Render the line + if (line_size.x > 0.0f) + { + ImGui::RenderTextClipped(line_pos, ImVec2(line_pos.x + line_size.x, line_pos.y + line_size.y), text_remaining, + line_end, &line_size, ImVec2(0.0f, 0.0f), &clip_rect); + } + + // Move to next line + pos.y += g.FontSize + g.Style.ItemSpacing.y; + text_remaining = line_end; + + // Skip trailing spaces at the beginning of the next line + while (text_remaining < text_end && *text_remaining == ' ') + text_remaining++; + } + + // Update cursor position to account for the rendered text + ImVec2 text_size = ImVec2(wrap_width, pos.y - text_pos.y); + ImRect bb(text_pos, text_pos + text_size); + ImGui::ItemSize(text_size); + ImGui::ItemAdd(bb, 0); +} + void ImGuiFullscreen::MenuHeading(std::string_view title, bool draw_line /*= true*/) { const float line_thickness = draw_line ? LayoutScale(1.0f) : 0.0f; @@ -1970,7 +2067,7 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa ImGui::PopStyleVar(2); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) { CloseFixedPopupDialog(); } @@ -2035,7 +2132,7 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa ImGui::PopStyleVar(2); - if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_CENTER_ALIGN_TEXT)) { CloseFixedPopupDialog(); } @@ -2122,6 +2219,125 @@ bool ImGuiFullscreen::EnumChoiceButtonImpl(std::string_view title, std::string_v return changed; } +void ImGuiFullscreen::BeginHorizontalMenuButtons(u32 num_items, float max_item_width /* = 0.0f */, + float x_padding /* = LAYOUT_MENU_BUTTON_Y_PADDING */, + float y_padding /* = LAYOUT_MENU_BUTTON_Y_PADDING */, + float item_height /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */, + float x_spacing /* = LAYOUT_MENU_BUTTON_X_PADDING */, + float x_margin /* = LAYOUT_MENU_WINDOW_X_PADDING */) +{ + s_state.menu_button_index = 0; + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(x_padding, y_padding)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, LayoutScale(UIStyle.MenuBorders ? 1.0f : 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, LayoutScale(x_spacing, 0.0f)); + + ImGuiWindow* const window = ImGui::GetCurrentWindow(); + const ImGuiStyle& style = ImGui::GetStyle(); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + + const float available_width = ImGui::GetContentRegionAvail().x; + const float space_per_item = ImFloor( + std::max((available_width - LayoutScale(x_margin) - (style.ItemSpacing.x * static_cast(num_items - 1))) / + static_cast(num_items), + 0.0f)); + s_state.horizontal_menu_button_size = + ImVec2(space_per_item, LayoutScale(item_height) + (style.FramePadding.y * 2.0f)); + s_state.horizontal_menu_button_size.x = + (max_item_width > 0.0f) ? + std::min(s_state.horizontal_menu_button_size.x, LayoutScale(max_item_width) + (style.FramePadding.x * 2.0f)) : + s_state.horizontal_menu_button_size.x; + + const float left_padding = + ImFloor((available_width - + (((s_state.horizontal_menu_button_size.x + style.ItemSpacing.x) * static_cast(num_items)) - + style.ItemSpacing.x)) * + 0.5f); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + left_padding); + + // need to prerender the backgrounds for all inactive items, otherwise the animation overlaps it + const ImU32 frame_background = ImGui::GetColorU32( + DarkerColor((window->Flags & ImGuiWindowFlags_Popup) ? UIStyle.PopupBackgroundColor : UIStyle.BackgroundColor)); + ImVec2 current_pos = ImGui::GetCursorScreenPos(); + for (u32 i = 0; i < num_items; i++) + { + ImGui::RenderFrame(current_pos, current_pos + s_state.horizontal_menu_button_size, frame_background, false, + LayoutScale(MENU_ITEM_BORDER_ROUNDING)); + + current_pos.x += s_state.horizontal_menu_button_size.x + style.ItemSpacing.x; + } + + PrerenderMenuButtonBorder(); +} + +void ImGuiFullscreen::EndHorizontalMenuButtons() +{ + ImGui::PopStyleVar(4); + ImGui::GetCurrentWindow()->DC.LayoutType = ImGuiLayoutType_Vertical; +} + +bool ImGuiFullscreen::HorizontalMenuButton(std::string_view title, bool enabled /* = true */, + const ImVec2& text_align /* = LAYOUT_CENTER_ALIGN_TEXT */) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImVec2 pos = window->DC.CursorPos; + const ImVec2& size = s_state.horizontal_menu_button_size; + + ImRect bb(pos, pos + size); + + const ImGuiID id = window->GetID(IMSTR_START_END(title)); + ImGui::ItemSize(size); + if (enabled) + { + if (!ImGui::ItemAdd(bb, id)) + return false; + } + else + { + if (ImGui::IsClippedEx(bb, id)) + return false; + } + + bool hovered; + bool held; + bool pressed; + if (enabled) + { + pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0); + if (hovered) + { + const ImU32 col = ImGui::GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); + + const float t = static_cast(std::min(std::abs(std::sin(ImGui::GetTime() * 0.75) * 1.1), 1.0)); + ImGui::PushStyleColor(ImGuiCol_Border, ImGui::GetColorU32(ImGuiCol_Border, t)); + + DrawMenuButtonFrame(bb.Min, bb.Max, col, true); + + ImGui::PopStyleColor(); + } + } + else + { + pressed = false; + held = false; + } + + const ImGuiStyle& style = ImGui::GetStyle(); + bb.Min += style.FramePadding; + bb.Max -= style.FramePadding; + + const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled]; + RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max, + ImGui::GetColorU32(color), title, nullptr, text_align, 0.0f, &bb); + + s_state.menu_button_index++; + return pressed; +} + void ImGuiFullscreen::BeginNavBar(float x_padding /*= LAYOUT_MENU_BUTTON_X_PADDING*/, float y_padding /*= LAYOUT_MENU_BUTTON_Y_PADDING*/) { @@ -3117,8 +3333,12 @@ void ImGuiFullscreen::MessageDialog::Draw() for (s32 button_index = 0; button_index < static_cast(m_buttons.size()); button_index++) { - if (!m_buttons[button_index].empty() && MenuButtonWithoutSummary(m_buttons[button_index])) + if (!m_buttons[button_index].empty() && + MenuButtonWithoutSummary(m_buttons[button_index], true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + LAYOUT_CENTER_ALIGN_TEXT)) + { result = button_index; + } } EndMenuButtons(); diff --git a/src/util/imgui_fullscreen.h b/src/util/imgui_fullscreen.h index f3e5f6a0f..bd69e4e70 100644 --- a/src/util/imgui_fullscreen.h +++ b/src/util/imgui_fullscreen.h @@ -54,6 +54,7 @@ static constexpr float LAYOUT_SMALL_POPUP_PADDING = 20.0f; static constexpr float LAYOUT_LARGE_POPUP_PADDING = 30.0f; static constexpr float LAYOUT_LARGE_POPUP_ROUNDING = 40.0f; static constexpr float LAYOUT_WIDGET_FRAME_ROUNDING = 20.0f; +static constexpr ImVec2 LAYOUT_CENTER_ALIGN_TEXT = ImVec2(0.5f, 0.0f); struct ALIGN_TO_CACHE_LINE UIStyles { @@ -282,24 +283,26 @@ bool MenuButtonFrame(std::string_view str_id, bool enabled, float height, bool* ImVec2* max, ImGuiButtonFlags flags = 0, float hover_alpha = 1.0f); void DrawMenuButtonFrame(const ImVec2& p_min, const ImVec2& p_max, ImU32 fill_col, bool border = true); void ResetMenuButtonFrame(); -void RenderShadowedTextClipped(ImFont* font, float font_size, float font_weight, const ImVec2& pos_min, const ImVec2& pos_max, u32 color, - std::string_view text, const ImVec2* text_size_if_known = nullptr, - const ImVec2& align = ImVec2(0, 0), float wrap_width = 0.0f, - const ImRect* clip_rect = nullptr); -void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, const ImVec2& pos_min, +void RenderShadowedTextClipped(ImFont* font, float font_size, float font_weight, const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text, const ImVec2* text_size_if_known = nullptr, const ImVec2& align = ImVec2(0, 0), float wrap_width = 0.0f, const ImRect* clip_rect = nullptr); -void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, const ImVec2& pos_min, - const ImVec2& pos_max, u32 color, std::string_view text, +void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, + const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text, + const ImVec2* text_size_if_known = nullptr, const ImVec2& align = ImVec2(0, 0), + float wrap_width = 0.0f, const ImRect* clip_rect = nullptr); +void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, + const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text, const ImVec2* text_size_if_known, const ImVec2& align, float wrap_width, const ImRect* clip_rect, float shadow_offset); +void TextAlignedMultiLine(float align_x, const char* text, const char* text_end = nullptr, float wrap_width = -1.0f); void MenuHeading(std::string_view title, bool draw_line = true); bool MenuHeadingButton(std::string_view title, std::string_view value = {}, bool enabled = true, bool draw_line = true); bool MenuButton(std::string_view title, std::string_view summary, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, const ImVec2& text_align = ImVec2(0.0f, 0.0f)); bool MenuButtonWithoutSummary(std::string_view title, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, const ImVec2& text_align = ImVec2(0.0f, 0.0f)); + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + const ImVec2& text_align = ImVec2(0.0f, 0.0f)); bool MenuButtonWithValue(std::string_view title, std::string_view summary, std::string_view value, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT); bool MenuImageButton(std::string_view title, std::string_view summary, ImTextureID user_texture_id, @@ -344,6 +347,16 @@ ALWAYS_INLINE static bool EnumChoiceButton(std::string_view title, std::string_v } } +void BeginHorizontalMenuButtons(u32 num_items, float max_item_width = 0.0f, + float x_padding = LAYOUT_MENU_BUTTON_Y_PADDING, + float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING, + float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, + float x_spacing = LAYOUT_MENU_BUTTON_X_PADDING, + float x_margin = LAYOUT_MENU_WINDOW_X_PADDING); +void EndHorizontalMenuButtons(); +bool HorizontalMenuButton(std::string_view title, bool enabled = true, + const ImVec2& text_align = LAYOUT_CENTER_ALIGN_TEXT); + void BeginNavBar(float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); void EndNavBar(); void NavTitle(std::string_view title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);