diff --git a/assets/images/img_learn_preview_basestations.png b/assets/images/img_learn_preview_basestations.png new file mode 100644 index 0000000..3f4ed70 Binary files /dev/null and b/assets/images/img_learn_preview_basestations.png differ diff --git a/assets/images/img_learn_preview_continuous.png b/assets/images/img_learn_preview_continuous.png new file mode 100644 index 0000000..414d882 Binary files /dev/null and b/assets/images/img_learn_preview_continuous.png differ diff --git a/assets/images/img_learn_preview_standard.png b/assets/images/img_learn_preview_standard.png new file mode 100644 index 0000000..c64d141 Binary files /dev/null and b/assets/images/img_learn_preview_standard.png differ diff --git a/assets/images/img_learn_preview_uitour.png b/assets/images/img_learn_preview_uitour.png new file mode 100644 index 0000000..d372a31 Binary files /dev/null and b/assets/images/img_learn_preview_uitour.png differ diff --git a/src/overlay/imgui_extensions.cpp b/src/overlay/imgui_extensions.cpp index 2f08ecb..d05b8ca 100644 --- a/src/overlay/imgui_extensions.cpp +++ b/src/overlay/imgui_extensions.cpp @@ -10,8 +10,7 @@ namespace ImGui::fonts { static ImVector s_GroupPanelLabelStack; -void ImGui::BeginGroupPanel(const char* name, const ImVec2& size) -{ +void ImGui::BeginGroupPanel(const char* name, const ImVec2& size) { ImGui::BeginGroup(); auto cursorPos = ImGui::GetCursorScreenPos(); @@ -60,8 +59,7 @@ void ImGui::BeginGroupPanel(const char* name, const ImVec2& size) s_GroupPanelLabelStack.push_back(ImRect(labelMin, labelMax)); } -void ImGui::EndGroupPanel() -{ +void ImGui::EndGroupPanel() { ImGui::PopItemWidth(); auto itemSpacing = ImGui::GetStyle().ItemSpacing; @@ -137,14 +135,14 @@ bool ImGui::CheckboxWithDescription(const char* title, bool* v, const char* desc ImVec2 startPos = ImGui::GetCursorPos(); float contentWidth = ImGui::GetContentRegionAvail().x; - + bool changed = ImGui::Checkbox(fmt::format("##{}", title).c_str(), v); - + ImGui::SameLine(); float yOffsetToTop = ImGui::GetStyle().FramePadding.y + (ImGui::GetFrameHeight() - (ImGui::GetStyle().FramePadding.y * 2.0f) - ImGui::GetTextLineHeight()) * 0.5f; ImGui::SetCursorPosY(ImGui::GetCursorPosY() - yOffsetToTop); - + ImGui::BeginGroup(); ImGui::TextUnformatted(title); ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetStyle().ItemSpacing.y + 2.0f); @@ -160,7 +158,7 @@ bool ImGui::CheckboxWithDescription(const char* title, bool* v, const char* desc *v = !(*v); changed = true; } - + ImGui::SetCursorPos(endPos); ImGui::Spacing(); return changed; @@ -219,4 +217,79 @@ void ImGui::PillText(const char* text, const ImVec4& bgColor, const ImVec4& text const ImVec2 textPos = ImVec2(bb.Min.x + padding.x, bb.Min.y + padding.y); window->DrawList->AddText(textPos, ImGui::ColorConvertFloat4ToU32(textColor), text); +} + +bool ImGui::InformationButton(const char* icon, const char* title, const char* desc, ImTextureID previewTex, ImVec2 previewSize) { + bool clicked = false; + + ImGui::PushID(title); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(ImGui::GetStyle().ItemInnerSpacing.x, 0.0f)); + + bool open = ImGui::BeginCard("##card", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY); + + if (open) { + ImVec2 startPos = ImGui::GetCursorPos(); + bool isHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); + bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); + + if (isHovered) { + ImU32 hoverCol = isMouseDown + ? ImGui::GetColorU32(ImGuiCol_HeaderActive) + : ImGui::GetColorU32(ImGuiCol_HeaderHovered); + + ImVec2 pMin = ImGui::GetWindowPos(); + ImVec2 pMax = ImVec2(pMin.x + ImGui::GetWindowWidth(), pMin.y + ImGui::GetWindowHeight()); + + ImGui::GetWindowDrawList()->AddRectFilled(pMin, pMax, hoverCol, 6.0f); + } + + int columns = previewTex ? 2 : 1; + if (ImGui::BeginTable("##layout", columns, ImGuiTableFlags_SizingStretchProp)) { + + if (previewTex) { + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, previewSize.x); + } + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); + + if (previewTex) { + ImGui::TableNextColumn(); + + float availX = ImGui::GetContentRegionAvail().x; + if (availX > previewSize.x) { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availX - previewSize.x)); + } + ImGui::Image(previewTex, previewSize); + } + + ImGui::TableNextColumn(); + + if (icon && *icon) { + ImGui::TextHeading("%s %s", icon, title); + } + else { + ImGui::TextHeading("%s", title); + } + + if (desc && *desc) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); + ImGui::TextWrapped("%s", desc); + ImGui::PopStyleColor(); + } + + ImGui::EndTable(); + } + + float contentHeight = ImGui::GetCursorPosY() - startPos.y - ImGui::GetStyle().ItemSpacing.y; + + ImGui::SetCursorPos(startPos); + ImGui::SetNextItemAllowOverlap(); + clicked = ImGui::InvisibleButton("##card_button", ImVec2(ImGui::GetContentRegionAvail().x, contentHeight)); + ImGui::SetCursorPosY(startPos.y + contentHeight); + } + + ImGui::EndCard(); + ImGui::PopStyleVar(); + + ImGui::PopID(); + return clicked; } \ No newline at end of file diff --git a/src/overlay/imgui_extensions.h b/src/overlay/imgui_extensions.h index 7517d68..9e25a28 100644 --- a/src/overlay/imgui_extensions.h +++ b/src/overlay/imgui_extensions.h @@ -154,46 +154,7 @@ namespace ImGui ImGui::PopStyleColor(5); } - // @TODO: polish these cards - inline bool InformationButton(const char* icon, const char* title, const char* desc, ImTextureID previewTex, ImVec2 previewSize) { - bool clicked = false; - - std::string id = fmt::format("id__{}", title); - if (BeginCard(id.c_str(), ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY)) { - - ImVec2 avail = ImGui::GetContentRegionAvail(); - ImGui::SetNextItemAllowOverlap(); - if (ImGui::Selectable(fmt::format("##sel_{}", id).c_str(), false, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, avail.y))) { - clicked = true; - } - - ImGui::SetCursorPos(ImGui::GetStyle().WindowPadding); - - if (ImGui::BeginTable(fmt::format("table_{}", id).c_str(), 2, ImGuiTableFlags_SizingStretchSame)) { - ImGui::TableNextColumn(); - - ImGui::TextHeading("%s %s", icon, title); - ImGui::Spacing(); - ImGui::TextDisabled("%s", desc); - - ImGui::TableNextColumn(); - - if (previewTex) { - float availX = ImGui::GetContentRegionAvail().x; - if (availX > previewSize.x) { - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availX - previewSize.x)); - } - ImGui::Image(previewTex, previewSize); - } - - ImGui::EndTable(); - } - - EndCard(); - } - - return clicked; - } + bool InformationButton(const char* icon, const char* title, const char* desc, ImTextureID previewTex, ImVec2 previewSize); bool CheckboxWithDescription(const char* title, bool* v, const char* desc); bool RadioButtonWithDescription(const char* title, bool v, const char* desc); diff --git a/src/overlay/user_interface.cpp b/src/overlay/user_interface.cpp index f61a3d7..74fd640 100644 --- a/src/overlay/user_interface.cpp +++ b/src/overlay/user_interface.cpp @@ -735,6 +735,7 @@ namespace spacecal { for (size_t i = 0; i < dwNumCalibrations; i++) { spacecal::TrackingSystemCalibration& calibration = spacecal::CalibrationManager::getInstance()->getCalibration(i); + ImGui::PushID(fmt::format("calibration_id__{}", i).c_str()); std::string szCalibrationStatusIcon = ""; std::string szCalibrationStatusMessage = ""; @@ -885,6 +886,8 @@ namespace spacecal { buildDeviceSelection(calibration); buildCalibrationCommonControls(calibration); } + + ImGui::PopID(); } } } @@ -1534,40 +1537,55 @@ namespace spacecal { default: case LearnPage_Home: { - // @TODO: image loading + constexpr float k_PREVIEW_IMAGE_SIZE_PIXELS = 140.0f; + constexpr float k_CELL_PADDING = 4.0f; + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(k_CELL_PADDING, k_CELL_PADDING)); + ImGui::BeginTable("ui_learn_toc_cards", 2, ImGuiTableFlags_None); + + ImGui::TableNextColumn(); if (ImGui::InformationButton(ICON_MS_TARGET, LOCALE_GET("learn_card_standard_calibration").c_str(), LOCALE_GET("learn_card_standard_calibration_description").c_str(), - ImTextureID_Invalid, - ImVec2(120.0f, 80.0f)) + g_state.textures[EImageId_Learn_Preview_Standard].hTexture, + ImVec2(k_PREVIEW_IMAGE_SIZE_PIXELS, k_PREVIEW_IMAGE_SIZE_PIXELS)) ) { g_state.dwSelectedLearnPage = LearnPage_Standard; } - if (ImGui::InformationButton(ICON_MS_TARGET, + ImGui::TableNextColumn(); + if (ImGui::InformationButton(ICON_MS_CYCLE, LOCALE_GET("learn_card_continuous_calibration").c_str(), LOCALE_GET("learn_card_continuous_calibration_description").c_str(), - ImTextureID_Invalid, - ImVec2(120.0f, 80.0f)) + g_state.textures[EImageId_Learn_Preview_Continuous].hTexture, + ImVec2(k_PREVIEW_IMAGE_SIZE_PIXELS, k_PREVIEW_IMAGE_SIZE_PIXELS)) ) { g_state.dwSelectedLearnPage = LearnPage_Continuous; } - if (ImGui::InformationButton(ICON_MS_TARGET, + + ImGui::TableNextColumn(); + if (ImGui::InformationButton(ICON_MS_SENSORS, LOCALE_GET("learn_card_base_station_management").c_str(), LOCALE_GET("learn_card_base_station_management_description").c_str(), - ImTextureID_Invalid, - ImVec2(120.0f, 80.0f)) + g_state.textures[EImageId_Learn_Preview_BaseStations].hTexture, + ImVec2(k_PREVIEW_IMAGE_SIZE_PIXELS, k_PREVIEW_IMAGE_SIZE_PIXELS)) ) { g_state.dwSelectedLearnPage = LearnPage_BaseStations; } - if (ImGui::InformationButton(ICON_MS_TARGET, + + ImGui::TableNextColumn(); + if (ImGui::InformationButton(ICON_MS_HANDHELD_CONTROLLER, LOCALE_GET("learn_card_ui_tour").c_str(), LOCALE_GET("learn_card_ui_tour_description").c_str(), - ImTextureID_Invalid, - ImVec2(120.0f, 80.0f)) + g_state.textures[EImageId_Learn_Preview_UITour].hTexture, + ImVec2(k_PREVIEW_IMAGE_SIZE_PIXELS, k_PREVIEW_IMAGE_SIZE_PIXELS)) ) { g_state.dwSelectedLearnPage = LearnPage_UITour; } + + ImGui::PopStyleVar(); + ImGui::EndTable(); + break; } case LearnPage_Standard: @@ -1878,6 +1896,11 @@ namespace spacecal { g_state.textures[EImageId_LearnUI_Unk0] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_ui_0.png"); g_state.textures[EImageId_LearnUI_Unk1] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_ui_1.png"); + g_state.textures[EImageId_Learn_Preview_Standard] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_preview_standard.png"); + g_state.textures[EImageId_Learn_Preview_Continuous] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_preview_continuous.png"); + g_state.textures[EImageId_Learn_Preview_BaseStations] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_preview_basestations.png"); + g_state.textures[EImageId_Learn_Preview_UITour] = renderer::getRenderContext(renderer::GraphicsBackend::OpenGL)->loadTexture(util::getSpaceCalibratorImagesDir() / "img_learn_preview_uitour.png"); + if (g_state.aBaseStations.capacity() == 0) { g_state.aBaseStations.reserve(64); } diff --git a/src/overlay/user_interface.h b/src/overlay/user_interface.h index e140048..7dd233f 100644 --- a/src/overlay/user_interface.h +++ b/src/overlay/user_interface.h @@ -42,6 +42,10 @@ namespace spacecal { EImageId_LearnBaseStation_20, EImageId_LearnUI_Unk0, EImageId_LearnUI_Unk1, + EImageId_Learn_Preview_Standard, + EImageId_Learn_Preview_Continuous, + EImageId_Learn_Preview_BaseStations, + EImageId_Learn_Preview_UITour, EImageId_Count, };