@@ -38,6 +38,10 @@ auto render(vamp_pico::display& display, link_status status, vamp_pico::remote_d
|
||||
display.show_remote(color_for(status), remote.title, remote.part, remote.page, remote.pages, remote.active);
|
||||
}
|
||||
|
||||
auto clear_remote(vamp_pico::remote_display_state& remote) -> void {
|
||||
remote = vamp_pico::remote_display_state{};
|
||||
}
|
||||
|
||||
auto connect_wifi(vamp_pico::display& display) -> std::bool_t {
|
||||
auto empty = vamp_pico::remote_display_state{};
|
||||
render(display, link_status::CONNECTING, empty);
|
||||
@@ -58,6 +62,7 @@ auto connect_wifi(vamp_pico::display& display) -> std::bool_t {
|
||||
auto send_command(vamp_protocol::page_command command, link_status& status, vamp_pico::remote_display_state& remote, vamp_pico::display& display) -> void {
|
||||
if (!vamp_pico::post_remote_command(command, remote)) {
|
||||
status = link_status::RETRYING;
|
||||
clear_remote(remote);
|
||||
render(display, status, remote);
|
||||
return;
|
||||
}
|
||||
@@ -136,6 +141,7 @@ auto main() -> int {
|
||||
status = link_status::CONNECTED;
|
||||
} else {
|
||||
status = link_status::RETRYING;
|
||||
clear_remote(remote);
|
||||
}
|
||||
render(display, status, remote);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,9 @@ auto run_display(vamp_zero::config const& config, vamp_zero::control_state& stat
|
||||
if (!endpoint) {
|
||||
return std::unexpected(endpoint.error());
|
||||
}
|
||||
auto ui = vamp_zero::zero_ui{io.get_executor(), config.display};
|
||||
auto ui = vamp_zero::zero_ui{io.get_executor(), config.display, [&state](vamp_protocol::page_command command) {
|
||||
static_cast<void>(state.apply(command));
|
||||
}};
|
||||
state.set_observer([&ui, &config](vamp_zero::app_state app) {
|
||||
auto status = ui.publish(vamp_zero::make_ui_state(config, std::move(app)));
|
||||
if (!status) {
|
||||
|
||||
+247
-28
@@ -86,10 +86,15 @@ constexpr auto tie_thickness_spaces = std::float32_t{0.13F};
|
||||
constexpr auto normal_stem_length_spaces = std::float32_t{3.50F};
|
||||
constexpr auto preamble_vertical_correction_spaces = std::float32_t{0.50F};
|
||||
constexpr auto stem_head_horizontal_overlap_spaces = std::float32_t{0.06F};
|
||||
constexpr auto stem_head_vertical_overlap_spaces = std::float32_t{0.20F};
|
||||
constexpr auto measure_right_guard_spaces = std::float32_t{1.50F};
|
||||
constexpr auto stem_head_vertical_overlap_spaces = std::float32_t{0.38F};
|
||||
constexpr auto measure_right_guard_spaces = std::float32_t{2.05F};
|
||||
constexpr auto chord_above_staff_spaces = std::float32_t{3.65F};
|
||||
constexpr auto chord_clearance_spaces = std::float32_t{0.45F};
|
||||
constexpr auto lyric_below_staff_spaces = std::float32_t{6.15F};
|
||||
constexpr auto lyric_clearance_spaces = std::float32_t{0.75F};
|
||||
constexpr auto tie_endpoint_gap_spaces = std::float32_t{0.28F};
|
||||
constexpr auto tie_note_offset_spaces = std::float32_t{0.50F};
|
||||
constexpr auto tie_arch_spaces = std::float32_t{0.72F};
|
||||
|
||||
auto to_raylib(luma::color color) -> Color {
|
||||
return {
|
||||
@@ -832,6 +837,26 @@ struct beam_group_layout {
|
||||
std::float32_t beam_y{};
|
||||
};
|
||||
|
||||
struct laid_out_tie_head {
|
||||
vamp_zero::score_pitch pitch{};
|
||||
luma::rect bounds{};
|
||||
std::float32_t center_y{};
|
||||
};
|
||||
|
||||
struct laid_out_note_ref {
|
||||
std::size_t system_index{};
|
||||
std::size_t measure_index{};
|
||||
std::size_t note_index{};
|
||||
luma::rect notation_bounds{};
|
||||
std::bool_t stem_up{true};
|
||||
std::vector<laid_out_tie_head> heads{};
|
||||
};
|
||||
|
||||
struct laid_out_measure_ref {
|
||||
std::size_t measure_index{};
|
||||
luma::rect notation_bounds{};
|
||||
};
|
||||
|
||||
auto layout_staff(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
std::float32_t x,
|
||||
@@ -1679,8 +1704,6 @@ auto layout_note_symbols(
|
||||
note_layout const& note,
|
||||
std::float32_t staff_top,
|
||||
std::float32_t spacing,
|
||||
std::float32_t next_x,
|
||||
std::float32_t measure_right_x,
|
||||
std::size_t system_index,
|
||||
std::size_t measure_index
|
||||
) -> void {
|
||||
@@ -1714,25 +1737,6 @@ auto layout_note_symbols(
|
||||
note.note_index);
|
||||
}
|
||||
}
|
||||
if (note.source != nullptr && note.source->tie_to_next) {
|
||||
auto tie_y = note.low_y + spacing * 0.62F;
|
||||
auto tie_start_x = note.visual_right - spacing * 0.45F;
|
||||
auto tie_end_x = std::min(next_x - spacing * 0.45F, measure_right_x - spacing * 0.30F);
|
||||
if (tie_end_x <= tie_start_x + spacing * 0.50F) {
|
||||
return;
|
||||
}
|
||||
add_curve(
|
||||
layout,
|
||||
vamp_zero::score_layout_item_role::TIE,
|
||||
{.x = tie_start_x, .y = tie_y},
|
||||
{.x = (tie_start_x + tie_end_x) * 0.5F, .y = tie_y + spacing * 0.78F},
|
||||
{.x = tie_end_x, .y = tie_y},
|
||||
at_least_one_pixel(scaled(tie_thickness_spaces, spacing)),
|
||||
score_ink(),
|
||||
system_index,
|
||||
measure_index,
|
||||
note.note_index);
|
||||
}
|
||||
}
|
||||
|
||||
auto note_x(std::float32_t start_x, std::float32_t width, std::int64_t total_ticks, std::int64_t elapsed_ticks, std::int64_t duration_ticks) -> std::float32_t {
|
||||
@@ -1837,6 +1841,23 @@ auto measure_notation_top_y(
|
||||
return top_y;
|
||||
}
|
||||
|
||||
auto measure_notation_bottom_y(std::vector<note_layout> const& notes, std::vector<beam_group_layout> const& beam_groups, std::float32_t staff_top, std::float32_t spacing) -> std::float32_t {
|
||||
auto bottom_y = staff_top + spacing * 4.0F;
|
||||
for (auto const& note : notes) {
|
||||
if (!note.hidden) {
|
||||
bottom_y = std::max(bottom_y, note.visual_bottom);
|
||||
}
|
||||
}
|
||||
for (auto const& group : beam_groups) {
|
||||
bottom_y = std::max(bottom_y, group.beam_y + scaled(beam_thickness_spaces, spacing) * 0.5F);
|
||||
}
|
||||
return bottom_y;
|
||||
}
|
||||
|
||||
auto lyric_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_bottom_y) -> std::float32_t {
|
||||
return std::max(staff_top + scaled(lyric_below_staff_spaces, spacing), notation_bottom_y + scaled(lyric_clearance_spaces, spacing));
|
||||
}
|
||||
|
||||
auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_top_y) -> std::float32_t {
|
||||
auto y = staff_top - scaled(chord_above_staff_spaces, spacing);
|
||||
auto const bottom_limit = notation_top_y - scaled(chord_clearance_spaces, spacing);
|
||||
@@ -1846,6 +1867,189 @@ auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float
|
||||
return y;
|
||||
}
|
||||
|
||||
auto same_pitch(vamp_zero::score_pitch const& left, vamp_zero::score_pitch const& right) -> std::bool_t {
|
||||
return normalized_letter(left.letter) == normalized_letter(right.letter)
|
||||
&& left.octave == right.octave
|
||||
&& pitch_alteration(left.accidental) == pitch_alteration(right.accidental);
|
||||
}
|
||||
|
||||
auto make_laid_out_note_ref(note_layout const& note, luma::rect notation_bounds, std::size_t system_index, std::size_t measure_index) -> laid_out_note_ref {
|
||||
auto ref = laid_out_note_ref{
|
||||
.system_index = system_index,
|
||||
.measure_index = measure_index,
|
||||
.note_index = note.note_index,
|
||||
.notation_bounds = notation_bounds,
|
||||
.stem_up = note.stem_up,
|
||||
};
|
||||
if (note.source == nullptr || note.hidden || note.rest) {
|
||||
return ref;
|
||||
}
|
||||
ref.heads.reserve(note.heads.size());
|
||||
for (auto const& head : note.heads) {
|
||||
if (head.source_pitch_index >= note.source->pitches.size()) {
|
||||
continue;
|
||||
}
|
||||
ref.heads.push_back({
|
||||
.pitch = note.source->pitches[head.source_pitch_index],
|
||||
.bounds = head.bounds,
|
||||
.center_y = head.center_y,
|
||||
});
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
auto find_note_ref(std::vector<laid_out_note_ref> const& refs, std::size_t measure_index, std::size_t note_index) -> laid_out_note_ref const* {
|
||||
auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) {
|
||||
return ref.measure_index == measure_index && ref.note_index == note_index;
|
||||
});
|
||||
return found == refs.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
auto find_measure_ref(std::vector<laid_out_measure_ref> const& refs, std::size_t measure_index) -> laid_out_measure_ref const* {
|
||||
auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) {
|
||||
return ref.measure_index == measure_index;
|
||||
});
|
||||
return found == refs.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
auto find_tie_head(laid_out_note_ref const& ref, vamp_zero::score_pitch const& pitch) -> laid_out_tie_head const* {
|
||||
auto found = std::find_if(ref.heads.begin(), ref.heads.end(), [&](auto const& head) {
|
||||
return same_pitch(head.pitch, pitch);
|
||||
});
|
||||
return found == ref.heads.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
struct tied_note_destination {
|
||||
std::size_t measure_index{};
|
||||
std::size_t note_index{};
|
||||
};
|
||||
|
||||
auto find_tied_destination(vamp_zero::score_document const& score, std::size_t measure_index, std::size_t note_index, vamp_zero::score_pitch const& pitch) -> std::optional<tied_note_destination> {
|
||||
for (auto current_measure = measure_index; current_measure < score.measures.size(); ++current_measure) {
|
||||
auto const& measure = score.measures[current_measure];
|
||||
auto first_note = current_measure == measure_index ? note_index + 1U : 0U;
|
||||
for (auto current_note = first_note; current_note < measure.notes.size(); ++current_note) {
|
||||
auto const& note = measure.notes[current_note];
|
||||
if (note.hidden || note.kind == "rest") {
|
||||
continue;
|
||||
}
|
||||
auto matching_pitch = std::find_if(note.pitches.begin(), note.pitches.end(), [&](auto const& candidate) {
|
||||
return same_pitch(candidate, pitch);
|
||||
});
|
||||
if (matching_pitch == note.pitches.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (note.tie_from_previous) {
|
||||
return tied_note_destination{.measure_index = current_measure, .note_index = current_note};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto add_tie_curve(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
luma::point from,
|
||||
luma::point to,
|
||||
std::bool_t below_note,
|
||||
std::float32_t spacing,
|
||||
std::size_t system_index,
|
||||
std::size_t measure_index,
|
||||
std::size_t note_index
|
||||
) -> void {
|
||||
if (to.x <= from.x + spacing * 0.40F) {
|
||||
return;
|
||||
}
|
||||
auto direction = below_note ? std::float32_t{1.0F} : std::float32_t{-1.0F};
|
||||
auto control = luma::point{
|
||||
.x = (from.x + to.x) * 0.5F,
|
||||
.y = (from.y + to.y) * 0.5F + direction * scaled(tie_arch_spaces, spacing),
|
||||
};
|
||||
add_curve(
|
||||
layout,
|
||||
vamp_zero::score_layout_item_role::TIE,
|
||||
from,
|
||||
control,
|
||||
to,
|
||||
at_least_one_pixel(scaled(tie_thickness_spaces, spacing)),
|
||||
score_ink(),
|
||||
system_index,
|
||||
measure_index,
|
||||
note_index);
|
||||
}
|
||||
|
||||
auto layout_page_ties(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
vamp_zero::score_document const& score,
|
||||
std::vector<laid_out_note_ref> const& note_refs,
|
||||
std::vector<laid_out_measure_ref> const& measure_refs,
|
||||
std::float32_t spacing
|
||||
) -> void {
|
||||
for (auto measure_index = std::size_t{}; measure_index < score.measures.size(); ++measure_index) {
|
||||
auto const& measure = score.measures[measure_index];
|
||||
for (auto note_index = std::size_t{}; note_index < measure.notes.size(); ++note_index) {
|
||||
auto const& note = measure.notes[note_index];
|
||||
if (!note.tie_to_next || note.hidden || note.kind == "rest") {
|
||||
continue;
|
||||
}
|
||||
auto const* start_ref = find_note_ref(note_refs, measure_index, note_index);
|
||||
for (auto const& pitch : note.pitches) {
|
||||
auto destination = find_tied_destination(score, measure_index, note_index, pitch);
|
||||
auto const* destination_ref = destination ? find_note_ref(note_refs, destination->measure_index, destination->note_index) : nullptr;
|
||||
auto const* start_head = start_ref == nullptr ? nullptr : find_tie_head(*start_ref, pitch);
|
||||
auto const* destination_head = destination_ref == nullptr ? nullptr : find_tie_head(*destination_ref, pitch);
|
||||
|
||||
if (start_ref != nullptr && start_head != nullptr && destination_ref != nullptr && destination_head != nullptr && start_ref->system_index == destination_ref->system_index) {
|
||||
auto below = start_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset},
|
||||
{.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
start_ref->system_index,
|
||||
start_ref->measure_index,
|
||||
start_ref->note_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start_ref != nullptr && start_head != nullptr) {
|
||||
auto below = start_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset},
|
||||
{.x = start_ref->notation_bounds.x + start_ref->notation_bounds.width, .y = start_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
start_ref->system_index,
|
||||
start_ref->measure_index,
|
||||
start_ref->note_index);
|
||||
}
|
||||
if (destination && destination_ref != nullptr && destination_head != nullptr) {
|
||||
auto const* measure_ref = find_measure_ref(measure_refs, destination->measure_index);
|
||||
if (measure_ref == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto below = destination_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = measure_ref->notation_bounds.x, .y = destination_head->center_y + y_offset},
|
||||
{.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
destination_ref->system_index,
|
||||
destination_ref->measure_index,
|
||||
destination_ref->note_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto layout_measure(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
vamp_zero::score_document const& score,
|
||||
@@ -1855,7 +2059,9 @@ auto layout_measure(
|
||||
std::size_t system_index,
|
||||
std::size_t measure_index,
|
||||
std::float32_t staff_top,
|
||||
std::float32_t spacing
|
||||
std::float32_t spacing,
|
||||
std::vector<laid_out_note_ref>& note_refs,
|
||||
std::vector<laid_out_measure_ref>& measure_refs
|
||||
) -> void {
|
||||
add_text(layout, vamp_zero::score_layout_item_role::MEASURE_NUMBER, bounds.x + spacing * 0.20F, bounds.y + spacing * 0.05F, std::to_string(measure_number), 1, score_muted_ink(), spacing * 4.0F, system_index, measure_index);
|
||||
|
||||
@@ -1863,6 +2069,10 @@ auto layout_measure(
|
||||
auto notation_right_x = bounds.x + bounds.width - scaled(measure_right_guard_spaces, spacing);
|
||||
auto notation_width = std::max<std::float32_t>(spacing * 3.0F, notation_right_x - notation_x);
|
||||
auto notation_bounds = luma::rect{.x = notation_x, .y = bounds.y, .width = notation_width, .height = bounds.height};
|
||||
measure_refs.push_back({
|
||||
.measure_index = measure_index,
|
||||
.notation_bounds = notation_bounds,
|
||||
});
|
||||
auto total_ticks = measure_ticks(measure);
|
||||
auto elapsed = std::int64_t{};
|
||||
auto accidentals = make_accidental_state(effective_key_signature(score, measure));
|
||||
@@ -1889,7 +2099,12 @@ auto layout_measure(
|
||||
}
|
||||
|
||||
auto const notation_top_y = measure_notation_top_y(measure, notes, beam_groups, staff_top, spacing);
|
||||
auto const notation_bottom_y = measure_notation_bottom_y(notes, beam_groups, staff_top, spacing);
|
||||
auto const chord_y = chord_symbol_y(staff_top, spacing, notation_top_y);
|
||||
auto const lyrics_y = lyric_y(staff_top, spacing, notation_bottom_y);
|
||||
for (auto const& note : notes) {
|
||||
note_refs.push_back(make_laid_out_note_ref(note, notation_bounds, system_index, measure_index));
|
||||
}
|
||||
if (!measure.section_label.empty()) {
|
||||
auto section_y = std::min(staff_top - spacing * 4.70F, chord_y - spacing * 0.65F);
|
||||
add_text(layout, vamp_zero::score_layout_item_role::SECTION_LABEL, bounds.x + spacing * 2.10F, section_y, measure.section_label, 2, score_accent_ink(), bounds.width - spacing * 2.6F, system_index, measure_index);
|
||||
@@ -1948,14 +2163,13 @@ auto layout_measure(
|
||||
add_flag(layout, note, spacing, system_index, measure_index);
|
||||
}
|
||||
for (auto index = std::size_t{}; index < notes.size(); ++index) {
|
||||
auto next_x = index + 1U < notes.size() ? notes[index + 1U].x : notes[index].x + spacing * 3.0F;
|
||||
layout_note_symbols(layout, notes[index], staff_top, spacing, next_x, notation_bounds.x + notation_bounds.width, system_index, measure_index);
|
||||
layout_note_symbols(layout, notes[index], staff_top, spacing, system_index, measure_index);
|
||||
if (notes[index].source != nullptr && !notes[index].hidden && !notes[index].source->lyric.empty()) {
|
||||
add_text(
|
||||
layout,
|
||||
vamp_zero::score_layout_item_role::LYRIC,
|
||||
notes[index].x - spacing,
|
||||
staff_top + spacing * 5.35F,
|
||||
lyrics_y,
|
||||
notes[index].source->lyric,
|
||||
1,
|
||||
score_muted_ink(),
|
||||
@@ -2027,6 +2241,8 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
auto const content_width = std::max<std::float32_t>(spacing * 18.0F, bounds.width - margin * 2.0F);
|
||||
auto const system_height = (bounds.height - 108.0F) / static_cast<std::float32_t>(vamp_zero::score_systems_per_page);
|
||||
auto const first_measure = page_index * vamp_zero::score_measures_per_page;
|
||||
auto note_refs = std::vector<laid_out_note_ref>{};
|
||||
auto measure_refs = std::vector<laid_out_measure_ref>{};
|
||||
|
||||
for (auto system = std::size_t{}; system < vamp_zero::score_systems_per_page; ++system) {
|
||||
auto const system_y = content_y + static_cast<std::float32_t>(system) * system_height;
|
||||
@@ -2052,7 +2268,9 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
system,
|
||||
measure_index,
|
||||
staff_top,
|
||||
spacing);
|
||||
spacing,
|
||||
note_refs,
|
||||
measure_refs);
|
||||
}
|
||||
|
||||
auto const bar_x = x + measure_width;
|
||||
@@ -2069,6 +2287,7 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
}
|
||||
}
|
||||
|
||||
layout_page_ties(layout, score, note_refs, measure_refs, spacing);
|
||||
add_stroke_rect(layout, vamp_zero::score_layout_item_role::BORDER, bounds, 1.1F, score_border());
|
||||
return layout;
|
||||
}
|
||||
|
||||
+116
-3
@@ -11,6 +11,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
|
||||
#include <luma.hpp>
|
||||
|
||||
@@ -22,6 +23,11 @@
|
||||
|
||||
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 colon = bind.rfind(':');
|
||||
@@ -404,7 +410,7 @@ 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) : state_{std::move(state)} {}
|
||||
explicit vamp_root_control(vamp_zero::ui_state state, touch_handler on_touch) : state_{std::move(state)}, 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);
|
||||
}
|
||||
@@ -438,13 +444,117 @@ public:
|
||||
draw_main_menu(context, content, state_.app);
|
||||
}
|
||||
}
|
||||
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_{};
|
||||
touch_handler on_touch_{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
vamp_zero::zero_ui::zero_ui(boost::asio::any_io_executor executor, luma::application_config config) : executor_{std::move(executor)}, application_{std::move(config)} {}
|
||||
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)} {}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
auto vamp_zero::zero_ui::cancel_pending_tap_locked() -> void {
|
||||
pending_tap_ = false;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
auto send_next = false;
|
||||
{
|
||||
auto lock = std::scoped_lock{touch_mutex_};
|
||||
send_next = pending_tap_;
|
||||
pending_tap_ = false;
|
||||
}
|
||||
if (send_next) {
|
||||
send_touch_command(vamp_protocol::page_command::NEXT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 = false;
|
||||
auto commands = std::vector<vamp_protocol::page_command>{};
|
||||
auto arm_tap = false;
|
||||
{
|
||||
auto lock = std::scoped_lock{touch_mutex_};
|
||||
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;
|
||||
hold_sent_ = false;
|
||||
tap_timer_.cancel();
|
||||
consumed = true;
|
||||
continue;
|
||||
}
|
||||
if (!active_touch_id_ || point.id != *active_touch_id_) {
|
||||
continue;
|
||||
}
|
||||
consumed = true;
|
||||
if (point.phase == luma::touch_phase::CANCEL) {
|
||||
active_touch_id_.reset();
|
||||
hold_sent_ = false;
|
||||
cancel_pending_tap_locked();
|
||||
tap_timer_.cancel();
|
||||
continue;
|
||||
}
|
||||
if (point.phase == luma::touch_phase::MOVE) {
|
||||
if (!hold_sent_ && event.time - touch_down_time_ >= touch_hold_duration) {
|
||||
hold_sent_ = true;
|
||||
cancel_pending_tap_locked();
|
||||
tap_timer_.cancel();
|
||||
commands.push_back(vamp_protocol::page_command::SELECT);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (point.phase == luma::touch_phase::UP) {
|
||||
active_touch_id_.reset();
|
||||
if (!hold_sent_ && event.time - touch_down_time_ >= touch_hold_duration) {
|
||||
commands.push_back(vamp_protocol::page_command::SELECT);
|
||||
} else if (!hold_sent_) {
|
||||
if (pending_tap_ && event.time - last_tap_time_ <= touch_double_tap_duration) {
|
||||
cancel_pending_tap_locked();
|
||||
tap_timer_.cancel();
|
||||
commands.push_back(vamp_protocol::page_command::PREVIOUS);
|
||||
} else {
|
||||
pending_tap_ = true;
|
||||
last_tap_time_ = event.time;
|
||||
arm_tap = true;
|
||||
}
|
||||
}
|
||||
hold_sent_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto command : commands) {
|
||||
send_touch_command(command);
|
||||
}
|
||||
if (arm_tap) {
|
||||
arm_pending_tap();
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
auto vamp_zero::zero_ui::run() -> std::expected<void, modulus::error> {
|
||||
auto state = snapshot();
|
||||
@@ -459,6 +569,7 @@ auto vamp_zero::zero_ui::run() -> std::expected<void, modulus::error> {
|
||||
}
|
||||
|
||||
auto vamp_zero::zero_ui::stop() -> void {
|
||||
tap_timer_.cancel();
|
||||
application_.request_stop();
|
||||
}
|
||||
|
||||
@@ -482,7 +593,9 @@ auto vamp_zero::zero_ui::snapshot() const -> vamp_zero::ui_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);
|
||||
return std::make_unique<vamp_root_control>(state, [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 {
|
||||
|
||||
Reference in New Issue
Block a user