From d719f9c5017ad8006c21b6d546a5d70e846e9502 Mon Sep 17 00:00:00 2001 From: ailurux Date: Mon, 12 Aug 2024 03:19:03 +0000 Subject: daniel/theme-setting (#87) - Themes can be loaded from disk and built-in - Themes can be selected in a new themes menu of the settings screen - Some touch-ups to existing themes - The saved theme is persisted in nvs Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/87 Reviewed-by: cooljqln Co-authored-by: ailurux Co-committed-by: ailurux --- lua/img/next.png | Bin 4811 -> 7828 bytes lua/img/pause.png | Bin 4771 -> 5963 bytes lua/img/play.png | Bin 4813 -> 6828 bytes lua/img/prev.png | Bin 4810 -> 7839 bytes lua/main.lua | 12 ++++++-- lua/main_menu.lua | 7 +++-- lua/playing.lua | 23 +++++++++------- lua/settings.lua | 61 ++++++++++++++++++++++++++++++++++++++++ lua/theme_dark.lua | 29 +++++++++++++++---- lua/theme_light.lua | 78 +++++++++++++++++++++++++++++++++++++++++++--------- 10 files changed, 175 insertions(+), 35 deletions(-) (limited to 'lua') diff --git a/lua/img/next.png b/lua/img/next.png index 1f6f044b..929c5f36 100644 Binary files a/lua/img/next.png and b/lua/img/next.png differ diff --git a/lua/img/pause.png b/lua/img/pause.png index e7011821..32c5a2b4 100644 Binary files a/lua/img/pause.png and b/lua/img/pause.png differ diff --git a/lua/img/play.png b/lua/img/play.png index a3b8a5af..833cb087 100644 Binary files a/lua/img/play.png and b/lua/img/play.png differ diff --git a/lua/img/prev.png b/lua/img/prev.png index b445c75a..714c7c20 100644 Binary files a/lua/img/prev.png and b/lua/img/prev.png differ diff --git a/lua/main.lua b/lua/main.lua index 4cd754b6..9dd88c84 100644 --- a/lua/main.lua +++ b/lua/main.lua @@ -14,9 +14,15 @@ local backstack = require("backstack") local main_menu = require("main_menu") local function init_ui() - -- Load the theme within init_ui because the theme needs fonts to be ready. - local theme_dark = require("theme_dark") - theme.set(theme_dark) + -- Theme is set within init_ui because the theme needs fonts to be ready. + -- Set the theme to the saved theme if available + local saved_theme = theme.theme_filename() + local res = theme.load_theme(saved_theme) + if not res then + -- Set a default theme (in case the saved theme does not load) + local default_theme = require("theme_light") + theme.set(default_theme) + end local lock_time = time.ticks() diff --git a/lua/main_menu.lua b/lua/main_menu.lua index f3b7a042..54f382a1 100644 --- a/lua/main_menu.lua +++ b/lua/main_menu.lua @@ -33,9 +33,9 @@ return widgets.MenuScreen:new { margin_all = 2, pad_bottom = 2, pad_column = 4, - border_color = "#FFFFFF", border_width = 1, }) + theme.set_style(now_playing, "now_playing"); local play_pause = now_playing:Image { src = img.play_small } local title = now_playing:Label { @@ -140,6 +140,7 @@ return widgets.MenuScreen:new { w = lvgl.PCT(100), h = lvgl.SIZE_CONTENT, pad_top = 4, + pad_bottom = 2, }) -- local queue_btn = bottom_bar:Button {} @@ -154,13 +155,13 @@ return widgets.MenuScreen:new { }) end) files_btn:Image { src = img.files } - theme.set_style(files_btn, "icon_enabled") + theme.set_style(files_btn, "menu_icon") local settings_btn = bottom_bar:Button {} settings_btn:onClicked(function() backstack.push(require("settings"):new()) end) settings_btn:Image { src = img.settings } - theme.set_style(settings_btn, "icon_enabled") + theme.set_style(settings_btn, "menu_icon") end, } diff --git a/lua/playing.lua b/lua/playing.lua index 90e20f49..97997366 100644 --- a/lua/playing.lua +++ b/lua/playing.lua @@ -74,7 +74,7 @@ return screen:new { align_items = "center", align_content = "center", }, - w = lvgl.PCT(100), + w = lvgl.PCT(95), h = lvgl.SIZE_CONTENT, } @@ -114,11 +114,12 @@ return screen:new { playlist:Object({ w = 3, h = 1 }) -- spacer local scrubber = self.root:Slider { - w = lvgl.PCT(100), + w = lvgl.PCT(90), h = 5, range = { min = 0, max = 100 }, value = 0, } + theme.set_style(scrubber, "scrubber"); local scrubber_desc = widgets.Description(scrubber, "Scrubber") scrubber:onevent(lvgl.EVENT.RELEASED, function() @@ -138,6 +139,8 @@ return screen:new { end end) + self.root:Object({ w = 1, h = 1 }) -- spacer + local controls = self.root:Object { flex = { flex_direction = "row", @@ -158,7 +161,7 @@ return screen:new { queue.repeat_track:set(not queue.repeat_track:get()) end) local repeat_img = repeat_btn:Image { src = img.repeat_src } - theme.set_style(repeat_img, icon_enabled_class) + theme.set_style(repeat_btn, icon_enabled_class) local repeat_desc = widgets.Description(repeat_btn) @@ -171,7 +174,7 @@ return screen:new { end end) local prev_img = prev_btn:Image { src = img.prev } - theme.set_style(prev_img, icon_enabled_class) + theme.set_style(prev_btn, icon_enabled_class) local prev_desc = widgets.Description(prev_btn, "Previous track") local play_pause_btn = controls:Button {} @@ -180,13 +183,13 @@ return screen:new { end) play_pause_btn:focus() local play_pause_img = play_pause_btn:Image { src = img.pause } - theme.set_style(play_pause_img, icon_enabled_class) + theme.set_style(play_pause_btn, icon_enabled_class) local play_pause_desc = widgets.Description(play_pause_btn, "Play") local next_btn = controls:Button {} next_btn:onClicked(queue.next) local next_img = next_btn:Image { src = img.next } - theme.set_style(next_img, icon_disabled_class) + theme.set_style(next_btn, icon_disabled_class) local next_desc = widgets.Description(next_btn, "Next track") local shuffle_btn = controls:Button {} @@ -194,7 +197,7 @@ return screen:new { queue.random:set(not queue.random:get()) end) local shuffle_img = shuffle_btn:Image { src = img.shuffle } - theme.set_style(shuffle_img, icon_enabled_class) + theme.set_style(shuffle_btn, icon_enabled_class) local shuffle_desc = widgets.Description(shuffle_btn) controls:Object({ flex_grow = 1, h = 1 }) -- spacer @@ -238,11 +241,11 @@ return screen:new { local can_next = pos < queue.size:get() or queue.random:get() theme.set_style( - next_img, can_next and icon_enabled_class or icon_disabled_class + next_btn, can_next and icon_enabled_class or icon_disabled_class ) end), queue.random:bind(function(shuffling) - theme.set_style(shuffle_img, shuffling and icon_enabled_class or icon_disabled_class) + theme.set_style(shuffle_btn, shuffling and icon_enabled_class or icon_disabled_class) if shuffling then shuffle_desc:set { text = "Disable shuffle" } else @@ -250,7 +253,7 @@ return screen:new { end end), queue.repeat_track:bind(function(en) - theme.set_style(repeat_img, en and icon_enabled_class or icon_disabled_class) + theme.set_style(repeat_btn, en and icon_enabled_class or icon_disabled_class) if en then repeat_desc:set { text = "Disable track repeat" } else diff --git a/lua/settings.lua b/lua/settings.lua index 79572ee9..3033b36e 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -7,9 +7,11 @@ local display = require("display") local controls = require("controls") local bluetooth = require("bluetooth") local theme = require("theme") +local filesystem = require("filesystem") local database = require("database") local usb = require("usb") local font = require("font") +local main_menu = require("main_menu") local SettingsScreen = widgets.MenuScreen:new { show_back = true, @@ -301,6 +303,64 @@ local DisplaySettings = SettingsScreen:new { end } +local ThemeSettings = SettingsScreen:new { + title = "Theme", + createUi = function(self) + SettingsScreen.createUi(self) + + theme.set_style(self.content:Label { + text = "Theme", + }, "settings_title") + + local themeOptions = {} + themeOptions["Dark"] = "/lua/theme_dark.lua" + themeOptions["Light"] = "/lua/theme_light.lua" + + -- Parse theme directory for more themes + local theme_dir_iter = filesystem.iterator("/.themes/") + for dir in theme_dir_iter do + local theme_name = tostring(dir):match("(.+).lua$") + themeOptions[theme_name] = "/sdcard/.themes/" .. theme_name .. ".lua" + end + + local saved_theme = theme.theme_filename(); + local saved_theme_name = saved_theme:match(".+/(.*).lua$") + + local options = "" + local idx = 0 + local selected_idx = -1 + for i, v in pairs(themeOptions) do + if (saved_theme == v) then + selected_idx = idx + end + if idx > 0 then + options = options .. "\n" + end + options = options .. i + idx = idx + 1 + end + + if (selected_idx == -1) then + options = options .. "\n" .. saved_theme_name + selected_idx = idx + end + + local theme_chooser = self.content:Dropdown { + options = options, + } + theme_chooser:set({selected = selected_idx}) + + theme_chooser:onevent(lvgl.EVENT.VALUE_CHANGED, function() + local option = theme_chooser:get('selected_str') + local selectedTheme = themeOptions[option] + if (selectedTheme) then + theme.load_theme(tostring(selectedTheme)) + backstack.reset(main_menu:new()) + end + end) + end +} + local InputSettings = SettingsScreen:new { title = "Input Method", createUi = function(self) @@ -742,6 +802,7 @@ return widgets.MenuScreen:new { section("Interface") submenu("Display", DisplaySettings) + submenu("Theme", ThemeSettings) submenu("Input Method", InputSettings) section("USB") diff --git a/lua/theme_dark.lua b/lua/theme_dark.lua index 6508f642..b9bcece2 100644 --- a/lua/theme_dark.lua +++ b/lua/theme_dark.lua @@ -95,24 +95,27 @@ local theme_dark = { switch = { {lvgl.PART.MAIN, lvgl.Style { bg_opa = lvgl.OPA(100), - width = 28, - height = 8, + width = 18, + height = 10, radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff bg_color = background_muted, - border_color = highlight_color, + border_color = text_color, + border_width = 1, }}, {lvgl.PART.INDICATOR, lvgl.Style { radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - bg_color = background_muted, + bg_color = background_color, }}, {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { bg_color = highlight_color, + bg_opa = lvgl.OPA(100), }}, {lvgl.PART.KNOB, lvgl.Style { radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - pad_all = 2, bg_opa = lvgl.OPA(100), bg_color = background_muted, + border_color = text_color, + border_width = 1, }}, {lvgl.PART.KNOB | lvgl.STATE.FOCUSED, lvgl.Style { bg_color = highlight_color, @@ -170,7 +173,21 @@ local theme_dark = { image_recolor = icon_enabled_color, }}, }, - + now_playing = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + border_width = 1, + border_color = highlight_color, + border_side = 15, -- LV_BORDER_SIDE_FULL + }}, + }, + menu_icon = { + {lvgl.PART.MAIN, lvgl.Style { + pad_all = 4, + radius = 4 + }}, + }, } return theme_dark diff --git a/lua/theme_light.lua b/lua/theme_light.lua index 05b7d291..96403de3 100644 --- a/lua/theme_light.lua +++ b/lua/theme_light.lua @@ -2,10 +2,10 @@ local lvgl = require("lvgl") local font = require("font") local background_color = "#ffffff" -local background_muted = "#fafafa" -local text_color = "#000000" -local highlight_color = "#ce93d8" -local icon_enabled_color = "#2c2c2c" +local background_muted = "#f2f2f2" +local text_color = "#2c2c2c" +local highlight_color = "#ff82bc" +local icon_enabled_color = "#ff82bc" local icon_disabled_color = "#999999" local theme_light = { @@ -47,6 +47,7 @@ local theme_light = { }}, {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { bg_opa = lvgl.OPA(100), + text_color = "#ffffff", bg_color = highlight_color, image_recolor_opa = 0, }}, @@ -55,6 +56,7 @@ local theme_light = { {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { bg_opa = lvgl.OPA(100), bg_color = highlight_color, + text_color = "#ffffff" }}, }, bar = { @@ -75,16 +77,41 @@ local theme_light = { }}, {lvgl.PART.KNOB, lvgl.Style { radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - pad_all = 2, bg_color = background_muted, shadow_width = 5, - shadow_opa = lvgl.OPA(100) + shadow_opa = lvgl.OPA(100), + pad_all = 2, + }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = background_muted, + }}, + {lvgl.PART.KNOB | lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = highlight_color, + }}, + {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { + bg_color = highlight_color, + }}, + }, + scrubber = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = background_muted, + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + }}, + {lvgl.PART.INDICATOR, lvgl.Style { + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + bg_color = highlight_color, + }}, + {lvgl.PART.KNOB, lvgl.Style { + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + bg_color = background_muted, }}, {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { bg_color = background_muted, }}, {lvgl.PART.KNOB | lvgl.STATE.FOCUSED, lvgl.Style { bg_color = highlight_color, + pad_all = 1, }}, {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { bg_color = highlight_color, @@ -93,24 +120,27 @@ local theme_light = { switch = { {lvgl.PART.MAIN, lvgl.Style { bg_opa = lvgl.OPA(100), - width = 28, - height = 8, + width = 18, + height = 10, radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff bg_color = background_muted, - border_color = highlight_color, + border_color = text_color, + border_width = 1, }}, {lvgl.PART.INDICATOR, lvgl.Style { radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - bg_color = background_muted, + bg_color = background_color, }}, {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { + bg_opa = lvgl.OPA(100), bg_color = highlight_color, }}, {lvgl.PART.KNOB, lvgl.Style { radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - pad_all = 2, bg_opa = lvgl.OPA(100), - bg_color = background_muted, + bg_color = background_color, + border_color = text_color, + border_width = 1, }}, {lvgl.PART.KNOB | lvgl.STATE.FOCUSED, lvgl.Style { bg_color = highlight_color, @@ -161,14 +191,36 @@ local theme_light = { image_recolor_opa = 180, image_recolor = icon_disabled_color, }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + image_recolor_opa = 0, + image_recolor = "#ffffff", + }}, }, icon_enabled = { {lvgl.PART.MAIN, lvgl.Style { image_recolor_opa = 180, image_recolor = icon_enabled_color, }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + image_recolor_opa = 0, + image_recolor = "#ffffff", + }}, + }, + now_playing = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + border_width = 1, + border_color = highlight_color, + border_side = 15, -- LV_BORDER_SIDE_FULL + }}, + }, + menu_icon = { + {lvgl.PART.MAIN, lvgl.Style { + pad_all = 4, + radius = 4 + }}, }, - } return theme_light -- cgit v1.2.3