diff --git a/src/experimental/platform/ux/spec_editor.cc b/src/experimental/platform/ux/spec_editor.cc index 38eab60d..ae3b7d7c 100644 --- a/src/experimental/platform/ux/spec_editor.cc +++ b/src/experimental/platform/ux/spec_editor.cc @@ -24,6 +24,11 @@ namespace mujoco::platform { SpecEditor::SpecEditor(int history_size) : capacity_(history_size) {} +void SpecEditor::Reset() { + ref_spec_.reset(); + active_spec_.reset(); +} + void SpecEditor::Reset(const mjSpec& spec) { active_element_key_ = kInvalidElementKey; active_element_ = nullptr; @@ -55,6 +60,10 @@ void SpecEditor::Reset(const mjSpec& spec) { } std::unique_ptr SpecEditor::Compile() { + if (!active_spec_) { + return nullptr; + } + auto holder = ModelHolder::FromSpec(mj_copySpec(active_spec_.get())); if (holder->ok()) { ref_spec_ = Copy(active_spec_.get()); @@ -66,6 +75,10 @@ std::unique_ptr SpecEditor::Compile() { mjSpec* SpecEditor::GetActiveSpec() const { return active_spec_.get(); } mjsElement* SpecEditor::AddElement(mjtObj type) { + if (!active_spec_) { + return nullptr; + } + // TODO: check that type is only for spec elements. mjsElement* element = AddElementToSpec(active_spec_.get(), type); if (element) { @@ -82,6 +95,10 @@ mjsElement* SpecEditor::AddElement(mjtObj type) { } mjsElement* SpecEditor::AddBodyElement(mjsBody* body, mjtObj type) { + if (!active_spec_) { + return nullptr; + } + // TODO: check that type is only for body elements. mjsElement* element = AddElementToSpec(active_spec_.get(), type, body); if (element) { @@ -98,6 +115,10 @@ mjsElement* SpecEditor::AddBodyElement(mjsBody* body, mjtObj type) { } void SpecEditor::DeleteActiveElement() { + if (!active_spec_) { + return; + } + if (active_element_) { const mjtObj type = active_element_->elemtype; const int index = active_map_.Remove(active_element_key_); @@ -116,6 +137,10 @@ void SpecEditor::DeleteActiveElement() { } void SpecEditor::SetActiveElement(mjsElement* element) { + if (!active_spec_) { + return; + } + if (element == nullptr) { ref_element_ = nullptr; active_element_ = nullptr; @@ -152,6 +177,10 @@ void SpecEditor::SetActiveElement(mjsElement* element) { } void SpecEditor::UpdateReferenceElement() { + if (!active_spec_) { + return; + } + if (active_element_ == nullptr) { ref_element_ = nullptr; } else { @@ -175,6 +204,10 @@ mjsElement* SpecEditor::GetActiveElement() const { return active_element_; } mjsElement* SpecEditor::GetRefElement() const { return ref_element_; } void SpecEditor::CommitChanges(mjsElement* element) { + if (!active_spec_) { + return; + } + if (element == nullptr) { mju_warning("Element is null."); return; @@ -233,6 +266,10 @@ void SpecEditor::Redo() { bool SpecEditor::CanRedo() const { return cursor_ < history_.size() - 1; } void SpecEditor::AppendHistory(HistoryEntry entry) { + if (!active_spec_) { + return; + } + ++cursor_; while (history_.size() > cursor_) { history_.pop_back(); diff --git a/src/experimental/platform/ux/spec_editor.h b/src/experimental/platform/ux/spec_editor.h index 1576396d..779a149a 100644 --- a/src/experimental/platform/ux/spec_editor.h +++ b/src/experimental/platform/ux/spec_editor.h @@ -38,6 +38,9 @@ class SpecEditor { public: explicit SpecEditor(int history_size = 256); + // Resets the editor with an empty spec. No editing will be allowed. + void Reset(); + // Resets the editor to manage the given spec, clearing all cached data. // Assumes the given spec is correctly formed (i.e. compilable). void Reset(const mjSpec& spec); diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index e886b31f..b247e800 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -161,7 +161,11 @@ void App::LoadModelFromFile(const std::string& filepath) { model_holder_ = platform::ModelHolder::FromFile(resolved_file); if (model_holder_->ok()) { OnModelLoaded(filepath, kModelFromFile); - spec_editor_.Reset(*spec()); + if (spec()) { + spec_editor_.Reset(*spec()); + } else { + spec_editor_.Reset(); + } UpdateFilePaths(resolved_file); if (model() && model()->names) { // Assumes the first string in the model is the name of the model itself. @@ -188,7 +192,11 @@ void App::LoadModelFromBuffer(std::span buffer, } else { SetLoadError(std::string(model_holder_->error())); } - spec_editor_.Reset(*spec()); + if (spec()) { + spec_editor_.Reset(*spec()); + } else { + spec_editor_.Reset(); + } } void App::OnModelLoaded(std::string filename, ModelKind model_kind) {