Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
model_manager_view.hpp
Go to the documentation of this file.
1#pragma once
2
3// ModelManagerView — the "you need a model" manager UI. Lists available +
4// installed models with their status, shows per-row download progress + cancel, and
5// exposes Download / Resume / Set-default / Remove actions via callbacks the host wires
6// to runtime::install_model / activate_model / remove_model. A header "Done" closes back
7// to the editor when there is one. Pure composition over Pulp widgets (View flex + Label
8// + ToggleButton) so it renders on the Skia/GPU canvas and is headless-capturable.
9//
10// Layout: each model is a two-line block — line 1 is the name + status + action
11// buttons; line 2 is a full-width progress bar (only while downloading/paused) — so the
12// bar never crowds or overlaps the name.
13
14#include <pulp/view/view.hpp>
15#include <pulp/view/widgets.hpp>
16#include <pulp/view/buttons.hpp> // TextButton (centered, momentary action buttons)
19
20#include <functional>
21#include <map>
22#include <string>
23
24namespace pulp::view {
25
26class ModelManagerView : public View {
27public:
28 using ModelAction = std::function<void(const std::string& model_id)>;
29
32 flex().padding = 20.0f;
33 flex().gap = 12.0f;
34 set_background_color(canvas::Color::rgba8(32, 33, 36, 255)); // GREY_900-ish
35 rebuild();
36 }
37
40 models_ = models;
41 rebuild();
42 }
43
48 void set_download_progress(const std::string& model_id, float fraction) {
49 if (fraction < 0.0f)
50 progress_.erase(model_id);
51 else
52 progress_[model_id] = fraction;
53 rebuild();
54 }
55
57 void set_can_close(bool can_close) {
58 if (can_close_ == can_close) return;
59 can_close_ = can_close;
60 rebuild();
61 }
62
67 std::function<void()> on_done;
68
69private:
70 static constexpr canvas::Color kMuted() { return canvas::Color::rgba8(170, 170, 175, 255); }
71 static constexpr canvas::Color kWhite() { return canvas::Color::rgba8(235, 235, 240, 255); }
72 static constexpr canvas::Color kTeal() { return canvas::Color::rgba8(132, 243, 237, 255); }
73 static constexpr canvas::Color kBlue() { return canvas::Color::rgba8(127, 178, 255, 255); }
74
75 static std::unique_ptr<Label> make_label(const std::string& text, float size, canvas::Color color,
76 float width = 0.0f) {
77 auto l = std::make_unique<Label>(text);
78 l->set_font_size(size);
79 l->set_text_color(color);
80 if (width > 0.0f) l->flex().preferred_width = width;
81 return l;
82 }
83
84 std::unique_ptr<TextButton> make_action(const std::string& label, const std::string& id,
85 ModelAction ModelManagerView::* member) {
86 // Momentary TextButton (centered label, proper hover/press) rather than a latched
87 // ToggleButton — these are one-shot actions, and the label should read centered.
88 auto b = std::make_unique<TextButton>(label);
89 b->flex().preferred_width = 96.0f;
90 b->flex().preferred_height = 28.0f;
91 b->on_click = [this, id, member] {
92 if (this->*member) (this->*member)(id);
93 };
94 return b;
95 }
96
97 // Full-width determinate bar: a dark track with a constant-accent fill sized
98 // proportionally via flex-grow (NOT the audio Meter, which colors by level).
99 static std::unique_ptr<View> make_progress_bar(float fraction) {
100 const float f = fraction < 0.0f ? 0.0f : (fraction > 1.0f ? 1.0f : fraction);
101 auto track = std::make_unique<View>();
102 track->flex().direction = FlexDirection::row;
103 track->flex().flex_grow = 1.0f;
104 track->flex().preferred_height = 6.0f;
105 track->set_background_color(canvas::Color::rgba8(55, 56, 60, 255));
106 auto fill = std::make_unique<View>();
107 fill->flex().flex_grow = f < 0.001f ? 0.001f : f;
108 fill->set_background_color(canvas::Color::rgba8(127, 178, 255, 255));
109 auto rest = std::make_unique<View>();
110 rest->flex().flex_grow = (1.0f - f) < 0.001f ? 0.001f : (1.0f - f);
111 track->add_child(std::move(fill));
112 track->add_child(std::move(rest));
113 return track;
114 }
115
116 void rebuild() {
117 while (child_count() > 0) remove_child(child_at(0));
118
119 // Header: title + (Done, right-aligned, only when there's an editor to return to).
120 auto header = std::make_unique<View>();
121 header->flex().direction = FlexDirection::row;
122 header->flex().align_items = FlexAlign::center;
123 header->flex().preferred_height = 30.0f;
124 auto title = make_label("Models", 20.0f, kWhite());
125 title->set_font_weight(700);
126 title->flex().flex_grow = 1.0f;
127 header->add_child(std::move(title));
128 if (can_close_ && on_done) {
129 auto done = std::make_unique<ToggleButton>();
130 done->set_label("Done");
131 done->flex().preferred_width = 88.0f;
132 done->flex().preferred_height = 28.0f;
133 done->on_toggle = [this](bool) { if (on_done) on_done(); };
134 header->add_child(std::move(done));
135 }
136 add_child(std::move(header));
137
138 add_child(make_label("Download a model to enable generation. Stored once, shared across plugins.",
139 12.0f, kMuted()));
140
141 if (models_.models.empty()) {
142 add_child(make_label("No models available yet.", 14.0f, kMuted()));
143 return;
144 }
145
146 for (const auto& listed : models_.models) {
147 const std::string id = listed.model.model_id;
148 const auto it = progress_.find(id);
149 const bool downloading = it != progress_.end();
150 const bool paused = !downloading && listed.status == "partial";
151
152 auto block = std::make_unique<View>();
153 block->flex().direction = FlexDirection::column;
154 block->flex().gap = 6.0f;
155
156 // Line 1: name (grows, never shrinks) + status + action buttons.
157 auto line = std::make_unique<View>();
158 line->flex().direction = FlexDirection::row;
159 line->flex().align_items = FlexAlign::center;
160 line->flex().gap = 10.0f;
161 line->flex().preferred_height = 30.0f;
162
163 std::string name = listed.model.display_name.empty() ? id : listed.model.display_name;
164 if (listed.model.is_recommended) name = "★ " + name; // ★
165 auto name_label = make_label(name, 14.0f, kWhite());
166 name_label->flex().flex_grow = 1.0f;
167 name_label->flex().flex_shrink = 0.0f; // never let the name shrink/overlap
168 line->add_child(std::move(name_label));
169
170 if (downloading) {
171 const int pct = static_cast<int>(it->second * 100.0f + 0.5f);
172 line->add_child(make_label(std::to_string(pct) + "%", 12.0f, kTeal(), 44.0f));
173 line->add_child(make_action("Cancel", id, &ModelManagerView::on_cancel));
174 } else if (paused) {
175 const float frac = listed.partial_fraction < 0.0f ? 0.0f : listed.partial_fraction;
176 line->add_child(make_label("Paused " + std::to_string(static_cast<int>(frac * 100.0f + 0.5f)) + "%",
177 12.0f, kMuted(), 90.0f));
178 line->add_child(make_action("Resume", id, &ModelManagerView::on_download));
179 line->add_child(make_action("Delete", id, &ModelManagerView::on_remove));
180 } else if (listed.status == "installed") {
181 line->add_child(make_label(listed.active ? "Active" : "Installed", 12.0f,
182 listed.active ? kTeal() : kBlue(), 80.0f));
183 if (!listed.active)
184 line->add_child(make_action("Set default", id, &ModelManagerView::on_activate));
185 line->add_child(make_action("Remove", id, &ModelManagerView::on_remove));
186 } else {
187 line->add_child(make_label("Available", 12.0f, kMuted(), 80.0f));
188 line->add_child(make_action("Download", id, &ModelManagerView::on_download));
189 }
190 block->add_child(std::move(line));
191
192 // Line 2: a full-width progress bar, only while downloading or paused.
193 if (downloading)
194 block->add_child(make_progress_bar(it->second));
195 else if (paused)
196 block->add_child(make_progress_bar(listed.partial_fraction));
197
198 add_child(std::move(block));
199 }
200 }
201
202 runtime::ModelListResult models_;
203 std::map<std::string, float> progress_;
204 bool can_close_ = false;
205};
206
207} // namespace pulp::view
void set_models(const runtime::ModelListResult &models)
Populate from runtime::list_models(registry, subsystem).
Definition model_manager_view.hpp:39
ModelAction on_download
Download / Resume.
Definition model_manager_view.hpp:63
std::function< void()> on_done
Done (return to the editor).
Definition model_manager_view.hpp:67
ModelAction on_cancel
Cancel (mid-download).
Definition model_manager_view.hpp:66
std::function< void(const std::string &model_id)> ModelAction
Definition model_manager_view.hpp:28
ModelManagerView()
Definition model_manager_view.hpp:30
void set_download_progress(const std::string &model_id, float fraction)
Per-row download progress in [0,1].
Definition model_manager_view.hpp:48
ModelAction on_remove
Remove / Delete.
Definition model_manager_view.hpp:65
ModelAction on_activate
Set default.
Definition model_manager_view.hpp:64
void set_can_close(bool can_close)
Show the header "Done" (return to the editor). Off when there is no editor yet.
Definition model_manager_view.hpp:57
void set_background_color(Color c)
Background color (optional — if set, painted before children).
Definition view.hpp:1123
View * child_at(size_t index)
Definition view.hpp:161
std::unique_ptr< View > add_child(std::unique_ptr< View > child)
Attaches child and returns nullptr.
size_t child_count() const
Definition view.hpp:160
const std::string & id() const
Definition view.hpp:1059
@ label
Definition view.hpp:989
std::unique_ptr< View > remove_child(View *child)
Detaches child and returns its ownership, or nullptr when child is no longer this parent's child unde...
@ text
Definition view.hpp:2742
FlexStyle & flex()
Definition view.hpp:91
@ paused
Definition external_input.hpp:12
@ rest
Low for the whole stage — silence with duration.
Definition stage_sequencer.hpp:29
@ line
Definition web_event_translate.hpp:116
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ column
Definition geometry.hpp:353
@ row
Definition geometry.hpp:353
@ block
Filled glyph cell; the covered glyph is drawn in the bg color.
Definition caret.hpp:66
@ l
Definition input_events.hpp:40
@ f
Definition input_events.hpp:39
@ b
Definition input_events.hpp:39
@ center
Definition geometry.hpp:370
@ color
Definition css_animation.hpp:121
@ width
Definition css_animation.hpp:127
@ fill
Definition design_ir.hpp:61
Definition canvas.hpp:52
static constexpr Color rgba8(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255)
Construct from 8-bit channels [0,255] — convenience for legacy/hex code.
Definition canvas.hpp:61
Definition model_store.hpp:45
FlexDirection direction
Definition geometry.hpp:446
float padding
Definition geometry.hpp:475
float gap
Shorthand for both row_gap and column_gap.
Definition geometry.hpp:471