Add reset buttons to Studio sliders.
Slider rows in the joints and controls tabs could only be reset through a hidden right-click context menu. Replace it with a visible reset button at the right edge of each row, disabled while the value is at its reset target so the button column doubles as a modified indicator. Rows split the remaining width equally between slider and label, and labels clip at the button column. Adds an ImGui_ResetButton widget. PiperOrigin-RevId: 956627705 Change-Id: I9c178f6cd1cc806c7ffd6f531acf7f2d470d9346
This commit is contained in:
committed by
Copybara-Service
parent
e662350357
commit
98ecadcdf2
@@ -1289,11 +1289,35 @@ void NoiseGui(StepControl* step_control) {
|
|||||||
step_control->SetNoiseParameters(noise_scale, noise_rate);
|
step_control->SetNoiseParameters(noise_scale, noise_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slider row with a clipped label and a right-aligned reset button; the
|
||||||
|
// button is disabled while the value is at its reset target.
|
||||||
|
static void SliderRowWithReset(const char* name, mjtNum* value, mjtNum min,
|
||||||
|
mjtNum max, mjtNum reset) {
|
||||||
|
const float btn = ImGui::GetFrameHeight();
|
||||||
|
const float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||||
|
const float usable_end =
|
||||||
|
ImGui::GetWindowContentRegionMax().x - btn - spacing;
|
||||||
|
const float usable = usable_end - ImGui::GetCursorPosX();
|
||||||
|
ImGui::SetNextItemWidth(usable * 0.5f);
|
||||||
|
char hidden[104];
|
||||||
|
std::snprintf(hidden, sizeof(hidden), "##%s", name);
|
||||||
|
ImGui_Slider(hidden, value, min, max);
|
||||||
|
ImGui::SameLine();
|
||||||
|
const ImVec2 clip_min = ImGui::GetCursorScreenPos();
|
||||||
|
const ImVec2 clip_max(ImGui::GetWindowPos().x + usable_end,
|
||||||
|
clip_min.y + ImGui::GetFrameHeight());
|
||||||
|
ImGui::PushClipRect(clip_min, clip_max, true);
|
||||||
|
ImGui::TextUnformatted(name);
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
ImGui::BeginDisabled(*value == reset);
|
||||||
|
if (ImGui_ResetButton(name)) {
|
||||||
|
*value = reset;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
void JointsGui(const mjModel* model, const mjData* data,
|
void JointsGui(const mjModel* model, const mjData* data,
|
||||||
const mjvOption* vis_options) {
|
const mjvOption* vis_options) {
|
||||||
const float item_width = ImGui::GetWindowWidth() * .6f;
|
|
||||||
ImGui::PushItemWidth(item_width);
|
|
||||||
|
|
||||||
char name[100];
|
char name[100];
|
||||||
for (int i = 0; i < model->njnt; ++i) {
|
for (int i = 0; i < model->njnt; ++i) {
|
||||||
if (model->jnt_type[i] != mjJNT_HINGE &&
|
if (model->jnt_type[i] != mjJNT_HINGE &&
|
||||||
@@ -1326,23 +1350,13 @@ void JointsGui(const mjModel* model, const mjData* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int data_adr = model->jnt_qposadr[i];
|
const int data_adr = model->jnt_qposadr[i];
|
||||||
ImGui_Slider(name, &data->qpos[data_adr], min, max);
|
SliderRowWithReset(name, &data->qpos[data_adr], min, max,
|
||||||
if (ImGui::BeginPopupContextItem()) {
|
model->qpos0[data_adr]);
|
||||||
if (ImGui::MenuItem("Reset to default")) {
|
|
||||||
data->qpos[data_adr] = model->qpos0[data_adr];
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopItemWidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlsGui(const mjModel* model, mjData* data,
|
void ControlsGui(const mjModel* model, mjData* data,
|
||||||
const mjvOption* vis_options) {
|
const mjvOption* vis_options) {
|
||||||
const float item_width = ImGui::GetWindowWidth() * .6f;
|
|
||||||
ImGui::PushItemWidth(item_width);
|
|
||||||
|
|
||||||
if (ImGui::Button("Clear All")) {
|
if (ImGui::Button("Clear All")) {
|
||||||
mj_resetCtrl(model, data);
|
mj_resetCtrl(model, data);
|
||||||
}
|
}
|
||||||
@@ -1383,17 +1397,10 @@ void ControlsGui(const mjModel* model, mjData* data,
|
|||||||
min = model->actuator_ctrlrange[2 * j + 0];
|
min = model->actuator_ctrlrange[2 * j + 0];
|
||||||
max = model->actuator_ctrlrange[2 * j + 1];
|
max = model->actuator_ctrlrange[2 * j + 1];
|
||||||
}
|
}
|
||||||
ImGui_Slider(name, &data->ctrl[j], min, max);
|
SliderRowWithReset(name, &data->ctrl[j], min, max,
|
||||||
if (ImGui::BeginPopupContextItem()) {
|
mju_clip(0.0, min, max));
|
||||||
if (ImGui::MenuItem("Reset to 0")) {
|
|
||||||
data->ctrl[j] = mju_clip(0.0, min, max);
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopItemWidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetPlotXLimit(const mjData* data) {
|
static int GetPlotXLimit(const mjData* data) {
|
||||||
|
|||||||
@@ -343,6 +343,18 @@ bool ImGui_Slider(const char* name, mjtNum* value, mjtNum min, mjtNum max) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGui_ResetButton(const char* id, const char* icon,
|
||||||
|
const char* tooltip) {
|
||||||
|
const float size = ImGui::GetFrameHeight();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionMax().x - size);
|
||||||
|
ImGui::PushID(id);
|
||||||
|
const bool clicked = ImGui::SmallButton(icon);
|
||||||
|
ImGui::PopID();
|
||||||
|
ImGui::SetItemTooltip("%s", tooltip);
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
bool ImGui_BeginHSplit(const char* id, float* height, bool* open) {
|
bool ImGui_BeginHSplit(const char* id, float* height, bool* open) {
|
||||||
const ImVec2 region = ImGui::GetContentRegionAvail();
|
const ImVec2 region = ImGui::GetContentRegionAvail();
|
||||||
if (*height < 0) {
|
if (*height < 0) {
|
||||||
|
|||||||
@@ -460,6 +460,11 @@ void ImGui_EndHSplit(bool open);
|
|||||||
// ImGui Slider that supports both float and double types.
|
// ImGui Slider that supports both float and double types.
|
||||||
bool ImGui_Slider(const char* name, mjtNum* value, mjtNum min, mjtNum max);
|
bool ImGui_Slider(const char* name, mjtNum* value, mjtNum min, mjtNum max);
|
||||||
|
|
||||||
|
// Small right-aligned reset button on the current line; returns true when
|
||||||
|
// clicked. Use after a widget to give it a visible reset affordance.
|
||||||
|
bool ImGui_ResetButton(const char* id, const char* icon = ICON_FA_UNDO,
|
||||||
|
const char* tooltip = "Reset");
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool ImGui_Checkbox(const char* name, T& value) {
|
bool ImGui_Checkbox(const char* name, T& value) {
|
||||||
static_assert(std::is_integral<T>());
|
static_assert(std::is_integral<T>());
|
||||||
|
|||||||
Reference in New Issue
Block a user