4d88d79aa5
QFontMetrics(F)::width() has been deprecated and is replaced by horizontalAdvance(). This updates all usage of it in tests and documentation. It is worth noting that many or most of the usages of QFontMetrics::width() probably intended to use boundingRect().width(), but since it currently works, I have not looked into that, just replaced the function name mechanically. Change-Id: Iec382e5bad0b50f37a6cfff841bfb46ed4d4555f Reviewed-by: Lars Knoll <lars.knoll@qt.io>
500 lines
20 KiB
C++
500 lines
20 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the demonstration applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
** 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.
|
|
**
|
|
** BSD License Usage
|
|
** Alternatively, you may use this file under the terms of the BSD license
|
|
** as follows:
|
|
**
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions are
|
|
** met:
|
|
** * Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** * Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in
|
|
** the documentation and/or other materials provided with the
|
|
** distribution.
|
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
|
** contributors may be used to endorse or promote products derived
|
|
** from this software without specific prior written permission.
|
|
**
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "arthurstyle.h"
|
|
#include "arthurwidgets.h"
|
|
#include <QLayout>
|
|
#include <QPainter>
|
|
#include <QPainterPath>
|
|
#include <QPixmapCache>
|
|
#include <QRadioButton>
|
|
#include <QString>
|
|
#include <QStyleOption>
|
|
#include <QtDebug>
|
|
|
|
QPixmap cached(const QString &img)
|
|
{
|
|
if (QPixmap *p = QPixmapCache::find(img))
|
|
return *p;
|
|
|
|
QPixmap pm;
|
|
pm = QPixmap::fromImage(QImage(img), Qt::OrderedDither | Qt::OrderedAlphaDither);
|
|
if (pm.isNull())
|
|
return QPixmap();
|
|
|
|
QPixmapCache::insert(img, pm);
|
|
return pm;
|
|
}
|
|
|
|
|
|
ArthurStyle::ArthurStyle()
|
|
: QCommonStyle()
|
|
{
|
|
Q_INIT_RESOURCE(shared);
|
|
}
|
|
|
|
|
|
void ArthurStyle::drawHoverRect(QPainter *painter, const QRect &r) const
|
|
{
|
|
qreal h = r.height();
|
|
qreal h2 = r.height() / qreal(2);
|
|
QPainterPath path;
|
|
path.addRect(r.x() + h2, r.y() + 0, r.width() - h2 * 2, r.height());
|
|
path.addEllipse(r.x(), r.y(), h, h);
|
|
path.addEllipse(r.x() + r.width() - h, r.y(), h, h);
|
|
path.setFillRule(Qt::WindingFill);
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(QColor(191, 215, 191));
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
painter->drawPath(path);
|
|
}
|
|
|
|
|
|
void ArthurStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
|
|
QPainter *painter, const QWidget *widget) const
|
|
{
|
|
|
|
Q_ASSERT(option);
|
|
switch (element) {
|
|
case PE_FrameFocusRect:
|
|
break;
|
|
|
|
case PE_IndicatorRadioButton:
|
|
if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
|
|
bool hover = (button->state & State_Enabled) && (button->state & State_MouseOver);
|
|
painter->save();
|
|
QPixmap radio;
|
|
if (hover)
|
|
drawHoverRect(painter, widget->rect());
|
|
|
|
if (button->state & State_Sunken)
|
|
radio = cached(":res/images/radiobutton-on.png");
|
|
else if (button->state & State_On)
|
|
radio = cached(":res/images/radiobutton_on.png");
|
|
else
|
|
radio = cached(":res/images/radiobutton_off.png");
|
|
painter->drawPixmap(button->rect.topLeft(), radio);
|
|
|
|
painter->restore();
|
|
}
|
|
break;
|
|
|
|
case PE_PanelButtonCommand:
|
|
if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
|
|
bool hover = (button->state & State_Enabled) && (button->state & State_MouseOver);
|
|
|
|
painter->save();
|
|
const QPushButton *pushButton = qobject_cast<const QPushButton *>(widget);
|
|
Q_ASSERT(pushButton);
|
|
QWidget *parent = pushButton->parentWidget();
|
|
if (parent && qobject_cast<QGroupBox *>(parent)) {
|
|
QLinearGradient lg(0, 0, 0, parent->height());
|
|
lg.setColorAt(0, QColor(224,224,224));
|
|
lg.setColorAt(1, QColor(255,255,255));
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(lg);
|
|
painter->setBrushOrigin(-widget->mapToParent(QPoint(0,0)));
|
|
painter->drawRect(button->rect);
|
|
painter->setBrushOrigin(0,0);
|
|
}
|
|
|
|
bool down = (button->state & State_Sunken) || (button->state & State_On);
|
|
|
|
QPixmap left, right, mid;
|
|
if (down) {
|
|
left = cached(":res/images/button_pressed_cap_left.png");
|
|
right = cached(":res/images/button_pressed_cap_right.png");
|
|
mid = cached(":res/images/button_pressed_stretch.png");
|
|
} else {
|
|
left = cached(":res/images/button_normal_cap_left.png");
|
|
right = cached(":res/images/button_normal_cap_right.png");
|
|
mid = cached(":res/images/button_normal_stretch.png");
|
|
}
|
|
painter->drawPixmap(button->rect.topLeft(), left);
|
|
painter->drawTiledPixmap(QRect(button->rect.x() + left.width(),
|
|
button->rect.y(),
|
|
button->rect.width() - left.width() - right.width(),
|
|
left.height()),
|
|
mid);
|
|
painter->drawPixmap(button->rect.x() + button->rect.width() - right.width(),
|
|
button->rect.y(),
|
|
right);
|
|
if (hover)
|
|
painter->fillRect(widget->rect().adjusted(3,5,-3,-5), QColor(31,127,31,63));
|
|
painter->restore();
|
|
}
|
|
break;
|
|
|
|
case PE_FrameGroupBox:
|
|
if (const QStyleOptionFrame *group
|
|
= qstyleoption_cast<const QStyleOptionFrame *>(option)) {
|
|
const QRect &r = group->rect;
|
|
|
|
painter->save();
|
|
int radius = 14;
|
|
int radius2 = radius*2;
|
|
QPainterPath clipPath;
|
|
clipPath.moveTo(radius, 0);
|
|
clipPath.arcTo(r.right() - radius2, 0, radius2, radius2, 90, -90);
|
|
clipPath.arcTo(r.right() - radius2, r.bottom() - radius2, radius2, radius2, 0, -90);
|
|
clipPath.arcTo(r.left(), r.bottom() - radius2, radius2, radius2, 270, -90);
|
|
clipPath.arcTo(r.left(), r.top(), radius2, radius2, 180, -90);
|
|
painter->setClipPath(clipPath);
|
|
QPixmap titleStretch = cached(":res/images/title_stretch.png");
|
|
QPixmap topLeft = cached(":res/images/groupframe_topleft.png");
|
|
QPixmap topRight = cached(":res/images/groupframe_topright.png");
|
|
QPixmap bottomLeft = cached(":res/images/groupframe_bottom_left.png");
|
|
QPixmap bottomRight = cached(":res/images/groupframe_bottom_right.png");
|
|
QPixmap leftStretch = cached(":res/images/groupframe_left_stretch.png");
|
|
QPixmap topStretch = cached(":res/images/groupframe_top_stretch.png");
|
|
QPixmap rightStretch = cached(":res/images/groupframe_right_stretch.png");
|
|
QPixmap bottomStretch = cached(":res/images/groupframe_bottom_stretch.png");
|
|
QLinearGradient lg(0, 0, 0, r.height());
|
|
lg.setColorAt(0, QColor(224,224,224));
|
|
lg.setColorAt(1, QColor(255,255,255));
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(lg);
|
|
painter->drawRect(r.adjusted(0, titleStretch.height()/2, 0, 0));
|
|
painter->setClipping(false);
|
|
|
|
int topFrameOffset = titleStretch.height()/2 - 2;
|
|
painter->drawPixmap(r.topLeft() + QPoint(0, topFrameOffset), topLeft);
|
|
painter->drawPixmap(r.topRight() - QPoint(topRight.width()-1, 0)
|
|
+ QPoint(0, topFrameOffset), topRight);
|
|
painter->drawPixmap(r.bottomLeft() - QPoint(0, bottomLeft.height()-1), bottomLeft);
|
|
painter->drawPixmap(r.bottomRight() - QPoint(bottomRight.width()-1,
|
|
bottomRight.height()-1), bottomRight);
|
|
|
|
QRect left = r;
|
|
left.setY(r.y() + topLeft.height() + topFrameOffset);
|
|
left.setWidth(leftStretch.width());
|
|
left.setHeight(r.height() - topLeft.height() - bottomLeft.height() - topFrameOffset);
|
|
painter->drawTiledPixmap(left, leftStretch);
|
|
|
|
QRect top = r;
|
|
top.setX(r.x() + topLeft.width());
|
|
top.setY(r.y() + topFrameOffset);
|
|
top.setWidth(r.width() - topLeft.width() - topRight.width());
|
|
top.setHeight(topLeft.height());
|
|
painter->drawTiledPixmap(top, topStretch);
|
|
|
|
QRect right = r;
|
|
right.setX(r.right() - rightStretch.width()+1);
|
|
right.setY(r.y() + topRight.height() + topFrameOffset);
|
|
right.setWidth(rightStretch.width());
|
|
right.setHeight(r.height() - topRight.height()
|
|
- bottomRight.height() - topFrameOffset);
|
|
painter->drawTiledPixmap(right, rightStretch);
|
|
|
|
QRect bottom = r;
|
|
bottom.setX(r.x() + bottomLeft.width());
|
|
bottom.setY(r.bottom() - bottomStretch.height()+1);
|
|
bottom.setWidth(r.width() - bottomLeft.width() - bottomRight.width());
|
|
bottom.setHeight(bottomLeft.height());
|
|
painter->drawTiledPixmap(bottom, bottomStretch);
|
|
painter->restore();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
QCommonStyle::drawPrimitive(element, option, painter, widget);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void ArthurStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
|
|
QPainter *painter, const QWidget *widget) const
|
|
{
|
|
switch (control) {
|
|
case CC_Slider:
|
|
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
|
|
QRect groove = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
|
|
QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
|
|
|
|
painter->save();
|
|
|
|
bool hover = (slider->state & State_Enabled) && (slider->state & State_MouseOver);
|
|
if (hover) {
|
|
QRect moderated = widget->rect().adjusted(0, 4, 0, -4);
|
|
drawHoverRect(painter, moderated);
|
|
}
|
|
|
|
if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
|
|
QPixmap grv = cached(":res/images/slider_bar.png");
|
|
painter->drawPixmap(QRect(groove.x() + 5, groove.y(),
|
|
groove.width() - 10, grv.height()),
|
|
grv);
|
|
}
|
|
if ((option->subControls & SC_SliderHandle) && handle.isValid()) {
|
|
QPixmap hndl = cached(":res/images/slider_thumb_on.png");
|
|
painter->drawPixmap(handle.topLeft(), hndl);
|
|
}
|
|
|
|
painter->restore();
|
|
}
|
|
break;
|
|
case CC_GroupBox:
|
|
if (const QStyleOptionGroupBox *groupBox
|
|
= qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
|
|
QStyleOptionGroupBox groupBoxCopy(*groupBox);
|
|
groupBoxCopy.subControls &= ~SC_GroupBoxLabel;
|
|
QCommonStyle::drawComplexControl(control, &groupBoxCopy, painter, widget);
|
|
|
|
if (groupBox->subControls & SC_GroupBoxLabel) {
|
|
const QRect &r = groupBox->rect;
|
|
QPixmap titleLeft = cached(":res/images/title_cap_left.png");
|
|
QPixmap titleRight = cached(":res/images/title_cap_right.png");
|
|
QPixmap titleStretch = cached(":res/images/title_stretch.png");
|
|
int txt_width = groupBox->fontMetrics.horizontalAdvance(groupBox->text) + 20;
|
|
painter->drawPixmap(r.center().x() - txt_width/2, 0, titleLeft);
|
|
QRect tileRect = subControlRect(control, groupBox, SC_GroupBoxLabel, widget);
|
|
painter->drawTiledPixmap(tileRect, titleStretch);
|
|
painter->drawPixmap(tileRect.x() + tileRect.width(), 0, titleRight);
|
|
int opacity = 31;
|
|
painter->setPen(QColor(0, 0, 0, opacity));
|
|
painter->drawText(tileRect.translated(0, 1),
|
|
Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
|
|
painter->drawText(tileRect.translated(2, 1),
|
|
Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
|
|
painter->setPen(QColor(0, 0, 0, opacity * 2));
|
|
painter->drawText(tileRect.translated(1, 1),
|
|
Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
|
|
painter->setPen(Qt::white);
|
|
painter->drawText(tileRect, Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
QCommonStyle::drawComplexControl(control, option, painter, widget);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void ArthurStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option,
|
|
QPainter *painter, const QWidget *widget) const
|
|
{
|
|
switch (element) {
|
|
case CE_RadioButtonLabel:
|
|
if (const QStyleOptionButton *button
|
|
= qstyleoption_cast<const QStyleOptionButton *>(option)) {
|
|
|
|
if (button->text.isEmpty()) {
|
|
QCommonStyle::drawControl(element, option, painter, widget);
|
|
} else {
|
|
painter->save();
|
|
painter->setPen(Qt::black);
|
|
painter->drawText(button->rect, Qt::AlignVCenter, button->text);
|
|
painter->restore();
|
|
}
|
|
}
|
|
break;
|
|
case CE_PushButtonLabel:
|
|
if (const QStyleOptionButton *button
|
|
= qstyleoption_cast<const QStyleOptionButton *>(option)) {
|
|
|
|
if (button->text.isEmpty()) {
|
|
QCommonStyle::drawControl(element, option, painter, widget);
|
|
} else {
|
|
painter->save();
|
|
painter->setPen(Qt::black);
|
|
painter->drawText(button->rect, Qt::AlignVCenter | Qt::AlignHCenter, button->text);
|
|
painter->restore();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
QCommonStyle::drawControl(element, option, painter, widget);
|
|
break;
|
|
}
|
|
}
|
|
|
|
QRect ArthurStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
|
|
SubControl subControl, const QWidget *widget) const
|
|
{
|
|
QRect rect;
|
|
|
|
switch (control) {
|
|
default:
|
|
rect = QCommonStyle::subControlRect(control, option, subControl, widget);
|
|
break;
|
|
case CC_GroupBox:
|
|
if (const QStyleOptionGroupBox *group
|
|
= qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
|
|
switch (subControl) {
|
|
default:
|
|
rect = QCommonStyle::subControlRect(control, option, subControl, widget);
|
|
break;
|
|
case SC_GroupBoxContents:
|
|
rect = QCommonStyle::subControlRect(control, option, subControl, widget);
|
|
rect.adjust(0, -8, 0, 0);
|
|
break;
|
|
case SC_GroupBoxFrame:
|
|
rect = group->rect;
|
|
break;
|
|
case SC_GroupBoxLabel:
|
|
QPixmap titleLeft = cached(":res/images/title_cap_left.png");
|
|
QPixmap titleRight = cached(":res/images/title_cap_right.png");
|
|
QPixmap titleStretch = cached(":res/images/title_stretch.png");
|
|
int txt_width = group->fontMetrics.horizontalAdvance(group->text) + 20;
|
|
rect = QRect(group->rect.center().x() - txt_width/2 + titleLeft.width(), 0,
|
|
txt_width - titleLeft.width() - titleRight.width(),
|
|
titleStretch.height());
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (control == CC_Slider && subControl == SC_SliderHandle) {
|
|
rect.setWidth(13);
|
|
rect.setHeight(27);
|
|
} else if (control == CC_Slider && subControl == SC_SliderGroove) {
|
|
rect.setHeight(9);
|
|
rect.moveTop(27/2 - 9/2);
|
|
}
|
|
return rect;
|
|
}
|
|
|
|
QSize ArthurStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
|
|
const QSize &size, const QWidget *widget) const
|
|
{
|
|
QSize newSize = QCommonStyle::sizeFromContents(type, option, size, widget);
|
|
|
|
|
|
switch (type) {
|
|
case CT_RadioButton:
|
|
newSize += QSize(20, 0);
|
|
break;
|
|
|
|
case CT_PushButton:
|
|
newSize.setHeight(26);
|
|
break;
|
|
|
|
case CT_Slider:
|
|
newSize.setHeight(27);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return newSize;
|
|
}
|
|
|
|
int ArthurStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const
|
|
{
|
|
if (pm == PM_SliderLength)
|
|
return 13;
|
|
return QCommonStyle::pixelMetric(pm, opt, widget);
|
|
}
|
|
|
|
void ArthurStyle::polish(QWidget *widget)
|
|
{
|
|
if (widget->layout() && qobject_cast<QGroupBox *>(widget)) {
|
|
if (widget->findChildren<QGroupBox *>().size() == 0) {
|
|
widget->layout()->setSpacing(0);
|
|
widget->layout()->setMargin(12);
|
|
} else {
|
|
widget->layout()->setMargin(13);
|
|
}
|
|
}
|
|
|
|
if (qobject_cast<QPushButton *>(widget)
|
|
|| qobject_cast<QRadioButton *>(widget)
|
|
|| qobject_cast<QSlider *>(widget)) {
|
|
widget->setAttribute(Qt::WA_Hover);
|
|
}
|
|
|
|
QPalette pal = widget->palette();
|
|
if (widget->isWindow()) {
|
|
pal.setColor(QPalette::Background, QColor(241, 241, 241));
|
|
widget->setPalette(pal);
|
|
}
|
|
|
|
}
|
|
|
|
void ArthurStyle::unpolish(QWidget *widget)
|
|
{
|
|
if (qobject_cast<QPushButton *>(widget)
|
|
|| qobject_cast<QRadioButton *>(widget)
|
|
|| qobject_cast<QSlider *>(widget)) {
|
|
widget->setAttribute(Qt::WA_Hover, false);
|
|
}
|
|
}
|
|
|
|
void ArthurStyle::polish(QPalette &palette)
|
|
{
|
|
palette.setColor(QPalette::Background, QColor(241, 241, 241));
|
|
}
|
|
|
|
QRect ArthurStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
|
|
{
|
|
QRect r;
|
|
switch(element) {
|
|
case SE_RadioButtonClickRect:
|
|
r = widget->rect();
|
|
break;
|
|
case SE_RadioButtonContents:
|
|
r = widget->rect().adjusted(20, 0, 0, 0);
|
|
break;
|
|
default:
|
|
r = QCommonStyle::subElementRect(element, option, widget);
|
|
break;
|
|
}
|
|
|
|
if (qobject_cast<const QRadioButton*>(widget))
|
|
r = r.adjusted(5, 0, -5, 0);
|
|
|
|
return r;
|
|
}
|