Formatting
ci / checks (push) Successful in 42m54s

This commit is contained in:
khodowany
2026-09-15 00:06:02 -05:00
parent 44e100985a
commit fe42d811fa
81 changed files with 4349 additions and 3340 deletions
+213 -102
View File
@@ -22,14 +22,15 @@
#include <vamp_zero/color.hpp>
#include <vamp_zero/library.hpp>
#include <vamp_zero/navigation.hpp>
#include <vamp_zero/parse.hpp>
#include <vamp_zero/score.hpp>
struct vamp_zero::score_draw_cache {
struct entry {
std::string source_json{};
std::shared_ptr<vamp_zero::score_document const> document{};
};
auto get(std::string_view source_json, std::string source_path, std::string part_override) -> std::expected<std::shared_ptr<vamp_zero::score_document const>, modulus::error> {
auto key = source_path;
key.push_back('\n');
@@ -38,12 +39,10 @@ struct vamp_zero::score_draw_cache {
key += std::to_string(source_json.size());
key.push_back('\n');
key += std::to_string(std::hash<std::string_view>{}(source_json));
auto found = entries.find(key);
if (found != entries.end() && found->second.source_json == source_json && found->second.document) {
return found->second.document;
}
auto parsed = vamp_zero::parse_score_document(source_json, source_path);
if (!parsed) {
return std::unexpected(parsed.error());
@@ -58,15 +57,13 @@ struct vamp_zero::score_draw_cache {
};
return document;
}
std::unordered_map<std::string, entry> entries{};
std::shared_ptr<vamp_zero::score_document const> page_document{};
std::size_t page_index{};
std::optional<vamp_zero::score_page_layout> page{};
auto draw(luma::draw_context& context, luma::rect bounds, std::shared_ptr<vamp_zero::score_document const> document, std::size_t index) -> void {
if (!page || page_document != document || index != page_index
|| page->bounds.x != bounds.x || page->bounds.y != bounds.y || page->bounds.width != bounds.width || page->bounds.height != bounds.height) {
auto draw(luma::draw_context &context, luma::rect bounds, std::shared_ptr<vamp_zero::score_document const> document, std::size_t index) -> void {
if (!page || page_document != document || index != page_index || page->bounds.x != bounds.x ||
page->bounds.y != bounds.y || page->bounds.width != bounds.width || page->bounds.height != bounds.height) {
page = vamp_zero::layout_score_page(bounds, *document, index);
page_document = std::move(document);
page_index = index;
@@ -80,22 +77,16 @@ namespace {
constexpr auto touch_hold_duration = std::chrono::milliseconds{700};
constexpr auto touch_double_tap_duration = std::chrono::milliseconds{220};
using touch_handler = std::function<std::bool_t(luma::touch_event const&)>;
auto bind_port(vamp_zero::config const& config) -> std::uint16_t {
auto const& bind = config.control.endpoint.bind;
auto bind_port(vamp_zero::config const &config) -> std::uint16_t {
auto const &bind = config.control.endpoint.bind;
auto colon = bind.rfind(':');
if (colon == std::string::npos) {
return 8080;
}
try {
return static_cast<std::uint16_t>(std::stoul(bind.substr(colon + 1U)));
} catch (...) {
return 8080;
}
return vamp_zero::parse_integer<std::uint16_t>(std::string_view{bind}.substr(colon + 1U), "port").value_or(8080);
}
auto status_color(vamp_zero::app_state const& state) -> vamp_style::color {
auto status_color(vamp_zero::app_state const &state) -> vamp_style::color {
return state.pico_connected ? vamp_style::connected : vamp_style::retrying;
}
@@ -115,7 +106,7 @@ auto text_style(std::int32_t scale) -> luma::text_style {
};
}
auto draw_text(luma::draw_context& context, std::float32_t x, std::float32_t y, std::string const& text, std::int32_t scale, vamp_style::color color, std::float32_t max_width) -> void {
auto draw_text(luma::draw_context &context, std::float32_t x, std::float32_t y, std::string const &text, std::int32_t scale, vamp_style::color color, std::float32_t max_width) -> void {
if (text.empty() || max_width <= 0.0F) {
return;
}
@@ -125,23 +116,27 @@ auto draw_text(luma::draw_context& context, std::float32_t x, std::float32_t y,
context.pop_clip();
}
auto draw_header(luma::draw_context& context, luma::rect bounds, vamp_zero::ui_state const& state) -> void {
context.fill_rect({.x = bounds.x, .y = bounds.y, .width = bounds.width, .height = 80.0F}, vamp_zero::to_luma(vamp_style::panel));
context.fill_rect({.x = bounds.x, .y = bounds.y + 78.0F, .width = bounds.width, .height = 2.0F}, vamp_zero::to_luma(vamp_style::accent));
auto draw_header(luma::draw_context &context, luma::rect bounds, vamp_zero::ui_state const &state) -> void {
context.fill_rect(
{.x = bounds.x, .y = bounds.y, .width = bounds.width, .height = 80.0F}, vamp_zero::to_luma(vamp_style::panel));
context.fill_rect({.x = bounds.x, .y = bounds.y + 78.0F, .width = bounds.width, .height = 2.0F},
vamp_zero::to_luma(vamp_style::accent));
draw_text(context, bounds.x + 28.0F, bounds.y + 18.0F, "PRODUCT VAMP", 4, vamp_style::ink, 360.0F);
draw_text(context, bounds.x + 30.0F, bounds.y + 55.0F, "AP " + state.ap_ssid + " " + state.zero_ip + ":" + std::to_string(state.port), 1, vamp_style::muted, 520.0F);
auto indicator = status_color(state.app);
auto x = bounds.x + bounds.width - 148.0F;
context.fill_rect({.x = x, .y = bounds.y + 22.0F, .width = 24.0F, .height = 24.0F}, vamp_zero::to_luma(indicator));
draw_text(context, x + 36.0F, bounds.y + 26.0F, "PICO", 2, vamp_style::ink, 88.0F);
}
auto draw_row(luma::draw_context& context, luma::rect row, std::string const& primary, std::string const& secondary, std::bool_t selected) -> void {
auto draw_row(luma::draw_context &context, luma::rect row, std::string const &primary, std::string const &secondary, std::bool_t selected) -> void {
if (selected) {
context.fill_rect(row, vamp_zero::to_luma(vamp_style::selection));
}
context.stroke_line({.x = row.x, .y = row.y + row.height}, {.x = row.x + row.width, .y = row.y + row.height}, 1.0F, vamp_zero::to_luma(vamp_style::rule));
context.stroke_line({.x = row.x, .y = row.y + row.height},
{.x = row.x + row.width, .y = row.y + row.height},
1.0F,
vamp_zero::to_luma(vamp_style::rule));
draw_text(context, row.x + 16.0F, row.y + 10.0F, primary, 2, vamp_style::ink, row.width * 0.62F);
if (!secondary.empty()) {
draw_text(context, row.x + row.width - 250.0F, row.y + 17.0F, secondary, 1, selected ? vamp_style::ink : vamp_style::muted, 230.0F);
@@ -155,27 +150,46 @@ auto first_visible_row(std::size_t selected, std::size_t total, std::size_t max_
return selected - max_rows + 1U;
}
auto draw_main_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto draw_main_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, "MODE", 4, vamp_style::ink, 260.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, "SELECT WORKFLOW", 1, vamp_style::muted, 320.0F);
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y, .width = row_width, .height = row_height}, "Performance Mode", std::to_string(state.performance.groups.size()) + " SONGS", state.selected_main_menu_index == 0U);
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + row_height, .width = row_width, .height = row_height}, "Practice Mode", std::to_string(state.practice.exercises.size()) + " EXERCISES", state.selected_main_menu_index == 1U);
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + row_height * 2.0F, .width = row_width, .height = row_height}, "Track", state.audio.track_enabled ? "ON" : "OFF", state.selected_main_menu_index == 2U);
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + row_height * 3.0F, .width = row_width, .height = row_height}, "Metronome", state.audio.metronome_enabled ? "ON" : "OFF", state.selected_main_menu_index == 3U);
draw_row(context,
{.x = bounds.x + 36.0F, .y = list_y, .width = row_width, .height = row_height},
"Performance Mode",
std::to_string(state.performance.groups.size()) + " SONGS",
state.selected_main_menu_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F, .y = list_y + row_height, .width = row_width, .height = row_height},
"Practice Mode",
std::to_string(state.practice.exercises.size()) + " EXERCISES",
state.selected_main_menu_index == 1U);
draw_row(context,
{.x = bounds.x + 36.0F, .y = list_y + row_height * 2.0F, .width = row_width, .height = row_height},
"Track",
state.audio.track_enabled ? "ON" : "OFF",
state.selected_main_menu_index == 2U);
draw_row(context,
{.x = bounds.x + 36.0F, .y = list_y + row_height * 3.0F, .width = row_width, .height = row_height},
"Metronome",
state.audio.metronome_enabled ? "ON" : "OFF",
state.selected_main_menu_index == 3U);
auto tempo = std::to_string(state.audio.metronome_bpm) + " BPM";
if (state.audio.tempo_editing) {
tempo = "EDIT " + tempo;
}
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + row_height * 4.0F, .width = row_width, .height = row_height}, "Tempo", tempo, state.selected_main_menu_index == 4U);
draw_row(context,
{.x = bounds.x + 36.0F, .y = list_y + row_height * 4.0F, .width = row_width, .height = row_height},
"Tempo",
tempo,
state.selected_main_menu_index == 4U);
}
auto draw_performance_song_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto draw_performance_song_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, "PERFORMANCE", 4, vamp_style::ink, 360.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, std::to_string(state.performance.groups.size()) + " SONGS " + std::to_string(state.performance.parts.size()) + " PARTS", 1, vamp_style::muted, 320.0F);
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -185,26 +199,45 @@ auto draw_performance_song_menu(luma::draw_context& context, luma::rect bounds,
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_song_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_song_index == 0U);
continue;
}
auto const& group = state.performance.groups[index - 1U];
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, group.title, std::to_string(group.parts.size()) + " PARTS", index == state.selected_song_index);
auto const &group = state.performance.groups[index - 1U];
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
group.title,
std::to_string(group.parts.size()) + " PARTS",
index == state.selected_song_index);
}
if (state.performance.groups.empty()) {
draw_text(context, bounds.x + 38.0F, list_y + row_height + 28.0F, "NO SONGS FOUND", 3, vamp_style::retrying, 420.0F);
}
}
auto draw_performance_part_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto const* group = selected_group(state);
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, group == nullptr ? std::string{"PARTS"} : group->title, 4, vamp_style::ink, bounds.width - 72.0F);
auto draw_performance_part_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
auto const *group = selected_group(state);
draw_text(context,
bounds.x + 36.0F,
bounds.y + 28.0F,
group == nullptr ? std::string{"PARTS"} : group->title,
4,
vamp_style::ink,
bounds.width - 72.0F);
if (group == nullptr) {
draw_text(context, bounds.x + 38.0F, bounds.y + 118.0F, "NO PARTS FOUND", 3, vamp_style::retrying, 420.0F);
return;
}
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, group->composer, 1, vamp_style::muted, bounds.width - 72.0F);
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -214,18 +247,31 @@ auto draw_performance_part_menu(luma::draw_context& context, luma::rect bounds,
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_part_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_part_index == 0U);
continue;
}
auto const& part = group->parts[index - 1U];
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, part.part, std::to_string(part.total_pages) + " PAGES", index == state.selected_part_index);
auto const &part = group->parts[index - 1U];
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
part.part,
std::to_string(part.total_pages) + " PAGES",
index == state.selected_part_index);
}
}
auto draw_practice_type_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto draw_practice_type_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, "PRACTICE", 4, vamp_style::ink, 300.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, std::to_string(state.practice.types.size()) + " TYPES " + std::to_string(state.practice.exercises.size()) + " EXERCISES", 1, vamp_style::muted, 420.0F);
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -235,30 +281,49 @@ auto draw_practice_type_menu(luma::draw_context& context, luma::rect bounds, vam
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_practice_type_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_practice_type_index == 0U);
continue;
}
auto const& type = state.practice.types[index - 1U];
auto const &type = state.practice.types[index - 1U];
auto exercise_count = std::size_t{};
for (auto const& lesson : type.lesson_groups) {
for (auto const &lesson : type.lesson_groups) {
exercise_count += lesson.exercises.size();
}
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, type.title, std::to_string(exercise_count) + " EXERCISES", index == state.selected_practice_type_index);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
type.title,
std::to_string(exercise_count) + " EXERCISES",
index == state.selected_practice_type_index);
}
if (state.practice.types.empty()) {
draw_text(context, bounds.x + 38.0F, list_y + row_height + 28.0F, "NO PRACTICE EXERCISES FOUND", 3, vamp_style::retrying, 560.0F);
}
}
auto draw_practice_lesson_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto const* type = selected_practice_type(state);
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, type == nullptr ? std::string{"PRACTICE"} : type->title, 4, vamp_style::ink, bounds.width - 72.0F);
auto draw_practice_lesson_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
auto const *type = selected_practice_type(state);
draw_text(context,
bounds.x + 36.0F,
bounds.y + 28.0F,
type == nullptr ? std::string{"PRACTICE"} : type->title,
4,
vamp_style::ink,
bounds.width - 72.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, "SELECT LESSON GROUP", 1, vamp_style::muted, 360.0F);
if (type == nullptr) {
draw_text(context, bounds.x + 38.0F, bounds.y + 118.0F, "NO LESSON GROUPS FOUND", 3, vamp_style::retrying, 520.0F);
return;
}
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -268,23 +333,42 @@ auto draw_practice_lesson_menu(luma::draw_context& context, luma::rect bounds, v
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_practice_lesson_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_practice_lesson_index == 0U);
continue;
}
auto const& lesson = type->lesson_groups[index - 1U];
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, lesson.title, std::to_string(lesson.exercises.size()) + " EXERCISES", index == state.selected_practice_lesson_index);
auto const &lesson = type->lesson_groups[index - 1U];
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
lesson.title,
std::to_string(lesson.exercises.size()) + " EXERCISES",
index == state.selected_practice_lesson_index);
}
}
auto draw_practice_exercise_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto const* lesson = selected_practice_lesson(state);
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, lesson == nullptr ? std::string{"EXERCISES"} : lesson->title, 4, vamp_style::ink, bounds.width - 72.0F);
auto draw_practice_exercise_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
auto const *lesson = selected_practice_lesson(state);
draw_text(context,
bounds.x + 36.0F,
bounds.y + 28.0F,
lesson == nullptr ? std::string{"EXERCISES"} : lesson->title,
4,
vamp_style::ink,
bounds.width - 72.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, "SELECT EXERCISE", 1, vamp_style::muted, 320.0F);
if (lesson == nullptr) {
draw_text(context, bounds.x + 38.0F, bounds.y + 118.0F, "NO EXERCISES FOUND", 3, vamp_style::retrying, 440.0F);
return;
}
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -294,20 +378,40 @@ auto draw_practice_exercise_menu(luma::draw_context& context, luma::rect bounds,
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_practice_exercise_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_practice_exercise_index == 0U);
continue;
}
auto const& exercise = lesson->exercises[index - 1U];
auto secondary = supports_key_selection(exercise) ? std::string{"KEY SELECT"} : "KEY " + effective_practice_key(state, exercise);
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, exercise.title, secondary, index == state.selected_practice_exercise_index);
auto const &exercise = lesson->exercises[index - 1U];
auto secondary =
supports_key_selection(exercise) ? std::string{"KEY SELECT"} : "KEY " + effective_practice_key(state, exercise);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
exercise.title,
secondary,
index == state.selected_practice_exercise_index);
}
}
auto draw_practice_key_menu(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state) -> void {
auto const* exercise = selected_practice_exercise(state);
draw_text(context, bounds.x + 36.0F, bounds.y + 28.0F, exercise == nullptr ? std::string{"KEY"} : exercise->title, 4, vamp_style::ink, bounds.width - 72.0F);
auto draw_practice_key_menu(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state) -> void {
auto const *exercise = selected_practice_exercise(state);
draw_text(context,
bounds.x + 36.0F,
bounds.y + 28.0F,
exercise == nullptr ? std::string{"KEY"} : exercise->title,
4,
vamp_style::ink,
bounds.width - 72.0F);
draw_text(context, bounds.x + 38.0F, bounds.y + 66.0F, "SELECT PRACTICE KEY", 1, vamp_style::muted, 360.0F);
auto list_y = bounds.y + 112.0F;
auto row_height = std::float32_t{54.0F};
auto row_width = bounds.width - 72.0F;
@@ -317,21 +421,36 @@ auto draw_practice_key_menu(luma::draw_context& context, luma::rect bounds, vamp
for (auto row = std::size_t{}; row < max_rows; ++row) {
auto index = first + row;
if (index == 0U) {
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, "BACK", "", state.selected_practice_key_index == 0U);
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
"BACK",
"",
state.selected_practice_key_index == 0U);
continue;
}
auto key = exercise == nullptr ? std::string{"C"} : exercise->variants[index - 1U].key_signature;
auto source = exercise == nullptr || exercise->key_signature.empty() ? std::string{"SOURCE C"} : "SOURCE " + exercise->key_signature;
draw_row(context, {.x = bounds.x + 36.0F, .y = list_y + static_cast<std::float32_t>(row) * row_height, .width = row_width, .height = row_height}, key, source, index == state.selected_practice_key_index);
auto source = exercise == nullptr || exercise->key_signature.empty() ? std::string{"SOURCE C"}
: "SOURCE " + exercise->key_signature;
draw_row(context,
{.x = bounds.x + 36.0F,
.y = list_y + static_cast<std::float32_t>(row) * row_height,
.width = row_width,
.height = row_height},
key,
source,
index == state.selected_practice_key_index);
}
}
auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_state const& state, vamp_zero::score_draw_cache& score_cache) -> void {
auto draw_score(luma::draw_context &context, luma::rect bounds, vamp_zero::app_state const &state, vamp_zero::score_draw_cache &score_cache) -> void {
auto source_json = std::string_view{};
auto source_path = std::string{};
auto part_override = std::string{};
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE) {
auto const* part = active_part(state);
auto const *part = active_part(state);
if (part == nullptr) {
draw_performance_song_menu(context, bounds, state);
return;
@@ -339,12 +458,12 @@ auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_s
source_json = part->source_json;
source_path = part->source_path;
} else {
auto const* exercise = active_practice(state);
auto const *exercise = active_practice(state);
if (exercise == nullptr) {
draw_practice_type_menu(context, bounds, state);
return;
}
auto const* variant = selected_practice_variant(state, *exercise);
auto const *variant = selected_practice_variant(state, *exercise);
if (variant == nullptr) {
draw_practice_key_menu(context, bounds, state);
return;
@@ -353,7 +472,6 @@ auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_s
source_path = variant->source_path;
part_override = "Key: " + effective_practice_key(state, *exercise);
}
auto parsed = score_cache.get(source_json, source_path, std::move(part_override));
if (!parsed) {
draw_text(context, bounds.x + 36.0F, bounds.y + 38.0F, "SCORE ERROR", 4, vamp_style::danger, 360.0F);
@@ -371,12 +489,12 @@ auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_s
class vamp_root_control final : public luma::control {
public:
explicit vamp_root_control(vamp_zero::ui_state state, std::shared_ptr<vamp_zero::score_draw_cache> score_cache, touch_handler on_touch)
: state_{std::move(state)}, score_cache_{std::move(score_cache)}, on_touch_{std::move(on_touch)} {}
auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override {
explicit vamp_root_control(vamp_zero::ui_state state, std::shared_ptr<vamp_zero::score_draw_cache> score_cache, std::function<std::bool_t(luma::touch_event const &)> on_touch)
: state_{std::move(state)}, score_cache_{std::move(score_cache)}, on_touch_{std::move(on_touch)} {}
auto measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size override {
return luma::clamp({.width = 1024.0F, .height = 600.0F}, limits);
}
auto draw(luma::draw_context& context, luma::theme const&) const -> void override {
auto draw(luma::draw_context &context, luma::theme const &) const -> void override {
auto area = bounds();
context.fill_rect(area, vamp_zero::to_luma(vamp_style::background));
draw_header(context, area, state_);
@@ -400,7 +518,8 @@ public:
draw_practice_exercise_menu(context, content, state_.app);
} else if (state_.app.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU) {
draw_practice_key_menu(context, content, state_.app);
} else if (state_.app.mode == vamp_zero::app_mode::PERFORMANCE_SCORE || state_.app.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
} else if (state_.app.mode == vamp_zero::app_mode::PERFORMANCE_SCORE ||
state_.app.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
if (score_cache_) {
draw_score(context, content, state_.app, *score_cache_);
}
@@ -408,31 +527,26 @@ public:
draw_main_menu(context, content, state_.app);
}
}
auto handle_touch(luma::touch_event const& event) -> std::bool_t override {
auto handle_touch(luma::touch_event const &event) -> std::bool_t override {
return on_touch_ ? on_touch_(event) : false;
}
private:
vamp_zero::ui_state state_{};
std::shared_ptr<vamp_zero::score_draw_cache> score_cache_{};
touch_handler on_touch_{};
std::function<std::bool_t(luma::touch_event const &)> on_touch_{};
};
}
vamp_zero::zero_ui::zero_ui(boost::asio::any_io_executor executor, luma::application_config config, vamp_zero::zero_ui::command_handler command_handler)
: executor_{std::move(executor)},
tap_timer_{executor_},
application_{std::move(config)},
command_handler_{std::move(command_handler)},
score_cache_{std::make_shared<vamp_zero::score_draw_cache>()} {}
vamp_zero::zero_ui::zero_ui(boost::asio::any_io_executor executor, luma::application_config config, std::function<void(vamp_protocol::page_command)> command_handler)
: executor_{std::move(executor)}, tap_timer_{executor_}, application_{std::move(config)},
command_handler_{std::move(command_handler)}, score_cache_{std::make_shared<vamp_zero::score_draw_cache>()} {}
auto vamp_zero::zero_ui::send_touch_command(vamp_protocol::page_command command) -> void {
if (!command_handler_) {
return;
}
boost::asio::post(executor_, [handler = command_handler_, command] {
handler(command);
});
boost::asio::post(executor_, [handler = command_handler_, command] { handler(command); });
}
auto vamp_zero::zero_ui::cancel_pending_tap_locked() -> void {
@@ -441,7 +555,7 @@ auto vamp_zero::zero_ui::cancel_pending_tap_locked() -> void {
auto vamp_zero::zero_ui::arm_pending_tap() -> void {
tap_timer_.expires_after(touch_double_tap_duration);
tap_timer_.async_wait([this](boost::system::error_code const& error) {
tap_timer_.async_wait([this](boost::system::error_code const &error) {
if (error) {
return;
}
@@ -457,17 +571,16 @@ auto vamp_zero::zero_ui::arm_pending_tap() -> void {
});
}
auto vamp_zero::zero_ui::handle_touch(luma::touch_event const& event) -> std::bool_t {
auto vamp_zero::zero_ui::handle_touch(luma::touch_event const &event) -> std::bool_t {
if (!command_handler_ || event.points.empty()) {
return false;
}
auto consumed = std::bool_t{false};
auto commands = std::vector<vamp_protocol::page_command>{};
auto arm_tap = std::bool_t{false};
{
auto lock = std::scoped_lock{touch_mutex_};
for (auto const& point : event.points) {
for (auto const &point : event.points) {
if (point.phase == luma::touch_phase::DOWN && !active_touch_id_) {
active_touch_id_ = point.id;
touch_down_time_ = event.time;
@@ -515,7 +628,6 @@ auto vamp_zero::zero_ui::handle_touch(luma::touch_event const& event) -> std::bo
}
}
}
for (auto command : commands) {
send_touch_command(command);
}
@@ -561,13 +673,12 @@ auto vamp_zero::zero_ui::snapshot() const -> vamp_zero::ui_state {
return state_;
}
auto vamp_zero::zero_ui::make_root(vamp_zero::ui_state const& state) -> std::expected<std::unique_ptr<luma::control>, modulus::error> {
return std::make_unique<vamp_root_control>(state, score_cache_, [this](luma::touch_event const& event) {
return handle_touch(event);
});
auto vamp_zero::zero_ui::make_root(vamp_zero::ui_state const &state) -> std::expected<std::unique_ptr<luma::control>, modulus::error> {
return std::make_unique<vamp_root_control>(
state, score_cache_, [this](luma::touch_event const &event) { return handle_touch(event); });
}
auto vamp_zero::make_ui_state(vamp_zero::config const& config, vamp_zero::app_state app) -> vamp_zero::ui_state {
auto vamp_zero::make_ui_state(vamp_zero::config const &config, vamp_zero::app_state app) -> vamp_zero::ui_state {
return vamp_zero::ui_state{
.app = std::move(app),
.ap_ssid = config.control.ap_ssid,