/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include namespace AtomSampleViewer { const float ImGuiSidebar::s_widthMin = 200.0f; const float ImGuiSidebar::s_widthMax = 1000.0f; const float ImGuiSidebar::s_widthStepSmall = 25.0f; const float ImGuiSidebar::s_widthStepBig = 100.0f; void ImGuiSidebar::Reflect(AZ::ReflectContext* context) { ImGuiSidebar::ConfigFile::Reflect(context); } void ImGuiSidebar::ConfigFile::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(0) ->Field("Width", &ConfigFile::m_width) ; } } ImGuiSidebar::ImGuiSidebar(AZStd::string_view configFilePath) { char configFileFullPath[AZ_MAX_PATH_LEN] = {0}; AZ::IO::FileIOBase::GetInstance()->ResolvePath(configFilePath.data(), configFileFullPath, AZ_MAX_PATH_LEN); m_configFilePath = configFileFullPath; } bool ImGuiSidebar::LoadConfigFile() { // skip loading config file if the config file is not specified. if (m_configFilePath.empty()) { return false; } AZStd::unique_ptr configFile(AZ::Utils::LoadObjectFromFile(m_configFilePath)); if (configFile) { m_configFile = *configFile; return true; } else { return false; } } void ImGuiSidebar::SaveConfigFile() { if (!m_configFilePath.empty()) { if (!AZ::Utils::SaveObjectToFile(m_configFilePath, AZ::DataStream::ST_XML, &m_configFile)) { AZ_Error("ImGuiSidebar", false, "Failed to save '%s'", m_configFilePath.c_str()); } } } void ImGuiSidebar::Activate() { // only load the config file if it's specified if ( (!m_configFilePath.empty()) && AZ::IO::SystemFile::Exists(m_configFilePath.c_str()) ) { if (!LoadConfigFile()) { AZ_Warning("ImGuiSidebar", false, "Failed to load sidebar config from %s.", m_configFilePath.c_str()); } } } void ImGuiSidebar::Deactivate() { if (!m_configFilePath.empty()) { // We only report this message in Deactivate(), not inside SaveConfigFile(), to avoid spamming // this message when SaveConfigFile() is called in OnTick() AZ_TracePrintf("ImGuiSidebar", "Saving settings to '%s'\n", m_configFilePath.c_str()); SaveConfigFile(); } } void ImGuiSidebar::SetHideSidebar(bool isHidden) { m_hideSidebar = isHidden; } AzFramework::WindowSize ImGuiSidebar::BeginFrame() { AzFramework::NativeWindowHandle windowHandle = nullptr; AzFramework::WindowSystemRequestBus::BroadcastResult( windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); AzFramework::WindowSize windowSize; AzFramework::WindowRequestBus::EventResult( windowSize, windowHandle, &AzFramework::WindowRequestBus::Events::GetRenderResolution); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); return windowSize; } void ImGuiSidebar::EndFrame() { ImGui::PopStyleVar(); } bool ImGuiSidebar::Begin() { AZ_Assert(!m_isSidebarReady, "End() was not called"); AzFramework::WindowSize windowSize = BeginFrame(); // Apply some offset so we don't collide with the menu bar const float menuBarOffset = 18.0f; const float scale = ImGui::GetIO().FontGlobalScale; const float imguiWindowWidth = m_configFile.m_width * scale; const float imguiWindowHeight = windowSize.m_height - menuBarOffset; const float revealWindowWidth = 120.0f * scale; const float revealWindowHeight = 40.0f * scale; const ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; bool configChanged = false; // We can't append to the main menu so instead we have a // smaller window that takes up less space if (m_hideSidebar) { ImGui::SetNextWindowPos(ImVec2(windowSize.m_width - revealWindowWidth, menuBarOffset)); ImGui::SetNextWindowSize(ImVec2(revealWindowWidth, revealWindowHeight)); if (ImGui::Begin("##RevealSidebar", nullptr, windowFlags)) { if (ScriptableImGui::Button("Reveal Sidebar")) { m_hideSidebar = false; } ImGui::End(); } } else { ImGui::SetNextWindowPos(ImVec2(windowSize.m_width - imguiWindowWidth, menuBarOffset)); ImGui::SetNextWindowSize(ImVec2(imguiWindowWidth, imguiWindowHeight)); if (ImGui::Begin("##Sidebar", nullptr, windowFlags)) { const float itemSpacing = ImGui::GetStyle().ItemSpacing.x; // Make the "Hide Sidebar" button show up in about the same position as the "Reveal Sidebar" button const float rightMargin = 16.0f; const float buttonWidth = revealWindowWidth - rightMargin; ImGui::SameLine(imguiWindowWidth - revealWindowWidth + itemSpacing); if (ScriptableImGui::Button("Hide Sidebar", ImVec2(buttonWidth, 0))) { m_hideSidebar = true; } // Make the sidebar size buttons appear on the same line, aligned to the right, so they don't move. ImGui::NewLine(); static float resizeButtonWidthSmall = 20.0f; static float resizeButtonWidthBig = 20.0f; float pos = resizeButtonWidthBig + itemSpacing; ImGui::SameLine(imguiWindowWidth - pos); if (ImGui::Button(" >> ")) { m_configFile.m_width -= s_widthStepBig; m_configFile.m_width = AZStd::max(m_configFile.m_width, s_widthMin); configChanged = true; } resizeButtonWidthBig = ImGui::GetItemRectSize().x; // Get the actual width for the next frame pos += resizeButtonWidthSmall + itemSpacing; ImGui::SameLine(imguiWindowWidth - pos); if (ImGui::Button(" > ")) { m_configFile.m_width -= s_widthStepSmall; m_configFile.m_width = AZStd::max(m_configFile.m_width, s_widthMin); configChanged = true; } resizeButtonWidthSmall = ImGui::GetItemRectSize().x; // Get the actual width for the next frame pos += resizeButtonWidthSmall + itemSpacing; ImGui::SameLine(imguiWindowWidth - pos); if (ImGui::Button(" < ")) { m_configFile.m_width += s_widthStepSmall; m_configFile.m_width = AZStd::min(m_configFile.m_width, s_widthMax); configChanged = true; } pos += resizeButtonWidthBig + itemSpacing; ImGui::SameLine(imguiWindowWidth - pos); if (ImGui::Button(" << ")) { m_configFile.m_width += s_widthStepBig; m_configFile.m_width = AZStd::min(m_configFile.m_width, s_widthMax); configChanged = true; } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); m_isSidebarReady = true; } } if (configChanged) { SaveConfigFile(); } if (!m_isSidebarReady) { EndFrame(); } return m_isSidebarReady; } void ImGuiSidebar::End() { AZ_Assert(m_isSidebarReady, "Begin() was not called, or it returned false"); ImGui::End(); // Ends the ImGui::Begin("##Sidebar",...) m_isSidebarReady = false; EndFrame(); } } // namespace AtomSampleViewer