Implement UI for enqueuing instead of replacing the current track
This commit is contained in:
@@ -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_, "");
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user