acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

style.md (17644B)

      1 ```eval_rst
      2 .. include:: /header.rst
      3 :github_url: |github_link_base|/overview/style.md
      4 ```
      5 # Styles
      6 
      7 *Styles* are used to set the appearance of objects. Styles in lvgl are heavily inspired by CSS. The concept in a nutshell is as follows:
      8 - A style is an `lv_style_t` variable which can hold properties like border width, text color and so on. It's similar to a `class` in CSS.
      9 - Styles can be assigned to objects to change their appearance. Upon assignment, the target part (*pseudo-element* in CSS) and target state (*pseudo class*) can be specified.
     10 For example one can add `style_blue` to the knob of a slider when it's in pressed state.
     11 - The same style can be used by any number of objects.
     12 - Styles can be cascaded which means multiple styles may be assigned to an object and each style can have different properties.
     13 Therefore, not all properties have to be specified in a style. LVGL will search for a property until a style defines it or use a default if it's not specified by any of the styles.
     14 For example `style_btn` can result in a default gray button and `style_btn_red` can add only a `background-color=red` to overwrite the background color.
     15 - The most recently added style has higher precedence. This means if a property is specified in two styles the newest style in the object will be used.
     16 - Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in an object.
     17 - Objects can also have local styles with higher precedence than "normal" styles.
     18 - Unlike CSS (where pseudo-classes describe different states, e.g. `:focus`), in LVGL a property is assigned to a given state.
     19 - Transitions can be applied when the object changes state.
     20 
     21 
     22 ## States
     23 The objects can be in the combination of the following states:
     24 - `LV_STATE_DEFAULT` (0x0000) Normal, released state
     25 - `LV_STATE_CHECKED` (0x0001) Toggled or checked state
     26 - `LV_STATE_FOCUSED` (0x0002) Focused via keypad or encoder or clicked via touchpad/mouse
     27 - `LV_STATE_FOCUS_KEY` (0x0004) Focused via keypad or encoder but not via touchpad/mouse
     28 - `LV_STATE_EDITED` (0x0008) Edit by an encoder
     29 - `LV_STATE_HOVERED` (0x0010) Hovered by mouse (not supported now)
     30 - `LV_STATE_PRESSED` (0x0020) Being pressed
     31 - `LV_STATE_SCROLLED` (0x0040) Being scrolled
     32 - `LV_STATE_DISABLED` (0x0080) Disabled state
     33 - `LV_STATE_USER_1` (0x1000) Custom state
     34 - `LV_STATE_USER_2` (0x2000) Custom state
     35 - `LV_STATE_USER_3` (0x4000) Custom state
     36 - `LV_STATE_USER_4` (0x8000) Custom state
     37 
     38 An object can be in a combination of states such as being focused and pressed at the same time. This is represented as `LV_STATE_FOCUSED | LV_STATE_PRESSED`.
     39 
     40 A style can be added to any state or state combination.
     41 For example, setting a different background color for the default and pressed states.
     42 If a property is not defined in a state the best matching state's property will be used. Typically this means the property with `LV_STATE_DEFAULT` is used.˛
     43 If the property is not set even for the default state the default value will be used. (See later)
     44 
     45 But what does the "best matching state's property" really mean?
     46 States have a precedence which is shown by their value (see in the above list). A higher value means higher precedence.
     47 To determine which state's property to use let's take an example. Imagine the background color is defined like this:
     48 - `LV_STATE_DEFAULT`: white
     49 - `LV_STATE_PRESSED`: gray
     50 - `LV_STATE_FOCUSED`: red
     51 
     52 1. Initially the object is in the default state, so it's a simple case: the property is perfectly defined in the object's current state as white.
     53 2. When the object is pressed there are 2 related properties: default with white (default is related to every state) and pressed with gray.
     54 The pressed state has 0x0020 precedence which is higher than the default state's 0x0000 precedence, so gray color will be used.
     55 3. When the object is focused the same thing happens as in pressed state and red color will be used. (Focused state has higher precedence than default state).
     56 4. When the object is focused and pressed both gray and red would work, but the pressed state has higher precedence than focused so gray color will be used.
     57 5. It's possible to set e.g. rose color for `LV_STATE_PRESSED | LV_STATE_FOCUSED`.
     58 In this case, this combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than the pressed state's precedence so rose color would be used.
     59 6. When the object is in the checked state there is no property to set the background color for this state. So for lack of a better option, the object remains white from the default state's property.
     60 
     61 Some practical notes:
     62 - The precedence (value) of states is quite intuitive, and it's something the user would expect naturally. E.g. if an object is focused the user will still want to see if it's pressed, therefore the pressed state has a higher precedence.
     63 If the focused state had a higher precedence it would overwrite the pressed color.
     64 - If you want to set a property for all states (e.g. red background color) just set it for the default state. If the object can't find a property for its current state it will fall back to the default state's property.
     65 - Use ORed states to describe the properties for complex cases. (E.g. pressed + checked + focused)
     66 - It might be a good idea to use different style elements for different states.
     67 For example, finding background colors for released, pressed, checked + pressed, focused, focused + pressed, focused + pressed + checked, etc. states is quite difficult.
     68 Instead, for example, use the background color for pressed and checked states and indicate the focused state with a different border color.
     69 
     70 ## Cascading styles
     71 It's not required to set all the properties in one style. It's possible to add more styles to an object and have the latter added style modify or extend appearance.
     72 For example, create a general gray button style and create a new one for red buttons where only the new background color is set.
     73 
     74 This is much like in CSS when used classes are listed like `<div class=".btn .btn-red">`.
     75 
     76 Styles added later have precedence over ones set earlier. So in the gray/red button example above, the normal button style should be added first and the red style second.
     77 However, the precedence of the states are still taken into account.
     78 So let's examine the following case:
     79 - the basic button style defines dark-gray color for the default state and light-gray color for the pressed state
     80 - the red button style defines the background color as red only in the default state
     81 
     82 In this case, when the button is released (it's in default state) it will be red because a perfect match is found in the most recently added style (red).
     83 When the button is pressed the light-gray color is a better match because it describes the current state perfectly, so the button will be light-gray.
     84 
     85 ## Inheritance
     86 Some properties (typically those related to text) can be inherited from the parent object's styles.
     87 Inheritance is applied only if the given property is not set in the object's styles (even in default state).
     88 In this case, if the property is inheritable, the property's value will be searched in the parents until an object specifies a value for the property. The parents will use their own state to determine the value.
     89 So if a button is pressed, and the text color comes from here, the pressed text color will be used.
     90 
     91 
     92 ## Parts
     93 Objects can be composed of *parts* which may each have their own styles.
     94 
     95 The following predefined parts exist in LVGL:
     96 - `LV_PART_MAIN` A background like rectangle
     97 - `LV_PART_SCROLLBAR`  The scrollbar(s)
     98 - `LV_PART_INDICATOR` Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
     99 - `LV_PART_KNOB` Like a handle to grab to adjust a value
    100 - `LV_PART_SELECTED` Indicate the currently selected option or section
    101 - `LV_PART_ITEMS` Used if the widget has multiple similar elements (e.g. table cells)
    102 - `LV_PART_TICKS` Ticks on scales e.g. for a chart or meter
    103 - `LV_PART_CURSOR` Mark a specific place e.g. text area's or chart's cursor
    104 - `LV_PART_CUSTOM_FIRST` Custom part identifiers can be added starting from here.
    105 
    106 
    107 For example a [Slider](/widgets/core/slider) has three parts:
    108 - Background
    109 - Indicator
    110 - Knob
    111 
    112 This means all three parts of the slider can have their own styles. See later how to add styles to objects and parts.
    113 
    114 ## Initialize styles and set/get properties
    115 
    116 Styles are stored in `lv_style_t` variables. Style variables should be `static`, global or dynamically allocated.
    117 In other words they cannot be local variables in functions which are destroyed when the function exits.
    118 Before using a style it should be initialized with `lv_style_init(&my_style)`.
    119 After initializing a style, properties can be added or changed.
    120 
    121 Property set functions looks like this: `lv_style_set_<property_name>(&style, <value>);` For example:
    122 ```c
    123 static lv_style_t style_btn;
    124 lv_style_init(&style_btn);
    125 lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588));
    126 lv_style_set_bg_opa(&style_btn, LV_OPA_50);
    127 lv_style_set_border_width(&style_btn, 2);
    128 lv_style_set_border_color(&style_btn, lv_color_black());
    129 
    130 static lv_style_t style_btn_red;
    131 lv_style_init(&style_btn_red);
    132 lv_style_set_bg_color(&style_btn_red, lv_plaette_main(LV_PALETTE_RED));
    133 lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER);
    134 ```
    135 
    136 To remove a property use:
    137 
    138 ```c
    139 lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);
    140 ```
    141 
    142 To get a property's value from a style:
    143 ```c
    144 lv_style_value_t v;
    145 lv_res_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
    146 if(res == LV_RES_OK) {	/*Found*/
    147 	do_something(v.color);
    148 }
    149 ```
    150 
    151 `lv_style_value_t` has 3 fields:
    152 - `num` for integer, boolean and opacity properties
    153 - `color` for color properties
    154 - `ptr` for pointer properties
    155 
    156 To reset a style (free all its data) use:
    157 ```c
    158 lv_style_reset(&style);
    159 ```
    160 
    161 Styles can be built as `const` too to save RAM:
    162 ```c
    163 const lv_style_const_prop_t style1_props[] = {
    164    LV_STYLE_CONST_WIDTH(50),
    165    LV_STYLE_CONST_HEIGHT(50),
    166    LV_STYLE_PROP_INV,
    167 };
    168 
    169 LV_STYLE_CONST_INIT(style1, style1_props);
    170 ```
    171 
    172 Later `const` style can be used like any other style but (obviously) new properties can not be added.
    173 
    174 
    175 ## Add and remove styles to a widget
    176 A style on its own is not that useful. It must be assigned to an object to take effect.
    177 
    178 ### Add styles
    179 To add a style to an object use `lv_obj_add_style(obj, &style, <selector>)`. `<selector>` is an OR-ed value of parts and state to which the style should be added. Some examples:
    180 - `LV_PART_MAIN | LV_STATE_DEFAULT`
    181 - `LV_STATE_PRESSED`: The main part in pressed state. `LV_PART_MAIN` can be omitted
    182 - `LV_PART_SCROLLBAR`: The scrollbar part in the default state. `LV_STATE_DEFAULT` can be omitted.
    183 - `LV_PART_SCROLLBAR | LV_STATE_SCROLLED`: The scrollbar part when the object is being scrolled
    184 - `0` Same as `LV_PART_MAIN | LV_STATE_DEFAULT`.
    185 - `LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED` The indicator part when the object is pressed and checked at the same time.
    186 
    187 Using `lv_obj_add_style`:
    188 ```c
    189 lv_obj_add_style(btn, &style_btn, 0);      				  /*Default button style*/
    190 lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED);  /*Overwrite only some colors to red when pressed*/
    191 ```
    192 
    193 ### Remove styles
    194 To remove all styles from an object use `lv_obj_remove_style_all(obj)`.
    195 
    196 To remove specific styles use `lv_obj_remove_style(obj, style, selector)`. This function will remove `style` only if the `selector` matches with the `selector` used in `lv_obj_add_style`.
    197 `style` can be `NULL` to check only the `selector` and remove all matching styles. The `selector` can use the `LV_STATE_ANY` and `LV_PART_ANY` values to remove the style from any state or part.
    198 
    199 
    200 ### Report style changes
    201 If a style which is already assigned to an object changes (i.e. a property is added or changed), the objects using that style should be notified. There are 3 options to do this:
    202 1. If you know that the changed properties can be applied by a simple redraw (e.g. color or opacity changes) just call `lv_obj_invalidate(obj)` or `lv_obj_invalidate(lv_scr_act())`.
    203 2. If more complex style properties were changed or added, and you know which object(s) are affected by that style call `lv_obj_refresh_style(obj, part, property)`.
    204 To refresh all parts and properties use `lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY)`.
    205 3. To make LVGL check all objects to see if they use a style and refresh them when needed, call `lv_obj_report_style_change(&style)`. If `style` is `NULL` all objects will be notified about a style change.
    206 
    207 ### Get a property's value on an object
    208 To get a final value of property - considering cascading, inheritance, local styles and transitions (see below) - property get functions like this can be used:
    209 `lv_obj_get_style_<property_name>(obj, <part>)`.
    210 These functions use the object's current state and if no better candidate exists they return a default value.  
    211 For example:
    212 ```c
    213 lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);
    214 ```
    215 
    216 ## Local styles
    217 In addition to "normal" styles, objects can also store local styles. This concept is similar to inline styles in CSS (e.g. `<div style="color:red">`) with some modification.
    218 
    219 Local styles are like normal styles, but they can't be shared among other objects. If used, local styles are allocated automatically, and freed when the object is deleted.
    220 They are useful to add local customization to an object.
    221 
    222 Unlike in CSS, LVGL local styles can be assigned to states (*pseudo-classes*) and parts (*pseudo-elements*).
    223 
    224 To set a local property use functions like `lv_obj_set_style_<property_name>(obj, <value>, <selector>);`  
    225 For example:
    226 ```c
    227 lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);
    228 ```
    229 ## Properties
    230 For the full list of style properties click [here](/overview/style-props).
    231 
    232 ### Typical background properties
    233 In the documentation of the widgets you will see sentences like "The widget uses the typical background properties". These "typical background properties" are the ones related to:
    234 - Background
    235 - Border
    236 - Outline
    237 - Shadow
    238 - Padding
    239 - Width and height transformation
    240 - X and Y translation
    241 
    242 
    243 ## Transitions
    244 By default, when an object changes state (e.g. it's pressed) the new properties from the new state are set immediately. However, with transitions it's possible to play an animation on state change.
    245 For example, on pressing a button its background color can be animated to the pressed color over 300 ms.
    246 
    247 The parameters of the transitions are stored in the styles. It's possible to set
    248 - the time of the transition
    249 - the delay before starting the transition
    250 - the animation path (also known as the timing or easing function)
    251 - the properties to animate
    252 
    253 The transition properties can be defined for each state. For example, setting a 500 ms transition time in the default state means that when the object goes to the default state a 500 ms transition time is applied.
    254 Setting a 100 ms transition time in the pressed state causes a 100 ms transition when going to the pressed state.
    255 This example configuration results in going to the pressed state quickly and then going back to default slowly.
    256 
    257 To describe a transition an `lv_transition_dsc_t` variable needs to be initialized and added to a style:
    258 ```c
    259 /*Only its pointer is saved so must static, global or dynamically allocated */
    260 static const lv_style_prop_t trans_props[] = {
    261 											LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
    262 											0, /*End marker*/
    263 };
    264 
    265 static lv_style_transition_dsc_t trans1;
    266 lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);
    267 
    268 lv_style_set_transition(&style1, &trans1);
    269 ```
    270 
    271 ## Color filter
    272 TODO
    273 
    274 
    275 ## Themes
    276 Themes are a collection of styles. If there is an active theme LVGL applies it on every created widget.
    277 This will give a default appearance to the UI which can then be modified by adding further styles.
    278 
    279 Every display can have a different theme. For example, you could have a colorful theme on a TFT and monochrome theme on a secondary monochrome display.
    280 
    281 To set a theme for a display, two steps are required:
    282 1. Initialize a theme
    283 2. Assign the initialized theme to a display.
    284 
    285 Theme initialization functions can have different prototypes. This example shows how to set the "default" theme:
    286 ```c
    287 lv_theme_t * th = lv_theme_default_init(display,  /*Use the DPI, size, etc from this display*/
    288                                         LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN,   /*Primary and secondary palette*/
    289                                         false,    /*Light or dark mode*/
    290                                         &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/
    291 
    292 lv_disp_set_theme(display, th); /*Assign the theme to the display*/
    293 ```
    294 
    295 
    296 The included themes are enabled in `lv_conf.h`. If the default theme is enabled by `LV_USE_THEME_DEFAULT 1` LVGL automatically initializes and sets it when a display is created.
    297 
    298 ### Extending themes
    299 
    300 Built-in themes can be extended.
    301 If a custom theme is created, a parent theme can be selected. The parent theme's styles will be added before the custom theme's styles.
    302 Any number of themes can be chained this way. E.g. default theme -> custom theme -> dark theme.
    303 
    304 `lv_theme_set_parent(new_theme, base_theme)` extends the `base_theme` with the `new_theme`.
    305 
    306 There is an example for it below.
    307 
    308 ## Examples
    309 
    310 ```eval_rst
    311 
    312 .. include:: ../../examples/styles/index.rst
    313 
    314 ```
    315 
    316 ## API
    317 ```eval_rst
    318 
    319 .. doxygenfile:: lv_style.h
    320   :project: lvgl
    321 
    322 .. doxygenfile:: lv_theme.h
    323   :project: lvgl
    324 
    325 .. doxygenfile:: lv_obj_style_gen.h
    326   :project: lvgl
    327 
    328 .. doxygenfile:: lv_style_gen.h
    329   :project: lvgl
    330 
    331 
    332 ```