diff --git a/log.c b/log.c index fe874c0..ae2aaee 100644 --- a/log.c +++ b/log.c @@ -21,6 +21,7 @@ #include "log.h" #include +#include #include diff --git a/log.h b/log.h index 4f2c415..8f67d18 100644 --- a/log.h +++ b/log.h @@ -21,8 +21,6 @@ #ifndef UL_LOG_H #define UL_LOG_H -#include - /** * Log levels */ diff --git a/lv_conf.h b/lv_conf.h index 6c0d9b1..3f16dea 100644 --- a/lv_conf.h +++ b/lv_conf.h @@ -488,7 +488,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # define LV_THEME_DEFAULT_DARK 0 /*1: Enable grow on press*/ -# define LV_THEME_DEFAULT_GROW 1 +# define LV_THEME_DEFAULT_GROW 0 /*Default transition time in [ms]*/ # define LV_THEME_DEFAULT_TRANSITON_TIME 80 diff --git a/main.c b/main.c index c2be5b2..0a3e1fc 100644 --- a/main.c +++ b/main.c @@ -23,6 +23,8 @@ #include "indev.h" #include "log.h" #include "unl0kr.h" +#include "theme.h" +#include "themes.h" #include "lv_drivers/display/fbdev.h" @@ -38,15 +40,6 @@ #include -/** - * Custom fonts - */ - -LV_FONT_DECLARE(montserrat_extended_32); - -#define SYMBOL_ADJUST "\xef\x81\x82" // 0xF042 (https://fontawesome.com/v5.15/icons/adjust?style=solid) - - /** * Static variables */ @@ -54,14 +47,12 @@ LV_FONT_DECLARE(montserrat_extended_32); ul_cli_opts cli_opts; ul_config_opts conf_opts; -bool is_dark_theme = false; +bool is_alternate_theme = false; bool is_password_obscured = true; bool is_keyboard_hidden = false; lv_obj_t *keyboard = NULL; -lv_style_t style_text_normal; - /** * Static prototypes @@ -132,13 +123,6 @@ static void set_keyboard_hidden(bool is_hidden); */ static void keyboard_anim_y_cb(void *obj, int32_t value); -/** - * Handle LV_EVENT_READY events from the keyboard layout dropdown. - * - * @param event the event object - */ -static void layout_dropdown_ready_cb(lv_event_t *event); - /** * Handle LV_EVENT_VALUE_CHANGED events from the keyboard layout dropdown. * @@ -160,13 +144,6 @@ static void shutdown_btn_clicked_cb(lv_event_t *event); */ static void shutdown_mbox_value_changed_cb(lv_event_t *event); -/** - * Handle LV_EVENT_DRAW_PART_BEGIN events from the keyboard widget. - * - * @param event the event object - */ -static void keyboard_draw_part_begin_cb(lv_event_t *event); - /** * Handle LV_EVENT_VALUE_CHANGED events from the keyboard widget. * @@ -191,12 +168,12 @@ static void toggle_theme_btn_clicked_cb(lv_event_t *event) { } static void toggle_theme(void) { - is_dark_theme = !is_dark_theme; - set_theme(is_dark_theme); + is_alternate_theme = !is_alternate_theme; + set_theme(is_alternate_theme); } -static void set_theme(bool is_dark) { - lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_CYAN), is_dark, &montserrat_extended_32); +static void set_theme(bool is_alternate) { + ul_theme_apply(is_alternate ? &ul_themes_breezy_light : &ul_themes_breezy_dark); } static void toggle_pw_btn_clicked_cb(lv_event_t *event) { @@ -249,12 +226,6 @@ static void keyboard_anim_y_cb(void *obj, int32_t value) { lv_obj_set_y(obj, value); } -static void layout_dropdown_ready_cb(lv_event_t *event) { - lv_obj_t *dropdown = lv_event_get_target(event); - lv_obj_t *list = lv_dropdown_get_list(dropdown); - lv_obj_add_style(list, &style_text_normal, 0); -} - static void layout_dropdown_value_changed_cb(lv_event_t *event) { lv_obj_t *dropdown = lv_event_get_target(event); uint16_t idx = lv_dropdown_get_selected(dropdown); @@ -277,38 +248,6 @@ static void shutdown_mbox_value_changed_cb(lv_event_t *event) { lv_msgbox_close(mbox); } -static void keyboard_draw_part_begin_cb(lv_event_t *event) { - lv_obj_t *obj = lv_event_get_target(event); - lv_btnmatrix_t *btnm = (lv_btnmatrix_t *)obj; - lv_obj_draw_part_dsc_t *dsc = lv_event_get_param(event); - - if (dsc->part != LV_PART_ITEMS) { - return; - } - - if (lv_btnmatrix_get_selected_btn(obj) == dsc->id) { /* key is held down */ - if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_INACTIVE) == SQ2LV_CTRL_MOD_INACTIVE) { - dsc->rect_dsc->bg_color = lv_palette_lighten(LV_PALETTE_TEAL, 1); - } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_ACTIVE) == SQ2LV_CTRL_MOD_ACTIVE) { - dsc->rect_dsc->bg_color = lv_palette_lighten(LV_PALETTE_TEAL, 1); - } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_NON_CHAR) == SQ2LV_CTRL_NON_CHAR) { - dsc->rect_dsc->bg_color = lv_palette_darken(LV_PALETTE_BLUE_GREY, 3); - } else { - dsc->rect_dsc->bg_color = lv_palette_lighten(LV_PALETTE_BLUE_GREY, 1); - } - } else { /* key is not held down */ - if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_INACTIVE) == SQ2LV_CTRL_MOD_INACTIVE) { - dsc->rect_dsc->bg_color = lv_palette_darken(LV_PALETTE_BLUE_GREY, 4); - } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_ACTIVE) == SQ2LV_CTRL_MOD_ACTIVE) { - dsc->rect_dsc->bg_color = lv_palette_main(LV_PALETTE_TEAL); - } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_NON_CHAR) == SQ2LV_CTRL_NON_CHAR) { - dsc->rect_dsc->bg_color = lv_palette_darken(LV_PALETTE_BLUE_GREY, 4); - } else { - dsc->rect_dsc->bg_color = lv_palette_main(LV_PALETTE_BLUE_GREY); - } - } -} - static void keyboard_value_changed_cb(lv_event_t *event) { lv_obj_t *kb = lv_event_get_target(event); @@ -394,10 +333,8 @@ int main(int argc, char *argv[]) { is_keyboard_hidden = true; } - /* Initialise theme and styles */ - set_theme(is_dark_theme); - lv_style_init(&style_text_normal); - lv_style_set_text_font(&style_text_normal, &montserrat_extended_32); + /* Initialise theme */ + set_theme(is_alternate_theme); /* Figure out a few numbers for sizing and positioning */ const int keyboard_height = ver_res / 3; @@ -417,9 +354,8 @@ int main(int argc, char *argv[]) { lv_obj_set_grid_cell(toggle_theme_btn, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1); lv_obj_set_size(toggle_theme_btn, 64, 64); lv_obj_t *toggle_theme_btn_label = lv_label_create(toggle_theme_btn); - lv_label_set_text(toggle_theme_btn_label, SYMBOL_ADJUST); + lv_label_set_text(toggle_theme_btn_label, UL_SYMBOL_ADJUST); lv_obj_center(toggle_theme_btn_label); - lv_obj_add_style(toggle_theme_btn_label, &style_text_normal, 0); /* Show / hide keyboard button */ lv_obj_t *toggle_kb_btn = lv_btn_create(btn_row); @@ -429,17 +365,14 @@ int main(int argc, char *argv[]) { lv_obj_t *toggle_kb_btn_label = lv_label_create(toggle_kb_btn); lv_label_set_text(toggle_kb_btn_label, LV_SYMBOL_KEYBOARD); lv_obj_center(toggle_kb_btn_label); - lv_obj_add_style(toggle_kb_btn_label, &style_text_normal, 0); /* Keyboard layout dropdown */ lv_obj_t *layout_dropdown = lv_dropdown_create(btn_row); lv_dropdown_set_options(layout_dropdown, sq2lv_layout_short_names); - lv_obj_add_event_cb(layout_dropdown, layout_dropdown_ready_cb, LV_EVENT_READY, NULL); lv_obj_add_event_cb(layout_dropdown, layout_dropdown_value_changed_cb, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_set_grid_cell(layout_dropdown, LV_GRID_ALIGN_START, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1); - lv_obj_set_height(layout_dropdown, 64); + // lv_obj_set_height(layout_dropdown, 64); lv_obj_set_width(layout_dropdown, 160); - lv_obj_add_style(layout_dropdown, &style_text_normal, 0); /* Shutdown button */ lv_obj_t *shutdown_btn = lv_btn_create(btn_row); @@ -449,17 +382,16 @@ int main(int argc, char *argv[]) { lv_obj_t *shutdown_btn_label = lv_label_create(shutdown_btn); lv_label_set_text(shutdown_btn_label, LV_SYMBOL_POWER); lv_obj_center(shutdown_btn_label); - lv_obj_add_style(shutdown_btn_label, &style_text_normal, 0); /* Textarea */ lv_obj_t *textarea = lv_textarea_create(lv_scr_act()); lv_textarea_set_one_line(textarea, true); lv_textarea_set_password_mode(textarea, true); lv_textarea_set_placeholder_text(textarea, "Enter password..."); - lv_obj_set_size(textarea, hor_res - 60 > 512 ? 512 : hor_res - 60, 64); + // lv_obj_set_size(textarea, hor_res - 60 > 512 ? 512 : hor_res - 60, 64); + lv_obj_set_width(textarea, hor_res - 60 > 512 ? 512 : hor_res - 60); lv_obj_align(textarea, LV_ALIGN_CENTER, 0, ver_res / 2 - keyboard_height - 3 * row_height / 2); lv_obj_add_state(textarea, LV_STATE_FOCUSED); - lv_obj_add_style(textarea, &style_text_normal, 0); /* Route physical keyboard input into textarea */ ul_indev_set_up_textarea_for_keyboard_input(textarea); @@ -467,12 +399,11 @@ int main(int argc, char *argv[]) { /* Reveal / obscure password button */ lv_obj_t *toggle_pw_btn = lv_btn_create(lv_scr_act()); lv_obj_align(toggle_pw_btn, LV_ALIGN_CENTER, (hor_res - 60 > 512 ? 512 : hor_res - 60) / 2 + 32, ver_res / 2 - keyboard_height - 3 * row_height / 2); - lv_obj_set_size(toggle_pw_btn, 64, 64); + // lv_obj_set_size(toggle_pw_btn, 64, 64); lv_obj_t *toggle_pw_btn_label = lv_label_create(toggle_pw_btn); lv_obj_center(toggle_pw_btn_label); lv_label_set_text(toggle_pw_btn_label, LV_SYMBOL_EYE_OPEN); lv_obj_add_event_cb(toggle_pw_btn, toggle_pw_btn_clicked_cb, LV_EVENT_CLICKED, NULL); - lv_obj_add_style(toggle_pw_btn_label, &style_text_normal, 0); /* Label */ lv_obj_t *spangroup = lv_spangroup_create(lv_scr_act()); @@ -480,7 +411,6 @@ int main(int argc, char *argv[]) { lv_spangroup_set_mode(spangroup, LV_SPAN_MODE_BREAK); lv_obj_set_size(spangroup, hor_res - 40, 2 * row_height); lv_obj_align(spangroup, LV_ALIGN_CENTER, 0, ver_res / 2 - keyboard_height); - lv_obj_add_style(spangroup, &style_text_normal, 0); lv_span_t *span1 = lv_spangroup_new_span(spangroup); lv_span_set_text(span1, "Password required to unlock "); lv_span_t *span2 = lv_spangroup_new_span(spangroup); @@ -492,12 +422,11 @@ int main(int argc, char *argv[]) { lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); lv_keyboard_set_textarea(keyboard, textarea); lv_obj_remove_event_cb(keyboard, lv_keyboard_def_event_cb); - lv_obj_add_event_cb(keyboard, keyboard_draw_part_begin_cb, LV_EVENT_DRAW_PART_BEGIN, NULL); lv_obj_add_event_cb(keyboard, keyboard_value_changed_cb, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_add_event_cb(keyboard, keyboard_ready_cb, LV_EVENT_READY, NULL); lv_obj_set_pos(keyboard, 0, is_keyboard_hidden ? keyboard_height : 0); lv_obj_set_size(keyboard, hor_res, keyboard_height); - lv_obj_add_style(keyboard, &style_text_normal, 0); + ul_theme_prepare_keyboard(keyboard); /* Apply textarea options */ set_password_obscured(conf_opts.textarea.obscured); diff --git a/meson.build b/meson.build index 748254a..154c1bd 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,8 @@ unl0kr_sources = [ 'main.c', 'montserrat_extended_32.c', 'sq2lv_layouts.c', + 'theme.c', + 'themes.c' ] squeek2lvgl_sources = [ diff --git a/theme.c b/theme.c new file mode 100644 index 0000000..5909994 --- /dev/null +++ b/theme.c @@ -0,0 +1,341 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "theme.h" + +#include "log.h" +#include "sq2lv_layouts.h" +#include "unl0kr.h" + +#include "lvgl/lvgl.h" + + +/** + * Static variables + */ + +static ul_theme current_theme; +static lv_theme_t lv_theme; + +static struct { + lv_style_t widget; + lv_style_t window; + lv_style_t header; + lv_style_t keyboard; + lv_style_t key; + lv_style_t button; + lv_style_t button_pressed; + lv_style_t textarea; + lv_style_t textarea_placeholder; + lv_style_t textarea_cursor; + lv_style_t dropdown; + lv_style_t dropdown_list; + lv_style_t dropdown_list_selected; + lv_style_t label; + lv_style_t msgbox; + lv_style_t msgbox_btnmatrix; + lv_style_t msgbox_background; +} styles; + +static bool are_styles_initialised = false; + + +/** + * Static prototypes + */ + +/** + * Set up the static styles for a specific theme. + * + * @param theme theme to derive the styles from + */ +static void init_styles(ul_theme *theme); + +/** + * Initialise or reset a style. + * + * @param style style to reset + */ +static void reset_style(lv_style_t *style); + +/** + * Apply a theme to an object. + * + * @param theme theme to apply + * @param obj object to style + */ +static void apply_theme_cb(lv_theme_t *theme, lv_obj_t *obj); + +/** + * Handle LV_EVENT_DRAW_PART_BEGIN events from the keyboard widget. + * + * @param event the event object + */ +static void keyboard_draw_part_begin_cb(lv_event_t *event); + + +/** + * Static functions + */ + +static void init_styles(ul_theme *theme) { + reset_style(&(styles.widget)); + lv_style_set_text_font(&(styles.widget), &montserrat_extended_32); + + reset_style(&(styles.window)); + lv_style_set_bg_opa(&(styles.window), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.window), lv_color_hex(theme->window.bg_color)); + + reset_style(&(styles.header)); + lv_style_set_bg_opa(&(styles.header), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.header), lv_color_hex(theme->header.bg_color)); + lv_style_set_border_side(&(styles.header), LV_BORDER_SIDE_BOTTOM); + lv_style_set_border_width(&(styles.header), lv_dpx(theme->header.border_width)); + lv_style_set_border_color(&(styles.header), lv_color_hex(theme->header.border_color)); + lv_style_set_pad_all(&(styles.header), lv_dpx(theme->header.pad)); + lv_style_set_pad_gap(&(styles.header), lv_dpx(theme->header.gap)); + + reset_style(&(styles.keyboard)); + lv_style_set_bg_opa(&(styles.keyboard), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.keyboard), lv_color_hex(theme->keyboard.bg_color)); + lv_style_set_border_side(&(styles.keyboard), LV_BORDER_SIDE_TOP); + lv_style_set_border_width(&(styles.keyboard), lv_dpx(theme->keyboard.border_width)); + lv_style_set_border_color(&(styles.keyboard), lv_color_hex(theme->keyboard.border_color)); + lv_style_set_pad_all(&(styles.keyboard), lv_dpx(theme->keyboard.pad)); + lv_style_set_pad_gap(&(styles.keyboard), lv_dpx(theme->keyboard.gap)); + + reset_style(&(styles.key)); + lv_style_set_bg_opa(&(styles.key), LV_OPA_COVER); + lv_style_set_border_side(&(styles.key), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.key), lv_dpx(theme->keyboard.keys.border_width)); + lv_style_set_radius(&(styles.key), lv_dpx(theme->keyboard.keys.corner_radius)); + + reset_style(&(styles.button)); + lv_style_set_text_color(&(styles.button), lv_color_hex(theme->button.normal.fg_color)); + lv_style_set_bg_opa(&(styles.button), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.button), lv_color_hex(theme->button.normal.bg_color)); + lv_style_set_border_side(&(styles.button), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.button), lv_dpx(theme->button.border_width)); + lv_style_set_border_color(&(styles.button), lv_color_hex(theme->button.normal.border_color)); + lv_style_set_radius(&(styles.button), lv_dpx(theme->button.corner_radius)); + lv_style_set_pad_all(&(styles.button), lv_dpx(theme->button.pad)); + + reset_style(&(styles.button_pressed)); + lv_style_set_text_color(&(styles.button_pressed), lv_color_hex(theme->button.pressed.fg_color)); + lv_style_set_bg_color(&(styles.button_pressed), lv_color_hex(theme->button.pressed.bg_color)); + lv_style_set_border_color(&(styles.button_pressed), lv_color_hex(theme->button.pressed.border_color)); + + reset_style(&(styles.textarea)); + lv_style_set_text_color(&(styles.textarea), lv_color_hex(theme->textarea.fg_color)); + lv_style_set_bg_opa(&(styles.textarea), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.textarea), lv_color_hex(theme->textarea.bg_color)); + lv_style_set_border_side(&(styles.textarea), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.textarea), lv_dpx(theme->textarea.border_width)); + lv_style_set_border_color(&(styles.textarea), lv_color_hex(theme->textarea.border_color)); + lv_style_set_radius(&(styles.textarea), lv_dpx(theme->textarea.corner_radius)); + lv_style_set_pad_all(&(styles.textarea), lv_dpx(theme->textarea.pad)); + + reset_style(&(styles.textarea_placeholder)); + lv_style_set_text_color(&(styles.textarea_placeholder), lv_color_hex(theme->textarea.placeholder_color)); + + reset_style(&(styles.textarea_cursor)); + lv_style_set_border_side(&(styles.textarea_cursor), LV_BORDER_SIDE_LEFT); + lv_style_set_border_width(&(styles.textarea_cursor), lv_dpx(theme->textarea.cursor.width)); + lv_style_set_border_color(&(styles.textarea_cursor), lv_color_hex(theme->textarea.cursor.color)); + lv_style_set_anim_time(&(styles.textarea_cursor), theme->textarea.cursor.period); + + reset_style(&(styles.dropdown)); + lv_style_set_text_color(&(styles.dropdown), lv_color_hex(theme->dropdown.box.fg_color)); + lv_style_set_bg_opa(&(styles.dropdown), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.dropdown), lv_color_hex(theme->dropdown.box.bg_color)); + lv_style_set_border_side(&(styles.dropdown), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.dropdown), lv_dpx(theme->dropdown.box.border_width)); + lv_style_set_border_color(&(styles.dropdown), lv_color_hex(theme->dropdown.box.border_color)); + lv_style_set_radius(&(styles.dropdown), lv_dpx(theme->dropdown.box.corner_radius)); + lv_style_set_pad_all(&(styles.dropdown), lv_dpx(theme->dropdown.box.pad)); + + reset_style(&(styles.dropdown_list)); + lv_style_set_text_color(&(styles.dropdown_list), lv_color_hex(theme->dropdown.list.fg_color)); + lv_style_set_bg_opa(&(styles.dropdown_list), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.dropdown_list), lv_color_hex(theme->dropdown.list.bg_color)); + lv_style_set_border_side(&(styles.dropdown_list), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.dropdown_list), lv_dpx(theme->dropdown.list.border_width)); + lv_style_set_border_color(&(styles.dropdown_list), lv_color_hex(theme->dropdown.list.border_color)); + lv_style_set_radius(&(styles.dropdown_list), lv_dpx(theme->dropdown.list.corner_radius)); + lv_style_set_pad_all(&(styles.dropdown_list), lv_dpx(theme->dropdown.list.pad)); + + reset_style(&(styles.dropdown_list_selected)); + lv_style_set_text_color(&(styles.dropdown_list_selected), lv_color_hex(theme->dropdown.list.selection_fg_color)); + lv_style_set_bg_opa(&(styles.dropdown_list_selected), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.dropdown_list_selected), lv_color_hex(theme->dropdown.list.selection_bg_color)); + + reset_style(&(styles.label)); + lv_style_set_text_color(&(styles.label), lv_color_hex(theme->label.fg_color)); + + reset_style(&(styles.msgbox)); + lv_style_set_text_color(&(styles.msgbox), lv_color_hex(theme->msgbox.fg_color)); + lv_style_set_bg_opa(&(styles.msgbox), LV_OPA_COVER); + lv_style_set_bg_color(&(styles.msgbox), lv_color_hex(theme->msgbox.bg_color)); + lv_style_set_border_side(&(styles.msgbox), LV_BORDER_SIDE_FULL); + lv_style_set_border_width(&(styles.msgbox), lv_dpx(theme->msgbox.border_width)); + lv_style_set_border_color(&(styles.msgbox), lv_color_hex(theme->msgbox.border_color)); + lv_style_set_radius(&(styles.msgbox), lv_dpx(theme->msgbox.corner_radius)); + lv_style_set_pad_all(&(styles.msgbox), lv_dpx(theme->msgbox.pad)); + + reset_style(&(styles.msgbox_btnmatrix)); + lv_style_set_pad_top(&(styles.msgbox_btnmatrix), lv_dpx(theme->msgbox.buttons.pad)); + lv_style_set_pad_gap(&(styles.msgbox_btnmatrix), lv_dpx(theme->msgbox.buttons.gap)); + + reset_style(&(styles.msgbox_background)); + lv_style_set_bg_color(&(styles.msgbox_background), lv_color_hex(theme->msgbox.dimming.color)); + lv_style_set_bg_opa(&(styles.msgbox_background), theme->msgbox.dimming.opacity); + + are_styles_initialised = true; +} + +static void reset_style(lv_style_t *style) { + if (are_styles_initialised) { + lv_style_reset(style); + } else { + lv_style_init(style); + } +} + +static void apply_theme_cb(lv_theme_t *theme, lv_obj_t *obj) { + lv_obj_add_style(obj, &(styles.widget), 0); + + if (lv_obj_get_parent(obj) == NULL) { + lv_obj_add_style(obj, &(styles.window), 0); + return; + } + + if (lv_obj_check_type(obj, &lv_keyboard_class)) { + lv_obj_add_style(obj, &(styles.keyboard), 0); + lv_obj_add_style(obj, &(styles.key), LV_PART_ITEMS); + return; + } + + if (lv_obj_check_type(obj, &lv_btn_class)) { + lv_obj_add_style(obj, &(styles.button), 0); + lv_obj_add_style(obj, &(styles.button_pressed), LV_STATE_PRESSED); + return; + } + + if (lv_obj_check_type(obj, &lv_textarea_class)) { + lv_obj_add_style(obj, &(styles.textarea), 0); + lv_obj_add_style(obj, &(styles.textarea_placeholder), LV_PART_TEXTAREA_PLACEHOLDER); + lv_obj_add_style(obj, &(styles.textarea_cursor), LV_PART_CURSOR | LV_STATE_FOCUSED); + return; + } + + if (lv_obj_check_type(obj, &lv_dropdown_class)) { + lv_obj_add_style(obj, &(styles.dropdown), 0); + return; + } + + if (lv_obj_check_type(obj, &lv_dropdownlist_class)) { + lv_obj_add_style(obj, &(styles.dropdown_list), 0); + lv_obj_add_style(obj, &(styles.dropdown_list_selected), LV_PART_SELECTED | LV_STATE_CHECKED); + lv_obj_add_style(obj, &(styles.dropdown_list_selected), LV_PART_SELECTED | LV_STATE_PRESSED); + return; + } + + if (lv_obj_check_type(obj, &lv_label_class) || lv_obj_check_type(obj, &lv_spangroup_class)) { + lv_obj_add_style(obj, &(styles.label), 0); + return; + } + + if (lv_obj_check_type(obj, &lv_msgbox_class)) { + lv_obj_add_style(obj, &(styles.msgbox), 0); + return; + } + + if (lv_obj_check_type(obj, &lv_btnmatrix_class) && lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) { + lv_obj_add_style(obj, &(styles.msgbox_btnmatrix), 0); + lv_obj_add_style(obj, &(styles.button), LV_PART_ITEMS); + lv_obj_add_style(obj, &(styles.button_pressed), LV_PART_ITEMS | LV_STATE_PRESSED); + return; + } + + if (lv_obj_check_type(obj, &lv_msgbox_backdrop_class)) { + lv_obj_add_style(obj, &(styles.msgbox_background), 0); + return; + } + + /* If none of the if's above matched, this has to be the header */ + lv_obj_add_style(obj, &(styles.header), 0); +} + +static void keyboard_draw_part_begin_cb(lv_event_t *event) { + lv_obj_t *obj = lv_event_get_target(event); + lv_btnmatrix_t *btnm = (lv_btnmatrix_t *)obj; + lv_obj_draw_part_dsc_t *dsc = lv_event_get_param(event); + + if (dsc->part != LV_PART_ITEMS) { + return; + } + + ul_theme_key *key = NULL; + + if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_INACTIVE) == SQ2LV_CTRL_MOD_INACTIVE) { + key = &(current_theme.keyboard.keys.key_mod_inact); + } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_MOD_ACTIVE) == SQ2LV_CTRL_MOD_ACTIVE) { + key = &(current_theme.keyboard.keys.key_mod_act); + } else if ((btnm->ctrl_bits[dsc->id] & SQ2LV_CTRL_NON_CHAR) == SQ2LV_CTRL_NON_CHAR) { + key = &(current_theme.keyboard.keys.key_non_char); + } else { + key = &(current_theme.keyboard.keys.key_char); + } + + bool pressed = lv_btnmatrix_get_selected_btn(obj) == dsc->id; + + dsc->label_dsc->color = lv_color_hex((pressed ? key->pressed : key->normal).fg_color); + dsc->rect_dsc->bg_color = lv_color_hex((pressed ? key->pressed : key->normal).bg_color); + dsc->rect_dsc->border_color = lv_color_hex((pressed ? key->pressed : key->normal).border_color); +} + + +/** + * Public functions + */ + +void ul_theme_prepare_keyboard(lv_obj_t *keyboard) { + lv_obj_add_event_cb(keyboard, keyboard_draw_part_begin_cb, LV_EVENT_DRAW_PART_BEGIN, NULL); +} + +void ul_theme_apply(ul_theme *theme) { + if (!theme) { + ul_log(UL_LOG_LEVEL_ERROR, "Could not apply theme from NULL pointer"); + return; + } + + lv_theme.disp = NULL; + lv_theme.font_small = &montserrat_extended_32; + lv_theme.font_normal = &montserrat_extended_32; + lv_theme.font_large = &montserrat_extended_32; + lv_theme.apply_cb = apply_theme_cb; + + current_theme = *theme; + init_styles(theme); + + lv_obj_report_style_change(NULL); + lv_disp_set_theme(NULL, &lv_theme); + lv_theme_apply(lv_scr_act()); +} diff --git a/theme.h b/theme.h new file mode 100644 index 0000000..1610d60 --- /dev/null +++ b/theme.h @@ -0,0 +1,198 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef UL_THEME_H +#define UL_THEME_H + +#include "lvgl/lvgl.h" + +#include +#include + +/** + * Theming structs + */ + +/* Window theme */ +typedef struct { + uint32_t bg_color; +} ul_theme_window; + +/* Header theme */ +typedef struct { + uint32_t bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t pad; + lv_coord_t gap; +} ul_theme_header; + +/* Key theme for one specific key type and state */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + uint32_t border_color; +} ul_theme_key_state; + +/* Key theme for one specific key type and all states */ +typedef struct { + ul_theme_key_state normal; + ul_theme_key_state pressed; +} ul_theme_key; + +/* Key theme */ +typedef struct { + lv_coord_t border_width; + lv_coord_t corner_radius; + ul_theme_key key_char; + ul_theme_key key_non_char; + ul_theme_key key_mod_act; + ul_theme_key key_mod_inact; +} ul_theme_keys; + +/* Keyboard theme */ +typedef struct { + uint32_t bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t pad; + lv_coord_t gap; + ul_theme_keys keys; +} ul_theme_keyboard; + +/* Button theme for one specific button state */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + uint32_t border_color; +} ul_theme_button_state; + +/* Button theme */ +typedef struct { + lv_coord_t border_width; + lv_coord_t corner_radius; + lv_coord_t pad; + ul_theme_button_state normal; + ul_theme_button_state pressed; +} ul_theme_button; + +/* Text area cursor theme */ +typedef struct { + lv_coord_t width; + uint32_t color; + int period; +} ul_theme_textarea_cursor; + +/* Text area theme */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t corner_radius; + lv_coord_t pad; + uint32_t placeholder_color; + ul_theme_textarea_cursor cursor; +} ul_theme_textarea; + +/* Dropdown box theme */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t corner_radius; + lv_coord_t pad; +} ul_theme_dropdown_box; + +/* Dropdown list theme */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + uint32_t selection_fg_color; + uint32_t selection_bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t corner_radius; + lv_coord_t pad; +} ul_theme_dropdown_list; + +/* Dropdown theme */ +typedef struct { + ul_theme_dropdown_box box; + ul_theme_dropdown_list list; +} ul_theme_dropdown; + +/* Label */ +typedef struct { + uint32_t fg_color; +} ul_theme_label; + +/* Message box buttons theme */ +typedef struct { + lv_coord_t pad; + lv_coord_t gap; +} ul_theme_msgbox_buttons; + +/* Message box dimming theme */ +typedef struct { + uint32_t color; + short opacity; +} ul_theme_msgbox_dimming; + +/* Message box theme */ +typedef struct { + uint32_t fg_color; + uint32_t bg_color; + lv_coord_t border_width; + uint32_t border_color; + lv_coord_t corner_radius; + lv_coord_t pad; + ul_theme_msgbox_buttons buttons; + ul_theme_msgbox_dimming dimming; +} ul_theme_msgbox; + +/* Full theme */ +typedef struct { + ul_theme_window window; + ul_theme_header header; + ul_theme_keyboard keyboard; + ul_theme_button button; + ul_theme_textarea textarea; + ul_theme_dropdown dropdown; + ul_theme_label label; + ul_theme_msgbox msgbox; +} ul_theme; + +/** + * Prepare a keyboard widget to be themed with a theme. + * + * @param keyboard keyboard widget + */ +void ul_theme_prepare_keyboard(lv_obj_t *keyboard); + +/** + * Apply a UI theme. + * + * @param theme the theme to apply + */ +void ul_theme_apply(ul_theme *theme); + +#endif /* UL_THEME_H */ diff --git a/themes.c b/themes.c new file mode 100644 index 0000000..d3aa35e --- /dev/null +++ b/themes.c @@ -0,0 +1,311 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "themes.h" + + +/** + * Breezy light (based on KDE Breeze color palette, see https://develop.kde.org/hig/style/color/default/) + */ + +ul_theme ul_themes_breezy_light = { + .window = { + .bg_color = 0xeff0f1 + }, + .header = { + .bg_color = 0xfcfcfc, + .border_width = 1, + .border_color = 0xbdc3c7, + .pad = 10, + .gap = 10 + }, + .keyboard = { + .bg_color = 0xfcfcfc, + .border_width = 1, + .border_color = 0xbdc3c7, + .pad = 10, + .gap = 10, + .keys = { + .border_width = 1, + .corner_radius = 5, + .key_char = { + .normal = { + .fg_color = 0x232629, + .bg_color = 0xeff0f1, + .border_color = 0xbdc3c7 + }, + .pressed = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_non_char = { + .normal = { + .fg_color = 0x232629, + .bg_color = 0xbdc3c7, + .border_color = 0x7f8c8d + }, + .pressed = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_mod_act = { + .normal = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + }, + .pressed = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_mod_inact = { + .normal = { + .fg_color = 0x232629, + .bg_color = 0xbdc3c7, + .border_color = 0x7f8c8d + }, + .pressed = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + } + } + }, + .button = { + .border_width = 1, + .corner_radius = 5, + .pad = 5, + .normal = { + .fg_color = 0x232629, + .bg_color = 0xeff0f1, + .border_color = 0xbdc3c7 + }, + .pressed = { + .fg_color = 0x232629, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .textarea = { + .fg_color = 0x232629, + .bg_color = 0xfcfcfc, + .border_width = 1, + .border_color = 0xbdc3c7, + .corner_radius = 5, + .pad = 5, + .placeholder_color = 0x7f8c8d, + .cursor = { + .width = 1, + .color = 0x232629, + .period = 700 + } + }, + .dropdown = { + .box = { + .fg_color = 0x232629, + .bg_color = 0xeff0f1, + .border_width = 1, + .border_color = 0xbdc3c7, + .corner_radius = 5, + .pad = 5 + }, + .list = { + .fg_color = 0x232629, + .bg_color = 0xfcfcfc, + .selection_fg_color = 0x232629, + .selection_bg_color = 0x3daee9, + .border_width = 1, + .border_color = 0xbdc3c7, + .corner_radius = 0, + .pad = 0 + } + }, + .label = { + .fg_color = 0x232629 + }, + .msgbox = { + .fg_color = 0x232629, + .bg_color = 0xeff0f1, + .border_width = 1, + .border_color = 0xbdc3c7, + .corner_radius = 0, + .pad = 10, + .buttons = { + .pad = 10, + .gap = 5 + }, + .dimming = { + .color = 0x232629, + .opacity = 178 + } + } +}; + + +/** + * Breezy dark (based on KDE Breeze Dark color palette, see https://develop.kde.org/hig/style/color/dark/) + */ + +ul_theme ul_themes_breezy_dark = { + .window = { + .bg_color = 0x31363b + }, + .header = { + .bg_color = 0x232629, + .border_width = 1, + .border_color = 0x3b4045, + .pad = 10, + .gap = 10 + }, + .keyboard = { + .bg_color = 0x232629, + .border_width = 1, + .border_color = 0x3b4045, + .pad = 10, + .gap = 10, + .keys = { + .border_width = 1, + .corner_radius = 5, + .key_char = { + .normal = { + .fg_color = 0xeff0f1, + .bg_color = 0x31363b, + .border_color = 0x3b4045 + }, + .pressed = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_non_char = { + .normal = { + .fg_color = 0xeff0f1, + .bg_color = 0x4d4d4d, + .border_color = 0x7f8c8d + }, + .pressed = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_mod_act = { + .normal = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + }, + .pressed = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .key_mod_inact = { + .normal = { + .fg_color = 0xeff0f1, + .bg_color = 0x4d4d4d, + .border_color = 0x7f8c8d + }, + .pressed = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + } + } + }, + .button = { + .border_width = 1, + .corner_radius = 5, + .pad = 5, + .normal = { + .fg_color = 0xeff0f1, + .bg_color = 0x31363b, + .border_color = 0x3b4045 + }, + .pressed = { + .fg_color = 0xeff0f1, + .bg_color = 0x3daee9, + .border_color = 0x2980b9 + } + }, + .textarea = { + .fg_color = 0xeff0f1, + .bg_color = 0x232629, + .border_width = 1, + .border_color = 0x3b4045, + .corner_radius = 5, + .pad = 5, + .placeholder_color = 0x7f8c8d, + .cursor = { + .width = 1, + .color = 0x232629, + .period = 700 + } + }, + .dropdown = { + .box = { + .fg_color = 0xeff0f1, + .bg_color = 0x31363b, + .border_width = 1, + .border_color = 0x3b4045, + .corner_radius = 5, + .pad = 5 + }, + .list = { + .fg_color = 0xeff0f1, + .bg_color = 0x232629, + .selection_fg_color = 0x232629, + .selection_bg_color = 0x3daee9, + .border_width = 1, + .border_color = 0x3b4045, + .corner_radius = 0, + .pad = 0 + } + }, + .label = { + .fg_color = 0xeff0f1 + }, + .msgbox = { + .fg_color = 0xeff0f1, + .bg_color = 0x31363b, + .border_width = 1, + .border_color = 0x3b4045, + .corner_radius = 0, + .pad = 10, + .buttons = { + .pad = 10, + .gap = 5 + }, + .dimming = { + .color = 0x232629, + .opacity = 178 + } + } +}; diff --git a/themes.h b/themes.h new file mode 100644 index 0000000..97e939e --- /dev/null +++ b/themes.h @@ -0,0 +1,29 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef UL_THEMES_H +#define UL_THEMES_H + +#include "theme.h" + +extern ul_theme ul_themes_breezy_light; +extern ul_theme ul_themes_breezy_dark; + +#endif /* UL_THEMES_H */ diff --git a/unl0kr.h b/unl0kr.h index c6096d8..da3560a 100644 --- a/unl0kr.h +++ b/unl0kr.h @@ -21,8 +21,18 @@ #ifndef UL_UNL0KR_H #define UL_UNL0KR_H +#include "lvgl/lvgl.h" + #ifndef UL_VERSION #define UL_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */ #endif +/** + * Fonts + */ + +LV_FONT_DECLARE(montserrat_extended_32); + +#define UL_SYMBOL_ADJUST "\xef\x81\x82" // 0xF042 (https://fontawesome.com/v5.15/icons/adjust?style=solid) + #endif /* UL_UNL0KR_H */