Implement UI for enqueuing instead of replacing the current track

custom
jacqueline 2 years ago
parent 7a7fafdd92
commit 28633e857f
  1. 2
      src/ui/CMakeLists.txt
  2. 9
      src/ui/include/modal.hpp
  3. 36
      src/ui/include/modal_add_to_queue.hpp
  4. 127
      src/ui/modal_add_to_queue.cpp
  5. 1
      src/ui/screen_playing.cpp
  6. 10
      src/ui/ui_fsm.cpp

@ -7,7 +7,7 @@ idf_component_register(
"encoder_input.cpp" "screen_track_browser.cpp" "screen_playing.cpp"
"themes.cpp" "widget_top_bar.cpp" "screen.cpp" "screen_onboarding.cpp"
"modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp"
"event_binding.cpp"
"event_binding.cpp" "modal_add_to_queue.cpp"
"splash.c" "font_fusion.c" "font_symbols.c"
"icons/battery_empty.c" "icons/battery_full.c" "icons/battery_20.c"
"icons/battery_40.c" "icons/battery_60.c" "icons/battery_80.c" "icons/play.c"

@ -30,6 +30,15 @@ class Modal {
lv_obj_t* const root_;
lv_group_t* const group_;
std::pmr::vector<std::unique_ptr<EventBinding>> event_bindings_;
template <typename T>
auto lv_bind(lv_obj_t* obj, lv_event_code_t ev, T fn) -> void {
auto binding = std::make_unique<EventBinding>(obj, ev);
binding->signal().connect(fn);
event_bindings_.push_back(std::move(binding));
}
private:
Screen* host_;
};

@ -0,0 +1,36 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include <vector>
#include "database.hpp"
#include "index.hpp"
#include "lvgl.h"
#include "modal.hpp"
#include "source.hpp"
#include "track_queue.hpp"
namespace ui {
namespace modals {
class AddToQueue : public Modal {
public:
AddToQueue(Screen*,
audio::TrackQueue&,
std::shared_ptr<playlist::IndexRecordSource>);
private:
audio::TrackQueue& queue_;
std::shared_ptr<playlist::IndexRecordSource> item_;
lv_obj_t* container_;
};
} // namespace modals
} // namespace ui

@ -0,0 +1,127 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "modal_add_to_queue.hpp"
#include "core/lv_event.h"
#include "core/lv_obj.h"
#include "core/lv_obj_tree.h"
#include "esp_log.h"
#include "core/lv_group.h"
#include "core/lv_obj_pos.h"
#include "event_queue.hpp"
#include "extra/widgets/list/lv_list.h"
#include "extra/widgets/menu/lv_menu.h"
#include "extra/widgets/spinner/lv_spinner.h"
#include "hal/lv_hal_disp.h"
#include "index.hpp"
#include "misc/lv_area.h"
#include "source.hpp"
#include "track_queue.hpp"
#include "ui_events.hpp"
#include "ui_fsm.hpp"
#include "widget_top_bar.hpp"
#include "widgets/lv_btn.h"
#include "widgets/lv_label.h"
namespace ui {
namespace modals {
AddToQueue::AddToQueue(Screen* host,
audio::TrackQueue& queue,
std::shared_ptr<playlist::IndexRecordSource> item)
: Modal(host), queue_(queue), item_(item) {
lv_obj_set_layout(root_, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_t* label = lv_label_create(root_);
lv_label_set_text(label, "This track");
lv_obj_t* button_container = lv_obj_create(root_);
lv_obj_set_size(button_container, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_set_layout(button_container, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(button_container, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(button_container, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_t* btn = lv_btn_create(button_container);
label = lv_label_create(btn);
lv_label_set_text(label, "Play");
lv_group_add_obj(group_, btn);
lv_bind(btn, LV_EVENT_CLICKED, [this](lv_obj_t*) {
queue_.Clear();
auto track = item_->Current();
if (track) {
queue_.AddNext(*track);
}
events::Ui().Dispatch(internal::ModalCancelPressed{});
});
bool has_queue = queue.GetCurrent().has_value();
if (has_queue) {
btn = lv_btn_create(button_container);
label = lv_label_create(btn);
lv_label_set_text(label, "Enqueue");
lv_group_add_obj(group_, btn);
lv_bind(btn, LV_EVENT_CLICKED, [this](lv_obj_t*) {
auto track = item_->Current();
if (track) {
queue_.AddLast(*track);
}
events::Ui().Dispatch(internal::ModalCancelPressed{});
});
}
label = lv_label_create(root_);
lv_label_set_text(label, "All tracks");
button_container = lv_obj_create(root_);
lv_obj_set_size(button_container, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_set_layout(button_container, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(button_container, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(button_container, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
btn = lv_btn_create(button_container);
label = lv_label_create(btn);
lv_label_set_text(label, "Play");
lv_group_add_obj(group_, btn);
lv_bind(btn, LV_EVENT_CLICKED, [this](lv_obj_t*) {
queue_.Clear();
queue_.IncludeNext(item_);
events::Ui().Dispatch(internal::ModalCancelPressed{});
});
if (has_queue) {
btn = lv_btn_create(button_container);
label = lv_label_create(btn);
lv_label_set_text(label, "Enqueue");
lv_group_add_obj(group_, btn);
lv_bind(btn, LV_EVENT_CLICKED, [this](lv_obj_t*) {
queue_.IncludeLast(item_);
events::Ui().Dispatch(internal::ModalCancelPressed{});
});
}
btn = lv_btn_create(root_);
label = lv_label_create(btn);
lv_label_set_text(label, "Cancel");
lv_group_add_obj(group_, btn);
lv_bind(btn, LV_EVENT_CLICKED, [](lv_obj_t*) {
events::Ui().Dispatch(internal::ModalCancelPressed{});
});
}
} // namespace modals
} // namespace ui

@ -284,6 +284,7 @@ Playing::Playing(models::TopBar& top_bar_model,
lv_label_set_text(next_up_hint_, "");
return;
} else {
lv_label_set_text(next_up_label_, "Next up");
lv_label_set_text(next_up_hint_, "");
}

@ -19,6 +19,7 @@
#include "event_queue.hpp"
#include "gpios.hpp"
#include "lvgl_task.hpp"
#include "modal_add_to_queue.hpp"
#include "modal_confirm.hpp"
#include "model_playback.hpp"
#include "nvs.hpp"
@ -287,11 +288,10 @@ void Browse::react(const internal::RecordSelected& ev) {
if (record->track()) {
ESP_LOGI(kTag, "selected track '%s'", record->text()->c_str());
auto& queue = sServices->track_queue();
queue.Clear();
queue.IncludeLast(std::make_shared<playlist::IndexRecordSource>(
sServices->database(), ev.initial_page, 0, ev.page, ev.record));
ESP_LOGI(kTag, "transit to playing");
transit<Playing>();
auto source = std::make_shared<playlist::IndexRecordSource>(
sServices->database(), ev.initial_page, 0, ev.page, ev.record);
sCurrentModal.reset(
new modals::AddToQueue(sCurrentScreen.get(), queue, source));
} else {
ESP_LOGI(kTag, "selected record '%s'", record->text()->c_str());
auto cont = record->Expand(kRecordsPerPage);

Loading…
Cancel
Save