| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Free |
| ** Documentation License version 1.3 as published by the Free Software |
| ** Foundation and appearing in the file included in the packaging of |
| ** this file. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example widgets/styles |
| \meta {tag} {gallery} |
| \title Styles Example |
| \ingroup examples-widgets |
| \brief The Styles example illustrates how to create custom widget |
| drawing styles using Qt, and demonstrates Qt's predefined styles. |
| |
| \borderedimage styles-enabledwood.png |
| \caption Screenshot of the Styles example |
| |
| A style in Qt is a subclass of QStyle or of one of its |
| subclasses. Styles perform drawing on behalf of widgets. Qt |
| provides a whole range of predefined styles, either built into |
| the Qt Widgets module or found in plugins. Styles are usually |
| customized by subclassing QProxyStyle and reimplementing a few |
| virtual functions. While QProxyStyle provides a transparent way |
| to customize either a specific style or the appropriate platform's |
| default style, Qt also provides QCommonStyle as a convenient base |
| for full custom style implementations. |
| |
| In this example, the custom style is called \c NorwegianWoodStyle |
| and derives from QProxyStyle. Its main features are the wooden |
| textures used for filling most of the widgets and its round |
| buttons and comboboxes. |
| |
| To implement the style, we use some advanced features provided by |
| QPainter, such as \l{QPainter::Antialiasing}{antialiasing} (to |
| obtain smoother button edges), \l{QColor::alpha()}{alpha blending} |
| (to make the buttons appeared raised or sunken), and |
| \l{QPainterPath}{painter paths} (to fill the buttons and draw the |
| outline). We also use many features of QBrush and QPalette. |
| |
| The example consists of the following classes: |
| |
| \list |
| \li \c NorwegianWoodStyle inherits from QProxyStyle and implements |
| the Norwegian Wood style. |
| \li \c WidgetGallery is a \c QDialog subclass that shows the most |
| common widgets and allows the user to switch style |
| dynamically. |
| \endlist |
| |
| \section1 NorwegianWoodStyle Class Definition |
| |
| Here's the definition of the \c NorwegianWoodStyle class: |
| |
| \snippet widgets/styles/norwegianwoodstyle.h 0 |
| |
| The public functions are all declared in QStyle (QProxyStyle's |
| grandparent class) and reimplemented here to override the Windows |
| look and feel. The private functions are helper functions. |
| |
| \section1 NorwegianWoodStyle Class Implementation |
| |
| We will now review the implementation of the \c |
| NorwegianWoodStyle class. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 0 |
| |
| The \c standardPalette() function is reimplemented from QStyle. |
| It returns a QPalette with the style's preferred colors and textures. |
| Most styles don't need to reimplement that function. The |
| Norwegian Wood style reimplements it to set a "wooden" palette. |
| |
| We start by defining a few \l{QColor}s that we'll need. Then we |
| load two PNG images. The \c : prefix in the file path indicates |
| that the PNG files are \l{The Qt Resource System}{embedded |
| resources}. |
| |
| \table |
| \row \li \inlineimage widgets/styles/images/woodbackground.png |
| |
| \li \b{woodbackground.png} |
| |
| This texture is used as the background of most widgets. |
| The wood pattern is horizontal. |
| |
| \row \li \inlineimage widgets/styles/images/woodbutton.png |
| |
| \li \b{woodbutton.png} |
| |
| This texture is used for filling push buttons and |
| comboboxes. The wood pattern is vertical and more reddish |
| than the texture used for the background. |
| \endtable |
| |
| The \c midImage variable is initialized to be the same as \c |
| buttonImage, but then we use a QPainter and fill it with a 25% |
| opaque black color (a black with an \l{QColor::alpha()}{alpha |
| channel} of 63). The result is a somewhat darker image than \c |
| buttonImage. This image will be used for filling buttons that the |
| user is holding down. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 1 |
| |
| We initialize the palette. Palettes have various |
| \l{QPalette::ColorRole}{color roles}, such as QPalette::Base |
| (used for filling text editors, item views, etc.), QPalette::Text |
| (used for foreground text), and QPalette::Background (used for |
| the background of most widgets). Each role has its own QBrush, |
| which usually is a plain color but can also be a brush pattern or |
| even a texture (a QPixmap). |
| |
| In addition to the roles, palettes have several |
| \l{QPalette::ColorGroup}{color groups}: active, disabled, and |
| inactive. The active color group is used for painting widgets in |
| the active window. The disabled group is used for disabled |
| widgets. The inactive group is used for all other widgets. Most |
| palettes have identical active and inactive groups, while the |
| disabled group uses darker shades. |
| |
| We initialize the QPalette object with a brown color. Qt |
| automatically derivates all color roles for all color groups from |
| that single color. We then override some of the default values. For |
| example, we use Qt::darkGreen instead of the default |
| (Qt::darkBlue) for the QPalette::Highlight role. The |
| QPalette::setBrush() overload that we use here sets the same |
| color or brush for all three color groups. |
| |
| The \c setTexture() function is a private function that sets the |
| texture for a certain color role, while preserving the existing |
| color in the QBrush. A QBrush can hold both a solid color and a |
| texture at the same time. The solid color is used for drawing |
| text and other graphical elements where textures don't look good. |
| |
| At the end, we set the brush for the disabled color group of the |
| palette. We use \c woodbackground.png as the texture for all |
| disabled widgets, including buttons, and use a darker color to |
| accompany the texture. |
| |
| \image styles-disabledwood.png The Norwegian Wood style with disabled widgets |
| |
| Let's move on to the other functions reimplemented from |
| QProxyStyle: |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 3 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 4 |
| |
| This QStyle::polish() overload is called once on every widget |
| drawn using the style. We reimplement it to set the Qt::WA_Hover |
| attribute on \l{QPushButton}s and \l{QComboBox}es. When this |
| attribute is set, Qt generates paint events when the mouse |
| pointer enters or leaves the widget. This makes it possible to |
| render push buttons and comboboxes differently when the mouse |
| pointer is over them. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 5 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 6 |
| |
| This QStyle::unpolish() overload is called to undo any |
| modification done to the widget in \c polish(). For simplicity, |
| we assume that the flag wasn't set before \c polish() was called. |
| In an ideal world, we would remember the original state for each |
| widgets (e.g., using a QMap<QWidget *, bool>) and restore it in |
| \c unpolish(). |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 7 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 8 |
| |
| The \l{QStyle::pixelMetric()}{pixelMetric()} function returns the |
| size in pixels for a certain user interface element. By |
| reimplementing this function, we can affect the way certain |
| widgets are drawn and their size hint. Here, we return 8 as the |
| width around a shown in a QComboBox, ensuring that there is |
| enough place around the text and the arrow for the Norwegian Wood |
| round corners. The default value for this setting in the Windows |
| style is 2. |
| |
| We also change the extent of \l{QScrollBar}s, i.e., the height |
| for a horizontal scroll bar and the width for a vertical scroll |
| bar, to be 4 pixels more than in the Windows style. This makes the |
| style a bit more distinctive. |
| |
| For all other QStyle::PixelMetric elements, we use the Windows |
| settings. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 9 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 10 |
| |
| The \l{QStyle::styleHint()}{styleHint()} function returns some |
| hints to widgets or to the base style (in our case QProxyStyle) |
| about how to draw the widgets. The Windows style returns \c true |
| for the QStyle::SH_DitherDisabledText hint, resulting in a most |
| unpleasing visual effect. We override this behavior and return \c |
| false instead. We also return \c true for the |
| QStyle::SH_EtchDisabledText hint, meaning that disabled text is |
| rendered with an embossed look. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 11 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 12 |
| |
| The \l{QStyle::drawPrimitive()}{drawPrimitive()} function is |
| called by Qt widgets to draw various fundamental graphical |
| elements. Here we reimplement it to draw QPushButton and |
| QComboBox with round corners. The button part of these widgets is |
| drawn using the QStyle::PE_PanelButtonCommand primitive element. |
| |
| The \c option parameter, of type QStyleOption, contains |
| everything we need to know about the widget we want to draw on. |
| In particular, \c option->rect gives the rectangle within which |
| to draw the primitive element. The \c painter parameter is a |
| QPainter object that we can use to draw on the widget. |
| |
| The \c widget parameter is the widget itself. Normally, all the |
| information we need is available in \c option and \c painter, so |
| we don't need \c widget. We can use it to perform special |
| effects; for example, QMacStyle uses it to animate default |
| buttons. If you use it, be aware that the caller is allowed to |
| pass a null pointer. |
| |
| We start by defining three \l{QColor}s that we'll need later on. |
| We also put the x, y, width, and height components of the |
| widget's rectangle in local variables. The value used for the \c |
| semiTransparentWhite and for the \c semiTransparentBlack color's |
| alpha channel depends on whether the mouse cursor is over the |
| widget or not. Since we set the Qt::WA_Hover attribute on |
| \l{QPushButton}s and \l{QComboBox}es, we can rely on the |
| QStyle::State_MouseOver flag to be set when the mouse is over the |
| widget. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 13 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 14 |
| |
| The \c roundRect variable is a QPainterPath. A QPainterPath is is |
| a vectorial specification of a shape. Any shape (rectangle, |
| ellipse, spline, etc.) or combination of shapes can be expressed |
| as a path. We will use \c roundRect both for filling the button |
| background with a wooden texture and for drawing the outline. The |
| \c roundRectPath() function is a private function; we will come |
| back to it later. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 15 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 16 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 17 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 18 |
| |
| We define two variables, \c brush and \c darker, and initialize |
| them based on the state of the button: |
| |
| \list |
| \li If the button is a \l{QPushButton::flat}{flat button}, we use |
| the \l{QPalette::Background}{Background} brush. We set \c |
| darker to \c true if the button is |
| \l{QAbstractButton::down}{down} or |
| \l{QAbstractButton::checked}{checked}. |
| \li If the button is currently held down by the user or in the |
| \l{QAbstractButton::checked}{checked} state, we use the |
| \l{QPalette::Mid}{Mid} component of the palette. We set |
| \c darker to \c true if the button is |
| \l{QAbstractButton::checked}{checked}. |
| \li Otherwise, we use the \l{QPalette::Button}{Button} component |
| of the palette. |
| \endlist |
| |
| The screenshot below illustrates how \l{QPushButton}s are |
| rendered based on their state: |
| |
| \image styles-woodbuttons.png Norwegian Wood buttons in different states |
| |
| To discover whether the button is flat or not, we need to cast |
| the \c option parameter to QStyleOptionButton and check if the |
| \l{QStyleOptionButton::features}{features} member specifies the |
| QStyleOptionButton::Flat flag. The qstyleoption_cast() function |
| performs a dynamic cast; if \c option is not a |
| QStyleOptionButton, qstyleoption_cast() returns a null pointer. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 19 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 20 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 21 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 22 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 23 |
| |
| We turn on antialiasing on QPainter. Antialiasing is a technique |
| that reduces the visual distortion that occurs when the edges of |
| a shape are converted into pixels. For the Norwegian Wood style, |
| we use it to obtain smoother edges for the round buttons. |
| |
| \image styles-aliasing.png Norwegian wood buttons with and without antialiasing |
| |
| The first call to QPainter::fillPath() draws the background of |
| the button with a wooden texture. The second call to |
| \l{QPainter::fillPath()}{fillPath()} paints the same area with a |
| semi-transparent black color (a black color with an alpha channel |
| of 63) to make the area darker if \c darker is true. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 24 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 25 |
| |
| Next, we draw the outline. The top-left half of the outline and |
| the bottom-right half of the outline are drawn using different |
| \l{QPen}s to produce a 3D effect. Normally, the top-left half of |
| the outline is drawn lighter whereas the bottom-right half is |
| drawn darker, but if the button is |
| \l{QAbstractButton::down}{down} or |
| \l{QAbstractButton::checked}{checked}, we invert the two |
| \l{QPen}s to give a sunken look to the button. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 26 |
| |
| We draw the top-left part of the outline by calling |
| QPainter::drawPath() with an appropriate |
| \l{QPainter::setClipRegion()}{clip region}. If the |
| \l{QStyleOption::direction}{layout direction} is right-to-left |
| instead of left-to-right, we swap the \c x1, \c x2, \c x3, and \c |
| x4 variables to obtain correct results. On right-to-left desktop, |
| the "light" comes from the top-right corner of the screen instead |
| of the top-left corner; raised and sunken widgets must be drawn |
| accordingly. |
| |
| The diagram below illustrates how 3D effects are drawn according |
| to the layout direction. The area in red on the diagram |
| corresponds to the \c topHalf polygon: |
| |
| \image styles-3d.png |
| |
| An easy way to test how a style looks in right-to-left mode is to |
| pass the \c -reverse command-line option to the application. This |
| option is recognized by the QApplication constructor. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 32 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 33 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 34 |
| |
| The bottom-right part of the outline is drawn in a similar |
| fashion. Then we draw a one-pixel wide outline around the entire |
| button, using the \l{QPalette::Foreground}{Foreground} component |
| of the QPalette. |
| |
| This completes the QStyle::PE_PanelButtonCommand case of the \c |
| switch statement. Other primitive elements are handled by the |
| base style. Let's now turn to the other \c NorwegianWoodStyle |
| member functions: |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 35 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 36 |
| |
| We reimplement QStyle::drawControl() to draw the text on a |
| QPushButton in a bright color when the button is |
| \l{QAbstractButton::down}{down} or |
| \l{QAbstractButton::checked}{checked}. |
| |
| If the \c option parameter points to a QStyleOptionButton object |
| (it normally should), we take a copy of the object and modify its |
| \l{QStyleOption::palette}{palette} member to make the |
| QPalette::ButtonText be the same as the QPalette::BrightText |
| component (unless the widget is disabled). |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 37 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 38 |
| |
| The \c setTexture() function is a private function that sets the |
| \l{QBrush::texture()}{texture} component of the \l{QBrush}es for |
| a certain \l{QPalette::ColorRole}{color role}, for all three |
| \l{QPalette::ColorGroup}{color groups} (active, disabled, |
| inactive). We used it to initialize the Norwegian Wood palette in |
| \c standardPalette. |
| |
| \snippet widgets/styles/norwegianwoodstyle.cpp 39 |
| \snippet widgets/styles/norwegianwoodstyle.cpp 40 |
| |
| The \c roundRectPath() function is a private function that |
| constructs a QPainterPath object for round buttons. The path |
| consists of eight segments: four arc segments for the corners and |
| four lines for the sides. |
| |
| With around 250 lines of code, we have a fully functional custom |
| style based on one of the predefined styles. Custom styles can be |
| used to provide a distinct look to an application or family of |
| applications. |
| |
| \section1 WidgetGallery Class |
| |
| For completeness, we will quickly review the \c WidgetGallery |
| class, which contains the most common Qt widgets and allows the |
| user to change style dynamically. Here's the class definition: |
| |
| \snippet widgets/styles/widgetgallery.h 0 |
| \dots |
| \snippet widgets/styles/widgetgallery.h 1 |
| |
| Here's the \c WidgetGallery constructor: |
| |
| \snippet widgets/styles/widgetgallery.cpp 0 |
| |
| We start by creating child widgets. The \uicontrol Style combobox is |
| initialized with all the styles known to QStyleFactory, in |
| addition to \c NorwegianWood. The \c create...() functions are |
| private functions that set up the various parts of the \c |
| WidgetGallery. |
| |
| \snippet widgets/styles/widgetgallery.cpp 1 |
| \snippet widgets/styles/widgetgallery.cpp 2 |
| |
| We connect the \uicontrol Style combobox to the \c changeStyle() |
| private slot, the \uicontrol{Use style's standard palette} check box to |
| the \c changePalette() slot, and the \uicontrol{Disable widgets} check |
| box to the child widgets' |
| \l{QWidget::setDisabled()}{setDisabled()} slot. |
| |
| \snippet widgets/styles/widgetgallery.cpp 3 |
| \snippet widgets/styles/widgetgallery.cpp 4 |
| |
| Finally, we put the child widgets in layouts. |
| |
| \snippet widgets/styles/widgetgallery.cpp 5 |
| \snippet widgets/styles/widgetgallery.cpp 6 |
| |
| When the user changes the style in the combobox, we call |
| QApplication::setStyle() to dynamically change the style of the |
| application. |
| |
| \snippet widgets/styles/widgetgallery.cpp 7 |
| \snippet widgets/styles/widgetgallery.cpp 8 |
| |
| If the user turns the \uicontrol{Use style's standard palette} on, the |
| current style's \l{QStyle::standardPalette()}{standard palette} |
| is used; otherwise, the system's default palette is honored. |
| |
| \snippet widgets/styles/widgetgallery.cpp 9 |
| \snippet widgets/styles/widgetgallery.cpp 10 |
| |
| The \c advanceProgressBar() slot is called at regular intervals |
| to advance the progress bar. Since we don't know how long the |
| user will keep the Styles application running, we use a |
| logarithmic formula: The closer the progress bar gets to 100%, |
| the slower it advances. |
| |
| We will review \c createProgressBar() in a moment. |
| |
| \snippet widgets/styles/widgetgallery.cpp 11 |
| \snippet widgets/styles/widgetgallery.cpp 12 |
| |
| The \c createTopLeftGroupBox() function creates the QGroupBox |
| that occupies the top-left corner of the \c WidgetGallery. We |
| skip the \c createTopRightGroupBox(), \c |
| createBottomLeftTabWidget(), and \c createBottomRightGroupBox() |
| functions, which are very similar. |
| |
| \snippet widgets/styles/widgetgallery.cpp 13 |
| |
| In \c createProgressBar(), we create a QProgressBar at the bottom |
| of the \c WidgetGallery and connect its |
| \l{QTimer::timeout()}{timeout()} signal to the \c |
| advanceProgressBar() slot. |
| */ |