diff --git a/lib/luavgl/src/style.c b/lib/luavgl/src/style.c index 95c9f363..5bfba2bb 100644 --- a/lib/luavgl/src/style.c +++ b/lib/luavgl/src/style.c @@ -18,6 +18,7 @@ typedef enum { enum { LV_STYLE_SIZE = _LV_STYLE_LAST_BUILT_IN_PROP + 1, + LV_STYLE_MARGIN_ALL, LV_STYLE_PAD_ALL, LV_STYLE_PAD_VER, LV_STYLE_PAD_HOR, @@ -61,6 +62,10 @@ static const struct style_map_s { {"transform_pivot_x", LV_STYLE_TRANSFORM_PIVOT_X, STYLE_TYPE_INT }, {"transform_pivot_y", LV_STYLE_TRANSFORM_PIVOT_Y, STYLE_TYPE_INT }, #endif + {"margin_top", LV_STYLE_MARGIN_TOP, STYLE_TYPE_INT }, + {"margin_bottom", LV_STYLE_MARGIN_BOTTOM, STYLE_TYPE_INT }, + {"margin_left", LV_STYLE_MARGIN_LEFT, STYLE_TYPE_INT }, + {"margin_right", LV_STYLE_MARGIN_RIGHT, STYLE_TYPE_INT }, {"pad_top", LV_STYLE_PAD_TOP, STYLE_TYPE_INT }, {"pad_bottom", LV_STYLE_PAD_BOTTOM, STYLE_TYPE_INT }, {"pad_left", LV_STYLE_PAD_LEFT, STYLE_TYPE_INT }, @@ -134,6 +139,7 @@ static const struct style_map_s { /* styles combined */ {"size", LV_STYLE_SIZE, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT }, + {"margin_all", LV_STYLE_MARGIN_ALL, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT }, {"pad_all", LV_STYLE_PAD_ALL, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT }, {"pad_ver", LV_STYLE_PAD_VER, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT }, {"pad_hor", LV_STYLE_PAD_HOR, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT }, @@ -417,6 +423,13 @@ static int luavgl_set_style_kv(lua_State *L, style_set_cb_t cb, void *args) cb(LV_STYLE_HEIGHT, value, args); break; + case LV_STYLE_MARGIN_ALL: + cb(LV_STYLE_MARGIN_TOP, value, args); + cb(LV_STYLE_MARGIN_BOTTOM, value, args); + cb(LV_STYLE_MARGIN_LEFT, value, args); + cb(LV_STYLE_MARGIN_RIGHT, value, args); + break; + case LV_STYLE_PAD_ALL: cb(LV_STYLE_PAD_TOP, value, args); cb(LV_STYLE_PAD_BOTTOM, value, args); diff --git a/lua/images.lua b/lua/images.lua new file mode 100644 index 00000000..84c957e8 --- /dev/null +++ b/lua/images.lua @@ -0,0 +1,13 @@ +local lvgl = require("lvgl") + +return { + play = lvgl.ImgData("//lua/img/play.png"), + pause = lvgl.ImgData("//lua/img/pause.png"), + next = lvgl.ImgData("//lua/img/next.png"), + prev = lvgl.ImgData("//lua/img/prev.png"), + shuffle = lvgl.ImgData("//lua/img/shuffle.png"), + repeat_src = lvgl.ImgData("//lua/img/repeat.png"), -- repeat is a reserved word + queue = lvgl.ImgData("//lua/img/queue.png"), + files = lvgl.ImgData("//lua/img/files.png"), + settings = lvgl.ImgData("//lua/img/settings.png"), +} diff --git a/lua/img/files.png b/lua/img/files.png new file mode 100644 index 00000000..dd33559c Binary files /dev/null and b/lua/img/files.png differ diff --git a/lua/img/queue.png b/lua/img/queue.png new file mode 100644 index 00000000..57739772 Binary files /dev/null and b/lua/img/queue.png differ diff --git a/lua/img/settings.png b/lua/img/settings.png new file mode 100644 index 00000000..4fc29e51 Binary files /dev/null and b/lua/img/settings.png differ diff --git a/lua/main_menu.lua b/lua/main_menu.lua index 9c52340b..da90fd44 100644 --- a/lua/main_menu.lua +++ b/lua/main_menu.lua @@ -7,23 +7,94 @@ local playing = require("playing") local styles = require("styles") local filesystem = require("filesystem") local screen = require("screen") +local font = require("font") +local theme = require("theme") +local img = require("images") +local playback = require("playback") return widgets.MenuScreen:new { createUi = function(self) widgets.MenuScreen.createUi(self) + -- At the top, a card showing details about the current track. Hidden if + -- there is no track currently playing. + -- || Cool Song 0:01 + + local now_playing = lvgl.Button(self.root, { + flex = { + flex_direction = "row", + flex_wrap = "nowrap", + justify_content = "flex-start", + align_items = "center", + align_content = "flex-start", + }, + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, + margin_all = 2, + pad_column = 4, + border_color = "#FFFFFF", + border_width = 1, + }) + + local play_pause = now_playing:Image { src = img.play } + local title = now_playing:Label { + flex_grow = 1, + h = lvgl.SIZE_CONTENT, + text = " ", + long_mode = 1, + } + local time_remaining = now_playing:Label { + text = " ", + text_font = font.fusion_10, + } + + now_playing:onClicked(function() backstack.push(playing:new()) end) + + local has_focus = false + local track_duration = nil + + self.bindings = self.bindings + { + playback.playing:bind(function(playing) + if playing then + play_pause:set_src(img.play) + else + play_pause:set_src(img.pause) + end + end), + playback.track:bind(function(track) + if not track then + now_playing:add_flag(lvgl.FLAG.HIDDEN) + return + else + has_focus = true + now_playing:clear_flag(lvgl.FLAG.HIDDEN) + end + title:set { text = track.title } + if track.duration then + track_duration = track.duration + end + end), + playback.position:bind(function(pos) + if not pos then return end + if not track_duration then return end + local remaining = track_duration - pos + if remaining < 0 then remaining = 0 end + time_remaining:set { + text = string.format("%d:%02d", remaining // 60, remaining % 60) + } + end), + } + + -- Next, a list showing the user's prefer's music source. This defaults to + -- a list of all available database indexes, but could also be the contents + -- of the SD card root. + local list = lvgl.List(self.root, { w = lvgl.PCT(100), h = lvgl.PCT(100), flex_grow = 1, }) - local now_playing = list:add_btn(nil, "Now Playing") - now_playing:onClicked(function() - backstack.push(playing:new()) - end) - now_playing:add_style(styles.list_item) - local indexes = database.indexes() for _, idx in ipairs(indexes) do local btn = list:add_btn(nil, tostring(idx)) @@ -34,21 +105,46 @@ return widgets.MenuScreen:new { }) end) btn:add_style(styles.list_item) + if not has_focus then + has_focus = true + btn:focus() + end end - local files = list:add_btn(nil, "Files") - files:onClicked(function() + -- Finally, the bottom bar with icon buttons for other device features. + + local bottom_bar = lvgl.Object(self.root, { + flex = { + flex_direction = "row", + flex_wrap = "nowrap", + justify_content = "space-evenly", + align_items = "center", + align_content = "flex-start", + }, + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, + pad_top = 4, + }) + + local queue_btn = bottom_bar:Button {} + queue_btn:Image { src = img.queue } + theme.set_style(queue_btn, "icon_enabled") + + local files_btn = bottom_bar:Button {} + files_btn:onClicked(function() backstack.push(require("file_browser"):new { title = "Files", iterator = filesystem.iterator(""), }) end) - files:add_style(styles.list_item) + files_btn:Image { src = img.files } + theme.set_style(files_btn, "icon_enabled") - local settings = list:add_btn(nil, "Settings") - settings:onClicked(function() + local settings_btn = bottom_bar:Button {} + settings_btn:onClicked(function() backstack.push(require("settings"):new()) end) - settings:add_style(styles.list_item) + settings_btn:Image { src = img.settings } + theme.set_style(settings_btn, "icon_enabled") end, } diff --git a/lua/playing.lua b/lua/playing.lua index 6a9488e2..90e20f49 100644 --- a/lua/playing.lua +++ b/lua/playing.lua @@ -7,14 +7,7 @@ local queue = require("queue") local screen = require("screen") local theme = require("theme") -local img = { - play = lvgl.ImgData("//lua/img/play.png"), - pause = lvgl.ImgData("//lua/img/pause.png"), - next = lvgl.ImgData("//lua/img/next.png"), - prev = lvgl.ImgData("//lua/img/prev.png"), - shuffle = lvgl.ImgData("//lua/img/shuffle.png"), - repeat_src = lvgl.ImgData("//lua/img/repeat.png"), -- repeat is a reserved word -} +local img = require("images") local format_time = function(time) time = math.floor(time)