From 8e94af2ed0577936cabaffab336014c1be9002af Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 15 May 2023 16:30:25 +0200 Subject: Revamp the shapedclock example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename it to "Translucent Background", as that's what the example shows how to do. And modern applications shouldn't use a (binary) mask to create shaped windows. Instead, set the TranslucentBackground attribute, don't paint pixels that should be fully transparent and use anti-aliased or semi-opaque painting for pixels that should be translucent. Adjust the example and documentation accordingly. Move the statment that widget masks create coarse visual clipping to the QWidget::setMask documentation. Pick-to: 6.5 Change-Id: Id49d854093f2cb471afb178d32723081c7543543 Reviewed-by: Tor Arne Vestbø --- examples/widgets/doc/src/shapedclock.qdoc | 73 ++++++---------------- .../widgets/widgets/shapedclock/shapedclock.cpp | 14 +---- examples/widgets/widgets/shapedclock/shapedclock.h | 1 - src/widgets/kernel/qwidget.cpp | 16 +++-- 4 files changed, 34 insertions(+), 70 deletions(-) diff --git a/examples/widgets/doc/src/shapedclock.qdoc b/examples/widgets/doc/src/shapedclock.qdoc index 4ce341266a..8e69be1db4 100644 --- a/examples/widgets/doc/src/shapedclock.qdoc +++ b/examples/widgets/doc/src/shapedclock.qdoc @@ -3,17 +3,18 @@ /*! \example widgets/shapedclock - \title Shaped Clock Example + \title Translucent Background \ingroup examples-widgets - \brief The Shaped Clock example shows how to apply a translucent background - and a widget mask to a top-level widget to produce a shaped window. + \brief The example shows how to make a round window with a translucent + background. \borderedimage shapedclock-example.png - Widget masks are used to customize the shapes of top-level widgets by - restricting the area available for painting and mouse input. Using a - translucent background facilitates partially transparent windows and smooth - edges. On most window systems, setting certain window flags will cause the + Widgets that set their background to be translucent will be transparent for all + unpainted pixels, and the background will shine through pixels painted with an + opacity of less than 100%. Pixels that are not painted at all will also not + receive any mouse input. This can be used to customize the shapes of top-level + widgets. On most window systems, setting certain window flags will cause the window decoration (title bar, window frame, buttons) to be disabled, allowing specially-shaped windows to be created. In this example, we use this feature to create a circular window containing an analog clock. @@ -30,12 +31,10 @@ \snippet widgets/shapedclock/shapedclock.h 0 - The \l{QWidget::paintEvent()}{paintEvent()} implementation is the same as - that found in the \c AnalogClock class, with one important exception: we - now must also draw background (the clock face) ourselves, since the widget - background is just transparent. We implement \l{QWidget::sizeHint()}{sizeHint()} - so that we don't have to resize the widget explicitly. We also provide an event - handler for resize events. This allows us to update the mask if the clock is resized. + The \l{QWidget::paintEvent()}{paintEvent()} implementation draws an analog clock + on a semi-transparent background (the clock face). In addition, we implement + \l{QWidget::sizeHint()}{sizeHint()} so that we don't have to resize the widget + explicitly. Since the window containing the clock widget will have no title bar, we provide implementations for \l{QWidget::mouseMoveEvent()}{mouseMoveEvent()} and @@ -45,8 +44,9 @@ \section1 ShapedClock Class Implementation - The \c ShapedClock constructor performs many of the same tasks as the \c AnalogClock - constructor. We set up a timer and connect it to the widget's update() slot: + The \c ShapedClock constructor sets up a timer and connect it to the widget's + update() slot. In addition, we add an action to the widget, which will automatically + become available through a context menu when right-clicking on the widget. \snippet widgets/shapedclock/shapedclock.cpp 0 @@ -77,49 +77,14 @@ cursor position in global coordinates. If we drag the widget, we also accept the event. The \c paintEvent() function is mainly the same as described in the - \l{Analog Clock} example. The one addition is that we - use QPainter::drawEllipse() to draw a round clock face with the current - palette's default background color. We make the clock face a bit smaller - than the widget mask, so that the anti-aliased, semi-transparent pixels on - the edge are not clipped away by the widget mask. This gives the shaped - window smooth edges on the screen. + \l{Analog Clock} example. The one addition is that we use QPainter::drawEllipse() to + draw a round clock face. We reduce the painter's opacity to 90%, and use the palette's + default background color. \snippet widgets/shapedclock/shapedclock.cpp 3 - In the \c resizeEvent() handler, we re-use some of the code from the \c - paintEvent() to determine the region of the widget that is visible to the - user. This tells the system the area where mouse clicks should go to us, - and not to whatever window is behind us: - - \snippet widgets/shapedclock/shapedclock.cpp 4 - - Since the clock face is a circle drawn in the center of the widget, this is the region - we use as the mask. - - Although the lack of a window frame may make it difficult for the user to resize the - widget on some platforms, it will not necessarily be impossible. The \c resizeEvent() - function ensures that the widget mask will always be updated if the widget's dimensions - change, and additionally ensures that it will be set up correctly when the widget is - first displayed. - Finally, we implement the \c sizeHint() for the widget so that it is given a reasonable default size when it is first shown: - \snippet widgets/shapedclock/shapedclock.cpp 5 - - \section1 Notes on Widget Masks - - Widget masks are used to hint to the window system that the application - does not want mouse events for areas outside the mask. On most systems, - they also result in coarse visual clipping. To get smooth window edges, one - should use translucent background and anti-aliased painting, as shown in - this example. - - Since QRegion allows arbitrarily complex regions to be created, widget masks can be - made to suit the most unconventionally-shaped windows, and even allow widgets to be - displayed with holes in them. - - Widget masks can also be constructed by using the contents of pixmap to define the - opaque part of the widget. For a pixmap with an alpha channel, a suitable mask can be - obtained with QPixmap::mask(). + \snippet widgets/shapedclock/shapedclock.cpp 4 */ diff --git a/examples/widgets/widgets/shapedclock/shapedclock.cpp b/examples/widgets/widgets/shapedclock/shapedclock.cpp index 5cc4a54016..25225eb5b6 100644 --- a/examples/widgets/widgets/shapedclock/shapedclock.cpp +++ b/examples/widgets/widgets/shapedclock/shapedclock.cpp @@ -78,7 +78,9 @@ void ShapedClock::paintEvent(QPaintEvent *) painter.setPen(Qt::NoPen); painter.setBrush(palette().window()); + painter.setOpacity(0.9); painter.drawEllipse(QPoint(0, 0), 98, 98); + painter.setOpacity(1.0); painter.setPen(Qt::NoPen); painter.setBrush(hourColor); @@ -114,18 +116,8 @@ void ShapedClock::paintEvent(QPaintEvent *) //! [3] //! [4] -void ShapedClock::resizeEvent(QResizeEvent * /* event */) -{ - int side = qMin(width(), height()); - QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side, - side, QRegion::Ellipse); - setMask(maskedRegion); -} -//! [4] - -//! [5] QSize ShapedClock::sizeHint() const { return QSize(200, 200); } -//! [5] +//! [4] diff --git a/examples/widgets/widgets/shapedclock/shapedclock.h b/examples/widgets/widgets/shapedclock/shapedclock.h index c359a3a083..3dec93cc74 100644 --- a/examples/widgets/widgets/shapedclock/shapedclock.h +++ b/examples/widgets/widgets/shapedclock/shapedclock.h @@ -19,7 +19,6 @@ protected: void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; private: QPoint dragPosition; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index d0ad169024..1b8e9890b5 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -10233,7 +10233,7 @@ void QWidget::ensurePolished() const Returns the mask currently set on a widget. If no mask is set the return value will be an empty region. - \sa setMask(), clearMask(), QRegion::isEmpty(), {Shaped Clock Example} + \sa setMask(), clearMask(), QRegion::isEmpty() */ QRegion QWidget::mask() const { @@ -12964,8 +12964,16 @@ QPainter *QWidget::sharedPainter() const widget, window system controls in that area may or may not be visible, depending on the platform. - Note that this effect can be slow if the region is particularly - complex. + Since QRegion allows arbitrarily complex regions to be created, widget + masks can be made to suit the most unconventionally-shaped windows, and + even allow widgets to be displayed with holes in them. Note that this + effect can be slow if the region is particularly complex. + + Widget masks are used to hint to the window system that the application + does not want mouse events for areas outside the mask. On most systems, + they also result in coarse visual clipping. To get smooth window edges, use + translucent background and anti-aliased painting instead, as shown in the + \l{Translucent Background} example. \sa windowOpacity */ @@ -13051,7 +13059,7 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) Masked widgets receive mouse events only on their visible portions. - \sa clearMask(), windowOpacity(), {Shaped Clock Example} + \sa clearMask(), windowOpacity() */ void QWidget::setMask(const QBitmap &bitmap) { -- cgit v1.2.1