1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
|
/*
* Copyright 2015 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \page lomiri-theming.html
* \title Introduction
* \nextpage Styles
*
* Table of contents:
* \list
* \li \l Introduction
* \li \l Styles
* \li \l Themes
* \li \l Sub-theming
* \endlist
*
* \section2 Introduction
* The \l{Styles}{style} defines the visuals, the implicit size of the component and may provide
* additional logic for a given component. Some components have fixed style API the
* component relies on, and yet some others do not have any restriction on what the
* style contains or does. Components relying on a well defined API provide these
* interfaces through Lomiri.Components.Styles module, and styles must implement these
* interfaces.
*
* The implicit size of a component is driven by the style as well, but it is not
* mandatory for a style to provide those values, and components can override these
* values at any time. However each style component is anchor filled to the styled
* component and positioned to be under child components declared within the component
* itself.
*
* Styles can be declared as Components (like delegates), in a separate document
* loaded dynamically using a Loader or Qt.createComponent(), or can be grouped
* in \l{Themes}{themes}. When declared in themes, with some exceptions, each component's style
* document name is compound of the component name having the \e Style word as postfix,
* followed by the typical QML file name extension. In this way the Button component's
* style is ButtonStyle. In addition to styles, themes can also provide custom palette
* values. There are two themes in Lomiri, Ambiance and SuruDark themes, Ambiance
* being the default theme. Both themes are declared in separate modules, in \c
* Lomiri.Components.Themes.Ambiance as well as \c Lomiri.Components.Themes.SuruDark.
* Application should define the theme name using this dotted name format.
*
* Applications can decide which theme they want to use or they can provide their
* own themes. Also, applications can use multiple themes or set custom palette values.
*
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
*
* MainView {
* width: units,gu(40)
* height: units.gu(71)
*
* theme.name: "Lomiri.Components.Themes.SuruDark"
* }
* \endqml
*/
/*!
* \page lomiri-theming-styles.html
* \title Styles
* \previouspage Introduction
* \nextpage Themes
* \contentspage {Introduction} {Contents}
*
* \section2 Naming conventions
* With few exception, each toolkit component is having StyledItem as its base
* component. The component is aimed to be the base component for all styled
* elements in the toolkit. Modules providing additional components to UI Toolkit
* can also use this component as base, especially if they want to provide styling
* capabilities.
*
* As mentioned, each styled component is having a style pair in a theme which is
* implemented in a document named using the component name adding the \e Style word.
* The \l {Button}'s style is implemented by \c ButtonStyle.qml document in the theme, and
* \l Button is loading this style from the theme:
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
*
* AbstractButton {
* id: button
* // [...]
* styleName: "ButtonStyle"
* }
* \endqml
* Developers can override the style in two ways, depending on how they want to style
* the component:
* \list
* \li 1. by overriding the style using an other style component from the theme, or
* \li 2. by overriding the style with a custom style component.
* \endlist
* But let's see first how the styles are implemented.
*
* \section2 Creating styles
* As mentioned before, styles are not restricted to only provide visuals to a component or
* set of components, but can have also logic which drives the component functionality.
* These kind of styles must implement the given component's style API, listed in
* \c Lomiri.Components.Styles module.
*
* A good example of such a style is the ListItemStyle. The ListItem drives the style
* animation through the \l ListItemStyle::animatePanels property, value being false when
* the style is loaded during component creation, i.e. when the component requires some
* visuals to be present at creation time. Also, ListItem informs the style when to
* execute leading or trailing panel \l {ListItemStyle::rebound}{rebound}s and provides
* the style the ability to overrule the \l {ListItemStyle::swipeEvent}{swipe} coordinates
* calculated by the ListItem. On the other hand, the style must inform the component
* about the position of the \l {ListItemStyle::dragPanel}{drag panel} so the dragging
* (reordering of list items in a ListView) hot spot can be detected. A style implementation
* must use the API provided by ListItemStyle, otherwise ListItem will fail to function
* properly.
*
* Other styles are requested to provide additional \e content elements, which are then
* positioned by the styled component separately from the main visuals given by the style.
* A typical example of such a style API is the PullToRefresh component's style:
* \snippet Styles/1.2/PullToRefreshStyle.qml 0
* The default style implementation can be found under Lomiri.Components.Themes.Ambiance
* theme \l {https://gitlab.com/ubports/development/core/lomiri-ui-toolkit/-/blob/main/src/imports/Components/Themes/Ambiance/1.3/PullToRefreshStyle.qml}
* {src/imports/Components/Themes/Ambiance/1.3/PullToRefreshStyle.qml}.
*
* Beside these, component styles may provide default values for colors, fonts, widths,
* margins, thicknesses.
*
* Each style component has a \c styledItem context property defined by the StyledItem,
* which points to the StyledItem instance that uses the style. Style implementations can
* access the actual styled item through this property.
*
* \section2 Overriding the default component style
*
* \section3 Override with a different style from the theme
* Returning back to the ways to override a component's style, overriding by using a different
* style from the theme can simply be done by assigning the document name to the \l {StyledItem::styleName}
* {StyledItem.styleName} property as follows:
* \qml
* Button {
* id: button
* styleName: "SquaryButtonStyle"
* }
* \endqml
* \note The document extension doesn't have to be specified, the style creation will automatically
* append the .qml extension to it. This kind of style override assumes that the \c SquaryButtonStyle.qml
* document is present in the theme.
* This type of component styling makes sure the style will always have theme specific implementation
* or coloring, however it also requires the style document to be present in all the themes
* used by the application.
*
* \section3 Override with a custom component
* The other way is to override the style with a local component not present in any theme.
* The style component can be in-source (Component) or declared in a separate document,
* loaded dynamically with Loader or Qt.createComponent(). This kind of override will make
* sure the component will use the custom style no matter of the theme used. These styles
* however can still use the theme palette to be in sync with the theme coloring.
* \qml
* Button {
* id: button
* style: Rectangle {
* implicitWidth: units.gu(12)
* implicitHeight: units.gu(5)
* color: styledItem.color
* border {
* width: units.dp(1)
* color: styledItem.strokeColor
* }
* Label {
* text: styledItem.text
* font: styledItem.font
* }
* }
* }
* \endqml
* \note Specifying a component for the \l {StyledItem::style}{StyledItem.style} has precedence
* over the \l {StyledItem::styleName}{StyledItem::styleName}. When both set, the stlke specified
* \c style property will be used. When this property is set to undefined or null, the style specified
* in \c styleName will be used. Obviously, when both properties are invalid, no style will be used.
*/
/*!
* \page lomiri-theming-themes.html
* \title Themes
* \previouspage Styles
* \nextpage Sub-theming
* \contentspage {Introduction} {Contents}
*
* A theme is a collection of style implementations. The style component names are
* typically built using the component name and the Style word, exceptions being
* documented per component.
*
* In addition to the styles the theme can provide palette values used by the style
* and components. The palette values are defined in \c Palette.qml file, which must
* either be derived from \l Palette component or from a parent theme's Palette.
*
* There are two types of themes, shared themes and application themes. These themes
* do not differ in structure but in the way they are exposed. Shared themes are
* located either under \b QML2_IMPORT_PATH or \b XDG_DATA_DIR. Application themes
* are located under the application's private folder, therefore they are typically
* serving the application styling needs, and cannot be shared. Shared themes are also
* presented as QML extension modules, giving the possibility for application themes
* to extend them.
*
* \note Yet there is no possibility to install shared themes into the system through
* app store, only by providing them as part of the system image.
*
* The system provides two shared themes, \b Ambiance and \b SuruDark, the latest derived
* from Ambiance theme. Both themes can be used as base theme in application themes,
* by importing the modules defining them. If you decide to create a shared theme
* that can be used also as base for custom or application themes, it is recommended
* to do the same approach as the system themes do.
*
* \section2 Theme structure
* \image surudark-theme.png
* The theme structure is similar to a QML extension module structure, and this applies
* to both shared and application themes. Contains component style documents, palette
* description document, \c qmldir file and a special file called \c parent_theme. All
* these files are optional in a theme depending on context.
*
* \c parent_theme is a special file which defines the theme the current one is derifed from.
* The parent theme must be a shared theme and its name must be specified in dotted format.
* For example SuruDark theme is located under \c{$QML2_IMPORT_PATH/Lomiri/Components/Themes/SuruDark}
* folder. This means that the theme name is identified by the \b Lomiri.Components.Themes.SuruDark
* dotted format.
*
* In addition to the files mentioned, themes can provide components supporting the styling
* (i.e. components providing common visuals in the style implementations). Style implementations
* can also provide additional APIs alongside the standard style API. These additional APIs are
* typically there to configure the style itself, or to turn on/off features provided by styles
* in derived themes.
*
* Starting with Lomiri Components version 1.3, themes must provide versioned styles. This means
* that themes must store the styles in subfolders, meaning that toolkit version 1.3 must have
* the version specific styles under the theme's 1.3/ subfolder. The styling engine will look
* for the styles based on the theme version used by the component. If the style is not found
* with the requested version, it will fall back to the previous version of that style.
* \image surudark-theme-13.png
*
* \section3 Standalone theme
* A standalone theme is a theme which defines all style documents and theme palette,
* and it is not derived from any theme. The only standalone theme UI Toolkit provides is
* the Ambiance theme. Both shared and application themes can be standalone themes, however
* Applications should make sure they implement all the styles used by the toolkit components
* used in the application. \c qmldir file presence is mandatory only if the theme is
* shared.
*
* \section3 Derived themes
* As the name suggests derived themes are themes which use other themes (standalone or derived ones)
* as base theme. These themes must have the \c parent_theme file which contains the
* name of the theme they are derived from. These themes do not have to provide palette configuration
* as long as they use the derived theme's palette values.
*
* Derived themes should only list those style components which overrule the derived style or
* extend the derived style. The only requirement is to use the same document name as the parent theme
* is having.
*
* Let's take SuruDark theme as en example. The theme is derived from Ambiance theme, and only
* extends few style components. When theming engine loads the style components, it looks
* after the styles starting from the current theme. If the style is not found there, it
* tries to look after the style in the parent themes until it finds one.
*
* The extended (or even overridden) style component documents must follow the naming
* convention, and must have the names implied by the components styled.
*
* The style imports the Ambiance theme module, and extends the Ambiance ListItemStyle
* component. The same is done in the other style components. However, Palette defines
* own values, and does not re-use Ambiance palette values.
* \snippet Themes/SuruDark/1.3/Palette.qml 0
*
* \note If a theme derived from SuruDark wants to override style components not present
* in SuruDark, they must import the SuruDark's parent theme in the style component.
* As example, if FancyTheme would want to override the SwitchStyle, it would need to
* import Ambiance module in the component as SuruDark doesn't have that style component
* defined.
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
* import Lomiri.Components.Themes.Ambiance 1.3 as Ambiance
* Ambiance.SwitchStyle {
* // [...]
* }
* \endqml
*
* \section2 Application themes
* Application themes can also be standalone or derived themes. Usually applications
* need slight differences on certain component styles, colors. These can be configured
* in multiple ways, depending on the needs of the application. Applications can decide
* to have their own theme, and override the palette value in the theme, or to use the
* system themes and override few color values from the theme palette.
*
* Let's take an example of an application which changes some palette values of SuruDark
* theme.
*
* First, the application has to define the theme, preferably in a separate folder (e.g.
* theme). The folder should contain a \c parent_theme file with the content
* \code
* Lomiri.Components.Themes.SuruDark
* \endcode
* This will make sure theme engine will look after the style components that are not
* defined by the application theme inside the parent theme. Remember, the parent themes
* can have parent themes (SuruDark is derived from Ambiance) in which case the style components
* will be looked up in all these themes.
*
* As shown next, the application can define the palette.
* \snippet customtheme/theme/Palette.qml 0
* Note that the palette uses the SuruDark palette as base, and changes few colors from it.
*
* The application can use its own theme in the following way:
* \snippet customtheme/main.qml 0
*
* \note An application, which overrides multiple shared theme versions must provide separate
* style versions in its theme so the styling engine can identify the proper styles for the
* components. Note however that this is not mandatory, and it is up to the application developer
* to decide whether the application should support multiple style versions or not.
*/
/*!
* \page lomiri-theming-subtheming.html
* \title Sub-theming
* \previouspage Themes
* \contentspage {Introduction} {Contents}
*
* There can be situations when an application has a design which combines styles from different
* themes, which would not be possible or would be hard to be combined in a single theme. In these
* situations developers can use different themes in the components and its child components.
* This is called sub-theming, which was introduced in Lomiri.Components 1.3.
* The only thing the application has to do is to define a ThemeSettings instance for the
* component which is desired to use a different theme.
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
* import Lomiri.Components.Popups 1.3
* MainView {
* width: units.gu(40)
* height: units.gu(71)
*
* applicationName: "subthemed"
*
* // make sure the main theme is Ambiance
* theme.name: "Lomiri.Components.Themes.Ambiance"
*
* Component {
* id: dialogComponent
* Dialog {
* id: dialog
* title: "Input dialog"
* // the dialog and its children will use SuruDark
* theme: ThemeSettings {
* name: "Lomiri.Components.Themes.SuruDark"
* }
* TextField {
* placeholderText: "enter text"
* }
* Button {
* text: "Close"
* onClicked: PopupUtils.close(dialog)
* }
* }
* }
*
* Button {
* text: "Open dialog"
* onClicked: PopupUtils.open(dialogComponent)
* }
* }
* \endqml
*
* Another use-case is when a different palette set is needed in the application.
* One way to achieve that is to define a custom theme for the application, however
* that theme must be derived from one particular theme, so the application will be
* restricted to one given theme. If we want to have the same palette values to be used
* no matter where the component is used, we can override the palette values we want to
* change, by setting the theme palette to a \l Palette instance where only the desired
* palette values are changed. This can be combined with sub-theming, which will make
* sure that the palette values are applied only on a certain component sub-tree.
*
* The following example makes sure the Dialog and its child components will use a given
* palette value:
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
* import Lomiri.Components.Themes 1.3
*
* MainView {
* width: units.gu(40)
* height: units.gu(71)
*
* applicationName: "subthemed"
*
* Component {
* id: dialogComponent
* Dialog {
* id: dialog
* title: "Input dialog"
* // make sure the dialog and its children will use the same
* // theme as the rest of the application
* theme: ThemeSettings {
* name: parentTheme.name
* palette: Palette {
* id: config
* normal {
* foregroundText: LomiriColors.blue
* overlayText: "#BAFEDC"
* }
* selected {
* fieldText: "brown"
* foregroundText: Qt.rgba(0, 0, 1, 1)
* overlayText: config.normal.overlayText
* foreground: LomiriColors.green
* }
* }
* }
* TextField {
* placeholderText: "enter text"
* }
* Button {
* text: "Close"
* onClicked: PopupUtils.close(dialog)
* }
* }
* }
*
* Column {
* spacing: units.gu(1)
* Button {
* text: "Set Ambiance theme"
* onClicked: theme.name = "Lomiri.Components.Themes.Ambiance"
* }
* Button {
* text: "Set SuruDark theme"
* onClicked: theme.name = "Lomiri.Components.Themes.SuruDark"
* }
* Button {
* text: "Open dialog"
* onClicked: PopupUtils.open(dialogComponent)
* }
* }
* }
* \endqml
* \note Note the way the theme is changed! The first two buttons actually change the
* name of the theme they inherit, which is the application's theme. This means that
* the theme will actually be changed on the entire application, not only on the Button
* itself.
*
* The Dialog uses the \l {ThemeSettings::parentTheme}{parentTheme} property to load
* the same theme as its parent styled item is using, meaning that the Dialog will
* also load the same theme as the application does, and will change the loaded palette
* values with the ones defined in the \c config Palette instance, namely the \c
* foregroundText and \c overlayText of \c normal, as well as \c fieldText, \c foregroundText,
* \c overlayText and \c foreground on \c selected groups.
*
* There may be cases when a subset of components wants to use different style versions
* than the one provided by the module version. Remember, using earlier minor versions
* of the theme is perfectly fine while using newer versions may not work, as component
* styles may use newer APIs of the component which is not present in the component, thus
* the style will fail.
*
* Let's take the example above, and assume that we want to show the Dialog with the same
* theme as the application but with an earlier version. We can do this by specifying the
* theme version with \l {Lomiri::version}{Lomiri.version()} as follows:
* \qml
* import QtQuick 2.4
* import Lomiri.Components 1.3
* import Lomiri.Components.Themes 1.3
*
* MainView {
* width: units.gu(40)
* height: units.gu(71)
*
* applicationName: "subthemed"
*
* Component {
* id: dialogComponent
* Dialog {
* id: dialog
* title: "Input dialog"
* // make sure the dialog and its children will use the same
* // theme as the rest of the application
* theme: ThemeSettings {
* name: parentTheme.name
* // use version 1.2 of the theme
* version: Lomiri.version(1, 2)
* }
* TextField {
* placeholderText: "enter text"
* }
* Button {
* text: "Close"
* onClicked: PopupUtils.close(dialog)
* }
* }
* }
*
* Column {
* spacing: units.gu(1)
* Button {
* text: "Set Ambiance theme"
* onClicked: theme.name = "Lomiri.Components.Themes.Ambiance"
* }
* Button {
* text: "Set SuruDark theme"
* onClicked: theme.name = "Lomiri.Components.Themes.SuruDark"
* }
* Button {
* text: "Open dialog"
* onClicked: PopupUtils.open(dialogComponent)
* }
* }
* }
* \endqml
*
* \section1 That's it
* By now you should have learned what the styling means, what are the themes, what
* kind of themes the toolkit has, how can you create shared or application themes, where should
* you store them, how to extend styles, how to use multiple themes in an application
* and how to set custom palette values runtime. If you have questions or need guidance,
* you can contact us on \b{#lomiri-app-devel} IRC channel on freenode.
*/
|