diff --git a/dep/imgui/include/IconsEmoji.h b/dep/imgui/include/IconsEmoji.h index 38b48583b..f0061a5f7 100644 --- a/dep/imgui/include/IconsEmoji.h +++ b/dep/imgui/include/IconsEmoji.h @@ -42,3 +42,5 @@ #define ICON_EMOJI_CLOCK_FIVE_OCLOCK "\xf0\x9f\x95\x94" #define ICON_EMOJI_DESKTOP_COMPUTER "\xf0\x9f\x96\xa5" #define ICON_EMOJI_INDEX_DIVIDERS "\xf0\x9f\x97\x82" +#define ICON_EMOJI_CLIPBOARD "\xf0\x9f\x93\x8b" +#define ICON_EMOJI_NOTEBOOK "\xf0\x9f\x93\x99" diff --git a/src/core/fullscreenui_widgets.cpp b/src/core/fullscreenui_widgets.cpp index 6942612bb..2292f6d4a 100644 --- a/src/core/fullscreenui_widgets.cpp +++ b/src/core/fullscreenui_widgets.cpp @@ -50,7 +50,6 @@ LOG_CHANNEL(FullscreenUI); namespace FullscreenUI { static constexpr float MENU_BACKGROUND_ANIMATION_TIME = 0.5f; -static constexpr float MENU_ITEM_BORDER_ROUNDING = 10.0f; static constexpr float SMOOTH_SCROLLING_SPEED = 3.5f; static constexpr u32 LOADING_PROGRESS_SAMPLE_COUNT = 30; @@ -1572,7 +1571,7 @@ void FullscreenUI::PrerenderMenuButtonBorder() 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)); - ImGui::RenderFrame(min, max, col, true, LayoutScale(MENU_ITEM_BORDER_ROUNDING)); + ImGui::RenderFrame(min, max, col, true, LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); ImGui::PopStyleColor(); @@ -1773,7 +1772,7 @@ void FullscreenUI::DrawMenuButtonFrame(const ImVec2& p_min, const ImVec2& p_max, if (!s_state.rendered_menu_item_border) { s_state.rendered_menu_item_border = true; - ImGui::RenderFrame(frame_min, frame_max, fill_col, border, LayoutScale(MENU_ITEM_BORDER_ROUNDING)); + ImGui::RenderFrame(frame_min, frame_max, fill_col, border, LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); } } @@ -2324,17 +2323,21 @@ bool FullscreenUI::FloatingButton(std::string_view text, float x, float y, float else if (anchor_x == -0.5f) x -= (width * 0.5f); else if (anchor_x == 0.5f) - x = (window_size.x * 0.5f) - (width * 0.5f) - x; + x = (window_size.x * 0.5f) - (width * 0.5f) - LayoutScale(x); else if (anchor_x == 1.0f) - x = window_size.x - width - x; + x = window_size.x - width - LayoutScale(x); + else + x = LayoutScale(x); if (anchor_y == -1.0f) y -= height; else if (anchor_y == -0.5f) y -= (height * 0.5f); else if (anchor_y == 0.5f) - y = (window_size.y * 0.5f) - (height * 0.5f) - y; + y = (window_size.y * 0.5f) - (height * 0.5f) - LayoutScale(y); else if (anchor_y == 1.0f) - y = window_size.y - height - y; + y = window_size.y - height - LayoutScale(y); + else + y = LayoutScale(y); if (out_position) *out_position = ImVec2(x, y); @@ -2679,7 +2682,7 @@ void FullscreenUI::BeginHorizontalMenuButtons(u32 num_items, float max_item_widt 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)); + LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); current_pos.x += s_state.horizontal_menu_button_size.x + style.ItemSpacing.x; } @@ -2801,17 +2804,24 @@ void FullscreenUI::NavTitle(std::string_view title) ImGui::GetColorU32(ImGuiCol_Text), title, &text_size, ImVec2(0.0f, 0.0f), 0.0f, &bb); } -void FullscreenUI::RightAlignNavButtons(u32 num_items /*= 0*/) +void FullscreenUI::RightAlignNavButtons(u32 num_items) { ImGuiWindow* window = ImGui::GetCurrentWindow(); const ImGuiStyle& style = ImGui::GetStyle(); - const float total_item_width = style.FramePadding.x * 2.0f + style.FrameBorderSize + style.ItemSpacing.x + LayoutScale(LAYOUT_LARGE_FONT_SIZE - 1.0f); const float margin = total_item_width * static_cast(num_items); ImGui::SetCursorPosX(window->InnerClipRect.Max.x - margin - style.FramePadding.x); } +void FullscreenUI::RightAlignNavButtons(float total_width) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::SetCursorPosX(window->InnerClipRect.Max.x - total_width - style.FramePadding.x); +} + bool FullscreenUI::NavButton(std::string_view title, bool is_active, bool enabled /* = true */) { ImGuiWindow* window = ImGui::GetCurrentWindow(); @@ -2925,7 +2935,7 @@ bool FullscreenUI::NavTab(std::string_view title, bool is_active, bool enabled, { const ImU32 col = hovered ? ImGui::GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered, 1.0f) : ImGui::GetColorU32(DarkerColor(style.Colors[ImGuiCol_ButtonHovered])); - ImGui::RenderFrame(bb.Min, bb.Max, col, true, LayoutScale(MENU_ITEM_BORDER_ROUNDING)); + ImGui::RenderFrame(bb.Min, bb.Max, col, true, LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); } const ImVec2 pad(std::max((size.x - text_size.x) * 0.5f, 0.0f), std::max((size.y - text_size.y) * 0.5f, 0.0f)); @@ -2940,6 +2950,170 @@ bool FullscreenUI::NavTab(std::string_view title, bool is_active, bool enabled, return pressed; } +bool FullscreenUI::BeginFloatingNavBar(float x, float y, float items_width, + float font_size /* = UIStyle.LargeFontSize */, float anchor_x /* = 0.0f */, + float anchor_y /* = 0.0f */, + float x_padding /* = LAYOUT_MENU_BUTTON_X_PADDING */, + float y_padding /* = LAYOUT_MENU_BUTTON_Y_PADDING */) +{ + if (ImGui::GetCurrentWindowRead()->SkipItems) + return false; + + const ImVec2 padding = LayoutScale(x_padding, y_padding); + const float width = (padding.x * 2.0f) + items_width; + const float height = (padding.y * 2.0f) + font_size; + + const ImVec2 window_size = ImGui::GetWindowSize(); + ImVec2 local_pos = LayoutScale(x, y); + if (anchor_x == -1.0f) + local_pos.x -= width; + else if (anchor_x == -0.5f) + local_pos.x -= (width * 0.5f); + else if (anchor_x == 0.5f) + local_pos.x = (window_size.x * 0.5f) - (width * 0.5f) - local_pos.x; + else if (anchor_x == 1.0f) + local_pos.x = window_size.x - width - local_pos.x; + if (anchor_y == -1.0f) + local_pos.y -= height; + else if (anchor_y == -0.5f) + local_pos.y -= (height * 0.5f); + else if (anchor_y == 0.5f) + local_pos.y = (window_size.y * 0.5f) - (height * 0.5f) - local_pos.y; + else if (anchor_y == 1.0f) + local_pos.y = window_size.y - height - local_pos.y; + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(x_padding, y_padding)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, LayoutScale(1.0f, 0.0f)); + PushPrimaryColor(); + + const ImGuiStyle& style = ImGui::GetStyle(); + const ImVec2 nav_pos = ImGui::GetWindowPos() + local_pos; + const ImVec2 nav_size = ImVec2(items_width, font_size + style.FramePadding.y * 2.0f); + ImGui::GetWindowDrawList()->AddRectFilled(nav_pos, nav_pos + nav_size, + ImGui::GetColorU32(ModAlpha(UIStyle.BackgroundHighlight, 0.4f)), + LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); + ImGui::SetCursorPos(local_pos); + + return true; +} + +void FullscreenUI::EndFloatingNavBar() +{ + PopPrimaryColor(); + ImGui::PopStyleVar(4); +} + +float FullscreenUI::CalcFloatingNavBarButtonWidth(std::string_view title, float icon_size /* = UIStyle.LargeFontSize */, + float font_size /* = UIStyle.LargeFontSize */, + float font_weight /* = UIStyle.BoldFontWeight */, + float x_padding /* = LAYOUT_MENU_BUTTON_X_PADDING */) +{ + float text_width; + if (!title.empty()) + { + const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(font_size, font_weight, std::numeric_limits::max(), + 0.0f, IMSTR_START_END(title)); + text_width = text_size.x; + + if (icon_size > 0.0f) + { + text_width += + (UIStyle.Font->CalcTextSizeA(font_size, font_weight, std::numeric_limits::max(), 0.0f, " ").x * 2.0f); + } + } + else + { + text_width = 0.0f; + } + + return icon_size + text_width + (LayoutScale(x_padding) * 2.0f) + /* spacing */ LayoutScale(1.0f); +} + +bool FullscreenUI::FloatingNavBarIcon(std::string_view title, ImTextureID image, bool is_active, + float image_size /* = UIStyle.LargeFontSize */, + float font_size /* = UIStyle.LargeFontSize */, + float font_weight /* = UIStyle.BoldFontWeight */, bool enabled /* = true */, + const ImVec2& uv0 /* = ImVec2(0.0f, 0.0f) */, + const ImVec2& uv1 /* = ImVec2(1.0f, 1.0f) */) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImVec2 text_size = !title.empty() ? + UIStyle.Font->CalcTextSizeA(font_size, font_weight, std::numeric_limits::max(), + 0.0f, IMSTR_START_END(title)) : + ImVec2(); + const float text_space_size = + (!title.empty() && image) ? + (UIStyle.Font->CalcTextSizeA(font_size, font_weight, std::numeric_limits::max(), 0.0f, " ").x * 2.0f) : + 0.0f; + const ImVec2 pos(window->DC.CursorPos); + const ImGuiStyle& style = ImGui::GetStyle(); + const ImVec2 size = ImVec2(image_size + text_space_size + text_size.x + style.FramePadding.x * 2.0f, + text_size.y + style.FramePadding.y * 2.0f); + + ImGui::ItemSize( + ImVec2(size.x + style.FrameBorderSize + style.ItemSpacing.x, size.y + style.FrameBorderSize + style.ItemSpacing.y)); + ImGui::SameLine(); + + ImRect bb(pos, pos + size); + const ImGuiID id = window->GetID(IMSTR_START_END(title)); + if (enabled) + { + // bit contradictory - we don't want this button to be used for *gamepad* navigation, since they're usually + // activated with the bumpers and/or the back button. + if (!ImGui::ItemAdd(bb, id, nullptr, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus)) + return false; + } + else + { + if (ImGui::IsClippedEx(bb, id)) + return false; + } + + bool held; + bool pressed; + bool hovered; + if (enabled) + { + pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); + if (hovered || is_active) + { + // Intentionally no animation here. + const ImU32 col = ImGui::GetColorU32((hovered && !held) ? ImGuiCol_ButtonHovered : ImGuiCol_ButtonActive, 1.0f); + ImGui::RenderFrame(bb.Min, bb.Max, col, false, LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); + } + } + else + { + pressed = false; + held = false; + hovered = false; + } + + bb.Min += style.FramePadding; + bb.Max -= style.FramePadding; + + if (image) + { + const ImVec2& image_min = bb.Min; + const ImVec2 image_max = image_min + ImVec2(image_size, image_size); + ImGui::GetWindowDrawList()->AddImage(image, image_min, image_max, uv0, uv1, + ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled)); + bb.Min.x = image_max.x + text_space_size; + } + + RenderShadowedTextClipped( + UIStyle.Font, font_size, font_weight, bb.Min, bb.Max, + ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered), title, + &text_size, ImVec2(0.0f, 0.0f), 0.0f, &bb); + + return pressed; +} + bool FullscreenUI::BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, const ImVec4& bg_color, u32 num_items) { @@ -3556,7 +3730,7 @@ void FullscreenUI::ChoiceDialog::Draw() ImGui::RenderFrame(pos, pos + bb.frame_bb.GetSize(), ImGui::GetColorU32(DarkerColor(UIStyle.PopupBackgroundColor, 0.6f)), false, - LayoutScale(MENU_ITEM_BORDER_ROUNDING)); + LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING)); break; } } @@ -5195,7 +5369,7 @@ void FullscreenUI::UpdateTheme() UIStyle.PrimaryLightColor = HEX_TO_IMVEC4(0x235cd9, 0xff); UIStyle.PrimaryDarkColor = HEX_TO_IMVEC4(0x1d2953, 0xff); UIStyle.PrimaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); - UIStyle.DisabledColor = HEX_TO_IMVEC4(0x999999, 0xff); + UIStyle.DisabledColor = HEX_TO_IMVEC4(0x444444, 0xff); UIStyle.TextHighlightColor = HEX_TO_IMVEC4(0x8e8e8e, 0xff); UIStyle.PrimaryLineColor = HEX_TO_IMVEC4(0x000000, 0xff); UIStyle.SecondaryColor = HEX_TO_IMVEC4(0x2a3e78, 0xff); diff --git a/src/core/fullscreenui_widgets.h b/src/core/fullscreenui_widgets.h index eff8cba3c..1eaee5a39 100644 --- a/src/core/fullscreenui_widgets.h +++ b/src/core/fullscreenui_widgets.h @@ -54,6 +54,7 @@ inline constexpr float LAYOUT_MENU_WINDOW_X_PADDING = 12.0f; inline constexpr float LAYOUT_MENU_WINDOW_Y_PADDING = 12.0f; inline constexpr float LAYOUT_MENU_ITEM_TITLE_SUMMARY_SPACING = 6.0f; inline constexpr float LAYOUT_MENU_ITEM_EXTRA_HEIGHT = 2.0f; +static constexpr float LAYOUT_MENU_ITEM_BORDER_ROUNDING = 10.0f; inline constexpr float LAYOUT_FOOTER_PADDING = 10.0f; inline constexpr float LAYOUT_FOOTER_HEIGHT = LAYOUT_MEDIUM_FONT_SIZE + LAYOUT_FOOTER_PADDING * 2.0f; inline constexpr float LAYOUT_HORIZONTAL_MENU_HEIGHT = 320.0f; @@ -426,10 +427,23 @@ bool HorizontalMenuButton(std::string_view title, bool enabled = true, 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); -void RightAlignNavButtons(u32 num_items = 0); +void RightAlignNavButtons(u32 num_items); +void RightAlignNavButtons(float total_width); bool NavButton(std::string_view title, bool is_active, bool enabled = true); bool NavTab(std::string_view title, bool is_active, bool enabled, float width); +bool BeginFloatingNavBar(float x, float y, float items_width, float font_size = UIStyle.LargeFontSize, + float anchor_x = 0.0f, float anchor_y = 0.0f, float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, + float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); +void EndFloatingNavBar(); +float CalcFloatingNavBarButtonWidth(std::string_view title, float icon_size = UIStyle.LargeFontSize, + float font_size = UIStyle.LargeFontSize, float font_weight = UIStyle.BoldFontWeight, + float x_padding = LAYOUT_MENU_BUTTON_X_PADDING); +bool FloatingNavBarIcon(std::string_view title, ImTextureID image, bool is_active, + float image_size = UIStyle.LargeFontSize, float font_size = UIStyle.LargeFontSize, + float font_weight = UIStyle.BoldFontWeight, bool enabled = true, + const ImVec2& uv0 = ImVec2(0.0f, 0.0f), const ImVec2& uv1 = ImVec2(1.0f, 1.0f)); + bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, const ImVec4& bg_color, u32 num_items); void EndHorizontalMenu();