Add schedule sync and schedule-aware playback

This commit is contained in:
NeilClack
2026-09-11 19:14:55 -05:00
parent a043ec84d8
commit f37beaf07e
23 changed files with 893 additions and 32 deletions
+44 -12
View File
@@ -174,6 +174,16 @@ void ProclamationOptionsPage::rebuildItems()
{
items_.clear();
// Schedule master toggle: enables/disables following the portal schedule.
// It is a plain on/off row (selected stores the on state) and sits above
// the always-on proclamation so it is always the first list entry.
items_.push_back({
FlatItem::Type::ScheduleToggle,
"schedule",
"Schedule Active",
0, false, state_->config.get().proclamation.schedule_active, false,
});
// Always-on top-level proclamation, never inside a category.
items_.push_back({
FlatItem::Type::Proclamation,
@@ -258,6 +268,11 @@ void ProclamationOptionsPage::rebuildVisible()
for (size_t i = 0; i < items_.size(); ++i) {
const FlatItem& item = items_[i];
if (item.type == FlatItem::Type::ScheduleToggle) {
visible_.push_back(i);
continue;
}
if (item.type == FlatItem::Type::Category) {
currentCategory = static_cast<int>(i);
currentGroup = -1;
@@ -304,6 +319,18 @@ void ProclamationOptionsPage::toggleItem()
return;
}
if (item.type == FlatItem::Type::ScheduleToggle) {
item.selected = !item.selected;
const bool enabled = item.selected;
state_->config.update([enabled](radiantwave::config::AppConfig& cfg) {
cfg.proclamation.schedule_active = enabled;
});
statusMessage_ = enabled
? "Schedule active - playback follows the portal schedule"
: "Schedule inactive - playback uses your selections";
return;
}
if (item.isDefault) {
statusMessage_ = "The top-level proclamation is always on";
return;
@@ -715,14 +742,25 @@ void ProclamationOptionsPage::render(SDL_Renderer* renderer)
}
} else {
// --- Browse tree ---
// The schedule toggle renders its on/off state inline; category
// and group rows get an expand/collapse marker.
auto labelFor = [](const FlatItem& item) -> std::string {
if (item.type == FlatItem::Type::ScheduleToggle) {
return "Schedule Active: " +
std::string(item.selected ? "[ON]" : "[OFF]");
}
if (item.type == FlatItem::Type::Category ||
item.type == FlatItem::Type::Group) {
return (item.expanded ? "[-] " : "[+] ") + item.label;
}
return item.label;
};
// Stable width: compute from every item so the list doesn't
// shift left/right when groups expand or collapse.
float maxTotalWidth = 0.0f;
for (const auto& item : items_) {
std::string label = item.label;
if (item.type == FlatItem::Type::Category || item.type == FlatItem::Type::Group) {
label = (item.expanded ? "[-] " : "[+] ") + label;
}
std::string label = labelFor(item);
int w = 0, h = 0;
TTF_GetStringSize(fontItem_, label.c_str(), 0, &w, &h);
float total = item.depth * indent + static_cast<float>(w);
@@ -732,10 +770,7 @@ void ProclamationOptionsPage::render(SDL_Renderer* renderer)
std::vector<float> textWidths(visible_.size());
for (int i = scrollOffset_; i < scrollOffset_ + visibleCount_ && i < static_cast<int>(visible_.size()); ++i) {
const FlatItem& item = items_[visible_[i]];
std::string label = item.label;
if (item.type == FlatItem::Type::Category || item.type == FlatItem::Type::Group) {
label = (item.expanded ? "[-] " : "[+] ") + label;
}
std::string label = labelFor(item);
int w = 0, h = 0;
TTF_GetStringSize(fontItem_, label.c_str(), 0, &w, &h);
textWidths[i] = static_cast<float>(w);
@@ -750,10 +785,7 @@ void ProclamationOptionsPage::render(SDL_Renderer* renderer)
const FlatItem& item = items_[visible_[i]];
float y = listTop + rowIndex * rowPitch;
std::string label = item.label;
if (item.type == FlatItem::Type::Category || item.type == FlatItem::Type::Group) {
label = (item.expanded ? "[-] " : "[+] ") + label;
}
std::string label = labelFor(item);
SDL_Color color = white;
if (item.type == FlatItem::Type::Category) {