rename view_model -> view_mode_model; add shell-expands-FX test and parse-site comments
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#include "view_model.h"
|
||||
#include "view_mode_model.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// view_model implementation.
|
||||
// view_mode_model implementation.
|
||||
//
|
||||
// JSON is hand-rolled and self-contained, mirroring bank_model's approach (brief:
|
||||
// keep the pure core dependency-free — no third-party JSON lib). A compact writer
|
||||
@@ -124,31 +124,31 @@ TrackPlan makeRestorePlan(const std::string& guid, const TrackSnapshot& snap) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ViewModel
|
||||
// ViewModeModel
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
ViewModel::ViewModel() : activeModeId_(kArrangeModeId) {}
|
||||
ViewModeModel::ViewModeModel() : activeModeId_(kArrangeModeId) {}
|
||||
|
||||
bool ViewModel::setActiveMode(const std::string& modeId) {
|
||||
bool ViewModeModel::setActiveMode(const std::string& modeId) {
|
||||
if (!modes_.contains(modeId)) return false;
|
||||
activeModeId_ = modeId;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewModel::storeSnapshot(const std::string& guid, const TrackSnapshot& snap) {
|
||||
void ViewModeModel::storeSnapshot(const std::string& guid, const TrackSnapshot& snap) {
|
||||
snapshots_[guid] = snap;
|
||||
}
|
||||
|
||||
void ViewModel::clearSnapshot(const std::string& guid) {
|
||||
void ViewModeModel::clearSnapshot(const std::string& guid) {
|
||||
snapshots_.erase(guid);
|
||||
}
|
||||
|
||||
const TrackSnapshot* ViewModel::snapshot(const std::string& guid) const {
|
||||
const TrackSnapshot* ViewModeModel::snapshot(const std::string& guid) const {
|
||||
auto it = snapshots_.find(guid);
|
||||
return it == snapshots_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
bool ViewModel::leafBelongsToMode(const std::string& guid, const std::string& modeId) const {
|
||||
bool ViewModeModel::leafBelongsToMode(const std::string& guid, const std::string& modeId) const {
|
||||
const Membership* m = membership_.query(guid);
|
||||
if (!m) return modeId == kArrangeModeId; // untagged ⇒ Arrange default
|
||||
if (m->showBoth) return true; // show-both ⇒ every mode
|
||||
@@ -156,7 +156,7 @@ bool ViewModel::leafBelongsToMode(const std::string& guid, const std::string& mo
|
||||
return m->modeIds.count(modeId) > 0;
|
||||
}
|
||||
|
||||
std::set<std::string> ViewModel::visibleTracks(const FolderTree& tree,
|
||||
std::set<std::string> ViewModeModel::visibleTracks(const FolderTree& tree,
|
||||
const std::string& modeId) const {
|
||||
std::set<std::string> visible;
|
||||
|
||||
@@ -189,7 +189,7 @@ std::set<std::string> ViewModel::visibleTracks(const FolderTree& tree,
|
||||
return visible;
|
||||
}
|
||||
|
||||
TogglePlan ViewModel::planToggle(const FolderTree& tree, const std::string& targetMode) const {
|
||||
TogglePlan ViewModeModel::planToggle(const FolderTree& tree, const std::string& targetMode) const {
|
||||
TogglePlan plan;
|
||||
|
||||
// Index the tree so we can classify each tagged GUID (leaf vs parent vs stale).
|
||||
@@ -209,10 +209,10 @@ TogglePlan ViewModel::planToggle(const FolderTree& tree, const std::string& targ
|
||||
if (const TrackSnapshot* snap = snapshot(guid))
|
||||
plan.restore.push_back(makeRestorePlan(guid, *snap));
|
||||
} else {
|
||||
// Inactive tagged leaf ⇒ park. FX count is unknown to the pure model;
|
||||
// the shell expands per-FX offline from TrackFX_GetCount. We emit the
|
||||
// scalar flags and leave fxOffline to the shell for the count (park
|
||||
// offlines ALL, so no per-slot value decision is needed here).
|
||||
// Inactive tagged leaf ⇒ park. fxOffline is intentionally empty here:
|
||||
// the D2 shell expands per-FX offline writes using TrackFX_GetCount.
|
||||
// The pure model has no access to REAPER FX counts at plan time;
|
||||
// makeParkPlan(guid, 0) emits only the scalar flags as a result.
|
||||
plan.park.push_back(makeParkPlan(guid, /*fxCount=*/0));
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ TogglePlan ViewModel::planToggle(const FolderTree& tree, const std::string& targ
|
||||
return plan;
|
||||
}
|
||||
|
||||
bool ViewModel::operator==(const ViewModel& o) const {
|
||||
bool ViewModeModel::operator==(const ViewModeModel& o) const {
|
||||
return modes_ == o.modes_ && membership_ == o.membership_ &&
|
||||
activeModeId_ == o.activeModeId_ && snapshots_ == o.snapshots_;
|
||||
}
|
||||
@@ -301,7 +301,7 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string ViewModel::serialize() const {
|
||||
std::string ViewModeModel::serialize() const {
|
||||
std::string out;
|
||||
{
|
||||
ObjWriter root(out);
|
||||
@@ -378,7 +378,7 @@ namespace {
|
||||
class Parser {
|
||||
public:
|
||||
explicit Parser(const std::string& s) : s_(s) {}
|
||||
bool parseModel(ViewModel& out);
|
||||
bool parseModel(ViewModeModel& out);
|
||||
|
||||
private:
|
||||
const std::string& s_;
|
||||
@@ -622,6 +622,15 @@ bool Parser::parseMembership(MembershipIndex& idx) {
|
||||
// Install the entry verbatim (tag() would clear a multi-mode set and drop
|
||||
// show-both). A serialized entry is trusted to already satisfy the model's
|
||||
// invariants.
|
||||
//
|
||||
// Deliberate tolerance: we do NOT validate that membership modeIds reference
|
||||
// registered modes, and we do not validate snapshot GUIDs against the index.
|
||||
// Stale-GUID and stale-mode tolerance is a stated invariant of this model —
|
||||
// a deserialized entry is treated as trusted data, not as live cross-checked
|
||||
// state. Rejecting stale entries here would violate that invariant. The one
|
||||
// exception is activeMode (validated below in parseModel): a persisted active
|
||||
// mode that no longer exists has an immediate behavioral consequence, so it
|
||||
// is caught and the parse is rejected.
|
||||
if (!idx.restore(guid, mem)) return false;
|
||||
} while (consume(','));
|
||||
return consume(']');
|
||||
@@ -654,7 +663,7 @@ bool Parser::parseSnapshots(std::map<std::string, TrackSnapshot>& snaps) {
|
||||
return consume(']');
|
||||
}
|
||||
|
||||
bool Parser::parseModel(ViewModel& out) {
|
||||
bool Parser::parseModel(ViewModeModel& out) {
|
||||
if (!consume('{')) return false;
|
||||
skipWs();
|
||||
if (consume('}')) return true; // lenient empty root ⇒ default-seeded model
|
||||
@@ -682,7 +691,11 @@ bool Parser::parseModel(ViewModel& out) {
|
||||
} else if (key == "snapshots") {
|
||||
if (!parseSnapshots(snaps)) return false;
|
||||
} else {
|
||||
if (!skipValue()) return false; // version, unknown keys
|
||||
// Unknown keys and the "version" field are skipped here.
|
||||
// "version" is serialized as a forward-compat placeholder — there is no
|
||||
// active version gate yet; all persisted data is parsed the same way
|
||||
// regardless of the value. A future gate would add a version branch here.
|
||||
if (!skipValue()) return false;
|
||||
}
|
||||
} while (consume(','));
|
||||
|
||||
@@ -701,8 +714,8 @@ bool Parser::parseModel(ViewModel& out) {
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<ViewModel> ViewModel::deserialize(const std::string& json) {
|
||||
ViewModel vm;
|
||||
std::optional<ViewModeModel> ViewModeModel::deserialize(const std::string& json) {
|
||||
ViewModeModel vm;
|
||||
Parser p(json);
|
||||
if (!p.parseModel(vm)) return std::nullopt;
|
||||
return vm;
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
// view_model — the pure core of the Design View feature, deliberately free of any
|
||||
// view_mode_model — the pure core of the Design View feature, deliberately free of any
|
||||
// REAPER type so it compiles and unit-tests OUTSIDE the DAW. It is the mirror of
|
||||
// bank_model: it owns the mode registry, the GUID-keyed membership index, the
|
||||
// folder-tree-aware visibility derivation, the parking/restore planner, and the
|
||||
@@ -239,15 +239,15 @@ struct TogglePlan {
|
||||
std::vector<TrackPlan> restore; // active leaves returning -> snapshot values
|
||||
};
|
||||
|
||||
// -- The view model ----------------------------------------------------------
|
||||
// -- The view mode model -----------------------------------------------------
|
||||
//
|
||||
// Owns the mode registry, the membership index, the active mode, and the durable
|
||||
// per-track snapshots (kept for tracks currently parked so a save-while-parked
|
||||
// project restores correctly). Visibility and the toggle plan are computed against
|
||||
// a supplied FolderTree — the tree is never stored.
|
||||
class ViewModel {
|
||||
class ViewModeModel {
|
||||
public:
|
||||
ViewModel(); // Arrange + Design seeded; active mode = Arrange
|
||||
ViewModeModel(); // Arrange + Design seeded; active mode = Arrange
|
||||
|
||||
ModeRegistry& modes() { return modes_; }
|
||||
const ModeRegistry& modes() const { return modes_; }
|
||||
@@ -283,15 +283,19 @@ public:
|
||||
// leaves that become active AND have a stored snapshot are restored from it.
|
||||
// Parents, show-both leaves, the master, and untagged tracks are never parked.
|
||||
// Unknown/stale membership GUIDs absent from the tree are ignored (prune-safe).
|
||||
//
|
||||
// Note: park plans emitted here have an empty fxOffline vector. The D2 shell
|
||||
// expands per-FX offline writes using TrackFX_GetCount — the pure model has no
|
||||
// access to REAPER FX counts at plan time.
|
||||
TogglePlan planToggle(const FolderTree& tree, const std::string& targetMode) const;
|
||||
|
||||
bool operator==(const ViewModel& o) const;
|
||||
bool operator==(const ViewModeModel& o) const;
|
||||
|
||||
std::string serialize() const;
|
||||
|
||||
// Parses a JSON string produced by serialize(). std::nullopt on malformed
|
||||
// input. On success deserialize(serialize(x)) == x.
|
||||
static std::optional<ViewModel> deserialize(const std::string& json);
|
||||
static std::optional<ViewModeModel> deserialize(const std::string& json);
|
||||
|
||||
private:
|
||||
ModeRegistry modes_;
|
||||
Reference in New Issue
Block a user