Modernize shapedclock example
Relying on the hard clipping of QRegion widget masks to create non-rectangular windows is a solution from a bygone era. The result looks horrible with today's eyes, particularly on a high-dpi screen. Update the example to create smooth anti-aliased edges using translucent window bacground. Task-number: QTBUG-64229 Change-Id: I8859d61177d2a2dc446632c23f27f42050e0d7c7 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> Reviewed-by: Andy Shaw <andy.shaw@qt.io>
This commit is contained in:
parent
03dc30acca
commit
191ac31598
@ -29,16 +29,18 @@
|
||||
\example widgets/shapedclock
|
||||
\title Shaped Clock Example
|
||||
\ingroup examples-widgets
|
||||
\brief The Shaped Clock example shows how to apply a widget mask to a top-level
|
||||
widget to produce a shaped window.
|
||||
\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.
|
||||
|
||||
\borderedimage shapedclock-example.png
|
||||
|
||||
Widget masks are used to customize the shapes of top-level widgets by restricting
|
||||
the available area for painting. On some 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.
|
||||
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
|
||||
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.
|
||||
|
||||
Since this example's window does not provide a \uicontrol File menu or a close
|
||||
button, we provide a context menu with an \uicontrol Exit entry so that the example
|
||||
@ -52,8 +54,10 @@
|
||||
|
||||
\snippet widgets/shapedclock/shapedclock.h 0
|
||||
|
||||
The \l{QWidget::paintEvent()}{paintEvent()} implementation is the same as that found
|
||||
in the \c AnalogClock class. We implement \l{QWidget::sizeHint()}{sizeHint()}
|
||||
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.
|
||||
|
||||
@ -70,9 +74,11 @@
|
||||
|
||||
\snippet widgets/shapedclock/shapedclock.cpp 0
|
||||
|
||||
We inform the window manager that the widget is not to be decorated with a window
|
||||
frame by setting the Qt::FramelessWindowHint flag on the widget. As a result, we need
|
||||
to provide a way for the user to move the clock around the screen.
|
||||
We request a transparent window by setting the Qt::WA_TranslucentBackground
|
||||
widget attribute. We inform the window manager that the widget is not to be
|
||||
decorated with a window frame by setting the Qt::FramelessWindowHint flag
|
||||
on the widget. As a result, we need to provide a way for the user to move
|
||||
the clock around the screen.
|
||||
|
||||
Mouse button events are delivered to the \c mousePressEvent() handler:
|
||||
|
||||
@ -94,14 +100,20 @@
|
||||
widget is moved to the point given by subtracting the \c dragPosition from the current
|
||||
cursor position in global coordinates. If we drag the widget, we also accept the event.
|
||||
|
||||
The \c paintEvent() function is given for completeness. See the
|
||||
\l{Analog Clock Example}{Analog Clock} example for a description of the process used
|
||||
to render the clock.
|
||||
The \c paintEvent() function is mainly the same as described in the
|
||||
\l{Analog Clock Example}{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.
|
||||
|
||||
\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:
|
||||
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
|
||||
|
||||
@ -121,6 +133,12 @@
|
||||
|
||||
\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.
|
||||
|
@ -61,6 +61,7 @@
|
||||
ShapedClock::ShapedClock(QWidget *parent)
|
||||
: QWidget(parent, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint)
|
||||
{
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, QOverload<>::of(&ShapedClock::update));
|
||||
timer->start(1000);
|
||||
@ -122,6 +123,10 @@ void ShapedClock::paintEvent(QPaintEvent *)
|
||||
painter.translate(width() / 2, height() / 2);
|
||||
painter.scale(side / 200.0, side / 200.0);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(palette().window());
|
||||
painter.drawEllipse(QPoint(0, 0), 98, 98);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(hourColor);
|
||||
|
||||
@ -168,6 +173,6 @@ void ShapedClock::resizeEvent(QResizeEvent * /* event */)
|
||||
//! [5]
|
||||
QSize ShapedClock::sizeHint() const
|
||||
{
|
||||
return QSize(100, 100);
|
||||
return QSize(200, 200);
|
||||
}
|
||||
//! [5]
|
||||
|
Loading…
x
Reference in New Issue
Block a user