Add missing files >.<

custom
jacqueline 2 years ago
parent 80170642ea
commit 191441ebe2
  1. 29
      src/ui/include/screen_playing.hpp
  2. 64
      src/ui/include/screen_track_browser.hpp
  3. 43
      src/ui/screen_playing.cpp
  4. 266
      src/ui/screen_track_browser.cpp

@ -0,0 +1,29 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include "lvgl.h"
#include "database.hpp"
#include "screen.hpp"
namespace ui {
namespace screens {
class Playing : public Screen {
public:
explicit Playing(database::Track t);
~Playing();
private:
database::Track track_;
};
} // namespace screens
} // namespace ui

@ -0,0 +1,64 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "lvgl.h"
#include "database.hpp"
#include "screen.hpp"
namespace ui {
namespace screens {
class TrackBrowser : public Screen {
public:
TrackBrowser(
std::weak_ptr<database::Database> db,
const std::string& title,
std::future<database::Result<database::IndexRecord>*>&& initial_page);
~TrackBrowser() {}
auto Tick() -> void override;
auto OnItemSelected(lv_event_t* ev) -> void;
auto OnItemClicked(lv_event_t* ev) -> void;
private:
enum Position {
START = 0,
END = 1,
};
auto AddLoadingIndictor(Position pos) -> void;
auto AddResults(Position pos, database::Result<database::IndexRecord>*)
-> void;
auto DropPage(Position pos) -> void;
auto FetchNewPage(Position pos) -> void;
auto GetNumRecords() -> std::size_t;
auto GetItemIndex(lv_obj_t* obj) -> std::optional<std::size_t>;
auto GetRecordByIndex(std::size_t index)
-> std::optional<database::IndexRecord>;
std::weak_ptr<database::Database> db_;
lv_obj_t* list_;
lv_obj_t* loading_indicator_;
std::optional<Position> loading_pos_;
std::optional<std::future<database::Result<database::IndexRecord>*>>
loading_page_;
std::deque<std::unique_ptr<database::Result<database::IndexRecord>>>
current_pages_;
};
} // namespace screens
} // namespace ui

@ -0,0 +1,43 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "screen_playing.hpp"
#include "esp_log.h"
#include "lvgl.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 "track.hpp"
#include "ui_events.hpp"
#include "ui_fsm.hpp"
#include "widgets/lv_label.h"
namespace ui {
namespace screens {
Playing::Playing(database::Track track) : track_(track) {
lv_obj_t* container = lv_obj_create(root_);
lv_obj_set_align(container, LV_ALIGN_CENTER);
lv_obj_set_size(container, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
// bro idk
lv_obj_t* label = lv_label_create(container);
lv_label_set_text_static(label, track.TitleOrFilename().c_str());
lv_obj_set_align(label, LV_ALIGN_CENTER);
}
Playing::~Playing() {}
} // namespace screens
} // namespace ui

@ -0,0 +1,266 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include <algorithm>
#include <memory>
#include "database.hpp"
#include "event_queue.hpp"
#include "lvgl.h"
#include "screen_menu.hpp"
#include "core/lv_event.h"
#include "esp_log.h"
#include "core/lv_group.h"
#include "core/lv_obj_pos.h"
#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 "misc/lv_area.h"
#include "screen_track_browser.hpp"
#include "ui_events.hpp"
#include "ui_fsm.hpp"
#include "widgets/lv_label.h"
static constexpr char kTag[] = "browser";
static constexpr int kMaxPages = 3;
static constexpr int kPageBuffer = 5;
namespace ui {
namespace screens {
static void item_click_cb(lv_event_t* ev) {
if (ev->user_data == NULL) {
return;
}
TrackBrowser* instance = reinterpret_cast<TrackBrowser*>(ev->user_data);
instance->OnItemClicked(ev);
}
static void item_select_cb(lv_event_t* ev) {
if (ev->user_data == NULL) {
return;
}
TrackBrowser* instance = reinterpret_cast<TrackBrowser*>(ev->user_data);
instance->OnItemSelected(ev);
}
TrackBrowser::TrackBrowser(
std::weak_ptr<database::Database> db,
const std::string& title,
std::future<database::Result<database::IndexRecord>*>&& initial_page)
: db_(db),
list_(nullptr),
loading_indicator_(nullptr),
loading_pos_(END),
loading_page_(std::move(initial_page)),
current_pages_() {
lv_obj_t* title_obj = lv_label_create(root_);
lv_label_set_text(title_obj, title.c_str());
list_ = lv_list_create(root_);
lv_obj_set_size(list_, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(list_);
}
auto TrackBrowser::Tick() -> void {
if (!loading_page_) {
return;
}
if (!loading_page_->valid()) {
// TODO(jacqueline): error case.
return;
}
if (loading_page_->wait_for(std::chrono::seconds(0)) ==
std::future_status::ready) {
auto result = loading_page_->get();
AddResults(loading_pos_.value_or(END), result);
loading_page_.reset();
loading_pos_.reset();
}
}
auto TrackBrowser::OnItemSelected(lv_event_t* ev) -> void {
auto index = GetItemIndex(lv_event_get_target(ev));
if (!index) {
return;
}
if (index < kPageBuffer) {
FetchNewPage(START);
return;
}
if (index > GetNumRecords() - kPageBuffer) {
FetchNewPage(END);
return;
}
}
auto TrackBrowser::OnItemClicked(lv_event_t* ev) -> void {
auto index = GetItemIndex(lv_event_get_target(ev));
if (!index) {
return;
}
auto record = GetRecordByIndex(*index);
if (!record) {
return;
}
ESP_LOGI(kTag, "clicked item %u (%s)", *index,
record->text().value_or("[nil]").c_str());
events::Dispatch<internal::RecordSelected, UiState>(
internal::RecordSelected{.record = *record});
}
auto TrackBrowser::AddLoadingIndictor(Position pos) -> void {
if (loading_indicator_) {
return;
}
loading_indicator_ = lv_list_add_text(list_, "Loading...");
if (pos == START) {
lv_obj_move_to_index(loading_indicator_, 0);
}
}
auto TrackBrowser::AddResults(Position pos,
database::Result<database::IndexRecord>* results)
-> void {
if (loading_indicator_ != nullptr) {
lv_obj_del(loading_indicator_);
loading_indicator_ = nullptr;
}
auto fn = [&](const database::IndexRecord& record) {
auto text = record.text();
if (!text) {
// TODO(jacqueline): Display category-specific text.
text = "[ no data ]";
}
lv_obj_t* item = lv_list_add_btn(list_, NULL, text->c_str());
lv_obj_add_event_cb(item, item_click_cb, LV_EVENT_CLICKED, this);
lv_obj_add_event_cb(item, item_select_cb, LV_EVENT_FOCUSED, this);
lv_group_add_obj(group_, item);
if (pos == START) {
lv_obj_move_to_index(item, 0);
}
};
switch (pos) {
case START:
std::for_each(results->values().rbegin(), results->values().rend(), fn);
current_pages_.emplace_front(results);
break;
case END:
std::for_each(results->values().begin(), results->values().end(), fn);
current_pages_.emplace_back(results);
break;
}
}
auto TrackBrowser::DropPage(Position pos) -> void {
if (pos == START) {
for (int i = 0; i < current_pages_.front()->values().size(); i++) {
lv_obj_t* item = lv_obj_get_child(list_, 0);
if (item == NULL) {
continue;
}
lv_obj_del(item);
}
current_pages_.pop_front();
} else if (pos == END) {
for (int i = 0; i < current_pages_.back()->values().size(); i++) {
lv_obj_t* item = lv_obj_get_child(list_, lv_obj_get_child_cnt(list_) - 1);
if (item == NULL) {
continue;
}
lv_group_remove_obj(item);
lv_obj_del(item);
}
current_pages_.pop_back();
}
}
auto TrackBrowser::FetchNewPage(Position pos) -> void {
if (loading_page_) {
return;
}
auto db = db_.lock();
if (!db) {
return;
}
// If we already have a complete set of pages, drop the page that's furthest
// away.
if (current_pages_.size() >= kMaxPages) {
switch (pos) {
case START:
DropPage(END);
break;
case END:
DropPage(START);
break;
}
}
std::optional<database::Continuation<database::IndexRecord>> cont;
switch (pos) {
case START:
cont = current_pages_.front()->prev_page();
break;
case END:
cont = current_pages_.back()->next_page();
break;
}
if (!cont) {
return;
}
loading_pos_ = pos;
loading_page_ = db->GetPage(&cont.value());
}
auto TrackBrowser::GetNumRecords() -> std::size_t {
return lv_obj_get_child_cnt(list_) - (loading_indicator_ != nullptr ? 1 : 0);
}
auto TrackBrowser::GetItemIndex(lv_obj_t* obj) -> std::optional<std::size_t> {
std::size_t child_count = lv_obj_get_child_cnt(list_);
std::size_t index = 0;
for (int i = 0; i < child_count; i++) {
lv_obj_t* child = lv_obj_get_child(list_, i);
if (child == loading_indicator_) {
continue;
}
if (child == obj) {
return index;
}
index++;
}
return {};
}
auto TrackBrowser::GetRecordByIndex(std::size_t index)
-> std::optional<database::IndexRecord> {
std::size_t current_index = 0;
for (const auto& page : current_pages_) {
if (index > current_index + page->values().size()) {
current_index += page->values().size();
continue;
}
if (index < current_index) {
// uhhh
break;
}
std::size_t index_in_page = index - current_index;
return page->values().at(index_in_page);
}
return {};
}
} // namespace screens
} // namespace ui
Loading…
Cancel
Save