Move Style and Layout configuration to shared GUI library.

PiperOrigin-RevId: 840246253
Change-Id: Ic4664b62e86e70fd6780cd9eb885978064c5f585
This commit is contained in:
Haroon Qureshi
2025-12-04 08:01:24 -08:00
committed by Copybara-Service
parent 5f59104877
commit 5ee8b646f7
4 changed files with 154 additions and 129 deletions
+112
View File
@@ -22,6 +22,7 @@
#include <vector>
#include <imgui.h>
#include <imgui_internal.h>
#include <implot.h>
#include <mujoco/mujoco.h>
#include "experimental/platform/helpers.h"
@@ -29,12 +30,123 @@
namespace mujoco::platform {
static constexpr int kToolsBarHeight = 48;
static constexpr int kStatusBarHeight = 32;
static constexpr float kOptionsRelWidth = 0.22f;
static constexpr float kInspectorRelWidth = 0.18f;
static constexpr float kInfoRelHeight = 0.3f;
static ImVec2 GetFlexElementSize(int num_cols) {
const float width = (ImGui::GetContentRegionAvail().x / num_cols) -
ImGui::GetStyle().FramePadding.x * 2;
return ImVec2(width, 0);
}
void SetupTheme(GuiTheme theme) {
ImGuiStyle& s = ImGui::GetStyle();
if (theme == GuiTheme::kDark) {
ImGui::StyleColorsDark(&s);
} else {
ImGui::StyleColorsLight(&s);
}
s.FrameBorderSize = 1;
}
ImVec4 ConfigureDockingLayout() {
ImGuiViewport* viewport = ImGui::GetMainViewport();
const ImVec2 dockspace_pos{
viewport->WorkPos.x,
viewport->WorkPos.y + kToolsBarHeight
};
const ImVec2 dockspace_size{
viewport->WorkSize.x,
viewport->WorkSize.y - kToolsBarHeight - kStatusBarHeight
};
ImGuiID root = ImGui::GetID("Root");
const bool first_time = (ImGui::DockBuilderGetNode(root) == nullptr);
if (first_time) {
ImGui::DockBuilderRemoveNode(root);
ImGui::DockBuilderAddNode(root, ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(root, dockspace_size);
// Slice up the main dock space.
ImGuiID main = root;
ImGuiID options = 0;
ImGui::DockBuilderSplitNode(main, ImGuiDir_Left, kOptionsRelWidth,
&options, &main);
ImGuiID inspector = 0;
ImGui::DockBuilderSplitNode(main, ImGuiDir_Right, kInspectorRelWidth,
&inspector, &main);
ImGuiID info = 0;
ImGui::DockBuilderSplitNode(inspector, ImGuiDir_Down, kInfoRelHeight,
&info, &inspector);
ImGui::DockBuilderDockWindow("Dockspace", main);
ImGui::DockBuilderDockWindow("Options", options);
ImGui::DockBuilderDockWindow("Inspector", inspector);
ImGui::DockBuilderDockWindow("Info", info);
ImGui::DockBuilderFinish(root);
}
// Create a dummy window filling the entire workspace in which we can perform
// docking.
ImGui::SetNextWindowPos(dockspace_pos);
ImGui::SetNextWindowSize(dockspace_size);
ImGui::SetNextWindowViewport(viewport->ID);
const ImGuiWindowFlags kWorkspaceFlags =
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
style.Var(ImGuiStyleVar_WindowBorderSize, 0.0f);
style.Var(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Dockspace", nullptr, kWorkspaceFlags);
style.Reset();
const ImGuiDockNodeFlags kDockSpaceFlags =
ImGuiDockNodeFlags_PassthruCentralNode |
ImGuiDockNodeFlags_NoDockingOverCentralNode |
ImGuiDockNodeFlags_AutoHideTabBar;
ImGui::DockSpace(root, ImVec2(0.0f, 0.0f), kDockSpaceFlags);
ImGui::End();
const ImGuiWindowFlags kFixedFlags = ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoDocking;
// Toolbar is fixed at the top.
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight), ImGuiCond_Always);
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
ImGui::End();
// StatusBar is fixed at the bottom.
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight), ImGuiCond_Always);
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
ImGui::End();
const int settings_width = dockspace_size.x * kOptionsRelWidth;
const int inspector_width = dockspace_size.x * kInspectorRelWidth;
const float workspace_x = dockspace_pos.x + settings_width;
const float workspace_y = dockspace_pos.y;
const float workspace_w = dockspace_size.x - settings_width - inspector_width;
const float workspace_h = dockspace_size.y;
return ImVec4(workspace_x, workspace_y, workspace_w, workspace_h);
}
void SensorGui(const mjModel* model, const mjData* data) {
ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.1f, 0.1f));
if (ImPlot::BeginPlot("Sensors", ImVec2(-1, 0),
+27
View File
@@ -25,10 +25,37 @@
#include <vector>
#include <imgui.h>
#include <mujoco/mujoco.h>
namespace mujoco::platform {
// Standard default UX themes for MuJoCo applications.
enum class GuiTheme {
kLight,
kDark,
};
// Updates the ImGui internal style state to match the requested theme.
void SetupTheme(GuiTheme theme);
// Configures the ImGui docking module to the standard layout used by Studio.
// This includes the following named sections:
// "ToolBar": fixed size bar spanning the top of the window; for placing
// buttons and other controls that are always needed.
// "StatusBar": fixed size bar spanning the bottom of the window; for
// placing information and controls that are always needed.
// "Options": resizable section of the left; designed for GUI elements that
// are used to configure the simulation (e.g. PhysicsGui).
// "Inspector": resizable section of the right; designed for inspecting
// or manipulating mjData elements (e.g. ControlsGui).
// "Info": resizable section below the inspector; designed for displaying
// basic simulation information (e.g. InfoGui); hidden by default.
//
// Returns the size and position of the remaining workspace area which can then
// be used to place additional elements (e.g. floating charts).
ImVec4 ConfigureDockingLayout();
// UX for controlling the physics simulation parameters (e.g. integrator,
// solver, etc.) in mjModel.
void PhysicsGui(mjModel* model, float min_width);
+12 -120
View File
@@ -34,7 +34,6 @@
#include <vector>
#include <imgui.h>
#include <imgui_internal.h>
#include <implot.h>
#include <mujoco/mujoco.h>
#include "experimental/platform/gui.h"
@@ -122,12 +121,6 @@ static constexpr const char* ICON_NEXT_FRAME = ICON_FA_CARET_RIGHT;
static constexpr const char* ICON_CURR_FRAME = ICON_FA_FAST_FORWARD;
static constexpr const char* ICON_SPEED = ICON_FA_TACHOMETER;
static constexpr int kToolsBarHeight = 48;
static constexpr int kStatusBarHeight = 32;
static constexpr float kOptionsRelWidth = 0.22f;
static constexpr float kInspectorRelWidth = 0.18f;
static constexpr float kInfoRelHeight = 0.3f;
// UI labels for mjtLabel.
static constexpr const char* kLabelNames[] = {
"None", "Body", "Joint", "Geom", "Site", "Camera",
@@ -758,8 +751,8 @@ void App::MoveCamera(platform::CameraMotion motion, mjtNum reldx, mjtNum reldy)
}
void App::BuildGui() {
SetupStyle(ui_.style);
const ImVec4 workspace_rect = ConfigureDockingLayout();
SetupTheme(ui_.theme);
const ImVec4 workspace_rect = platform::ConfigureDockingLayout();
// Place charts in bottom right corner of the workspace.
const ImVec2 chart_size(250, 250);
@@ -890,114 +883,11 @@ void App::BuildGui() {
}
}
void App::SetupStyle(Style style) {
if (tmp_.style_editor) {
return;
void App::SetupTheme(platform::GuiTheme theme) {
if (!tmp_.style_editor) {
platform::SetupTheme(theme);
ui_.theme = theme;
}
ImGuiStyle& s = ImGui::GetStyle();
if (style == kDark) {
ImGui::StyleColorsDark(&s);
} else {
ImGui::StyleColorsLight(&s);
}
s.FrameBorderSize = 1;
ui_.style = style;
}
ImVec4 App::ConfigureDockingLayout() {
ImGuiViewport* viewport = ImGui::GetMainViewport();
const ImVec2 dockspace_pos{
viewport->WorkPos.x,
viewport->WorkPos.y + kToolsBarHeight
};
const ImVec2 dockspace_size{
viewport->WorkSize.x,
viewport->WorkSize.y - kToolsBarHeight - kStatusBarHeight
};
ImGuiID root = ImGui::GetID("Root");
const bool first_time = (ImGui::DockBuilderGetNode(root) == nullptr);
if (first_time) {
ImGui::DockBuilderRemoveNode(root);
ImGui::DockBuilderAddNode(root, ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(root, dockspace_size);
// Slice up the main dock space.
ImGuiID main = root;
ImGuiID options = 0;
ImGui::DockBuilderSplitNode(main, ImGuiDir_Left, kOptionsRelWidth,
&options, &main);
ImGuiID inspector = 0;
ImGui::DockBuilderSplitNode(main, ImGuiDir_Right, kInspectorRelWidth,
&inspector, &main);
ImGuiID info = 0;
ImGui::DockBuilderSplitNode(inspector, ImGuiDir_Down, kInfoRelHeight,
&info, &inspector);
ImGui::DockBuilderDockWindow("Dockspace", main);
ImGui::DockBuilderDockWindow("Options", options);
ImGui::DockBuilderDockWindow("Inspector", inspector);
ImGui::DockBuilderDockWindow("Info", info);
ImGui::DockBuilderFinish(root);
}
// Create a dummy window filling the entire workspace in which we can perform
// docking.
ImGui::SetNextWindowPos(dockspace_pos);
ImGui::SetNextWindowSize(dockspace_size);
ImGui::SetNextWindowViewport(viewport->ID);
const ImGuiWindowFlags kWorkspaceFlags =
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
style.Var(ImGuiStyleVar_WindowBorderSize, 0.0f);
style.Var(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Dockspace", nullptr, kWorkspaceFlags);
style.Reset();
const ImGuiDockNodeFlags kDockSpaceFlags =
ImGuiDockNodeFlags_PassthruCentralNode |
ImGuiDockNodeFlags_NoDockingOverCentralNode |
ImGuiDockNodeFlags_AutoHideTabBar;
ImGui::DockSpace(root, ImVec2(0.0f, 0.0f), kDockSpaceFlags);
ImGui::End();
const ImGuiWindowFlags kFixedFlags = ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoDocking;
// Toolbar is fixed at the top.
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight), ImGuiCond_Always);
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
ImGui::End();
// StatusBar is fixed at the bottom.
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight), ImGuiCond_Always);
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
ImGui::End();
const int settings_width = dockspace_size.x * kOptionsRelWidth;
const int inspector_width = dockspace_size.x * kInspectorRelWidth;
const float workspace_x = dockspace_pos.x + settings_width;
const float workspace_y = dockspace_pos.y;
const float workspace_w = dockspace_size.x - settings_width - inspector_width;
const float workspace_h = dockspace_size.y;
return ImVec4(workspace_x, workspace_y, workspace_w, workspace_h);
}
void App::ModelOptionsGui() {
@@ -1246,11 +1136,13 @@ void App::ToolBarGui() {
// Style selection.
ImGui::SameLine();
if (ImGui::Button(ui_.style == kDark ? ICON_DARKMODE : ICON_LIGHTMODE)) {
if (ui_.style == kDark) {
SetupStyle(kLight);
if (ImGui::Button(ui_.theme == platform::GuiTheme::kDark
? ICON_DARKMODE
: ICON_LIGHTMODE)) {
if (ui_.theme == platform::GuiTheme::kDark) {
SetupTheme(platform::GuiTheme::kLight);
} else {
SetupStyle(kDark);
SetupTheme(platform::GuiTheme::kDark);
}
}
ImGui::SetItemTooltip("%s", "Switch Style");
+3 -9
View File
@@ -25,8 +25,8 @@
#include <unordered_map>
#include <vector>
#include <imgui.h>
#include <mujoco/mujoco.h>
#include "experimental/platform/gui.h"
#include "experimental/platform/helpers.h"
#include "experimental/platform/interaction.h"
#include "experimental/platform/renderer.h"
@@ -65,18 +65,13 @@ class App {
static int LoadAssetCallback(const char* path, void* user_data,
unsigned char** out, std::uint64_t* out_size);
enum Style {
kLight,
kDark,
};
// UI state that is persisted across application runs
struct UiState {
char watch_field[1000] = "qpos";
int watch_index = 0;
int camera_idx = platform::kTumbleCameraIdx;
int key_idx = 0;
Style style = kLight;
platform::GuiTheme theme = platform::GuiTheme::kLight;
using Dict = std::unordered_map<std::string, std::string>;
Dict ToDict() const;
@@ -148,8 +143,7 @@ class App {
void HandleKeyboardEvents();
void MoveCamera(platform::CameraMotion motion, mjtNum reldx, mjtNum reldy);
void SetupStyle(Style style);
ImVec4 ConfigureDockingLayout();
void SetupTheme(platform::GuiTheme theme);
void MainMenuGui();
void ToolBarGui();