1.. _styles_overview: 2 3=============== 4Styles Overview 5=============== 6 7Styles are used to set the appearance of Widgets. Styles in LVGL are 8heavily inspired by CSS. The concept in a nutshell is that a 9style is an :cpp:type:`lv_style_t` variable which can hold properties like 10border width, font, text color and so on. It's similar to a ``class`` in CSS. 11 12- Styles can be assigned to Widgets to change their appearance. Upon 13 assignment, the target part (pseudo-element_ in CSS) and target state 14 (pseudo-class_ in CSS) can be specified. For example one can add 15 ``style_blue`` to the knob of a slider when it's in pressed state. 16- The same style can be used by any number of Widgets. 17- Styles can be cascaded which means multiple styles may be assigned to a Widget and 18 each style can have different properties. Therefore, not all properties 19 have to be specified in a style. LVGL will search for a property until a 20 style defines it or use a default value if it's not specified by any of the 21 styles. For example ``style_btn`` can result in a default gray button 22 and ``style_btn_red`` can add only a ``background-color=red`` to 23 overwrite the background color. 24- The most recently added style has higher precedence. This means if a property 25 is specified in two styles the newest style in the Widget will be used. 26- Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in a Widget. 27- Widgets can also have :ref:`local styles <style_local>` with higher precedence than "normal" styles. 28- Unlike CSS (where pseudo-classes_ describe different states, e.g. ``:focus``), 29 in LVGL a property is assigned to a given state. 30- Transitions can be applied when the Widget changes state. 31 32 33 34.. _style_states: 35 36States 37****** 38 39The Widgets can be in the combination of the following states: 40 41- :cpp:enumerator:`LV_STATE_DEFAULT`: (0x0000) Normal, released state 42- :cpp:enumerator:`LV_STATE_CHECKED`: (0x0001) Toggled or checked state 43- :cpp:enumerator:`LV_STATE_FOCUSED`: (0x0002) Focused via keypad or encoder or clicked via touchpad/mouse 44- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: (0x0004) Focused via keypad or encoder but not via touchpad/mouse 45- :cpp:enumerator:`LV_STATE_EDITED`: (0x0008) Edit by an encoder 46- :cpp:enumerator:`LV_STATE_HOVERED`: (0x0010) Hovered by mouse 47- :cpp:enumerator:`LV_STATE_PRESSED`: (0x0020) Being pressed 48- :cpp:enumerator:`LV_STATE_SCROLLED`: (0x0040) Being scrolled 49- :cpp:enumerator:`LV_STATE_DISABLED`: (0x0080) Disabled state 50- :cpp:enumerator:`LV_STATE_USER_1`: (0x1000) Custom state 51- :cpp:enumerator:`LV_STATE_USER_2`: (0x2000) Custom state 52- :cpp:enumerator:`LV_STATE_USER_3`: (0x4000) Custom state 53- :cpp:enumerator:`LV_STATE_USER_4`: (0x8000) Custom state 54 55A Widget can be in a combination of states such as being focused and 56pressed at the same time. This is represented as :cpp:expr:`LV_STATE_FOCUSED | LV_STATE_PRESSED`. 57 58A style can be added to any state or state combination. For example, 59setting a different background color for the default and pressed states. 60If a property is not defined in a state the best matching state's 61property will be used. Typically this means the property with 62:cpp:enumerator:`LV_STATE_DEFAULT` is used.˛ If the property is not set even for the 63default state the default value will be used. (See later) 64 65What does the "best matching state's property" mean? 66---------------------------------------------------- 67States have a precedence which is shown by their value (see in the above list). 68A higher value means higher precedence. To determine which state's 69property to use let's take an example. Imagine the background color is 70defined like this: 71 72- :cpp:enumerator:`LV_STATE_DEFAULT`: white 73- :cpp:enumerator:`LV_STATE_PRESSED`: gray 74- :cpp:enumerator:`LV_STATE_FOCUSED`: red 75 761. Initially the Widget is in the default state, so it's a simple case: 77 the property is perfectly defined in the Widget's current state as 78 white. 792. When the Widget is pressed there are 2 related properties: default 80 with white (default is related to every state) and pressed with gray. 81 The pressed state has 0x0020 precedence which is higher than the 82 default state's 0x0000 precedence, so gray color will be used. 833. When the Widget has focus the same thing happens as in pressed state 84 and red color will be used. (Focused state has higher precedence than 85 default state). 864. When the Widget has focus and pressed both gray and red would work, 87 but the pressed state has higher precedence than focused so gray 88 color will be used. 895. It's possible to set e.g. rose color for :cpp:expr:`LV_STATE_PRESSED | LV_STATE_FOCUSED`. 90 In this case, this combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than 91 the pressed state's precedence so rose color would be used. 926. When the Widget is in the checked state there is no property to set 93 the background color for this state. So for lack of a better option, 94 the Widget remains white from the default state's property. 95 96Some practical notes: 97 98- The precedence (value) of states is quite intuitive, and it's something the 99 user would expect naturally. Example: if a Widget has focus the user will still 100 want to see if it's pressed, therefore the pressed state has a higher 101 precedence. If the focused state had a higher precedence it would overwrite 102 the pressed color. 103- If you want to set a property for all states (e.g. red background color) 104 just set it for the default state. If the Widget can't find a property 105 for its current state it will fall back to the default state's property. 106- Use ORed states to describe the properties for complex cases (e.g. 107 pressed + checked + focused). 108- It might be a good idea to use different 109 style elements for different states. For example, finding background 110 colors for released, pressed, checked + pressed, focused, focused + 111 pressed, focused + pressed + checked, etc. states is quite difficult. 112 Instead, for example, use the background color for pressed and checked 113 states and indicate the focused state with a different border color. 114 115 116 117.. _style_cascading: 118 119Cascading Styles 120**************** 121 122It's not required to set all the properties in one style. It's possible 123to add more styles to a Widget and have the latter added style modify 124or extend appearance. For example, create a general gray button style 125and create a new one for red buttons where only the new background color 126is set. 127 128This is much like in CSS when used classes are listed like 129``<div class=".btn .btn-red">``. 130 131Styles added later have precedence over ones set earlier. So in the 132gray/red button example above, the normal button style should be added 133first and the red style second. However, the precedence of the states 134are still taken into account. So let's examine the following case: 135 136- the basic button style defines dark-gray color for the default state and 137 light-gray color for the pressed state 138- the red button style defines the background color as red only in the default state 139 140In this case, when the button is released (it's in default state) it 141will be red because a perfect match is found in the most recently added 142style (red). When the button is pressed the light-gray color is a better 143match because it describes the current state perfectly, so the button 144will be light-gray. 145 146 147 148.. _style_inheritance: 149 150Inheritance 151*********** 152 153Some properties (typically those related to text) can be inherited from 154the parent Widget's styles. Inheritance is applied only if the given 155property is not set in the Widget's styles (even in default state). In 156this case, if the property is inheritable, the property's value will be 157searched up the parent hierarchy until a Widget specifies a value for the 158property. The parents will use their own state to determine the value. 159So if a button is pressed, and the text color comes from a parent, the 160pressed text color will be used. 161 162 163 164.. _style_parts: 165 166Parts 167***** 168 169Widgets can be composed of *parts* which may each have their own styles. 170 171The following predefined parts exist in LVGL: 172 173- :cpp:enumerator:`LV_PART_MAIN`: A background like rectangle 174- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar(s) 175- :cpp:enumerator:`LV_PART_INDICATOR`: Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox 176- :cpp:enumerator:`LV_PART_KNOB`: Like a handle to grab to adjust a value 177- :cpp:enumerator:`LV_PART_SELECTED`: Indicate the currently selected option or section 178- :cpp:enumerator:`LV_PART_ITEMS`: Used if the widget has multiple similar elements (e.g. table cells) 179- :cpp:enumerator:`LV_PART_CURSOR`: Mark a specific place e.g. Text Area's or chart's cursor 180- :cpp:enumerator:`LV_PART_CUSTOM_FIRST`: Custom part identifiers can be added starting from here. 181 182For example a :ref:`Slider <lv_slider>` has three parts: 183 184- Main (background) 185- Indicator 186- Knob 187 188This means all three parts of the slider can have their own styles. See 189later how to add styles to Widgets and parts. 190 191 192 193.. _style_initialize: 194 195Initialize Styles and Set/Get Properties 196**************************************** 197 198Styles are stored in :cpp:type:`lv_style_t` variables. Style variables should be 199``static``, global or dynamically allocated. In other words they cannot 200be local variables in functions which are destroyed when the function 201exits. Before using a style it should be initialized with 202:cpp:expr:`lv_style_init(&my_style)`. After initializing a style, properties can 203be added or changed. 204 205Property set functions looks like this: 206``lv_style_set_<property_name>(&style, <value>);`` For example: 207 208.. code-block:: c 209 210 static lv_style_t style_btn; 211 lv_style_init(&style_btn); 212 lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588)); 213 lv_style_set_bg_opa(&style_btn, LV_OPA_50); 214 lv_style_set_border_width(&style_btn, 2); 215 lv_style_set_border_color(&style_btn, lv_color_black()); 216 217 static lv_style_t style_btn_red; 218 lv_style_init(&style_btn_red); 219 lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED)); 220 lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER); 221 222To remove a property use: 223 224.. code-block:: c 225 226 lv_style_remove_prop(&style, LV_STYLE_BG_COLOR); 227 228To get a property's value from a style: 229 230.. code-block:: c 231 232 lv_style_value_t v; 233 lv_result_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v); 234 if(res == LV_RESULT_OK) { /* Found */ 235 do_something(v.color); 236 } 237 238:cpp:union:`lv_style_value_t` has 3 fields, only one of which will apply, depending 239on the type of property it is applied to: 240 241- :cpp:member:`num`: for integer, boolean and opacity properties 242- :cpp:member:`color`: for color properties 243- :cpp:member:`ptr`: for pointer properties 244 245To reset a style (free all its data) use: 246 247.. code-block:: c 248 249 lv_style_reset(&style); 250 251Styles can be built as ``const`` as well to save RAM: 252 253.. code-block:: c 254 255 const lv_style_const_prop_t style1_props[] = { 256 LV_STYLE_CONST_WIDTH(50), 257 LV_STYLE_CONST_HEIGHT(50), 258 LV_STYLE_CONST_PROPS_END 259 }; 260 261 LV_STYLE_CONST_INIT(style1, style1_props); 262 263Later ``const`` style can be used like any other style but (obviously) 264new properties cannot be added. 265 266 267 268.. _style_add_remove: 269 270Add and remove styles to a widget 271********************************* 272 273A style on its own has no effect until it is added (assigned) to a Widget. 274 275 276Add styles 277---------- 278 279To add a style to a Widget use 280``lv_obj_add_style(widget, &style, <selector>)``. ``<selector>`` is an 281OR-ed value of parts and state to which the style should be added. Some 282examples: 283 284- :cpp:expr:`LV_PART_MAIN | LV_STATE_DEFAULT` 285- :cpp:enumerator:`LV_STATE_PRESSED`: The main part in pressed state. :cpp:enumerator:`LV_PART_MAIN` can be omitted 286- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar part in the default state. :cpp:enumerator:`LV_STATE_DEFAULT` can be omitted. 287- :cpp:expr:`LV_PART_SCROLLBAR | LV_STATE_SCROLLED`: The scrollbar part when the Widget is being scrolled 288- :cpp:expr:`LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED` The indicator part when the Widget is pressed and checked at the same time. 289 290Using :cpp:func:`lv_obj_add_style`: 291 292.. code-block:: c 293 294 lv_obj_add_style(btn, &style_btn, 0); /* Default button style */ 295 lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); /* Overwrite only some colors to red when pressed */ 296 297Replace styles 298-------------- 299 300To replace a specific style of a Widget use 301:cpp:expr:`lv_obj_replace_style(widget, old_style, new_style, selector)`. This 302function will only replace ``old_style`` with ``new_style`` if the 303``selector`` matches the ``selector`` used in ``lv_obj_add_style``. Both 304``old_style`` and ``new_style`` must not be ``NULL``. Separate functions exist for 305adding and removing styles. If the combination of 306``old_style`` and ``selector`` exists multiple times in ``obj``\ 's 307styles, all occurrences will be replaced. The return value of the 308function indicates whether at least one successful replacement took 309place. 310 311Using :cpp:func:`lv_obj_replace_style`: 312 313.. code-block:: c 314 315 lv_obj_add_style(btn, &style_btn, 0); /* Add a button style */ 316 lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0); /* Replace the button style with a different one */ 317 318Remove styles 319------------- 320 321To remove all styles from a Widget use :cpp:expr:`lv_obj_remove_style_all(widget)`. 322 323To remove specific styles use 324:cpp:expr:`lv_obj_remove_style(widget, style, selector)`. This function will remove 325``style`` only if the ``selector`` matches with the ``selector`` used in 326:cpp:func:`lv_obj_add_style`. ``style`` can be ``NULL`` to check only the 327``selector`` and remove all matching styles. The ``selector`` can use 328the :cpp:enumerator:`LV_STATE_ANY` and :cpp:enumerator:`LV_PART_ANY` values to remove the style from 329any state or part. 330 331Reporting style changes 332----------------------- 333 334If a style which is already assigned to a Widget changes (i.e. a 335property is added or changed), the Widgets using that style should be 336notified. There are 3 options to do this: 337 3381. If you know that the changed properties can be applied by a simple redraw 339 (e.g. color or opacity changes) just call :cpp:expr:`lv_obj_invalidate(widget)` 340 or :cpp:expr:`lv_obj_invalidate(lv_screen_active())`. 3412. If more complex style properties were changed or added, and you know which 342 Widget(s) are affected by that style call :cpp:expr:`lv_obj_refresh_style(widget, part, property)`. 343 To refresh all parts and properties use :cpp:expr:`lv_obj_refresh_style(widget, LV_PART_ANY, LV_STYLE_PROP_ANY)`. 3443. To make LVGL check all Widgets to see if they use a style and refresh them 345 when needed, call :cpp:expr:`lv_obj_report_style_change(&style)`. If ``style`` 346 is ``NULL`` all Widgets will be notified about a style change. 347 348Get a style property's value on a Widget 349---------------------------------------- 350 351To get the final value of a style's property considering 352 353- cascading, 354- inheritance, 355- local styles and transitions (see below) 356 357property "get" functions like this can be used: ``lv_obj_get_style_<property_name>(widget, <part>)``. 358These functions use the Widget's current state and if no better candidate exists they return the default value. 359For example: 360 361.. code-block:: c 362 363 lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN); 364 365 366 367.. _style_local: 368 369Local Styles 370************ 371 372In addition to "normal" styles, Widgets can also store local styles. 373This concept is similar to inline styles in CSS 374(e.g. ``<div style="color:red">``) with some modification. 375 376Local styles are like normal styles, but they can't be shared among 377other Widgets. If used, local styles are allocated automatically, and 378freed when the Widget is deleted. They are useful to add local 379customization to a Widget. 380 381Unlike in CSS, LVGL local styles can be assigned to states 382(pseudo-classes_) and parts (pseudo-elements_). 383 384To set a local property use functions like 385``lv_obj_set_style_<property_name>(widget, <value>, <selector>);`` For example: 386 387.. code-block:: c 388 389 lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED); 390 391 392 393.. _style_properties_overview: 394 395Style Properties Overview 396************************* 397 398For the full list of style properties click :ref:`here <style_properties>`. 399 400 401.. _typical bg props: 402 403Typical background properties 404----------------------------- 405 406In documentation of widgets you will see sentences like "The 407_____ Widget uses the typical background style properties". These "typical 408background properties" are the properties being referred to: 409 410- Background 411- Border 412- Outline 413- Shadow 414- Padding 415- Width and height transformation 416- X and Y translation 417 418See :ref:`boxing_model` for the meanings of these terms. 419 420 421 422.. _style_transitions: 423 424Transitions 425*********** 426 427By default, when a Widget changes state (e.g. it's pressed) the new 428properties from the new state are set immediately. However, with 429transitions it's possible to play an animation on state change. For 430example, on pressing a button its background color can be animated to 431the pressed color over 300 ms. 432 433The parameters of the transitions are stored in the styles. It's 434possible to set 435 436- the time of the transition 437- the delay before starting the transition 438- the animation path (also known as the timing or easing function) 439- the properties to animate 440 441The transition properties can be defined for each state. For example, 442setting a 500 ms transition time in the default state means that when 443the Widget goes to the default state a 500 ms transition time is 444applied. Setting a 100 ms transition time in the pressed state causes a 445100 ms transition when going to the pressed state. This example 446configuration results in going to the pressed state quickly and then 447going back to default slowly. 448 449To describe a transition an :cpp:struct:`lv_transition_dsc_t` variable needs to be 450initialized and added to a style: 451 452.. code-block:: c 453 454 /* Only its pointer is saved so must static, global or dynamically allocated */ 455 static const lv_style_prop_t trans_props[] = { 456 LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR, 457 0, /* End marker */ 458 }; 459 460 static lv_style_transition_dsc_t trans1; 461 lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms); 462 463 lv_style_set_transition(&style1, &trans1); 464 465 466 467.. _style_opacity_blend_modes_transformations: 468 469Opacity, Blend Modes and Transformations 470**************************************** 471 472If the ``opa``, ``blend_mode``, ``transform_angle``, or 473``transform_zoom`` properties are set to a non-default value LVGL 474creates a snapshot of the widget and its children in order to 475blend the whole widget with the set opacity, blend mode and 476transformation properties. 477 478These properties have this effect only on the ``MAIN`` part of the 479widget. 480 481The created snapshot is called "intermediate layer" or simply "layer". 482If only ``opa`` and/or ``blend_mode`` is set to a non-default value LVGL 483can build the layer from smaller chunks. The size of these chunks can be 484configured by the following properties in ``lv_conf.h``: 485 486- :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE`: [bytes] the optimal target buffer size. LVGL will try to allocate this size of memory. 487- :cpp:enumerator:`LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE`: [bytes] used if :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. 488 489If transformation properties were also used the layer cannot be 490rendered in chunks, but one larger memory block needs to be allocated. The 491required memory depends on the angle, zoom and pivot parameters, and the 492size of the area to redraw, but it's never larger than the size of the 493widget (including the extra draw size used for shadow, outline, etc). 494 495If the widget can fully cover the area to redraw, LVGL creates an RGB 496layer (which is faster to render and uses less memory). If the opposite 497case ARGB rendering needs to be used, a widget might not cover its area 498if it has radius, ``bg_opa < 255``, has shadow, outline, etc. 499 500The click area of the widget is also transformed accordingly. 501 502 503 504.. _style_color_filter: 505 506Color Filter 507************ 508 509TODO 510 511 512 513.. _style_themes: 514 515Themes 516****** 517 518Themes are a collection of styles. If there is an active theme LVGL 519applies it to every newly-created widget. This will give a default appearance 520to the UI which can then be modified by adding further styles. 521 522Every display can have a different theme. For example, you could have a 523colorful theme on a TFT and monochrome theme on a secondary monochrome 524display. 525 526To set a theme for a display, two steps are required: 527 5281. Initialize a theme 5292. Assign the initialized theme to a display. 530 531Theme initialization functions can have different prototypes. This 532example shows how to set the "default" theme: 533 534.. code-block:: c 535 536 lv_theme_t * th = lv_theme_default_init(display, /* Use DPI, size, etc. from this display */ 537 LV_COLOR_PALETTE_BLUE, /* Primary and secondary palette */ 538 LV_COLOR_PALETTE_CYAN, 539 false, /* Dark theme? False = light theme. */ 540 &lv_font_montserrat_10, /* Small, normal, large fonts */ 541 &lv_font_montserrat_14, 542 &lv_font_montserrat_18); 543 544 lv_display_set_theme(display, th); /* Assign theme to display */ 545 546The included themes are enabled in ``lv_conf.h``. If the default theme 547is enabled by :c:macro:`LV_USE_THEME_DEFAULT` LVGL automatically initializes 548and sets it when a display is created. 549 550Extending themes 551---------------- 552 553Built-in themes can be extended. If a custom theme is created, a parent 554theme can be selected. The parent theme's styles will be added before 555the custom theme's styles. Any number of themes can be chained this way. 556E.g. default theme -> custom theme -> dark theme. 557 558:cpp:expr:`lv_theme_set_parent(new_theme, base_theme)` extends the 559``base_theme`` with the ``new_theme``. 560 561There is an example of this below. 562 563.. _styles_example: 564 565Examples 566******** 567 568.. include:: ../../../examples/styles/index.rst 569 570 571 572.. Hyperlinks 573 574.. _pseudo-elements: 575.. _pseudo-element: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors#pseudo-classes_and_pseudo-elements 576.. _pseudo-classes: 577.. _pseudo-class: https://developer.mozilla.org/en-US/docs/Glossary/Pseudo-class 578 579 580 581 582.. _styles_api: 583 584API 585*** 586