implement saving children's memento in Sequencer/Timeline
This commit is contained in:
parent
c6308fa2a2
commit
972f5ce0a1
@ -80,6 +80,7 @@ target_sources(nf7
|
||||
common/luajit_thread.cc
|
||||
common/luajit_thread_lambda.hh
|
||||
common/memento.hh
|
||||
common/memento_recorder.hh
|
||||
common/native_file.hh
|
||||
common/node.hh
|
||||
common/node_link_store.hh
|
||||
|
56
common/memento_recorder.hh
Normal file
56
common/memento_recorder.hh
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/generic_history.hh"
|
||||
#include "common/memento.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class MementoRecorder final {
|
||||
public:
|
||||
MementoRecorder(nf7::Memento* mem) noexcept : mem_(mem) {
|
||||
}
|
||||
MementoRecorder(const MementoRecorder&) = delete;
|
||||
MementoRecorder(MementoRecorder&&) = delete;
|
||||
MementoRecorder& operator=(const MementoRecorder&) = delete;
|
||||
MementoRecorder& operator=(MementoRecorder&&) = delete;
|
||||
|
||||
std::unique_ptr<nf7::History::Command> CreateCommandIf() noexcept {
|
||||
if (mem_) {
|
||||
auto ptag = std::exchange(tag_, mem_->Save());
|
||||
if (ptag != tag_) {
|
||||
return std::make_unique<RestoreCommand>(*this, ptag);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Memento* const mem_;
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
|
||||
|
||||
class RestoreCommand final : public nf7::History::Command {
|
||||
public:
|
||||
RestoreCommand(MementoRecorder& rec, const std::shared_ptr<nf7::Memento::Tag>& tag) noexcept :
|
||||
rec_(&rec), tag_(tag) {
|
||||
}
|
||||
|
||||
void Apply() override { Exec(); }
|
||||
void Revert() override { Exec(); }
|
||||
|
||||
private:
|
||||
MementoRecorder* const rec_;
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
|
||||
void Exec() {
|
||||
auto& mem = *rec_->mem_;
|
||||
rec_->tag_ = std::exchange(tag_, mem.Save());
|
||||
mem.Restore(rec_->tag_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -29,63 +29,20 @@
|
||||
#include "common/gui_popup.hh"
|
||||
#include "common/gui_timeline.hh"
|
||||
#include "common/gui_window.hh"
|
||||
#include "common/memento.hh"
|
||||
#include "common/memento_recorder.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/sequencer.hh"
|
||||
#include "common/squashed_history.hh"
|
||||
#include "common/yas_nf7.hh"
|
||||
|
||||
#include <thread>
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
// TODO: for test use
|
||||
class Null final : public nf7::File, public nf7::Sequencer {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Null> kType =
|
||||
{"Sequencer/Null", {"Sequencer"}};
|
||||
Null(Env& env) noexcept :
|
||||
File(kType, env), Sequencer(Sequencer::kTooltip) {
|
||||
}
|
||||
|
||||
Null(Env& env, Deserializer&) : Null(env) {
|
||||
}
|
||||
void Serialize(Serializer&) const noexcept override {
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<Null>(env);
|
||||
}
|
||||
|
||||
std::shared_ptr<Sequencer::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>& parent) noexcept override {
|
||||
class Emitter final : public Sequencer::Lambda,
|
||||
public std::enable_shared_from_this<Emitter> {
|
||||
public:
|
||||
using Sequencer::Lambda::Lambda;
|
||||
|
||||
void Run(const std::shared_ptr<Sequencer::Session>& ss) noexcept override {
|
||||
env().ExecAsync(shared_from_this(), [ss]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
ss->Finish();
|
||||
});
|
||||
}
|
||||
};
|
||||
return std::make_shared<Emitter>(*this, parent);
|
||||
}
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<nf7::Sequencer>(t).Select(this);
|
||||
}
|
||||
|
||||
void UpdateTooltip(Sequencer::Editor&) noexcept override {
|
||||
ImGui::Text("hello");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<TL> kType =
|
||||
@ -104,6 +61,8 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
class Session;
|
||||
class Lambda;
|
||||
|
||||
class ConfigModifyCommand;
|
||||
|
||||
using ItemId = uint64_t;
|
||||
|
||||
TL(Env& env,
|
||||
@ -226,12 +185,12 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
void MoveDisplayLayerOfSelected(int64_t diff) noexcept;
|
||||
|
||||
// history
|
||||
void ExecUnDo() {
|
||||
void ExecUnDo() noexcept {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "reverting commands to undo"),
|
||||
[this]() { history_.UnDo(); });
|
||||
}
|
||||
void ExecReDo() {
|
||||
void ExecReDo() noexcept {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "applying commands to redo"),
|
||||
[this]() { history_.ReDo(); });
|
||||
@ -247,7 +206,6 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
input_ = seq_inputs_;
|
||||
output_ = seq_outputs_;
|
||||
output_.push_back("_cursor");
|
||||
Touch();
|
||||
}
|
||||
static std::vector<std::string> ParseSocketSpecifier(std::string_view names) {
|
||||
const auto n = names.size();
|
||||
@ -330,12 +288,14 @@ struct TL::Timing {
|
||||
};
|
||||
|
||||
|
||||
class TL::Item final {
|
||||
class TL::Item final : nf7::Env::Watcher {
|
||||
public:
|
||||
Item() = delete;
|
||||
Item(ItemId id, std::unique_ptr<nf7::File>&& f, const Timing& t) :
|
||||
Watcher(f->env()),
|
||||
id_(id), file_(std::move(f)),
|
||||
seq_(&file_->interfaceOrThrow<nf7::Sequencer>()),
|
||||
mem_(f->interface<nf7::Memento>()),
|
||||
timing_(t), display_timing_(t) {
|
||||
}
|
||||
Item(const Item&) = delete;
|
||||
@ -363,6 +323,7 @@ class TL::Item final {
|
||||
owner_ = &f;
|
||||
MoveTo(layer);
|
||||
file_->MoveUnder(f, std::to_string(id_));
|
||||
Watch(file_->id());
|
||||
}
|
||||
void Detach() noexcept {
|
||||
assert(owner_);
|
||||
@ -408,11 +369,27 @@ class TL::Item final {
|
||||
ItemId id_;
|
||||
std::unique_ptr<nf7::File> file_;
|
||||
nf7::Sequencer* const seq_;
|
||||
nf7::MementoRecorder mem_;
|
||||
|
||||
Timing timing_;
|
||||
|
||||
Timing display_timing_;
|
||||
TL::Layer* display_layer_ = nullptr;
|
||||
|
||||
void Handle(const nf7::File::Event& ev) noexcept override {
|
||||
switch (ev.type) {
|
||||
case nf7::File::Event::kUpdate:
|
||||
if (owner_) {
|
||||
if (auto cmd = mem_.CreateCommandIf()) {
|
||||
owner_->history_.Add(std::move(cmd));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1156,6 +1133,65 @@ void TL::MoveDisplayLayerOfSelected(int64_t diff) noexcept {
|
||||
}
|
||||
|
||||
|
||||
class TL::ConfigModifyCommand final : public nf7::History::Command {
|
||||
public:
|
||||
struct Builder final {
|
||||
public:
|
||||
Builder(TL& f) noexcept :
|
||||
prod_(std::make_unique<ConfigModifyCommand>(f)) {
|
||||
}
|
||||
|
||||
Builder& length(uint64_t v) noexcept {
|
||||
prod_->length_ = v;
|
||||
return *this;
|
||||
}
|
||||
Builder& inputs(std::vector<std::string>&& v) noexcept {
|
||||
prod_->seq_inputs_ = std::move(v);
|
||||
return *this;
|
||||
}
|
||||
Builder& outputs(std::vector<std::string>&& v) noexcept {
|
||||
prod_->seq_outputs_ = std::move(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<ConfigModifyCommand> Build() noexcept {
|
||||
return std::move(prod_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ConfigModifyCommand> prod_;
|
||||
};
|
||||
|
||||
ConfigModifyCommand(TL& f) noexcept : owner_(&f) {
|
||||
}
|
||||
|
||||
void Apply() override { Exec(); }
|
||||
void Revert() override { Exec(); }
|
||||
|
||||
private:
|
||||
TL* const owner_;
|
||||
|
||||
std::optional<uint64_t> length_;
|
||||
std::optional<std::vector<std::string>> seq_inputs_, seq_outputs_;
|
||||
|
||||
void Exec() noexcept {
|
||||
if (length_) {
|
||||
std::swap(owner_->length_, *length_);
|
||||
}
|
||||
if (seq_inputs_) {
|
||||
std::swap(owner_->seq_inputs_, *seq_inputs_);
|
||||
}
|
||||
if (seq_outputs_) {
|
||||
std::swap(owner_->seq_outputs_, *seq_outputs_);
|
||||
}
|
||||
|
||||
if (seq_inputs_ || seq_outputs_) {
|
||||
owner_->ApplySeqSocketChanges();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::unique_ptr<nf7::File> TL::Clone(nf7::Env& env) const noexcept {
|
||||
std::vector<std::unique_ptr<TL::Layer>> layers;
|
||||
layers.reserve(layers_.size());
|
||||
@ -1199,7 +1235,10 @@ void TL::Update() noexcept {
|
||||
|
||||
UpdateEditorWindow();
|
||||
|
||||
history_.Squash();
|
||||
if (history_.Squash()) {
|
||||
env().ExecMain(std::make_shared<nf7::GenericContext>(*this),
|
||||
[this]() { Touch(); });
|
||||
}
|
||||
}
|
||||
void TL::UpdateMenu() noexcept {
|
||||
ImGui::MenuItem("Editor", nullptr, &win_.shown());
|
||||
@ -1533,17 +1572,14 @@ void TL::ConfigPopup::Update() noexcept {
|
||||
|
||||
if (ImGui::Button("ok")) {
|
||||
try {
|
||||
auto i = ParseSocketSpecifier(inputs_);
|
||||
auto o = ParseSocketSpecifier(outputs_);
|
||||
auto cmd = ConfigModifyCommand::Builder(*owner_).
|
||||
inputs(ParseSocketSpecifier(inputs_)).
|
||||
outputs(ParseSocketSpecifier(outputs_)).
|
||||
Build();
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(
|
||||
*owner_, "updating Sequencer/Timeline config");
|
||||
owner_->env().ExecMain(ctx, [f = owner_, i = std::move(i), o = std::move(o)]() {
|
||||
f->seq_inputs_ = std::move(i);
|
||||
f->seq_outputs_ = std::move(o);
|
||||
f->ApplySeqSocketChanges();
|
||||
});
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "updating config");
|
||||
owner_->history_.Add(std::move(cmd)).ExecApply(ctx);
|
||||
} catch (nf7::Exception& e) {
|
||||
error_ = e.msg();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user