Commit 1b6d97b2 authored by Alessandro Portale's avatar Alessandro Portale

Utils: New ThemeHelper class with icon handling tools

http://blog.qt.io/blog/author/didesous/ announced new designs/themes
for Qt Creator. This patch implements the helper functions for the
loading and theme-specific recoloring of icons.

Change-Id: Ie6e96eeecb70dccd6b95338c653a9d3b760557cc
Reviewed-by: default avatarAlessandro Portale <alessandro.portale@theqtcompany.com>
parent b8bdc6f6
......@@ -48,6 +48,15 @@ FancyTabWidgetEnabledUnselectedTextColor=text
FancyToolButtonHoverColor=hoverBackground
FancyToolButtonSelectedColor=selectedBackground
FutureProgressBackgroundColor=shadowBackground
IconsBaseColor=ffdcdcdc
IconsInfoColor=ff43aced
IconsWarningColor=fff9ce1f
IconsErrorColor=ffe8555a
IconsRunColor=ff7fc341
IconsStopColor=ffe7353b
IconsDebugColor=ffb8c6ff
IconsInterruptColor=ff7488db
IconsNavigationArrowsColor=ffebc322
InfoBarBackground=ff505000
InfoBarText=text
MenuBarEmptyAreaBackgroundColor=shadowBackground
......
......@@ -42,6 +42,15 @@ FancyTabWidgetEnabledUnselectedTextColor=ffffffff
FancyToolButtonHoverColor=28ffffff
FancyToolButtonSelectedColor=32000000
FutureProgressBackgroundColor=ffff0000
IconsBaseColor=ffdcdcdc
IconsInfoColor=ff43aced
IconsWarningColor=fff9ce1f
IconsErrorColor=ffe8555a
IconsRunColor=ff7fc341
IconsStopColor=ffe7353b
IconsDebugColor=ffb8c6ff
IconsInterruptColor=ff7488db
IconsNavigationArrowsColor=ffebc322
InfoBarBackground=ffffffe1
InfoBarText=ff000000
MenuBarEmptyAreaBackgroundColor=ffff0000
......
......@@ -515,13 +515,31 @@ QString StyleHelper::dpiSpecificImageFile(const QString &fileName)
{
// See QIcon::addFile()
if (qApp->devicePixelRatio() > 1.0) {
const QFileInfo fi(fileName);
const QString at2xfileName = fi.path() + QLatin1Char('/')
+ fi.completeBaseName() + QStringLiteral("@2x.") + fi.suffix();
if (QFile::exists(at2xfileName))
return at2xfileName;
const QString atDprfileName =
imageFileWithResolution(fileName, qRound(qApp->devicePixelRatio()));
if (QFile::exists(atDprfileName))
return atDprfileName;
}
return fileName;
}
QString StyleHelper::imageFileWithResolution(const QString &fileName, int dpr)
{
const QFileInfo fi(fileName);
return dpr == 1 ? fileName :
fi.path() + QLatin1Char('/') + fi.completeBaseName()
+ QLatin1Char('@') + QString::number(dpr)
+ QLatin1String("x.") + fi.suffix();
}
QList<int> StyleHelper::availableImageResolutions(const QString &fileName)
{
QList<int> result;
const int maxResolutions = qApp->devicePixelRatio();
for (int i = 1; i <= maxResolutions; ++i)
if (QFile::exists(imageFileWithResolution(fileName, i)))
result.append(i);
return result;
}
} // namespace Utils
......@@ -95,6 +95,8 @@ public:
static QLinearGradient statusBarGradient(const QRect &statusBarRect);
static QString dpiSpecificImageFile(const QString &fileName);
static QString imageFileWithResolution(const QString &fileName, int dpr);
static QList<int> availableImageResolutions(const QString &fileName);
private:
static QColor m_baseColor;
......
......@@ -130,6 +130,18 @@ public:
TreeViewArrowColorNormal,
TreeViewArrowColorSelected,
/* Icons */
IconsBaseColor,
IconsInfoColor,
IconsWarningColor,
IconsErrorColor,
IconsRunColor,
IconsStopColor,
IconsInterruptColor,
IconsDebugColor,
IconsNavigationArrowsColor,
/* Output panes */
OutputPanes_DebugTextColor,
......
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** 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 http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "themehelper.h"
#include "theme/theme.h"
#include "stylehelper.h"
#include <QApplication>
#include <QIcon>
#include <QImage>
#include <QMetaEnum>
#include <QPainter>
namespace Utils {
static QPixmap maskToColorAndAlpha(const QPixmap &mask, const QColor &color)
{
QImage result(mask.toImage().convertToFormat(QImage::Format_ARGB32));
result.setDevicePixelRatio(mask.devicePixelRatio());
QRgb *bitsStart = reinterpret_cast<QRgb*>(result.bits());
const QRgb *bitsEnd = bitsStart + result.width() * result.height();
const QRgb tint = color.rgb() & 0x00ffffff;
const qreal alpha = color.alphaF();
for (QRgb *pixel = bitsStart; pixel < bitsEnd; ++pixel) {
QRgb pixelAlpha = ~(*pixel & 0xff) * alpha;
*pixel = (pixelAlpha << 24) | tint;
}
return QPixmap::fromImage(result);
}
typedef QPair<QPixmap, QColor> MaskAndColor;
typedef QList<MaskAndColor> MasksAndColors;
static MasksAndColors masksAndColors(const QString &mask, int dpr)
{
MasksAndColors result;
const QStringList list = mask.split(QLatin1Char(','));
for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
const QStringList items = (*it).split(QLatin1Char('|'));
const QString fileName = items.first().trimmed();
QColor color = creatorTheme()->color(Theme::IconsBaseColor);
if (items.count() > 1) {
const QString colorName = items.at(1);
static const QMetaObject &m = Theme::staticMetaObject;
static const QMetaEnum colorEnum = m.enumerator(m.indexOfEnumerator("Color"));
bool keyFound = false;
const int colorEnumKey = colorEnum.keyToValue(colorName.toLatin1().constData(), &keyFound);
if (keyFound) {
color = creatorTheme()->color(static_cast<Theme::Color>(colorEnumKey));
} else {
const QColor colorFromName(colorName);
if (colorFromName.isValid())
color = colorFromName;
}
}
const QString dprFileName = StyleHelper::availableImageResolutions(fileName).contains(dpr) ?
StyleHelper::imageFileWithResolution(fileName, dpr) : fileName;
result.append(qMakePair(QPixmap(dprFileName), color));
}
return result;
}
static void smearPixmap(QPainter *painter, const QPixmap &pixmap)
{
painter->drawPixmap(QPointF(-0.501, -0.501), pixmap);
painter->drawPixmap(QPointF(0, -0.501), pixmap);
painter->drawPixmap(QPointF( 0.5, -0.501), pixmap);
painter->drawPixmap(QPointF( 0.5, 0), pixmap);
painter->drawPixmap(QPointF( 0.5, 0.5), pixmap);
painter->drawPixmap(QPointF( 0, 0.5), pixmap);
painter->drawPixmap(QPointF(-0.501, 0.5), pixmap);
painter->drawPixmap(QPointF(-0.501, 0), pixmap);
}
static QPixmap combinedMask(const MasksAndColors &masks)
{
if (masks.count() == 1)
return masks.first().first;
QPixmap result(masks.first().first);
QPainter p(&result);
p.setCompositionMode(QPainter::CompositionMode_Darken);
MasksAndColors::const_iterator maskImage = masks.constBegin();
maskImage++;
for (;maskImage != masks.constEnd(); ++maskImage) {
p.save();
p.setOpacity(0.4);
p.setCompositionMode(QPainter::CompositionMode_Lighten);
smearPixmap(&p, maskToColorAndAlpha((*maskImage).first, Qt::white));
p.restore();
p.drawPixmap(0, 0, (*maskImage).first);
}
p.end();
return result;
}
static QPixmap masksToIcon(const MasksAndColors &masks, const QPixmap &combinedMask)
{
QPixmap result(combinedMask.size());
result.setDevicePixelRatio(combinedMask.devicePixelRatio());
result.fill(Qt::transparent);
QPainter p(&result);
for (MasksAndColors::const_iterator maskImage = masks.constBegin();
maskImage != masks.constEnd(); ++maskImage) {
if (maskImage != masks.constBegin()) {
// Punch a transparent outline around an overlay.
p.save();
p.setOpacity(0.4);
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
smearPixmap(&p, maskToColorAndAlpha((*maskImage).first, Qt::white));
p.restore();
}
p.drawPixmap(0, 0, maskToColorAndAlpha((*maskImage).first, (*maskImage).second));
}
const QPixmap shadowMask = maskToColorAndAlpha(combinedMask, Qt::black);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.setOpacity(0.05);
p.drawPixmap(QPointF(0, -0.501), shadowMask);
p.drawPixmap(QPointF(-0.501, 0), shadowMask);
p.drawPixmap(QPointF(0.5, 0), shadowMask);
p.drawPixmap(QPointF(0.5, 0.5), shadowMask);
p.drawPixmap(QPointF(-0.501, 0.5), shadowMask);
p.setOpacity(0.2);
p.drawPixmap(0, 1, shadowMask);
p.end();
return result;
}
QIcon ThemeHelper::themedIcon(const QString &mask)
{
QIcon result;
const MasksAndColors masks = masksAndColors(mask, qRound(qApp->devicePixelRatio()));
const QPixmap combinedMask = Utils::combinedMask(masks);
result.addPixmap(masksToIcon(masks, combinedMask));
QColor disabledColor = creatorTheme()->palette().mid().color();
disabledColor.setAlphaF(0.6);
result.addPixmap(maskToColorAndAlpha(combinedMask, disabledColor), QIcon::Disabled);
return result;
}
QPixmap ThemeHelper::themedIconPixmap(const QString &mask)
{
const MasksAndColors masks =
masksAndColors(mask, qRound(qApp->devicePixelRatio()));
const QPixmap combinedMask = Utils::combinedMask(masks);
return masksToIcon(masks, combinedMask);
}
QPixmap ThemeHelper::recoloredPixmap(const QString &maskImage, const QColor &color)
{
return maskToColorAndAlpha(QPixmap(Utils::StyleHelper::dpiSpecificImageFile(maskImage)), color);
}
QColor ThemeHelper::inputfieldIconColor()
{
// See QLineEdit::paintEvent
QColor color = QApplication::palette().text().color();
color.setAlpha(128);
return color;
}
} // namespace Utils
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** 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 http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef THEMEHELPER_H
#define THEMEHELPER_H
#include "utils_global.h"
QT_FORWARD_DECLARE_CLASS(QColor)
QT_FORWARD_DECLARE_CLASS(QIcon)
QT_FORWARD_DECLARE_CLASS(QPixmap)
namespace Utils {
class QTCREATOR_UTILS_EXPORT ThemeHelper
{
public:
// Returns a recolored icon with shadow and custom disabled state for a
// grayscale mask. The mask can range from a single image filename to
// a list of filename|color,... pairs.
// The color can be a Theme::Color enum key name. If invalid, it is run
// through QColor(const QString &name).
static QIcon themedIcon(const QString &mask);
// Same as themedIcon() but without disabled state.
static QPixmap themedIconPixmap(const QString &mask);
// Simple recoloring of a mask. No shadow. maskImage is a single file.
static QPixmap recoloredPixmap(const QString &maskImage, const QColor &color);
static QColor inputfieldIconColor();
};
} // namespace Utils
#endif // THEMEHELPER_H
......@@ -88,6 +88,7 @@ SOURCES += $$PWD/environment.cpp \
$$PWD/proxycredentialsdialog.cpp \
$$PWD/macroexpander.cpp \
$$PWD/theme/theme.cpp \
$$PWD/themehelper.cpp \
$$PWD/progressindicator.cpp \
$$PWD/fadingindicator.cpp \
$$PWD/overridecursor.cpp \
......@@ -191,6 +192,7 @@ HEADERS += \
$$PWD/macroexpander.h \
$$PWD/theme/theme.h \
$$PWD/theme/theme_p.h \
$$PWD/themehelper.h \
$$PWD/progressindicator.h \
$$PWD/fadingindicator.h \
$$PWD/executeondestruction.h \
......
......@@ -195,6 +195,8 @@ QtcLibrary {
"textfieldcombobox.h",
"textfileformat.cpp",
"textfileformat.h",
"themehelper.cpp",
"themehelper.h",
"treemodel.cpp",
"treemodel.h",
"treeviewcombobox.cpp",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment