summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lvgl/src/extra/widgets/list/lv_list.c2
-rw-r--r--lua/assets/audio.pngbin623 -> 4318 bytes
-rw-r--r--lua/assets/battery_20.pngbin617 -> 4337 bytes
-rw-r--r--lua/assets/battery_40.pngbin617 -> 4338 bytes
-rw-r--r--lua/assets/battery_60.pngbin618 -> 4338 bytes
-rw-r--r--lua/assets/battery_80.pngbin622 -> 4340 bytes
-rw-r--r--lua/assets/battery_empty.pngbin614 -> 4332 bytes
-rw-r--r--lua/assets/battery_full.pngbin618 -> 4339 bytes
-rw-r--r--lua/assets/bt.pngbin8502 -> 4595 bytes
-rw-r--r--lua/assets/bt_conn.pngbin654 -> 4363 bytes
-rw-r--r--lua/assets/pause.pngbin608 -> 4286 bytes
-rw-r--r--lua/assets/play.pngbin616 -> 4309 bytes
-rw-r--r--lua/browser.lua113
-rw-r--r--lua/main_menu.lua8
-rw-r--r--lua/widgets.lua13
-rw-r--r--src/database/database.cpp12
-rw-r--r--src/database/include/database.hpp1
-rw-r--r--src/lua/lua_database.cpp77
-rw-r--r--src/ui/ui_fsm.cpp15
19 files changed, 199 insertions, 42 deletions
diff --git a/lib/lvgl/src/extra/widgets/list/lv_list.c b/lib/lvgl/src/extra/widgets/list/lv_list.c
index 29355fd3..1d58615f 100644
--- a/lib/lvgl/src/extra/widgets/list/lv_list.c
+++ b/lib/lvgl/src/extra/widgets/list/lv_list.c
@@ -91,7 +91,7 @@ lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * icon, const char * txt)
if(txt) {
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, txt);
- lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
+ lv_label_set_long_mode(label, LV_LABEL_LONG_DOT);
lv_obj_set_flex_grow(label, 1);
}
diff --git a/lua/assets/audio.png b/lua/assets/audio.png
index b8ad9071..6151307f 100644
--- a/lua/assets/audio.png
+++ b/lua/assets/audio.png
Binary files differ
diff --git a/lua/assets/battery_20.png b/lua/assets/battery_20.png
index 9012376f..3a702397 100644
--- a/lua/assets/battery_20.png
+++ b/lua/assets/battery_20.png
Binary files differ
diff --git a/lua/assets/battery_40.png b/lua/assets/battery_40.png
index 88a0b448..ae7e3d7b 100644
--- a/lua/assets/battery_40.png
+++ b/lua/assets/battery_40.png
Binary files differ
diff --git a/lua/assets/battery_60.png b/lua/assets/battery_60.png
index d86c997a..44ba7954 100644
--- a/lua/assets/battery_60.png
+++ b/lua/assets/battery_60.png
Binary files differ
diff --git a/lua/assets/battery_80.png b/lua/assets/battery_80.png
index 344b3703..33bae9fc 100644
--- a/lua/assets/battery_80.png
+++ b/lua/assets/battery_80.png
Binary files differ
diff --git a/lua/assets/battery_empty.png b/lua/assets/battery_empty.png
index c9176e8c..5f3bfdbc 100644
--- a/lua/assets/battery_empty.png
+++ b/lua/assets/battery_empty.png
Binary files differ
diff --git a/lua/assets/battery_full.png b/lua/assets/battery_full.png
index 57122a23..f5da13a3 100644
--- a/lua/assets/battery_full.png
+++ b/lua/assets/battery_full.png
Binary files differ
diff --git a/lua/assets/bt.png b/lua/assets/bt.png
index 180e6b3a..73f3179f 100644
--- a/lua/assets/bt.png
+++ b/lua/assets/bt.png
Binary files differ
diff --git a/lua/assets/bt_conn.png b/lua/assets/bt_conn.png
index 7a5f2b27..91f9964d 100644
--- a/lua/assets/bt_conn.png
+++ b/lua/assets/bt_conn.png
Binary files differ
diff --git a/lua/assets/pause.png b/lua/assets/pause.png
index ec388cd5..f1a3d8cb 100644
--- a/lua/assets/pause.png
+++ b/lua/assets/pause.png
Binary files differ
diff --git a/lua/assets/play.png b/lua/assets/play.png
index 0d0bb34d..81927a8a 100644
--- a/lua/assets/play.png
+++ b/lua/assets/play.png
Binary files differ
diff --git a/lua/browser.lua b/lua/browser.lua
new file mode 100644
index 00000000..380922a8
--- /dev/null
+++ b/lua/browser.lua
@@ -0,0 +1,113 @@
+local lvgl = require("lvgl")
+local widgets = require("widgets")
+local legacy_ui = require("legacy_ui")
+local database = require("database")
+local backstack = require("backstack")
+
+local browser = {}
+
+function browser.create(opts)
+ local screen = {}
+ screen.root = lvgl.Object(nil, {
+ flex = {
+ flex_direction = "column",
+ flex_wrap = "wrap",
+ justify_content = "flex-start",
+ align_items = "flex-start",
+ align_content = "flex-start",
+ },
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ })
+ screen.root:center()
+
+ screen.status_bar = widgets.StatusBar(screen.root, {
+ back_cb = backstack.pop,
+ title = opts.title,
+ })
+
+ if opts.breadcrumb then
+ local header = screen.root:Object {
+ flex = {
+ flex_direction = "column",
+ flex_wrap = "wrap",
+ justify_content = "flex-start",
+ align_items = "flex-start",
+ align_content = "flex-start",
+ },
+ w = lvgl.HOR_RES(),
+ h = lvgl.SIZE_CONTENT,
+ pad_left = 4,
+ pad_right = 4,
+ pad_top = 2,
+ pad_bottom = 2,
+ bg_opa = lvgl.OPA(100),
+ bg_color = "#f3e5f5",
+ scrollbar_mode = lvgl.SCROLLBAR_MODE.OFF,
+ }
+
+ header:Label { text = opts.breadcrumb }
+
+ local buttons = header:Object({
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap",
+ justify_content = "flex-end",
+ align_items = "center",
+ align_content = "center",
+ },
+ w = lvgl.PCT(100),
+ h = lvgl.SIZE_CONTENT,
+ pad_column = 4,
+ })
+ local enqueue = buttons:Button {}
+ enqueue:Label { text = "Enqueue" }
+ enqueue:add_flag(lvgl.FLAG.HIDDEN)
+ local play = buttons:Button {}
+ play:Label { text = "Play all" }
+ end
+
+ screen.list = lvgl.List(screen.root, {
+ w = lvgl.PCT(100),
+ h = lvgl.PCT(100),
+ flex_grow = 1,
+ scrollbar_mode = lvgl.SCROLLBAR_MODE.OFF,
+ })
+
+ screen.focused_item = 0
+ screen.last_item = 0
+ screen.add_item = function(item)
+ if not item then return end
+ screen.last_item = screen.last_item + 1
+ local this_item = screen.last_item
+ local btn = screen.list:add_btn(nil, tostring(item))
+ btn:onClicked(function()
+ local contents = item:contents()
+ if type(contents) == "function" then
+ backstack.push(function()
+ return browser.create({
+ title = opts.title,
+ iterator = contents,
+ breadcrumb = tostring(item),
+ })
+ end)
+ else
+ print("selected track", contents)
+ end
+ end)
+ btn:onevent(lvgl.EVENT.FOCUSED, function()
+ screen.focused_item = this_item
+ if screen.last_item - 5 < this_item then
+ opts.iterator(screen.add_item)
+ end
+ end)
+ end
+
+ for _ = 1, 8 do
+ opts.iterator(screen.add_item)
+ end
+
+ return screen
+end
+
+return browser.create
diff --git a/lua/main_menu.lua b/lua/main_menu.lua
index e38ed2c1..7c236a23 100644
--- a/lua/main_menu.lua
+++ b/lua/main_menu.lua
@@ -2,6 +2,8 @@ local lvgl = require("lvgl")
local widgets = require("widgets")
local legacy_ui = require("legacy_ui")
local database = require("database")
+local backstack = require("backstack")
+local browser = require("browser")
return function()
local menu = {}
@@ -35,6 +37,12 @@ return function()
local btn = menu.list:add_btn(nil, tostring(idx))
btn:onClicked(function()
legacy_ui.open_browse(id);
+ -- backstack.push(function()
+ -- return browser {
+ -- title = tostring(idx),
+ -- iterator = idx:iter()
+ -- }
+ -- end)
end)
end
diff --git a/lua/widgets.lua b/lua/widgets.lua
index 9807bc09..76f7c839 100644
--- a/lua/widgets.lua
+++ b/lua/widgets.lua
@@ -17,23 +17,20 @@ function widgets.StatusBar(parent, opts)
},
w = lvgl.HOR_RES(),
h = lvgl.SIZE_CONTENT,
- bg_opa = lvgl.OPA(100),
- bg_color = "#fff",
pad_top = 1,
pad_bottom = 1,
pad_column = 1,
- shadow_width = 6,
- shadow_opa = lvgl.OPA(50),
- shaddow_ofs_x = 0,
+ bg_opa = lvgl.OPA(100),
+ bg_color = "#e1bee7",
scrollbar_mode = lvgl.SCROLLBAR_MODE.OFF,
}
if opts.back_cb then
- status_bar.back = status_bar.root:Label {
+ status_bar.back = status_bar.root:Button {
w = lvgl.SIZE_CONTENT,
h = 12,
- text = "<",
}
+ status_bar.back:Label({ text = "<", align = lvgl.ALIGN.CENTER })
status_bar.back:onClicked(opts.back_cb)
end
@@ -44,7 +41,7 @@ function widgets.StatusBar(parent, opts)
flex_grow = 1,
}
if opts.title then
- status_bar.title.set { text = opts.title }
+ status_bar.title:set { text = opts.title }
end
status_bar.playing = status_bar.root:Image {}
diff --git a/src/database/database.cpp b/src/database/database.cpp
index 88ae7bbe..dad983d0 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -846,8 +846,7 @@ auto IndexRecord::Expand(std::size_t page_size) const
if (track_) {
return {};
}
- IndexKey::Header new_header = ExpandHeader(key_.header, key_.item);
- std::string new_prefix = EncodeIndexPrefix(new_header);
+ std::string new_prefix = EncodeIndexPrefix(ExpandHeader());
return Continuation{
.prefix = {new_prefix.data(), new_prefix.size()},
.start_key = {new_prefix.data(), new_prefix.size()},
@@ -857,6 +856,10 @@ auto IndexRecord::Expand(std::size_t page_size) const
};
}
+auto IndexRecord::ExpandHeader() const -> IndexKey::Header {
+ return ::database::ExpandHeader(key_.header, key_.item);
+}
+
Iterator::Iterator(std::weak_ptr<Database> db, const IndexInfo& idx)
: db_(db), pos_mutex_(), current_pos_(), prev_pos_() {
std::string prefix = EncodeIndexPrefix(
@@ -887,6 +890,11 @@ auto Iterator::Next(Callback cb) -> void {
db->dbGetPage<IndexRecord>(*current_pos_)};
prev_pos_ = current_pos_;
current_pos_ = res->next_page();
+ if (!res || res->values().empty() || !res->values()[0]) {
+ ESP_LOGI(kTag, "dropping empty result");
+ InvokeNull(cb);
+ return;
+ }
std::invoke(cb, *res->values()[0]);
});
}
diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp
index 63014bed..e18701eb 100644
--- a/src/database/include/database.hpp
+++ b/src/database/include/database.hpp
@@ -80,6 +80,7 @@ class IndexRecord {
auto track() const -> std::optional<TrackId>;
auto Expand(std::size_t) const -> std::optional<Continuation>;
+ auto ExpandHeader() const -> IndexKey::Header;
private:
IndexKey key_;
diff --git a/src/lua/lua_database.cpp b/src/lua/lua_database.cpp
index d8ae86f6..79916115 100644
--- a/src/lua/lua_database.cpp
+++ b/src/lua/lua_database.cpp
@@ -20,7 +20,9 @@
#include "event_queue.hpp"
#include "index.hpp"
#include "property.hpp"
+#include "records.hpp"
#include "service_locator.hpp"
+#include "track.hpp"
#include "ui_events.hpp"
namespace lua {
@@ -55,6 +57,42 @@ static auto indexes(lua_State* state) -> int {
static const struct luaL_Reg kDatabaseFuncs[] = {{"indexes", indexes},
{NULL, NULL}};
+/*
+ * Struct to be used as userdata for the Lua representation of database records.
+ * In order to push these large values into PSRAM as much as possible, memory
+ * for these is allocated and managed by Lua. This struct must therefore be
+ * trivially copyable.
+ */
+struct LuaRecord {
+ database::TrackId id_or_zero;
+ database::IndexKey::Header header_at_next_depth;
+ size_t text_size;
+ char text[];
+};
+
+static_assert(std::is_trivially_copyable_v<LuaRecord> == true);
+
+static auto push_lua_record(lua_State* L, const database::IndexRecord& r)
+ -> void {
+ // Bake out the text into something concrete.
+ auto text = r.text().value_or("");
+
+ // Create and init the userdata.
+ LuaRecord* record = reinterpret_cast<LuaRecord*>(
+ lua_newuserdata(L, sizeof(LuaRecord) + text.size()));
+ luaL_setmetatable(L, kDbRecordMetatable);
+
+ // Init all the fields
+ *record = {
+ .id_or_zero = r.track().value_or(0),
+ .header_at_next_depth = r.ExpandHeader(),
+ .text_size = text.size(),
+ };
+
+ // Copy the string data across.
+ std::memcpy(record->text, text.data(), text.size());
+}
+
static auto db_iterate(lua_State* state) -> int {
luaL_checktype(state, 1, LUA_TFUNCTION);
int callback_ref = luaL_ref(state, LUA_REGISTRYINDEX);
@@ -66,11 +104,7 @@ static auto db_iterate(lua_State* state) -> int {
events::Ui().RunOnTask([=]() {
lua_rawgeti(state, LUA_REGISTRYINDEX, callback_ref);
if (res) {
- database::IndexRecord** record =
- reinterpret_cast<database::IndexRecord**>(
- lua_newuserdata(state, sizeof(uintptr_t)));
- *record = new database::IndexRecord(*res);
- luaL_setmetatable(state, kDbRecordMetatable);
+ push_lua_record(state, *res);
} else {
lua_pushnil(state);
}
@@ -105,40 +139,37 @@ static auto push_iterator(
lua_pushcclosure(state, db_iterate, 1);
}
+
static auto record_text(lua_State* state) -> int {
- database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>(
+ LuaRecord* data = reinterpret_cast<LuaRecord*>(
luaL_checkudata(state, 1, kDbRecordMetatable));
- lua_pushstring(state,
- data->text().value_or("[tell jacqueline u saw this]").c_str());
+ lua_pushlstring(state, data->text, data->text_size);
return 1;
}
static auto record_contents(lua_State* state) -> int {
- database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>(
+ LuaRecord* data = reinterpret_cast<LuaRecord*>(
luaL_checkudata(state, 1, kDbRecordMetatable));
- if (data->track()) {
- lua_pushinteger(state, *data->track());
+ if (data->id_or_zero) {
+ lua_pushinteger(state, data->id_or_zero);
} else {
- push_iterator(state, data->Expand(1).value());
+ std::string p = database::EncodeIndexPrefix(data->header_at_next_depth);
+ push_iterator(state, database::Continuation{
+ .prefix = {p.data(), p.size()},
+ .start_key = {p.data(), p.size()},
+ .forward = true,
+ .was_prev_forward = true,
+ .page_size = 1,
+ });
}
return 1;
}
-static auto record_gc(lua_State* state) -> int {
- database::IndexRecord** data = reinterpret_cast<database::IndexRecord**>(
- luaL_checkudata(state, 1, kDbRecordMetatable));
- if (data != NULL) {
- delete *data;
- }
- return 0;
-}
-
static const struct luaL_Reg kDbRecordFuncs[] = {{"title", record_text},
{"contents", record_contents},
{"__tostring", record_text},
- {"__gc", record_gc},
{NULL, NULL}};
static auto index_name(lua_State* state) -> int {
@@ -207,4 +238,4 @@ auto RegisterDatabaseModule(lua_State* s) -> void {
lua_pop(s, 1);
}
-} // namespace lua \ No newline at end of file
+} // namespace lua
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index d5de53f0..ed0624df 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -219,6 +219,7 @@ void Lua::entry() {
{"pop", [&](lua_State* s) { return PopLuaScreen(s); }},
});
+ sCurrentScreen.reset();
sLua->RunScript("/lua/main.lua");
}
}
@@ -243,12 +244,6 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
// Store the reference for the table the constructor returned.
new_screen->SetObjRef(s);
- // Ensure that we don't pollute the new screen's group. We leave the luavgl
- // root alone.
- // FIXME: maybe we should set the luavgl root to some catch-all that throws
- // when anything is added to it? this may help catch bugs!
- lv_group_set_default(NULL);
-
// Finally, push the now-initialised screen as if it were a regular C++
// screen.
PushScreen(new_screen);
@@ -256,12 +251,16 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
return 0;
}
-auto Lua::PopLuaScreen(lua_State*) -> int {
+auto Lua::PopLuaScreen(lua_State* s) -> int {
PopScreen();
+ luavgl_set_root(s, sCurrentScreen->root());
+ lv_group_set_default(sCurrentScreen->group());
return 0;
}
-void Lua::exit() {}
+void Lua::exit() {
+ lv_group_set_default(NULL);
+}
void Lua::react(const internal::IndexSelected& ev) {
auto db = sServices->database().lock();