Improve error/warning display in studio.
PiperOrigin-RevId: 940299502 Change-Id: I8b13e2f984627a638a01b52f7dadca43f2c2ca80
This commit is contained in:
committed by
Copybara-Service
parent
407be2fc0b
commit
584ccd73d5
@@ -17,7 +17,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <span>
|
#include <span> // NOLINT
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
@@ -89,6 +89,11 @@ void ModelHolder::PostInit() {
|
|||||||
SetLoadError(mjs_getError(spec_));
|
SetLoadError(mjs_getError(spec_));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int num_warnings = mjs_numWarnings(spec_);
|
||||||
|
for (int i = 0; i < num_warnings; ++i) {
|
||||||
|
if (!warning_.empty()) warning_ += '\n';
|
||||||
|
warning_ += mjs_getWarning(spec_, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data_ = mj_makeData(model_);
|
data_ = mj_makeData(model_);
|
||||||
if (!data_) {
|
if (!data_) {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <span>
|
#include <span> // NOLINT
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
@@ -56,6 +57,9 @@ class ModelHolder {
|
|||||||
// Returns the error message if the model failed to load.
|
// Returns the error message if the model failed to load.
|
||||||
std::string_view error() const { return error_; }
|
std::string_view error() const { return error_; }
|
||||||
|
|
||||||
|
// Returns the warning message(s), newline-separated.
|
||||||
|
std::string_view warning() const { return warning_; }
|
||||||
|
|
||||||
// Releases ownership of the underlying MuJoCo structures. Callers are now
|
// Releases ownership of the underlying MuJoCo structures. Callers are now
|
||||||
// responsible for calling the appropriate `mj_delete*` function on these
|
// responsible for calling the appropriate `mj_delete*` function on these
|
||||||
// objects.
|
// objects.
|
||||||
@@ -78,6 +82,7 @@ class ModelHolder {
|
|||||||
mjModel* model_ = nullptr;
|
mjModel* model_ = nullptr;
|
||||||
mjData* data_ = nullptr;
|
mjData* data_ = nullptr;
|
||||||
char error_[1000] = "";
|
char error_[1000] = "";
|
||||||
|
std::string warning_;
|
||||||
};
|
};
|
||||||
} // namespace mujoco::platform
|
} // namespace mujoco::platform
|
||||||
|
|
||||||
|
|||||||
@@ -505,18 +505,6 @@ void DrawTextAt(const char* text, float x, float y, float z) {
|
|||||||
}
|
}
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
float MeasureTextWidth(const char* text) {
|
|
||||||
float max_width = 0.0f;
|
|
||||||
const char* start = text;
|
|
||||||
while (*start) {
|
|
||||||
const char* end = start;
|
|
||||||
while (*end && *end != '\n') ++end;
|
|
||||||
max_width = std::max(max_width, ImGui::CalcTextSize(start, end).x);
|
|
||||||
start = *end ? end + 1 : end;
|
|
||||||
}
|
|
||||||
return max_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNextWindowPosInside(OverlayPos pos, ImVec4 rect) {
|
void SetNextWindowPosInside(OverlayPos pos, ImVec4 rect) {
|
||||||
// compute anchor point and pivot from position enum
|
// compute anchor point and pivot from position enum
|
||||||
ImVec2 anchor, pivot;
|
ImVec2 anchor, pivot;
|
||||||
@@ -584,7 +572,7 @@ void TextOverlay(const char* id, OverlayPos pos, ImVec4 workspace_rect,
|
|||||||
float scale = ImGui::GetWindowDpiScale();
|
float scale = ImGui::GetWindowDpiScale();
|
||||||
float padding = 30.0f * scale;
|
float padding = 30.0f * scale;
|
||||||
float max_width = std::max(0.0f, workspace_rect.z - 20.0f);
|
float max_width = std::max(0.0f, workspace_rect.z - 20.0f);
|
||||||
float text_width = MeasureTextWidth(text) * font_scale;
|
float text_width = ImGui::CalcTextSize(text).x * font_scale;
|
||||||
float min_target = std::min(min_width * scale, max_width);
|
float min_target = std::min(min_width * scale, max_width);
|
||||||
float target = 0.0f;
|
float target = 0.0f;
|
||||||
if (text_width + padding > max_width || min_width > 0.0f) {
|
if (text_width + padding > max_width || min_width > 0.0f) {
|
||||||
|
|||||||
@@ -196,13 +196,16 @@ void App::OnModelLoaded(std::string filename, ModelKind model_kind) {
|
|||||||
step_error_ = "";
|
step_error_ = "";
|
||||||
edit_error_ = "";
|
edit_error_ = "";
|
||||||
|
|
||||||
|
if (!model_holder_->warning().empty()) {
|
||||||
|
load_error_ = model_holder_->warning();
|
||||||
|
}
|
||||||
model_path_ = std::move(filename);
|
model_path_ = std::move(filename);
|
||||||
|
|
||||||
if (model_kind_ == kEmptyModel) {
|
if (model_kind_ == kEmptyModel) {
|
||||||
step_control_.SetPauseState(PauseState::kUnpaused);
|
step_control_.SetPauseState(PauseState::kUnpaused);
|
||||||
}
|
}
|
||||||
model_kind_ = model_kind;
|
model_kind_ = model_kind;
|
||||||
if (model_kind_ == kEmptyModel) {
|
if (model_kind_ == kEmptyModel || !load_error_.empty()) {
|
||||||
step_control_.SetPauseState(PauseState::kNormalPaused);
|
step_control_.SetPauseState(PauseState::kNormalPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +243,7 @@ void App::OnModelLoaded(std::string filename, ModelKind model_kind) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
tmp_.update_threadpool = true;
|
tmp_.update_threadpool = true;
|
||||||
|
last_pause_state_ = step_control_.GetPauseState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::UpdateFilePaths(const std::string& resolved_path) {
|
void App::UpdateFilePaths(const std::string& resolved_path) {
|
||||||
@@ -377,6 +381,12 @@ bool App::Update() {
|
|||||||
|
|
||||||
ProcessPendingLoads();
|
ProcessPendingLoads();
|
||||||
|
|
||||||
|
PauseState current_pause = step_control_.GetPauseState();
|
||||||
|
if (current_pause != last_pause_state_) {
|
||||||
|
load_error_ = "";
|
||||||
|
last_pause_state_ = current_pause;
|
||||||
|
}
|
||||||
|
|
||||||
// Only update the simulation if a popup window is not open. Note that the
|
// Only update the simulation if a popup window is not open. Note that the
|
||||||
// simulation itself will only update if it is not paused.
|
// simulation itself will only update if it is not paused.
|
||||||
if (tmp_.file_dialog == UiTempState::FileDialog_None) {
|
if (tmp_.file_dialog == UiTempState::FileDialog_None) {
|
||||||
@@ -987,6 +997,40 @@ void App::BuildGui() {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!load_error_.empty()) {
|
||||||
|
const float scale = ImGui::GetWindowDpiScale();
|
||||||
|
const float max_line_width =
|
||||||
|
ImGui::CalcTextSize(load_error_.c_str()).x;
|
||||||
|
const float padding = 30.0f * scale;
|
||||||
|
const float max_workspace_width = std::max(0.0f, workspace_rect.z - 20.0f);
|
||||||
|
const float min_target_width = std::min(350.0f * scale, max_workspace_width);
|
||||||
|
const float target_width = std::clamp(max_line_width + padding,
|
||||||
|
min_target_width, max_workspace_width);
|
||||||
|
|
||||||
|
if (platform::BeginOverlay("WarningOverlay", platform::OverlayPos::kBottom,
|
||||||
|
workspace_rect, target_width, 0.8f)) {
|
||||||
|
const bool is_dark = ImGui::GetStyle().Colors[ImGuiCol_WindowBg].x < 0.5f;
|
||||||
|
if (!model_holder_->warning().empty()) {
|
||||||
|
const ImVec4 warning_color = is_dark ? ImVec4(1.0f, 0.8f, 0.2f, 1.0f)
|
||||||
|
: ImVec4(0.7f, 0.45f, 0.0f, 1.0f);
|
||||||
|
ImGui::TextColored(warning_color, "Warning:");
|
||||||
|
} else {
|
||||||
|
const ImVec4 error_color = is_dark ? ImVec4(1.0f, 0.4f, 0.4f, 1.0f)
|
||||||
|
: ImVec4(0.8f, 0.1f, 0.1f, 1.0f);
|
||||||
|
ImGui::TextColored(error_color, "Compiler Error:");
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if (max_line_width + padding > max_workspace_width) {
|
||||||
|
ImGui::PushTextWrapPos(max_workspace_width - padding);
|
||||||
|
ImGui::TextUnformatted(load_error_.c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
} else {
|
||||||
|
ImGui::TextUnformatted(load_error_.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
platform::EndOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
if (tmp_.info) {
|
if (tmp_.info) {
|
||||||
const float scale = ImGui::GetWindowDpiScale();
|
const float scale = ImGui::GetWindowDpiScale();
|
||||||
if (platform::BeginOverlay("Info", platform::OverlayPos::kBottomLeft,
|
if (platform::BeginOverlay("Info", platform::OverlayPos::kBottomLeft,
|
||||||
@@ -1890,9 +1934,6 @@ void App::StatusBarGui() {
|
|||||||
if (!step_error_.empty()) {
|
if (!step_error_.empty()) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text(" | Step Error: %s", step_error_.c_str());
|
ImGui::Text(" | Step Error: %s", step_error_.c_str());
|
||||||
} else if (!load_error_.empty()) {
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Text(" | Load Error: %s", load_error_.c_str());
|
|
||||||
} else if (!edit_error_.empty()) {
|
} else if (!edit_error_.empty()) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text(" | Edit Error: %s", edit_error_.c_str());
|
ImGui::Text(" | Edit Error: %s", edit_error_.c_str());
|
||||||
|
|||||||
@@ -247,6 +247,8 @@ class App {
|
|||||||
std::string load_error_;
|
std::string load_error_;
|
||||||
std::string step_error_;
|
std::string step_error_;
|
||||||
std::string edit_error_;
|
std::string edit_error_;
|
||||||
|
platform::StepControl::PauseState last_pause_state_ =
|
||||||
|
platform::StepControl::PauseState::kNormalPaused;
|
||||||
|
|
||||||
std::optional<std::string> pending_load_;
|
std::optional<std::string> pending_load_;
|
||||||
std::function<void()> pending_op_;
|
std::function<void()> pending_op_;
|
||||||
|
|||||||
Reference in New Issue
Block a user