From 1133d4621508b7ec6bac4ab8731f3493066ceeee Mon Sep 17 00:00:00 2001 From: ailurux Date: Sun, 10 Mar 2024 13:20:17 +1100 Subject: [PATCH] WIP Lua Theming- style classes --- lua/browser.lua | 4 +- lua/main.lua | 93 +--------------------------- lua/theme_dark.lua | 127 ++++++++++++++++++++++++++++++++++++++ lua/theme_light.lua | 106 +++++++++++++++++++++++++++++++ lua/widgets.lua | 6 +- src/lua/lua_theme.cpp | 25 ++++---- src/ui/include/themes.hpp | 2 +- src/ui/modal.cpp | 2 - src/ui/screen_lua.cpp | 5 +- src/ui/themes.cpp | 64 ++----------------- 10 files changed, 264 insertions(+), 170 deletions(-) create mode 100644 lua/theme_dark.lua create mode 100644 lua/theme_light.lua diff --git a/lua/browser.lua b/lua/browser.lua index e174a05d..055c8641 100644 --- a/lua/browser.lua +++ b/lua/browser.lua @@ -6,6 +6,7 @@ local queue = require("queue") local playing = require("playing") local styles = require("styles") local playback = require("playback") +local theme = require("theme") local browser = {} @@ -43,9 +44,10 @@ function browser.create(opts) pad_right = 4, pad_bottom = 2, bg_opa = lvgl.OPA(100), - bg_color = "#fafafa", scrollbar_mode = lvgl.SCROLLBAR_MODE.OFF, } + theme.set_style(header, "header") + header:Label { text = opts.breadcrumb, diff --git a/lua/main.lua b/lua/main.lua index 6cdef17d..f2533387 100644 --- a/lua/main.lua +++ b/lua/main.lua @@ -35,97 +35,8 @@ GLOBAL_BINDINGS = { end), } -local lvgl = require("lvgl") -local my_theme = { - base = { - {lvgl.PART.MAIN, lvgl.Style { - bg_opa = lvgl.OPA(0), - text_font = font.fusion_12, - text_color = "#000000", - }}, - {lvgl.STATE.FOCUSED, lvgl.Style { - bg_opa = lvgl.OPA(100), - bg_color = "#E3F2FD", - }}, - }, - button = { - {lvgl.PART.MAIN, lvgl.Style { - pad_left = 2, - pad_right = 2, - pad_top = 1, - pad_bottom = 1, - bg_color = "#ffffff", - radius = 5, - }}, - }, - bar = { - {lvgl.PART.MAIN, lvgl.Style { - bg_opa = lvgl.OPA(100), - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - }}, - }, - slider = { - {lvgl.PART.MAIN, lvgl.Style { - bg_opa = lvgl.OPA(100), - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - }}, - {lvgl.PART.INDICATOR, lvgl.Style { - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - bg_color = "#2196F3", - }}, - {lvgl.PART.KNOB, lvgl.Style { - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - pad_all = 2, - bg_color = "#ffffff", - shadow_width = 5, - shadow_opa = lvgl.OPA(100) - }}, - {lvgl.STATE.FOCUSED, lvgl.Style { - bg_color = "#BBDEFB", - }}, - }, - switch = { - {lvgl.PART.MAIN, lvgl.Style { - bg_opa = lvgl.OPA(100), - width = 28, - height = 18, - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - }}, - {lvgl.PART.INDICATOR, lvgl.Style { - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - bg_color = "#9E9E9E", - }}, - {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { - bg_color = "#2196F3", - }}, - {lvgl.PART.KNOB, lvgl.Style { - radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff - pad_all = 2, - bg_opa = lvgl.OPA(100), - bg_color = "#ffffff", - }}, - }, - dropdown = { - {lvgl.PART.MAIN, lvgl.Style{ - radius = 2, - pad_all = 2, - border_width = 1, - border_color = "#2196F3", - border_side = 15, -- LV_BORDER_SIDE_FULL - }} - }, - dropdownlist = { - {lvgl.PART.MAIN, lvgl.Style{ - radius = 2, - pad_all = 2, - border_width = 1, - border_color = "#607D8B", - bg_opa = lvgl.OPA(100), - bg_color = "#ffffff" - }} - } -} -theme.set(my_theme) +local theme_light = require("theme_dark") +theme.set(theme_light) local backstack = require("backstack") local main_menu = require("main_menu") diff --git a/lua/theme_dark.lua b/lua/theme_dark.lua new file mode 100644 index 00000000..5c91ea1e --- /dev/null +++ b/lua/theme_dark.lua @@ -0,0 +1,127 @@ +local lvgl = require("lvgl") +local font = require("font") + +local background_color = "#242933" +local background_muted = "#353c4b" +local text_color = "#fefefe" +local highlight_color = "#ff0077" + +local theme_dark = { + base = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(0), + text_font = font.fusion_12, + text_color = text_color, + }}, + }, + root = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = background_color, -- Root background color + }}, + }, + header = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = background_muted, + }}, + }, + button = { + {lvgl.PART.MAIN, lvgl.Style { + pad_left = 2, + pad_right = 2, + pad_top = 1, + pad_bottom = 1, + bg_color = background_color, + radius = 5, + }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = highlight_color, + }}, + }, + listbutton = { + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = highlight_color, + }}, + }, + bar = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + }}, + }, + slider = { + {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 + pad_all = 2, + bg_color = background_color, + shadow_width = 5, + shadow_opa = lvgl.OPA(100) + }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = background_muted, + }}, + {lvgl.PART.KNOB | lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = highlight_color, + }}, + }, + switch = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + width = 28, + height = 12, + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = background_muted, + }}, + {lvgl.PART.INDICATOR, lvgl.Style { + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + bg_color = background_muted, + }}, + {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { + 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 = text_color, + }}, + }, + dropdown = { + {lvgl.PART.MAIN, lvgl.Style{ + radius = 2, + pad_all = 2, + border_width = 1, + border_color = text_color, + border_side = 15, -- LV_BORDER_SIDE_FULL + }}, + {lvgl.PART.MAIN | lvgl.STATE.FOCUSED, lvgl.Style { + border_color = highlight_color, + }}, + }, + dropdownlist = { + {lvgl.PART.MAIN, lvgl.Style{ + radius = 2, + pad_all = 2, + border_width = 1, + border_color = text_color, + bg_opa = lvgl.OPA(100), + bg_color = background_color + }}, + } +} + +return theme_dark diff --git a/lua/theme_light.lua b/lua/theme_light.lua new file mode 100644 index 00000000..637861d9 --- /dev/null +++ b/lua/theme_light.lua @@ -0,0 +1,106 @@ +local lvgl = require("lvgl") +local font = require("font") + +local background_color = "#FFFFFF" +local background_muted = "#FFFFFF" +local text_color = "#000000" +local highlight_color = "#E3F2FD" + +local theme_light = { + base = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(0), + bg_color = background_color, -- Root background color + text_font = font.fusion_12, + text_color = text_color, + }}, + {lvgl.STATE.FOCUSED, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = highlight_color, + }}, + }, + root = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + bg_color = background_color, -- Root background color + }}, + }, + button = { + {lvgl.PART.MAIN, lvgl.Style { + pad_left = 2, + pad_right = 2, + pad_top = 1, + pad_bottom = 1, + bg_color = background_color, + radius = 5, + }}, + }, + bar = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + }}, + }, + slider = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + 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 + pad_all = 2, + bg_color = background_color, + shadow_width = 5, + shadow_opa = lvgl.OPA(100) + }}, + {lvgl.STATE.FOCUSED, lvgl.Style { + bg_color = highlight_color, + }}, + }, + switch = { + {lvgl.PART.MAIN, lvgl.Style { + bg_opa = lvgl.OPA(100), + width = 28, + height = 18, + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + }}, + {lvgl.PART.INDICATOR, lvgl.Style { + radius = 32767, -- LV_RADIUS_CIRCLE = 0x7fff + bg_color = background_muted, + }}, + {lvgl.PART.INDICATOR | lvgl.STATE.CHECKED, lvgl.Style { + 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_color, + }}, + }, + dropdown = { + {lvgl.PART.MAIN, lvgl.Style{ + radius = 2, + pad_all = 2, + border_width = 1, + border_color = "#2196F3", + border_side = 15, -- LV_BORDER_SIDE_FULL + }} + }, + dropdownlist = { + {lvgl.PART.MAIN, lvgl.Style{ + radius = 2, + pad_all = 2, + border_width = 1, + border_color = "#607D8B", + bg_opa = lvgl.OPA(100), + bg_color = background_color + }} + } +} + +return theme_light diff --git a/lua/widgets.lua b/lua/widgets.lua index 8253041b..15212ded 100644 --- a/lua/widgets.lua +++ b/lua/widgets.lua @@ -5,6 +5,7 @@ local font = require("font") local backstack = require("backstack") local styles = require("styles") local database = require("database") +local theme = require("theme") local widgets = {} @@ -66,10 +67,7 @@ function widgets.StatusBar(parent, opts) } if not opts.transparent_bg then - status_bar.root:set { - bg_opa = lvgl.OPA(100), - bg_color = "#fafafa", - } + theme.set_style(status_bar.root, "header"); end if opts.back_cb then diff --git a/src/lua/lua_theme.cpp b/src/lua/lua_theme.cpp index 7b007f4d..2fcd71e5 100644 --- a/src/lua/lua_theme.cpp +++ b/src/lua/lua_theme.cpp @@ -22,17 +22,19 @@ namespace lua { -static auto set_theme(lua_State* L) -> int { - // lv_style_t* style = luavgl_to_style(L, -1); - // if (style == NULL) { - // ESP_LOGI("DANIEL", "Style was null or malformed??"); - // return 0; - // } - - // ESP_LOGI("DANIEL", "GOT ONE!"); - // themes::Theme::instance()->...; +static auto set_style(lua_State* L) -> int { + // Get the object and class name from the stack + if (lua_type(L, -1) == LUA_TSTRING) { + std::string class_name = lua_tostring(L, -1); + lv_obj_t* obj = luavgl_to_obj(L, -2); + if (obj != NULL) { + ui::themes::Theme::instance()->ApplyStyle(obj, class_name); + } + } + return 0; +} - /* table is in the stack at index 't' */ +static auto set_theme(lua_State* L) -> int { std::string class_name; lua_pushnil(L); /* first key */ while (lua_next(L, -2) != 0) { @@ -61,7 +63,6 @@ static auto set_theme(lua_State* L) -> int { return 0; } else { ui::themes::Theme::instance()->AddStyle(class_name, selector, style); - ESP_LOGI("DANIEL", "Got style for class %s with selector %d", class_name.c_str(), selector); } } lua_pop(L, 1); @@ -75,7 +76,7 @@ static auto set_theme(lua_State* L) -> int { return 0; } -static const struct luaL_Reg kThemeFuncs[] = {{"set", set_theme}, {NULL, NULL}}; +static const struct luaL_Reg kThemeFuncs[] = {{"set", set_theme}, {"set_style", set_style}, {NULL, NULL}}; static auto lua_theme(lua_State* L) -> int { luaL_newlib(L, kThemeFuncs); diff --git a/src/ui/include/themes.hpp b/src/ui/include/themes.hpp index 65462f65..09b9cdce 100644 --- a/src/ui/include/themes.hpp +++ b/src/ui/include/themes.hpp @@ -22,7 +22,7 @@ class Theme { public: void Apply(void); void Callback(lv_obj_t* obj); - void ApplyStyle(lv_obj_t* obj, Style style); + void ApplyStyle(lv_obj_t* obj, std::string style_key); void AddStyle(std::string key, int selector, lv_style_t* style); diff --git a/src/ui/modal.cpp b/src/ui/modal.cpp index 88f6d3ef..ec541914 100644 --- a/src/ui/modal.cpp +++ b/src/ui/modal.cpp @@ -41,8 +41,6 @@ Modal::Modal(Screen* host) lv_obj_set_style_bg_opa(root_, LV_OPA_COVER, 0); lv_obj_set_style_bg_color(root_, lv_color_white(), 0); - themes::Theme::instance()->ApplyStyle(root_, themes::Style::kPopup); - host_->modal_group(group_); } diff --git a/src/ui/screen_lua.cpp b/src/ui/screen_lua.cpp index 5130b4f7..b3554241 100644 --- a/src/ui/screen_lua.cpp +++ b/src/ui/screen_lua.cpp @@ -8,13 +8,16 @@ #include "core/lv_obj_tree.h" #include "lua.hpp" +#include "themes.hpp" #include "luavgl.h" namespace ui { namespace screens { -Lua::Lua() : s_(nullptr), obj_ref_() {} +Lua::Lua() : s_(nullptr), obj_ref_() { + themes::Theme::instance()->ApplyStyle(root_, "root"); +} Lua::~Lua() { if (s_ && obj_ref_) { diff --git a/src/ui/themes.cpp b/src/ui/themes.cpp index 4fd477ab..88f45b1b 100644 --- a/src/ui/themes.cpp +++ b/src/ui/themes.cpp @@ -44,6 +44,8 @@ void Theme::Callback(lv_obj_t* obj) { std::string class_name; if (lv_obj_check_type(obj, &lv_btn_class)) { class_name = "button"; + } else if (lv_obj_check_type(obj, &lv_list_btn_class)) { + class_name = "listbutton"; } else if (lv_obj_check_type(obj, &lv_bar_class)) { class_name = "bar"; } else if (lv_obj_check_type(obj, &lv_slider_class)) { @@ -65,65 +67,11 @@ void Theme::Callback(lv_obj_t* obj) { } -void Theme::ApplyStyle(lv_obj_t* obj, Style style) { - switch (style) { - case Style::kTopBar: - lv_obj_set_style_pad_bottom(obj, 1, LV_PART_MAIN); - - lv_obj_set_style_shadow_width(obj, 6, LV_PART_MAIN); - lv_obj_set_style_shadow_opa(obj, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_shadow_ofs_x(obj, 0, LV_PART_MAIN); - break; - case Style::kPopup: - lv_obj_set_style_shadow_width(obj, 6, LV_PART_MAIN); - lv_obj_set_style_shadow_opa(obj, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_shadow_ofs_x(obj, 0, LV_PART_MAIN); - lv_obj_set_style_shadow_ofs_y(obj, 0, LV_PART_MAIN); - - lv_obj_set_style_radius(obj, 5, LV_PART_MAIN); - - lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(obj, lv_color_white(), LV_PART_MAIN); - - lv_obj_set_style_pad_top(obj, 2, LV_PART_MAIN); - lv_obj_set_style_pad_bottom(obj, 2, LV_PART_MAIN); - lv_obj_set_style_pad_left(obj, 2, LV_PART_MAIN); - lv_obj_set_style_pad_right(obj, 2, LV_PART_MAIN); - break; - case Style::kTab: - lv_obj_set_style_radius(obj, 0, LV_PART_MAIN); - - lv_obj_set_style_border_width(obj, 1, LV_STATE_CHECKED); - lv_obj_set_style_border_color(obj, lv_palette_main(LV_PALETTE_BLUE), - LV_STATE_CHECKED); - lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM, - LV_STATE_CHECKED); - break; - case Style::kButtonPrimary: - lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(obj, lv_palette_main(LV_PALETTE_BLUE), - LV_PART_MAIN); - lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_FULL, LV_PART_MAIN); - break; - case Style::kMenuSubheadFirst: - case Style::kMenuSubhead: - lv_obj_set_style_text_color(obj, lv_palette_darken(LV_PALETTE_GREY, 3), - LV_PART_MAIN); - lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); - - lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(obj, lv_palette_lighten(LV_PALETTE_GREY, 3), - LV_PART_MAIN); - - if (style == Style::kMenuSubhead) { - lv_obj_set_style_border_side( - obj, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); - } else { - lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); +void Theme::ApplyStyle(lv_obj_t* obj, std::string style_key) { + if (auto search = style_map.find(style_key); search != style_map.end()) { + for (const auto& pair : search->second) { + lv_obj_add_style(obj, pair.second, pair.first); } - break; - default: - break; } }