Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,147 @@
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
add_subdirectory(tools/kiconfinder)
if (KICONTHEMES_USE_QTQUICK)
####################### add_subdirectory(qml)
endif()
if (APPLE)
add_subdirectory(tools/ksvg2icns)
endif()
add_subdirectory(widgets)
add_library(KF6IconThemes)
add_library(KF6::IconThemes ALIAS KF6IconThemes)
set_target_properties(KF6IconThemes PROPERTIES
VERSION ${KICONTHEMES_VERSION}
SOVERSION ${KICONTHEMES_SOVERSION}
EXPORT_NAME IconThemes
)
target_sources(KF6IconThemes PRIVATE
kiconcolors.cpp
kiconcolors.h
kiconeffect.cpp
kiconeffect.h
kiconengine.cpp
kiconengine.h
kiconengineplugin.cpp
kiconloader.cpp
kiconloader.h
kicontheme.cpp
kicontheme.h
kquickiconprovider.h
hicolor.qrc
)
ecm_qt_declare_logging_category(KF6IconThemes
HEADER debug.h
IDENTIFIER KICONTHEMES
CATEGORY_NAME kf.iconthemes
OLD_CATEGORY_NAMES kf5.kiconthemes
DESCRIPTION "KIconThemes"
EXPORT KICONTHEMES
)
ecm_generate_export_header(KF6IconThemes
BASE_NAME KIconThemes
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 5.0 6.5
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
target_include_directories(KF6IconThemes INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIconThemes>")
target_link_libraries(KF6IconThemes
PUBLIC
Qt6::Gui
PRIVATE
Qt6::GuiPrivate
Qt6::Svg
KF6::Archive # for KCompressionDevice
KF6::I18n # for KLocalizedString::localizedFilePath in KIconTheme
KF6::ColorScheme
)
if (HAVE_QTDBUS)
target_compile_definitions(KF6IconThemes PRIVATE WITH_QTDBUS)
target_link_libraries(KF6IconThemes PRIVATE Qt6::DBus)
endif()
if(TARGET KF6::BreezeIcons)
target_link_libraries(KF6IconThemes
PRIVATE
KF6::BreezeIcons
)
endif ()
ecm_generate_headers(KIconThemes_HEADERS
HEADER_NAMES
KIconColors
KIconEffect
KIconLoader
KIconTheme
KIconEngine
KQuickIconProvider
REQUIRED_HEADERS KIconThemes_HEADERS
)
install(TARGETS KF6IconThemes EXPORT KF6IconThemesTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/kiconthemes_export.h
${KIconThemes_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIconThemes COMPONENT Devel
)
if(BUILD_QCH)
ecm_add_qch(
KF6IconThemes_QCH
NAME KIconThemes
BASE_NAME KF6IconThemes
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${KIconThemes_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
LINK_QCHS
Qt6Widgets_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KICONTHEMES_EXPORT
KICONTHEMES_DEPRECATED
KICONTHEMES_DEPRECATED_EXPORT
"KICONTHEMES_DEPRECATED_VERSION(x, y, t)"
"KICONTHEMES_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
"KICONTHEMES_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
"KICONTHEMES_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
add_library(KIconEnginePlugin MODULE kiconengineplugin.cpp)
target_link_libraries(KIconEnginePlugin
PRIVATE
Qt6::Gui
KF6::IconThemes
)
# install in an extra directory we will add to the plugin path later just for this one engine
# we can overwrite the system one with that trick
install(TARGETS KIconEnginePlugin DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/kiconthemes6/iconengines)
ecm_qt_install_logging_categories(
EXPORT KICONTHEMES
FILE kiconthemes.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# Invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources.
# The results are stored in a pseudo .cpp file to be picked up by xgettext.
lst=`find . -name \*.rc -o -name \*.ui -o -name \*.kcfg`
if [ -n "$lst" ] ; then
$EXTRACTRC $lst >> rc.cpp
fi
# Extract strings from all source files.
# If your framework depends on KI18n, use $XGETTEXT. If it uses Qt translation
# system, use $EXTRACT_TR_STRINGS.
$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kiconthemes6.pot
@@ -0,0 +1,3 @@
#pragma once
#cmakedefine01 USE_BreezeIcons
@@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="icons/hicolor/index.theme">hicolor.theme</file>
</qresource>
</RCC>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,272 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kiconcolors.h"
#include <KColorScheme>
static QString STYLESHEET_TEMPLATE()
{
/* clang-format off */
return QStringLiteral(".ColorScheme-Text { color:%1; }\
.ColorScheme-Background{ color:%2; }\
.ColorScheme-Highlight{ color:%3; }\
.ColorScheme-HighlightedText{ color:%4; }\
.ColorScheme-PositiveText{ color:%5; }\
.ColorScheme-NeutralText{ color:%6; }\
.ColorScheme-NegativeText{ color:%7; }\
.ColorScheme-ActiveText{ color:%8; }\
.ColorScheme-Complement{ color:%9; }\
.ColorScheme-Contrast{ color:%10; }\
.ColorScheme-Accent{ color:%11; }\
");
/* clang-format on */
}
class KIconColorsPrivate : public QSharedData
{
public:
KIconColorsPrivate()
{
}
KIconColorsPrivate(const KIconColorsPrivate &other)
: QSharedData(other)
, text(other.text)
, background(other.background)
, highlight(other.highlight)
, highlightedText(other.highlightedText)
, accent(other.accent)
, positiveText(other.positiveText)
, neutralText(other.neutralText)
, negativeText(other.negativeText)
{
}
~KIconColorsPrivate()
{
}
QColor text;
QColor background;
QColor highlight;
QColor highlightedText;
QColor accent;
QColor positiveText;
QColor neutralText;
QColor negativeText;
QColor activeText;
static std::optional<QPalette> lastPalette;
static std::optional<KColorScheme> lastColorScheme;
};
std::optional<QPalette> KIconColorsPrivate::lastPalette;
std::optional<KColorScheme> KIconColorsPrivate::lastColorScheme;
KIconColors::KIconColors()
: KIconColors(QPalette())
{
}
KIconColors::KIconColors(const KIconColors &other)
: d_ptr(other.d_ptr)
{
}
KIconColors KIconColors::operator=(const KIconColors &other)
{
if (d_ptr != other.d_ptr) {
d_ptr = other.d_ptr;
}
return *this;
}
KIconColors::KIconColors(const QColor &colors)
: d_ptr(new KIconColorsPrivate)
{
Q_D(KIconColors);
d->text = colors;
d->background = colors;
d->highlight = colors;
d->highlightedText = colors;
d->positiveText = colors;
d->neutralText = colors;
d->negativeText = colors;
d->accent = colors;
}
KIconColors::KIconColors(const QPalette &palette)
: d_ptr(new KIconColorsPrivate)
{
Q_D(KIconColors);
d->text = palette.windowText().color();
d->background = palette.window().color();
d->highlight = palette.highlight().color();
d->highlightedText = palette.highlightedText().color();
d->accent = palette.accent().color();
if (!d->lastColorScheme || !d->lastPalette || palette != d->lastPalette) {
d->lastPalette = palette;
d->lastColorScheme = KColorScheme(QPalette::Active, KColorScheme::Window);
}
d->positiveText = d->lastColorScheme->foreground(KColorScheme::PositiveText).color().name();
d->neutralText = d->lastColorScheme->foreground(KColorScheme::NeutralText).color().name();
d->negativeText = d->lastColorScheme->foreground(KColorScheme::NegativeText).color().name();
d->activeText = d->lastColorScheme->foreground(KColorScheme::ActiveText).color().name();
}
KIconColors::~KIconColors()
{
}
qreal luma(const QColor &color) {
return (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255;
}
QString KIconColors::stylesheet(KIconLoader::States state) const
{
Q_D(const KIconColors);
const QColor complement =
luma(d->background) > 0.5 ? Qt::white : Qt::black;
const QColor contrast =
luma(d->background) > 0.5 ? Qt::black : Qt::white;
QColor accentColor = d->accent;
// When selected, tint the accent color with a small portion of highlighted text color,
// because since the accent color used to be the same as the highlight color, it might cause
// icons, especially folders to "disappear" against the background
if (state == KIconLoader::SelectedState) {
const qreal tintRatio = 0.85;
const qreal r = accentColor.redF() * tintRatio + d->highlightedText.redF() * (1.0 - tintRatio);
const qreal g = accentColor.greenF() * tintRatio + d->highlightedText.greenF() * (1.0 - tintRatio);
const qreal b = accentColor.blueF() * tintRatio + d->highlightedText.blueF() * (1.0 - tintRatio);
accentColor.setRgbF(r, g, b, accentColor.alphaF());
}
return STYLESHEET_TEMPLATE()
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->text.name())
.arg(state == KIconLoader::SelectedState ? d->highlight.name() : d->background.name())
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->highlight.name())
.arg(state == KIconLoader::SelectedState ? d->highlight.name() : d->highlightedText.name())
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->positiveText.name())
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->neutralText.name())
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->negativeText.name())
.arg(state == KIconLoader::SelectedState ? d->highlightedText.name() : d->activeText.name())
.arg(complement.name())
.arg(contrast.name())
.arg(accentColor.name());
}
QColor KIconColors::highlight() const
{
Q_D(const KIconColors);
return d->highlight;
}
QColor KIconColors::highlightedText() const
{
Q_D(const KIconColors);
return d->highlightedText;
}
QColor KIconColors::accent() const
{
Q_D(const KIconColors);
return d->accent;
}
QColor KIconColors::background() const
{
Q_D(const KIconColors);
return d->background;
}
QColor KIconColors::text() const
{
Q_D(const KIconColors);
return d->text;
}
QColor KIconColors::negativeText() const
{
Q_D(const KIconColors);
return d->negativeText;
}
QColor KIconColors::positiveText() const
{
Q_D(const KIconColors);
return d->positiveText;
}
QColor KIconColors::neutralText() const
{
Q_D(const KIconColors);
return d->neutralText;
}
QColor KIconColors::activeText() const
{
Q_D(const KIconColors);
return d->activeText;
}
void KIconColors::setText(const QColor &color)
{
Q_D(KIconColors);
d->text = color;
}
void KIconColors::setBackground(const QColor &color)
{
Q_D(KIconColors);
d->background = color;
}
void KIconColors::setHighlight(const QColor &color)
{
Q_D(KIconColors);
d->highlight = color;
}
void KIconColors::setHighlightedText(const QColor &color)
{
Q_D(KIconColors);
d->highlightedText = color;
}
void KIconColors::setAccent(const QColor &color)
{
Q_D(KIconColors);
d->accent = color;
}
void KIconColors::setNegativeText(const QColor &color)
{
Q_D(KIconColors);
d->negativeText = color;
}
void KIconColors::setNeutralText(const QColor &color)
{
Q_D(KIconColors);
d->neutralText = color;
}
void KIconColors::setPositiveText(const QColor &color)
{
Q_D(KIconColors);
d->positiveText = color;
}
void KIconColors::setActiveText(const QColor &color)
{
Q_D(KIconColors);
d->activeText = color;
}
@@ -0,0 +1,93 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONCOLORS_H
#define KICONCOLORS_H
#include "kiconloader.h"
#include <QPalette>
#include <QSharedDataPointer>
class KIconColorsPrivate;
/**
* @class KIconColors
*
* Sepecifies which colors will be used when recoloring icons as its stylesheet.
*
* KIconLoader supports re-coloring svg icons based on a set of colors. This
* class will define them.
*
* @see KIconEngine
* @see KDE::icon
*/
class KICONTHEMES_EXPORT KIconColors
{
public:
/**
* Will fill the colors based on the default QPalette() constructor.
*/
KIconColors();
/**
* Makes all the color property be @p colors
*/
explicit KIconColors(const QColor &colors);
/**
* Uses @palette to define text, highlight, highlightedText, accent and background.
* The rest being positiveText, negativeText and neutralText are filled from
* KColorScheme(QPalette::Active, KColorScheme::Window);
*/
explicit KIconColors(const QPalette &palette);
KIconColors(const KIconColors &other);
~KIconColors();
KIconColors operator=(const KIconColors &other);
QColor text() const;
QColor highlight() const;
QColor highlightedText() const;
QColor accent() const;
QColor background() const;
QColor neutralText() const;
QColor positiveText() const;
QColor negativeText() const;
QColor activeText() const;
void setText(const QColor &color);
void setHighlight(const QColor &color);
void setHighlightedText(const QColor &color);
void setAccent(const QColor &color);
void setBackground(const QColor &color);
void setNeutralText(const QColor &color);
void setPositiveText(const QColor &color);
void setNegativeText(const QColor &color);
void setActiveText(const QColor& color);
protected:
/**
* @returns a CSS stylesheet to be used SVG icon files.
* @param state defines the state we are rendering the stylesheet for
*
* Specifies: .ColorScheme-Text, .ColorScheme-Background, .ColorScheme-Highlight,
* .ColorScheme-HighlightedText, .ColorScheme-PositiveText, .ColorScheme-NeutralText
* .ColorScheme-NegativeText, .ColorScheme-ActiveText, .ColorScheme-Complement,
* .ColorScheme-Contrast, .ColorScheme-Accent,
*/
QString stylesheet(KIconLoader::States state) const;
private:
Q_DECLARE_PRIVATE(KIconColors)
friend class KIconLoaderPrivate;
QExplicitlySharedDataPointer<KIconColorsPrivate> d_ptr;
};
#endif
@@ -0,0 +1,724 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2007 Daniel M. Duley <daniel.duley@verizon.net>
with minor additions and based on ideas from
SPDX-FileContributor: Torsten Rahn <torsten@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kiconeffect.h"
#include "debug.h"
#include "kiconloader.h"
#include <KColorScheme>
#include <QDebug>
#include <QPalette>
#include <QSysInfo>
#include <qplatformdefs.h>
#include <math.h>
class KIconEffectPrivate
{
public:
// http://en.cppreference.com/w/cpp/language/zero_initialization
KIconEffectPrivate()
: effect{{}}
, value{{}}
, trans{{}}
, key{{}}
{
}
public:
int effect[KIconLoader::LastGroup][KIconLoader::LastState];
float value[KIconLoader::LastGroup][KIconLoader::LastState];
bool trans[KIconLoader::LastGroup][KIconLoader::LastState];
QString key[KIconLoader::LastGroup][KIconLoader::LastState];
};
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
KIconEffect::KIconEffect()
: d(new KIconEffectPrivate)
{
init();
}
#endif
KIconEffect::~KIconEffect() = default;
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
void KIconEffect::init()
{
int i;
// FIXME: this really should be using KIconLoader::metaObject() to guarantee synchronization
// performance wise it's also practically guaranteed to be faster
QStringList groups;
groups += QStringLiteral("Desktop");
groups += QStringLiteral("Toolbar");
groups += QStringLiteral("MainToolbar");
groups += QStringLiteral("Small");
groups += QStringLiteral("Panel");
groups += QStringLiteral("Dialog");
QStringList states;
states += QStringLiteral("Default");
states += QStringLiteral("Active");
states += QStringLiteral("Disabled");
QStringList::ConstIterator it;
for (it = groups.constBegin(), i = 0; it != groups.constEnd(); ++it, ++i) {
d->effect[i][KIconLoader::DefaultState] = NoEffect;
d->effect[i][KIconLoader::ActiveState] = ((i == KIconLoader::Desktop) || (KIconLoader::Panel == 4)) ? ToGamma : NoEffect;
d->effect[i][KIconLoader::DisabledState] = ToGray;
d->trans[i][KIconLoader::DefaultState] = false;
d->trans[i][KIconLoader::ActiveState] = false;
d->trans[i][KIconLoader::DisabledState] = true;
d->value[i][KIconLoader::DefaultState] = 1.0;
d->value[i][KIconLoader::ActiveState] = ((i == KIconLoader::Desktop) || (KIconLoader::Panel == 4)) ? 0.7 : 1.0;
d->value[i][KIconLoader::DisabledState] = 1.0;
}
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
bool KIconEffect::hasEffect(int group, int state) const
{
if (group < 0 || group >= KIconLoader::LastGroup //
|| state < 0 || state >= KIconLoader::LastState) {
return false;
}
return d->effect[group][state] != NoEffect;
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QString KIconEffect::fingerprint(int group, int state) const
{
if (group < 0 || group >= KIconLoader::LastGroup //
|| state < 0 || state >= KIconLoader::LastState) {
return QString();
}
QString cached = d->key[group][state];
if (cached.isEmpty()) {
QString tmp;
cached = tmp.setNum(d->effect[group][state]);
cached += QLatin1Char(':');
cached += tmp.setNum(d->value[group][state]);
cached += QLatin1Char(':');
cached += d->trans[group][state] ? QLatin1String("trans") : QLatin1String("notrans");
d->key[group][state] = cached;
}
return cached;
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QImage KIconEffect::apply(const QImage &image, int group, int state) const
{
if (state >= KIconLoader::LastState) {
qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States";
return image;
}
if (group >= KIconLoader::LastGroup) {
qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
return image;
}
return apply(image, d->effect[group][state], d->value[group][state], QColor(), QColor(), d->trans[group][state]);
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QImage KIconEffect::apply(const QImage &image, int effect, float value, const QColor &col, bool trans) const
{
return apply(image, effect, value, col, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans);
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QImage KIconEffect::apply(const QImage &img, int effect, float value, const QColor &col, const QColor &col2, bool trans) const
{
QImage image = img;
if (effect >= LastEffect) {
qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects";
return image;
}
if (value > 1.0) {
value = 1.0;
} else if (value < 0.0) {
value = 0.0;
}
switch (effect) {
case ToGray:
toGray(image, value);
break;
case DeSaturate:
deSaturate(image, value);
break;
case Colorize:
colorize(image, col, value);
break;
case ToGamma:
toGamma(image, value);
break;
case ToMonochrome:
toMonochrome(image, col, col2, value);
break;
}
if (trans == true) {
semiTransparent(image);
}
return image;
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QPixmap KIconEffect::apply(const QPixmap &pixmap, int group, int state) const
{
if (state >= KIconLoader::LastState) {
qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States";
return pixmap;
}
if (group >= KIconLoader::LastGroup) {
qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
return pixmap;
}
return apply(pixmap, d->effect[group][state], d->value[group][state], QColor(), QColor(), d->trans[group][state]);
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, bool trans) const
{
return apply(pixmap, effect, value, col, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans);
}
#endif
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, const QColor &col2, bool trans) const
{
QPixmap result;
if (effect >= LastEffect) {
qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects";
return result;
}
if ((trans == true) && (effect == NoEffect)) {
result = pixmap;
semiTransparent(result);
} else if (effect != NoEffect) {
QImage tmpImg = pixmap.toImage();
tmpImg = apply(tmpImg, effect, value, col, col2, trans);
result = QPixmap::fromImage(std::move(tmpImg));
} else {
result = pixmap;
}
return result;
}
#endif
struct KIEImgEdit {
QImage &img;
QList<QRgb> colors;
unsigned int *data;
unsigned int pixels;
KIEImgEdit(QImage &_img)
: img(_img)
{
if (img.depth() > 8) {
// Code using data and pixels assumes that the pixels are stored
// in 32bit values and that the image is not premultiplied
if ((img.format() != QImage::Format_ARGB32) //
&& (img.format() != QImage::Format_RGB32)) {
img.convertTo(QImage::Format_ARGB32);
}
data = (unsigned int *)img.bits();
pixels = img.width() * img.height();
} else {
pixels = img.colorCount();
colors = img.colorTable();
data = (unsigned int *)colors.data();
}
}
~KIEImgEdit()
{
if (img.depth() <= 8) {
img.setColorTable(colors);
}
}
KIEImgEdit(const KIEImgEdit &) = delete;
KIEImgEdit &operator=(const KIEImgEdit &) = delete;
};
// Taken from KImageEffect. We don't want to link kdecore to kdeui! As long
// as this code is not too big, it doesn't seem much of a problem to me.
void KIconEffect::toGray(QImage &img, float value)
{
if (value == 0.0) {
return;
}
KIEImgEdit ii(img);
QRgb *data = ii.data;
QRgb *end = data + ii.pixels;
unsigned char gray;
if (value == 1.0) {
while (data != end) {
gray = qGray(*data);
*data = qRgba(gray, gray, gray, qAlpha(*data));
++data;
}
} else {
unsigned char val = (unsigned char)(255.0 * value);
while (data != end) {
gray = qGray(*data);
*data = qRgba((val * gray + (0xFF - val) * qRed(*data)) >> 8,
(val * gray + (0xFF - val) * qGreen(*data)) >> 8,
(val * gray + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
++data;
}
}
}
void KIconEffect::colorize(QImage &img, const QColor &col, float value)
{
if (value == 0.0) {
return;
}
KIEImgEdit ii(img);
QRgb *data = ii.data;
QRgb *end = data + ii.pixels;
float rcol = col.red();
float gcol = col.green();
float bcol = col.blue();
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned char gray;
unsigned char val = (unsigned char)(255.0 * value);
while (data != end) {
gray = qGray(*data);
if (gray < 128) {
red = static_cast<unsigned char>(rcol / 128 * gray);
green = static_cast<unsigned char>(gcol / 128 * gray);
blue = static_cast<unsigned char>(bcol / 128 * gray);
} else if (gray > 128) {
red = static_cast<unsigned char>((gray - 128) * (2 - rcol / 128) + rcol - 1);
green = static_cast<unsigned char>((gray - 128) * (2 - gcol / 128) + gcol - 1);
blue = static_cast<unsigned char>((gray - 128) * (2 - bcol / 128) + bcol - 1);
} else {
red = static_cast<unsigned char>(rcol);
green = static_cast<unsigned char>(gcol);
blue = static_cast<unsigned char>(bcol);
}
*data = qRgba((val * red + (0xFF - val) * qRed(*data)) >> 8,
(val * green + (0xFF - val) * qGreen(*data)) >> 8,
(val * blue + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
++data;
}
}
void KIconEffect::toMonochrome(QImage &img, const QColor &black, const QColor &white, float value)
{
if (value == 0.0) {
return;
}
KIEImgEdit ii(img);
QRgb *data = ii.data;
QRgb *end = data + ii.pixels;
// Step 1: determine the average brightness
double values = 0.0;
double sum = 0.0;
bool grayscale = true;
while (data != end) {
sum += qGray(*data) * qAlpha(*data) + 255 * (255 - qAlpha(*data));
values += 255;
if ((qRed(*data) != qGreen(*data)) || (qGreen(*data) != qBlue(*data))) {
grayscale = false;
}
++data;
}
double medium = sum / values;
// Step 2: Modify the image
unsigned char val = (unsigned char)(255.0 * value);
int rw = white.red();
int gw = white.green();
int bw = white.blue();
int rb = black.red();
int gb = black.green();
int bb = black.blue();
data = ii.data;
if (grayscale) {
while (data != end) {
if (qRed(*data) <= medium) {
*data = qRgba((val * rb + (0xFF - val) * qRed(*data)) >> 8,
(val * gb + (0xFF - val) * qGreen(*data)) >> 8,
(val * bb + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
} else {
*data = qRgba((val * rw + (0xFF - val) * qRed(*data)) >> 8,
(val * gw + (0xFF - val) * qGreen(*data)) >> 8,
(val * bw + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
}
++data;
}
} else {
while (data != end) {
if (qGray(*data) <= medium) {
*data = qRgba((val * rb + (0xFF - val) * qRed(*data)) >> 8,
(val * gb + (0xFF - val) * qGreen(*data)) >> 8,
(val * bb + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
} else {
*data = qRgba((val * rw + (0xFF - val) * qRed(*data)) >> 8,
(val * gw + (0xFF - val) * qGreen(*data)) >> 8,
(val * bw + (0xFF - val) * qBlue(*data)) >> 8,
qAlpha(*data));
}
++data;
}
}
}
void KIconEffect::deSaturate(QImage &img, float value)
{
if (value == 0.0) {
return;
}
KIEImgEdit ii(img);
QRgb *data = ii.data;
QRgb *end = data + ii.pixels;
QColor color;
int h;
int s;
int v;
while (data != end) {
color.setRgb(*data);
color.getHsv(&h, &s, &v);
color.setHsv(h, (int)(s * (1.0 - value) + 0.5), v);
*data = qRgba(color.red(), color.green(), color.blue(), qAlpha(*data));
++data;
}
}
void KIconEffect::toGamma(QImage &img, float value)
{
KIEImgEdit ii(img);
QRgb *data = ii.data;
QRgb *end = data + ii.pixels;
float gamma = 1 / (2 * value + 0.5);
while (data != end) {
*data = qRgba(static_cast<unsigned char>(pow(static_cast<float>(qRed(*data)) / 255, gamma) * 255),
static_cast<unsigned char>(pow(static_cast<float>(qGreen(*data)) / 255, gamma) * 255),
static_cast<unsigned char>(pow(static_cast<float>(qBlue(*data)) / 255, gamma) * 255),
qAlpha(*data));
++data;
}
}
void KIconEffect::semiTransparent(QImage &img)
{
if (img.depth() == 32) {
if (img.format() == QImage::Format_ARGB32_Premultiplied) {
img.convertTo(QImage::Format_ARGB32);
}
int width = img.width();
int height = img.height();
unsigned char *line;
for (int y = 0; y < height; ++y) {
if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
line = img.scanLine(y);
} else {
line = img.scanLine(y) + 3;
}
for (int x = 0; x < width; ++x) {
*line >>= 1;
line += 4;
}
}
} else if (img.depth() == 8) {
// not running on 8 bit, we can safely install a new colorTable
QList<QRgb> colorTable = img.colorTable();
for (int i = 0; i < colorTable.size(); ++i) {
colorTable[i] = (colorTable[i] & 0x00ffffff) | ((colorTable[i] & 0xfe000000) >> 1);
}
img.setColorTable(colorTable);
} else {
// Insert transparent pixel into the clut.
int transColor = -1;
// search for a color that is already transparent
for (int x = 0; x < img.colorCount(); ++x) {
// try to find already transparent pixel
if (qAlpha(img.color(x)) < 127) {
transColor = x;
break;
}
}
// FIXME: image must have transparency
if (transColor < 0 || transColor >= img.colorCount()) {
return;
}
img.setColor(transColor, 0);
unsigned char *line;
if (img.depth() == 8) {
for (int y = 0; y < img.height(); ++y) {
line = img.scanLine(y);
for (int x = (y % 2); x < img.width(); x += 2) {
line[x] = transColor;
}
}
} else {
const bool setOn = (transColor != 0);
if (img.format() == QImage::Format_MonoLSB) {
for (int y = 0; y < img.height(); ++y) {
line = img.scanLine(y);
for (int x = (y % 2); x < img.width(); x += 2) {
if (!setOn) {
*(line + (x >> 3)) &= ~(1 << (x & 7));
} else {
*(line + (x >> 3)) |= (1 << (x & 7));
}
}
}
} else {
for (int y = 0; y < img.height(); ++y) {
line = img.scanLine(y);
for (int x = (y % 2); x < img.width(); x += 2) {
if (!setOn) {
*(line + (x >> 3)) &= ~(1 << (7 - (x & 7)));
} else {
*(line + (x >> 3)) |= (1 << (7 - (x & 7)));
}
}
}
}
}
}
}
void KIconEffect::semiTransparent(QPixmap &pix)
{
QImage img = pix.toImage();
semiTransparent(img);
pix = QPixmap::fromImage(img);
}
QImage KIconEffect::doublePixels(const QImage &src) const
{
int w = src.width();
int h = src.height();
QImage dst(w * 2, h * 2, src.format());
if (src.depth() == 1) {
qWarning() << "image depth 1 not supported";
return QImage();
}
int x;
int y;
if (src.depth() == 32) {
QRgb *l1;
QRgb *l2;
for (y = 0; y < h; ++y) {
l1 = (QRgb *)src.scanLine(y);
l2 = (QRgb *)dst.scanLine(y * 2);
for (x = 0; x < w; ++x) {
l2[x * 2] = l2[x * 2 + 1] = l1[x];
}
memcpy(dst.scanLine(y * 2 + 1), l2, dst.bytesPerLine());
}
} else {
for (x = 0; x < src.colorCount(); ++x) {
dst.setColor(x, src.color(x));
}
const unsigned char *l1;
unsigned char *l2;
for (y = 0; y < h; ++y) {
l1 = src.scanLine(y);
l2 = dst.scanLine(y * 2);
for (x = 0; x < w; ++x) {
l2[x * 2] = l1[x];
l2[x * 2 + 1] = l1[x];
}
memcpy(dst.scanLine(y * 2 + 1), l2, dst.bytesPerLine());
}
}
return dst;
}
void KIconEffect::overlay(QImage &src, QImage &overlay)
{
if (src.depth() != overlay.depth()) {
qWarning() << "Image depth src (" << src.depth() << ") != overlay "
<< "(" << overlay.depth() << ")!";
return;
}
if (src.size() != overlay.size()) {
qWarning() << "Image size src != overlay";
return;
}
if (src.format() == QImage::Format_ARGB32_Premultiplied) {
src.convertTo(QImage::Format_ARGB32);
}
if (overlay.format() == QImage::Format_RGB32) {
qWarning() << "Overlay doesn't have alpha buffer!";
return;
} else if (overlay.format() == QImage::Format_ARGB32_Premultiplied) {
overlay.convertTo(QImage::Format_ARGB32);
}
int i;
int j;
// We don't do 1 bpp
if (src.depth() == 1) {
qWarning() << "1bpp not supported!";
return;
}
// Overlay at 8 bpp doesn't use alpha blending
if (src.depth() == 8) {
if (src.colorCount() + overlay.colorCount() > 255) {
qWarning() << "Too many colors in src + overlay!";
return;
}
// Find transparent pixel in overlay
int trans;
for (trans = 0; trans < overlay.colorCount(); trans++) {
if (qAlpha(overlay.color(trans)) == 0) {
qWarning() << "transparent pixel found at " << trans;
break;
}
}
if (trans == overlay.colorCount()) {
qWarning() << "transparent pixel not found!";
return;
}
// Merge color tables
int nc = src.colorCount();
src.setColorCount(nc + overlay.colorCount());
for (i = 0; i < overlay.colorCount(); ++i) {
src.setColor(nc + i, overlay.color(i));
}
// Overwrite nontransparent pixels.
unsigned char *oline;
unsigned char *sline;
for (i = 0; i < src.height(); ++i) {
oline = overlay.scanLine(i);
sline = src.scanLine(i);
for (j = 0; j < src.width(); ++j) {
if (oline[j] != trans) {
sline[j] = oline[j] + nc;
}
}
}
}
// Overlay at 32 bpp does use alpha blending
if (src.depth() == 32) {
QRgb *oline;
QRgb *sline;
int r1;
int g1;
int b1;
int a1;
int r2;
int g2;
int b2;
int a2;
for (i = 0; i < src.height(); ++i) {
oline = (QRgb *)overlay.scanLine(i);
sline = (QRgb *)src.scanLine(i);
for (j = 0; j < src.width(); ++j) {
r1 = qRed(oline[j]);
g1 = qGreen(oline[j]);
b1 = qBlue(oline[j]);
a1 = qAlpha(oline[j]);
r2 = qRed(sline[j]);
g2 = qGreen(sline[j]);
b2 = qBlue(sline[j]);
a2 = qAlpha(sline[j]);
r2 = (a1 * r1 + (0xff - a1) * r2) >> 8;
g2 = (a1 * g1 + (0xff - a1) * g2) >> 8;
b2 = (a1 * b1 + (0xff - a1) * b2) >> 8;
a2 = qMax(a1, a2);
sline[j] = qRgba(r2, g2, b2, a2);
}
}
}
}
void KIconEffect::toDisabled(QImage &image)
{
KIconEffect::toGray(image, 1);
KIconEffect::semiTransparent(image);
}
void KIconEffect::toDisabled(QPixmap &pixmap)
{
QImage img = pixmap.toImage();
toDisabled(img);
pixmap = QPixmap::fromImage(img);
}
void KIconEffect::toActive(QImage &image)
{
KIconEffect::toGamma(image, 0.7);
}
void KIconEffect::toActive(QPixmap &pixmap)
{
QImage img = pixmap.toImage();
KIconEffect::toGamma(img, 0.7);
pixmap = QPixmap::fromImage(img);
}
@@ -0,0 +1,310 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2007 Daniel M. Duley <daniel.duley@verizon.net>
with minor additions and based on ideas from
SPDX-FileContributor: Torsten Rahn <torsten@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONEFFECT_H
#define KICONEFFECT_H
#include <kiconthemes_export.h>
#include <QColor>
#include <QImage>
#include <QPixmap>
#include <memory>
class KIconEffectPrivate;
/**
* @class KIconEffect kiconeffect.h KIconEffect
*
* Applies effects to icons.
*
* This class applies effects to icons depending on their state and
* group. For example, it can be used to make all disabled icons
* in a toolbar gray.
*
* \image html kiconeffect-apply.png "Various Effects applied to an image"
*
* @see QIcon::fromTheme
*/
class KICONTHEMES_EXPORT KIconEffect
{
public:
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Create a new KIconEffect.
* You will most likely never have to use this to create a new KIconEffect
* yourself, as you can use the KIconEffect provided by the global KIconLoader
* (which itself is accessible by KIconLoader::global()) through its
* iconEffect() function.
*
* @deprecated since 6.5, use the static API
*
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
KIconEffect();
#endif
~KIconEffect();
KIconEffect(const KIconEffect &) = delete;
KIconEffect &operator=(const KIconEffect &) = delete;
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* This is the enumeration of all possible icon effects.
* Note that 'LastEffect' is no valid icon effect but only
* used internally to check for invalid icon effects.
*
* @li NoEffect: Do not apply any icon effect
* @li ToGray: Tints the icon gray
* @li Colorize: Tints the icon with a specific color
* @li ToGamma: Change the gamma value of the icon
* @li DeSaturate: Reduce the saturation of the icon
* @li ToMonochrome: Produces a monochrome icon
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
enum Effects {
NoEffect,
ToGray,
Colorize,
ToGamma,
DeSaturate,
ToMonochrome,
LastEffect,
};
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Rereads configuration.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
void init();
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Tests whether an effect has been configured for the given icon group.
* @param group the group to check, see KIconLoader::Group
* @param state the state to check, see KIconLoader::States
* @returns true if an effect is configured for the given @p group
* in @p state, otherwise false.
* @see KIconLoader::Group
* KIconLoader::States
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
bool hasEffect(int group, int state) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Returns a fingerprint for the effect by encoding
* the given @p group and @p state into a QString. This
* is useful for caching.
* @param group the group, see KIconLoader::Group
* @param state the state, see KIconLoader::States
* @return the fingerprint of the given @p group+@p state
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QString fingerprint(int group, int state) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Applies an effect to an image. The effect to apply depends on the
* @p group and @p state parameters, and is configured by the user.
* @param src The image.
* @param group The group for the icon, see KIconLoader::Group
* @param state The icon's state, see KIconLoader::States
* @return An image with the effect applied.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QImage apply(const QImage &src, int group, int state) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Applies an effect to an image.
* @param src The image.
* @param effect The effect to apply, one of KIconEffect::Effects.
* @param value Strength of the effect. 0 <= @p value <= 1.
* @param rgb Color parameter for effects that need one.
* @param trans Add Transparency if trans = true.
* @return An image with the effect applied.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QImage apply(const QImage &src, int effect, float value, const QColor &rgb, bool trans) const;
QImage apply(const QImage &src, int effect, float value, const QColor &rgb, const QColor &rgb2, bool trans) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Applies an effect to a pixmap.
* @param src The pixmap.
* @param group The group for the icon, see KIconLoader::Group
* @param state The icon's state, see KIconLoader::States
* @return A pixmap with the effect applied.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QPixmap apply(const QPixmap &src, int group, int state) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Applies an effect to a pixmap.
* @param src The pixmap.
* @param effect The effect to apply, one of KIconEffect::Effects.
* @param value Strength of the effect. 0 <= @p value <= 1.
* @param rgb Color parameter for effects that need one.
* @param trans Add Transparency if trans = true.
* @return A pixmap with the effect applied.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QPixmap apply(const QPixmap &src, int effect, float value, const QColor &rgb, bool trans) const;
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QPixmap apply(const QPixmap &src, int effect, float value, const QColor &rgb, const QColor &rgb2, bool trans) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Returns an image twice as large, consisting of 2x2 pixels.
* @param src the image.
* @return the scaled image.
*
* @deprecated since 6.5, use the static API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static API")
QImage doublePixels(const QImage &src) const;
#endif
/**
* Tints an image gray.
*
* @param image The image
* @param value Strength of the effect. 0 <= @p value <= 1
*/
static void toGray(QImage &image, float value);
/**
* Colorizes an image with a specific color.
*
* @param image The image
* @param col The color with which the @p image is tinted
* @param value Strength of the effect. 0 <= @p value <= 1
*/
static void colorize(QImage &image, const QColor &col, float value);
/**
* Produces a monochrome icon with a given foreground and background color
*
* @param image The image
* @param white The color with which the white parts of @p image are painted
* @param black The color with which the black parts of @p image are painted
* @param value Strength of the effect. 0 <= @p value <= 1
*/
static void toMonochrome(QImage &image, const QColor &black, const QColor &white, float value);
/**
* Desaturates an image.
*
* @param image The image
* @param value Strength of the effect. 0 <= @p value <= 1
*/
static void deSaturate(QImage &image, float value);
/**
* Changes the gamma value of an image.
*
* @param image The image
* @param value Strength of the effect. 0 <= @p value <= 1
*/
static void toGamma(QImage &image, float value);
/**
* Renders an image semi-transparent.
*
* @param image The image
*/
static void semiTransparent(QImage &image);
/**
* Renders a pixmap semi-transparent.
*
* @param pixmap The pixmap
*/
static void semiTransparent(QPixmap &pixmap);
/**
* Overlays an image with an other image.
*
* @param src The image
* @param overlay The image to overlay @p src with
*/
static void overlay(QImage &src, QImage &overlay);
/**
* Applies a disabled effect
*
* @param image The image
*
* @since 6.5
*/
static void toDisabled(QImage &image);
/**
* Applies a disabled effect
*
* @param pixmap The image
*
* @since 6.5
*/
static void toDisabled(QPixmap &pixmap);
/**
* Applies an effect for an icon that is in an 'active' state
*
* @param image The image
*
* @since 6.5
*/
static void toActive(QImage &image);
/**
* Applies an effect for an icon that is in an 'active' state
*
* @param pixmap The image
*
* @since 6.5
*/
static void toActive(QPixmap &pixmap);
private:
std::unique_ptr<KIconEffectPrivate> const d;
};
#endif
@@ -0,0 +1,223 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kiconengine.h"
#include "kiconloader_p.h"
#include <kiconloader.h>
#include "kiconcolors.h"
#include <KIconTheme>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QPainter>
#include <qscopeguard.h>
class KIconEnginePrivate
{
public:
QPointer<KIconLoader> mIconLoader;
bool mCustomColors = false;
KIconColors mColors;
QString mActualIconName;
};
KIconEngine::KIconEngine(const QString &iconName, KIconLoader *iconLoader, const QStringList &overlays)
: mIconName(iconName)
, mOverlays(overlays)
, d(new KIconEnginePrivate{iconLoader, false, {}, {}})
{
}
KIconEngine::KIconEngine(const QString &iconName, KIconLoader *iconLoader)
: mIconName(iconName)
, d(new KIconEnginePrivate{iconLoader, false, {}, {}})
{
}
KIconEngine::KIconEngine(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader)
: mIconName(iconName)
, d(new KIconEnginePrivate{iconLoader, true, colors, {}})
{
}
KIconEngine::KIconEngine(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader, const QStringList &overlays)
: mIconName(iconName)
, mOverlays(overlays)
, d(new KIconEnginePrivate{iconLoader, true, colors, {}})
{
}
KIconEngine::~KIconEngine()
{
delete d;
}
static inline int qIconModeToKIconState(QIcon::Mode mode)
{
switch (mode) {
case QIcon::Normal:
return KIconLoader::DefaultState;
case QIcon::Active:
return KIconLoader::ActiveState;
case QIcon::Disabled:
return KIconLoader::DisabledState;
case QIcon::Selected:
return KIconLoader::SelectedState;
default:
return KIconLoader::DefaultState;
}
}
QSize KIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
return QIconEngine::actualSize(size, mode, state);
}
void KIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
{
if (!d->mIconLoader) {
return;
}
const qreal dpr = painter->device()->devicePixelRatioF();
const QPixmap pix = createPixmap(rect.size(), dpr, mode, state);
painter->drawPixmap(rect, pix);
}
QPixmap KIconEngine::createPixmap(const QSize &logicalSize, qreal scale, QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(state)
if (scale < 1) {
scale = 1;
}
if (logicalSize.isEmpty()) {
return QPixmap();
}
if (!d->mIconLoader) {
QPixmap pm(logicalSize * scale);
pm.setDevicePixelRatio(scale);
pm.fill(Qt::transparent);
return pm;
}
QString iconPath;
const int kstate = qIconModeToKIconState(mode);
QPixmap pix = d->mIconLoader->loadScaledIcon(mIconName,
KIconLoader::Desktop,
scale,
logicalSize,
kstate,
mOverlays,
&iconPath,
false,
d->mCustomColors ? std::make_optional(d->mColors) : std::nullopt);
if (!iconPath.isEmpty() && !d->mActualIconName.isEmpty()) {
d->mActualIconName = QFileInfo(iconPath).completeBaseName();
}
if (pix.size() == logicalSize * scale) {
return pix;
}
QPixmap pix2(logicalSize * scale);
pix2.setDevicePixelRatio(scale);
pix2.fill(QColor(0, 0, 0, 0));
QPainter painter(&pix2);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
const QSizeF targetSize = pix.size().scaled(logicalSize, Qt::KeepAspectRatio);
QRectF targetRect({0, 0}, targetSize);
targetRect.moveCenter(QRectF(pix2.rect()).center() / scale);
painter.drawPixmap(targetRect, pix, pix.rect());
return pix2;
}
QPixmap KIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
return createPixmap(size, 1 /*scale*/, mode, state);
}
QPixmap KIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
{
// Since https://codereview.qt-project.org/c/qt/qtbase/+/563553 size is in logical pixels
if (QLibraryInfo::version() >= QVersionNumber(6, 8, 0)) {
return createPixmap(size, scale, mode, state);
} else {
return createPixmap(size / scale, scale, mode, state);
}
}
QString KIconEngine::iconName()
{
if (!d->mActualIconName.isEmpty()) {
return d->mActualIconName;
}
if (!d->mIconLoader) {
return QString();
}
const QString iconPath = KIconLoaderPrivate::get(d->mIconLoader)->preferredIconPath(mIconName);
if (iconPath.isEmpty()) {
return QString();
}
d->mActualIconName = QFileInfo(iconPath).completeBaseName();
return d->mActualIconName;
}
Q_GLOBAL_STATIC_WITH_ARGS(QList<QSize>,
sSizes,
(QList<QSize>() << QSize(16, 16) << QSize(22, 22) << QSize(32, 32) << QSize(48, 48) << QSize(64, 64) << QSize(128, 128)
<< QSize(256, 256)))
QList<QSize> KIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(mode);
Q_UNUSED(state);
if (!d->mIconLoader) {
return QList<QSize>();
}
const bool found = d->mIconLoader->hasIcon(mIconName);
return found ? *sSizes : QList<QSize>();
}
QString KIconEngine::key() const
{
return QStringLiteral("KIconEngine");
}
QIconEngine *KIconEngine::clone() const
{
return new KIconEngine(mIconName, d->mIconLoader, mOverlays);
}
bool KIconEngine::read(QDataStream &in)
{
in >> mIconName >> mOverlays;
return true;
}
bool KIconEngine::write(QDataStream &out) const
{
out << mIconName << mOverlays;
return true;
}
bool KIconEngine::isNull()
{
return !d->mIconLoader || !d->mIconLoader->hasIcon(mIconName);
}
@@ -0,0 +1,101 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONENGINE_H
#define KICONENGINE_H
#include "kiconthemes_export.h"
#include <QIconEngine>
#include <QPointer>
class KIconColors;
class KIconLoader;
class KIconEnginePrivate;
/**
* @class KIconEngine kiconengine.h KIconEngine
*
* \short A class to provide rendering of KDE icons.
*
* This is mostly used to provide Qt's icon loading in plasma-integration
*
* Application developers should use QIcon::fromTheme instead of using it directly.
*/
class KICONTHEMES_EXPORT KIconEngine : public QIconEngine // exported for plasma-integration
{
public:
/**
* Constructs an icon engine for a named icon.
*
* @param iconName the name of the icon to load
* @param iconLoader The icon loader that this engine is to use.
* @param overlays Add one or more overlays to the icon. See KIconLoader::Overlays.
*
* @sa KIconLoader
*/
KIconEngine(const QString &iconName, KIconLoader *iconLoader, const QStringList &overlays);
/**
* \overload
*/
KIconEngine(const QString &iconName, KIconLoader *iconLoader);
/**
* Constructs an icon engine for a KDE named icon with a specific palette.
*
* @param iconName the name of the icon to load
* @param colors defines the colors we want to be applied on this icon
* @param iconLoader The KDE icon loader that this engine is to use.
*/
KIconEngine(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader);
/**
* Constructs an icon engine for a KDE named icon with a specific palette and overlays.
*
* @param iconName the name of the icon to load
* @param colors defines the colors we want to be applied on this icon
* @param iconLoader The KDE icon loader that this engine is to use.
* @param overlays Add one or more overlays to the icon. See KIconLoader::Overlays.
*
* @since 6.1
*/
KIconEngine(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader, const QStringList &overlays);
/**
* Destructor.
*/
~KIconEngine() override;
/// Reimplementation
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
/// Reimplementation
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
/// Reimplementation
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
/// Reimplementation
QString iconName() override;
/// Reimplementation
QList<QSize> availableSizes(QIcon::Mode mode, QIcon::State state) override;
bool isNull() override;
QString key() const override;
QIconEngine *clone() const override;
bool read(QDataStream &in) override;
bool write(QDataStream &out) const override;
private:
// TODO KF6: move those into the d-pointer
QPixmap createPixmap(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state);
QString mIconName;
QStringList mOverlays;
KIconEnginePrivate *const d;
};
#endif
@@ -0,0 +1,31 @@
/*
kiconengineplugin.cpp: Qt plugin providing the ability to create a KIconEngine
This file is part of the KDE project, module kdeui.
SPDX-FileCopyrightText: 2018 Fabian Vogt <fabian@ritter-vogt.de>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include <QIconEnginePlugin>
#include <KIconEngine>
#include <KIconLoader>
QT_BEGIN_NAMESPACE
class KIconEnginePlugin : public QIconEnginePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QIconEngineFactoryInterface" FILE "kiconengineplugin.json")
public:
QIconEngine *create(const QString &file) override
{
return new KIconEngine(file, KIconLoader::global());
}
};
QT_END_NAMESPACE
#include "kiconengineplugin.moc"
@@ -0,0 +1,3 @@
{
"Keys": [ "KIconEngine", "svg", "svgz", "svg.gz" ]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,708 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONLOADER_H
#define KICONLOADER_H
#include <QObject>
#include <QSize>
#include <QString>
#include <QStringList>
#include <memory>
#if __has_include(<optional>) && __cplusplus >= 201703L
#include <optional>
#endif
#include <kiconthemes_export.h>
class QIcon;
class QMovie;
class QPixmap;
class KIconColors;
class KIconLoaderPrivate;
class KIconEffect;
class KIconTheme;
/**
* @class KIconLoader kiconloader.h KIconLoader
*
* Iconloader for KDE.
*
* @note For most icon loading use cases perfer using QIcon::fromTheme
*
* KIconLoader will load the current icon theme and all its base themes.
* Icons will be searched in any of these themes. Additionally, it caches
* icons and applies effects according to the user's preferences.
*
* In KDE, it is encouraged to load icons by "Group". An icon group is a
* location on the screen where icons are being used. Standard groups are:
* Desktop, Toolbar, MainToolbar, Small and Panel. Each group can have some
* centrally-configured effects applied to its icons. This makes it possible
* to offer a consistent icon look in all KDE applications.
*
* The standard groups are defined below.
*
* @li KIconLoader::Desktop: Icons in the iconview of konqueror, kdesktop and similar apps.
* @li KIconLoader::Toolbar: Icons in toolbars.
* @li KIconLoader::MainToolbar: Icons in the main toolbars.
* @li KIconLoader::Small: Various small (typical 16x16) places: titlebars, listviews
* and menu entries.
* @li KIconLoader::Panel: Icons in kicker's panel
*
* The icons are stored on disk in an icon theme or in a standalone
* directory. The icon theme directories contain multiple sizes and/or
* depths for the same icon. The iconloader will load the correct one based
* on the icon group and the current theme. Icon themes are stored globally
* in share/icons, or, application specific in share/apps/$appdir/icons.
*
* The standalone directories contain just one version of an icon. The
* directories that are searched are: $appdir/pics and $appdir/toolbar.
* Icons in these directories can be loaded by using the special group
* "User".
*
*/
class KICONTHEMES_EXPORT KIconLoader : public QObject
{
Q_OBJECT
public:
/**
* Defines the context of the icon.
*/
enum Context {
Any, ///< Some icon with unknown purpose.
Action, ///< An action icon (e.g. 'save', 'print').
Application, ///< An icon that represents an application.
Device, ///< An icon that represents a device.
MimeType, ///< An icon that represents a mime type (or file type).
Animation, ///< An icon that is animated.
Category, ///< An icon that represents a category.
Emblem, ///< An icon that adds information to an existing icon.
Emote, ///< An icon that expresses an emotion.
International, ///< An icon that represents a country's flag.
Place, ///< An icon that represents a location (e.g. 'home', 'trash').
StatusIcon, ///< An icon that represents an event.
};
Q_ENUM(Context)
/**
* The type of the icon.
*/
enum Type {
Fixed, ///< Fixed-size icon.
Scalable, ///< Scalable-size icon.
Threshold, ///< A threshold icon.
};
Q_ENUM(Type)
/**
* The type of a match.
*/
enum MatchType {
MatchExact, ///< Only try to find an exact match.
MatchBest, ///< Take the best match if there is no exact match.
MatchBestOrGreaterSize, ///< Take the best match or the match with a greater size if there is no exact match. @since 6.0
};
Q_ENUM(MatchType)
/**
* The group of the icon.
*/
enum Group {
/// No group
NoGroup = -1,
/// Desktop icons
Desktop = 0,
/// First group
FirstGroup = 0,
/// Toolbar icons
Toolbar,
/// Main toolbar icons
MainToolbar,
/// Small icons, e.g. for buttons
Small,
/// Panel (Plasma Taskbar) icons
// TODO KF6: remove this (See https://phabricator.kde.org/T14340)
Panel,
/// Icons for use in dialog titles, page lists, etc
Dialog,
/// Last group
LastGroup,
/// User icons
User,
};
Q_ENUM(Group)
/**
* These are the standard sizes for icons.
*/
enum StdSizes {
/// small icons for menu entries
SizeSmall = 16,
/// slightly larger small icons for toolbars, panels, etc
SizeSmallMedium = 22,
/// medium sized icons for the desktop
SizeMedium = 32,
/// large sized icons for the panel
SizeLarge = 48,
/// huge sized icons for iconviews
SizeHuge = 64,
/// enormous sized icons for iconviews
SizeEnormous = 128,
};
Q_ENUM(StdSizes)
/**
* Defines the possible states of an icon.
*/
enum States {
DefaultState, ///< The default state.
ActiveState, ///< Icon is active.
DisabledState, ///< Icon is disabled.
SelectedState, ///< Icon is selected. @since 5.22
LastState, ///< Last state (last constant)
};
Q_ENUM(States)
/**
* Constructs an iconloader.
* @param appname Add the data directories of this application to the
* icon search path for the "User" group. The default argument adds the
* directories of the current application.
* @param extraSearchPaths additional search paths, either absolute or relative to GenericDataLocation
*
* Usually, you use the default iconloader, which can be accessed via
* KIconLoader::global(), so you hardly ever have to create an
* iconloader object yourself. That one is the application's iconloader.
*/
explicit KIconLoader(const QString &appname = QString(), const QStringList &extraSearchPaths = QStringList(), QObject *parent = nullptr);
/**
* Cleanup
*/
~KIconLoader() override;
/**
* Returns the global icon loader initialized with the application name.
* @return global icon loader
*/
static KIconLoader *global();
/**
* Adds @p appname to the list of application specific directories with @p themeBaseDir as its base directory.
* Assume the icons are in /home/user/app/icons/hicolor/48x48/my_app.png, the base directory would be
* /home/user/app/icons; KIconLoader automatically searches @p themeBaseDir + "/hicolor"
* This directory must contain a dir structure as defined by the XDG icons specification
* @param appname The application name.
* @param themeBaseDir The base directory of the application's theme (eg. "/home/user/app/icons")
*/
void addAppDir(const QString &appname, const QString &themeBaseDir = QString());
/**
* Loads an icon. It will try very hard to find an icon which is
* suitable. If no exact match is found, a close match is searched.
* If neither an exact nor a close match is found, a null pixmap or
* the "unknown" pixmap is returned, depending on the value of the
* @p canReturnNull parameter.
*
* @param name The name of the icon, without extension.
* @param group The icon group. This will specify the size of and effects to
* be applied to the icon.
* @param size If nonzero, this overrides the size specified by @p group.
* See KIconLoader::StdSizes.
* @param state The icon state: @p DefaultState, @p ActiveState or
* @p DisabledState. Depending on the user's preferences, the iconloader
* may apply a visual effect to hint about its state.
* @param overlays a list of emblem icons to overlay, by name
* @see drawOverlays
* @param path_store If not null, the path of the icon is stored here,
* if the icon was found. If the icon was not found @p path_store
* is unaltered even if the "unknown" pixmap was returned.
* @param canReturnNull Can return a null pixmap? If false, the
* "unknown" pixmap is returned when no appropriate icon has been
* found. <em>Note:</em> a null pixmap can still be returned in the
* event of invalid parameters, such as empty names, negative sizes,
* and etc.
* @return the QPixmap. Can be null when not found, depending on
* @p canReturnNull.
*/
QPixmap loadIcon(const QString &name,
KIconLoader::Group group,
int size = 0,
int state = KIconLoader::DefaultState,
const QStringList &overlays = QStringList(),
QString *path_store = nullptr,
bool canReturnNull = false) const;
/**
* Loads an icon. It will try very hard to find an icon which is
* suitable. If no exact match is found, a close match is searched.
* If neither an exact nor a close match is found, a null pixmap or
* the "unknown" pixmap is returned, depending on the value of the
* @p canReturnNull parameter.
*
* @param name The name of the icon, without extension.
* @param group The icon group. This will specify the size of and effects to
* be applied to the icon.
* @param scale The scale of the icon group to use. If no icon exists in the
* scaled group, a 1x icon with its size multiplied by the scale will be
* loaded instead.
* @param size If nonzero, this overrides the size specified by @p group.
* See KIconLoader::StdSizes.
* @param state The icon state: @p DefaultState, @p ActiveState or
* @p DisabledState. Depending on the user's preferences, the iconloader
* may apply a visual effect to hint about its state.
* @param overlays a list of emblem icons to overlay, by name
* @see drawOverlays
* @param path_store If not null, the path of the icon is stored here,
* if the icon was found. If the icon was not found @p path_store
* is unaltered even if the "unknown" pixmap was returned.
* @param canReturnNull Can return a null pixmap? If false, the
* "unknown" pixmap is returned when no appropriate icon has been
* found. <em>Note:</em> a null pixmap can still be returned in the
* event of invalid parameters, such as empty names, negative sizes,
* and etc.
* @return the QPixmap. Can be null when not found, depending on
* @p canReturnNull.
* @since 5.48
*/
// TODO KF6 merge loadIcon() and loadScaledIcon()
QPixmap loadScaledIcon(const QString &name,
KIconLoader::Group group,
qreal scale,
int size = 0,
int state = KIconLoader::DefaultState,
const QStringList &overlays = QStringList(),
QString *path_store = nullptr,
bool canReturnNull = false) const;
/**
* Loads an icon. It will try very hard to find an icon which is
* suitable. If no exact match is found, a close match is searched.
* If neither an exact nor a close match is found, a null pixmap or
* the "unknown" pixmap is returned, depending on the value of the
* @p canReturnNull parameter.
*
* @param name The name of the icon, without extension.
* @param group The icon group. This will specify the size of and effects to
* be applied to the icon.
* @param scale The scale of the icon group to use. If no icon exists in the
* scaled group, a 1x icon with its size multiplied by the scale will be
* loaded instead.
* @param size If nonzero, this overrides the size specified by @p group.
* See KIconLoader::StdSizes. The icon will be fit into @p size
* without changing the aspect ratio, which particularly matters
* for non-square icons.
* @param state The icon state: @p DefaultState, @p ActiveState or
* @p DisabledState. Depending on the user's preferences, the iconloader
* may apply a visual effect to hint about its state.
* @param overlays a list of emblem icons to overlay, by name
* @see drawOverlays
* @param path_store If not null, the path of the icon is stored here,
* if the icon was found. If the icon was not found @p path_store
* is unaltered even if the "unknown" pixmap was returned.
* @param canReturnNull Can return a null pixmap? If false, the
* "unknown" pixmap is returned when no appropriate icon has been
* found. <em>Note:</em> a null pixmap can still be returned in the
* event of invalid parameters, such as empty names, negative sizes,
* and etc.
* @return the QPixmap. Can be null when not found, depending on
* @p canReturnNull.
* @since 5.81
*/
QPixmap loadScaledIcon(const QString &name,
KIconLoader::Group group,
qreal scale,
const QSize &size = {},
int state = KIconLoader::DefaultState,
const QStringList &overlays = QStringList(),
QString *path_store = nullptr,
bool canReturnNull = false) const;
#if __has_include(<optional>) && __cplusplus >= 201703L
/**
* Loads an icon. It will try very hard to find an icon which is
* suitable. If no exact match is found, a close match is searched.
* If neither an exact nor a close match is found, a null pixmap or
* the "unknown" pixmap is returned, depending on the value of the
* @p canReturnNull parameter.
*
* @param name The name of the icon, without extension.
* @param group The icon group. This will specify the size of and effects to
* be applied to the icon.
* @param scale The scale of the icon group to use. If no icon exists in the
* scaled group, a 1x icon with its size multiplied by the scale will be
* loaded instead.
* @param size If nonzero, this overrides the size specified by @p group.
* See KIconLoader::StdSizes. The icon will be fit into @p size
* without changing the aspect ratio, which particularly matters
* for non-square icons.
* @param state The icon state: @p DefaultState, @p ActiveState or
* @p DisabledState. Depending on the user's preferences, the iconloader
* may apply a visual effect to hint about its state.
* @param overlays a list of emblem icons to overlay, by name
* @see drawOverlays
* @param path_store If not null, the path of the icon is stored here,
* if the icon was found. If the icon was not found @p path_store
* is unaltered even if the "unknown" pixmap was returned.
* @param canReturnNull Can return a null pixmap? If false, the
* "unknown" pixmap is returned when no appropriate icon has been
* found. <em>Note:</em> a null pixmap can still be returned in the
* event of invalid parameters, such as empty names, negative sizes,
* and etc.
* @param colorScheme will define the stylesheet used to color this icon.
* Note this will only work if @p name is an svg file.
* @return the QPixmap. Can be null when not found, depending on
* @p canReturnNull.
* @since 5.88
*/
QPixmap loadScaledIcon(const QString &name,
KIconLoader::Group group,
qreal scale,
const QSize &size,
int state,
const QStringList &overlays,
QString *path_store,
bool canReturnNull,
const std::optional<KIconColors> &colorScheme) const;
#endif
/**
* Loads an icon for a mimetype.
* This is basically like loadIcon except that extra desktop themes are loaded if necessary.
*
* Consider using QIcon::fromTheme() with a fallback to "application-octet-stream" instead.
*
* @param iconName The name of the icon, without extension, usually from KMimeType.
* @param group The icon group. This will specify the size of and effects to
* be applied to the icon.
* @param size If nonzero, this overrides the size specified by @p group.
* See KIconLoader::StdSizes.
* @param state The icon state: @p DefaultState, @p ActiveState or
* @p DisabledState. Depending on the user's preferences, the iconloader
* may apply a visual effect to hint about its state.
* @param path_store If not null, the path of the icon is stored here.
* @param overlays a list of emblem icons to overlay, by name
* @see drawOverlays
* @return the QPixmap. Can not be null, the
* "unknown" pixmap is returned when no appropriate icon has been found.
*/
QPixmap loadMimeTypeIcon(const QString &iconName,
KIconLoader::Group group,
int size = 0,
int state = KIconLoader::DefaultState,
const QStringList &overlays = QStringList(),
QString *path_store = nullptr) const;
/**
* Returns the path of an icon.
* @param name The name of the icon, without extension. If an absolute
* path is supplied for this parameter, iconPath will return it
* directly.
* @param group_or_size If positive, search icons whose size is
* specified by the icon group @p group_or_size. If negative, search
* icons whose size is - @p group_or_size.
* See KIconLoader::Group and KIconLoader::StdSizes
* @param canReturnNull Can return a null string? If not, a path to the
* "unknown" icon will be returned.
* @return the path of an icon, can be null or the "unknown" icon when
* not found, depending on @p canReturnNull.
*/
QString iconPath(const QString &name, int group_or_size, bool canReturnNull = false) const;
/**
* Returns the path of an icon.
* @param name The name of the icon, without extension. If an absolute
* path is supplied for this parameter, iconPath will return it
* directly.
* @param group_or_size If positive, search icons whose size is
* specified by the icon group @p group_or_size. If negative, search
* icons whose size is - @p group_or_size.
* See KIconLoader::Group and KIconLoader::StdSizes
* @param canReturnNull Can return a null string? If not, a path to the
* "unknown" icon will be returned.
* @param scale The scale of the icon group.
* @return the path of an icon, can be null or the "unknown" icon when
* not found, depending on @p canReturnNull.
* @since 5.48
*/
// TODO KF6 merge iconPath() with and without "scale" and move that argument after "group_or_size"
QString iconPath(const QString &name, int group_or_size, bool canReturnNull, qreal scale) const;
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Loads an animated icon.
* @param name The name of the icon.
* @param group The icon group. See loadIcon().
* @param size Override the default size for @p group.
* See KIconLoader::StdSizes.
* @param parent The parent object of the returned QMovie.
* @return A QMovie object. Can be null if not found or not valid.
* Ownership is passed to the caller.
* @deprecated since 6.5, use QMovie API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use QMovie API")
QMovie *loadMovie(const QString &name, KIconLoader::Group group, int size = 0, QObject *parent = nullptr) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Returns the path to an animated icon.
* @param name The name of the icon.
* @param group The icon group. See loadIcon().
* @param size Override the default size for @p group.
* See KIconLoader::StdSizes.
* @return the full path to the movie, ready to be passed to QMovie's constructor.
* Empty string if not found.
* @deprecated since 6.5, use QMovie API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use QMovie API")
QString moviePath(const QString &name, KIconLoader::Group group, int size = 0) const;
#endif
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Loads an animated icon as a series of still frames. If you want to load
* a .mng animation as QMovie instead, please use loadMovie() instead.
* @param name The name of the icon.
* @param group The icon group. See loadIcon().
* @param size Override the default size for @p group.
* See KIconLoader::StdSizes.
* @return A QStringList containing the absolute path of all the frames
* making up the animation.
*
* @deprecated since 6.5, use QMovie API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use QMovie API")
QStringList loadAnimated(const QString &name, KIconLoader::Group group, int size = 0) const;
#endif
/**
* Queries all available icons for a specific group, having a specific
* context.
* @param group_or_size If positive, search icons whose size is
* specified by the icon group @p group_or_size. If negative, search
* icons whose size is - @p group_or_size.
* See KIconLoader::Group and KIconLoader::StdSizes
* @param context The icon context.
* @return a list of all icons
*/
QStringList queryIcons(int group_or_size, KIconLoader::Context context = KIconLoader::Any) const;
/**
* Queries all available icons for a specific context.
* @param group_or_size The icon preferred group or size. If available
* at this group or size, those icons will be returned, in other case,
* icons of undefined size will be returned. Positive numbers are groups,
* negative numbers are negated sizes. See KIconLoader::Group and
* KIconLoader::StdSizes
* @param context The icon context.
* @return A QStringList containing the icon names
* available for that context
*/
QStringList queryIconsByContext(int group_or_size, KIconLoader::Context context = KIconLoader::Any) const;
/**
* @internal
*/
bool hasContext(KIconLoader::Context context) const;
/**
* Returns a list of all icons (*.png or *.xpm extension) in the
* given directory.
* @param iconsDir the directory to search in
* @return A QStringList containing the icon paths
*/
QStringList queryIconsByDir(const QString &iconsDir) const;
/**
* Returns all the search paths for this icon loader, either absolute or
* relative to GenericDataLocation.
* Mostly internal (for KIconDialog).
* \since 5.0
*/
QStringList searchPaths() const;
/**
* Returns the size of the specified icon group.
* Using e.g. KIconLoader::SmallIcon will return 16.
* @param group the group to check.
* @return the current size for an icon group.
*/
int currentSize(KIconLoader::Group group) const;
/**
* Returns a pointer to the current theme. Can be used to query
* available and default sizes for groups.
* @note The KIconTheme will change if reconfigure() is called and
* therefore it's not recommended to store the pointer anywhere.
* @return a pointer to the current theme. 0 if no theme set.
*/
KIconTheme *theme() const;
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Returns a pointer to the KIconEffect object used by the icon loader.
* @return the KIconEffect.
*
* @deprecated since 6.5, use the static KIconEffect API
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use static KIconEffect API")
KIconEffect *iconEffect() const;
#endif
/**
* Reconfigure the icon loader, for instance to change the associated app name or extra search paths.
* This also clears the in-memory pixmap cache (even if the appname didn't change, which is useful for unittests)
* @param appname the application name (empty for the global iconloader)
* @param extraSearchPaths additional search paths, either absolute or relative to GenericDataLocation
*/
void reconfigure(const QString &appname, const QStringList &extraSearchPaths = QStringList());
/**
* Returns the unknown icon. An icon that is used when no other icon
* can be found.
* @return the unknown pixmap
*/
static QPixmap unknown();
#if KICONTHEMES_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Draws overlays on the specified pixmap, it takes the width and height
* of the pixmap into consideration
* @param overlays List of up to 4 overlays to blend over the pixmap. The first overlay
* will be in the bottom right corner, followed by bottom left, top left
* and top right. An empty QString can be used to leave the specific position
* blank.
* @param pixmap to draw on
* @since 4.7
* @deprecated since 6.5, use KIconUtils::addOverlays from KGuiAddons
*/
KICONTHEMES_DEPRECATED_VERSION(6, 5, "Use KIconUtils::addOverlays from KGuiAddons")
void drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state = KIconLoader::DefaultState) const;
#endif
bool hasIcon(const QString &iconName) const;
/**
* Sets the colors for this KIconLoader.
* NOTE: if you set a custom palette, if you are using some colors from
* application's palette, you need to track the application palette changes by yourself.
* If you no longer wish to use a custom palette, use resetPalette()
* @see resetPalette
* @since 5.39
*/
void setCustomPalette(const QPalette &palette);
/**
* The colors that will be used for the svg stylesheet in case the
* loaded icons are svg-based, icons can be colored in different ways in
* different areas of the application
* @return the palette, if any, an invalid one if the user didn't set it
* @since 5.39
*/
QPalette customPalette() const;
/**
* Resets the custom palette used by the KIconLoader to use the
* QGuiApplication::palette() again (and to follow it in case the
* application's palette changes)
* @since 5.39
*/
void resetPalette();
/**
* @returns whether we have set a custom palette using @f setCustomPalette
*
* @since 5.85
* @see resetPalette, setCustomPalette
*/
bool hasCustomPalette() const;
public Q_SLOTS:
// TODO: while marked as deprecated, newIconLoader() is still used:
// internally by KIconLoadeer as well as by Plasma's Icons kcm module (state: 5.17)
// this needs some further cleanup work before removing it from the API with KICONTHEMES_ENABLE_DEPRECATED_SINCE
/**
* Re-initialize the global icon loader
*
* @todo Check deprecation, still used internally.
* @deprecated Since 5.0, use emitChange(Group)
*/
KICONTHEMES_DEPRECATED_VERSION(5, 0, "Use KIconLoader::emitChange(Group)") // TODO KF6 remove
void newIconLoader();
/**
* Emits an iconChanged() signal on all the KIconLoader instances in the system
* indicating that a system's icon group has changed in some way. It will also trigger
* a reload in all of them to update to the new theme.
*
* @p group indicates the group that has changed
*
* @since 5.0
*/
static void emitChange(Group group);
Q_SIGNALS:
/**
* Emitted by newIconLoader once the new settings have been loaded
*/
void iconLoaderSettingsChanged();
/**
* Emitted when the system icon theme changes
*
* @since 5.0
*/
void iconChanged(int group);
private:
friend class KIconLoaderPrivate;
// @internal the data object
std::unique_ptr<KIconLoaderPrivate> const d;
Q_PRIVATE_SLOT(d, void _k_refreshIcons(int group))
};
namespace KDE
{
/**
* \relates KIconLoader
* Returns a QIcon with an appropriate
* KIconEngine to perform loading and rendering. KIcons thus adhere to
* KDE style and effect standards.
* @since 5.0
*/
KICONTHEMES_EXPORT QIcon icon(const QString &iconName, KIconLoader *iconLoader = nullptr);
KICONTHEMES_EXPORT QIcon icon(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader = nullptr);
/**
* \relates KIconLoader
* Returns a QIcon for the given icon, with additional overlays.
* @since 5.0
*/
KICONTHEMES_EXPORT QIcon icon(const QString &iconName, const QStringList &overlays, KIconLoader *iconLoader = nullptr);
}
inline KIconLoader::Group &operator++(KIconLoader::Group &group)
{
group = static_cast<KIconLoader::Group>(group + 1);
return group;
}
inline KIconLoader::Group operator++(KIconLoader::Group &group, int)
{
KIconLoader::Group ret = group;
++group;
return ret;
}
#endif // KICONLOADER_H
@@ -0,0 +1,224 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONLOADER_P_H
#define KICONLOADER_P_H
#include <QCache>
#include <QElapsedTimer>
#include <QPixmap>
#include <QSize>
#include <QString>
#include <QStringList>
#include "kiconcolors.h"
#include "kiconeffect.h"
#include "kiconloader.h"
class KIconThemeNode;
/*** KIconGroup: Icon type description. ***/
struct KIconGroup {
int size;
};
/**
* Holds a QPixmap for this process, along with its associated path on disk.
*/
struct PixmapWithPath {
QPixmap pixmap;
QString path;
};
/*** d pointer for KIconLoader. ***/
class KIconLoaderPrivate
{
public:
KIconLoaderPrivate(const QString &_appname, const QStringList &extraSearchPaths, KIconLoader *qq);
~KIconLoaderPrivate();
static KIconLoaderPrivate *get(KIconLoader *loader);
void clear();
/**
* @internal
*/
void init(const QString &_appname, const QStringList &extraSearchPaths = QStringList());
/**
* @internal
*/
void initIconThemes();
/**
* @internal
* tries to find an icon with the name. It tries some extension and
* match strategies
*/
QString findMatchingIcon(const QString &name, int size, qreal scale) const;
/**
* @internal
* tries to find an icon with the name.
* This is one layer above findMatchingIcon -- it also implements generic fallbacks
* such as generic icons for mimetypes.
*/
QString findMatchingIconWithGenericFallbacks(const QString &name, int size, qreal scale) const;
/**
* @internal
* returns the preferred icon path for an icon with the name.
* Can be used for a quick "hasIcon" check since it caches
* that an icon was not found.
*/
QString preferredIconPath(const QString &name);
/**
* @internal
* Adds themes installed in the application's directory.
**/
void addAppThemes(const QString &appname, const QString &themeBaseDir = QString());
/**
* @internal
* Adds all themes that are part of this node and the themes
* below (the fallbacks of the theme) into the tree.
*/
void addBaseThemes(KIconThemeNode *node, const QString &appname);
/**
* @internal
* Recursively adds all themes that are specified in the "Inherits"
* property of the given theme into the tree.
*/
void addInheritedThemes(KIconThemeNode *node, const QString &appname);
/**
* @internal
* Creates a KIconThemeNode out of a theme name, and adds this theme
* as well as all its inherited themes into the tree. Themes that already
* exist in the tree will be ignored and not added twice.
*/
void addThemeByName(const QString &themename, const QString &appname);
/**
* Adds all the default themes from other desktops at the end of
* the list of icon themes.
*/
void addExtraDesktopThemes();
/**
* @internal
* return the path for the unknown icon in that size
*/
QString unknownIconPath(int size, qreal scale) const;
/**
* Checks if name ends in one of the supported icon formats (i.e. .png)
* and returns the name without the extension if it does.
*/
QString removeIconExtension(const QString &name) const;
/**
* @internal
* Used with KIconLoader::loadIcon to convert the given name, size, group,
* and icon state information to valid states. All parameters except the
* name can be modified as well to be valid.
*/
void normalizeIconMetadata(KIconLoader::Group &group, QSize &size, int &state) const;
/**
* @internal
* Used with KIconLoader::loadIcon to get a base key name from the given
* icon metadata. Ensure the metadata is normalized first.
*/
QString makeCacheKey(const QString &name,
KIconLoader::Group group,
const QStringList &overlays,
const QSize &size,
qreal scale,
int state,
const KIconColors &colors) const;
/**
* @internal
* If the icon is an SVG file, process it generating a stylesheet
* following the current color scheme. in this case the icon can use named colors
* as text color, background color, highlight color, positive/neutral/negative color
* @see KColorScheme
*/
QByteArray processSvg(const QString &path, KIconLoader::States state, const KIconColors &colors) const;
/**
* @internal
* Creates the QImage for @p path, using SVG rendering as appropriate.
* @p size is only used for scalable images, but if non-zero non-scalable
* images will be resized anyways.
*/
QImage createIconImage(const QString &path, const QSize &size, qreal scale, KIconLoader::States state, const KIconColors &colors);
/**
* @internal
* Adds an QPixmap with its associated path to the shared icon cache.
*/
void insertCachedPixmapWithPath(const QString &key, const QPixmap &data, const QString &path);
/**
* @internal
* Retrieves the path and pixmap of the given key from the shared
* icon cache.
*/
bool findCachedPixmapWithPath(const QString &key, QPixmap &data, QString &path);
/**
* Find the given file in the search paths.
*/
QString locate(const QString &fileName);
/**
* @internal
* React to a global icon theme change
*/
void _k_refreshIcons(int group);
bool shouldCheckForUnknownIcons();
KIconLoader *const q;
QStringList mThemesInTree;
KIconGroup *mpGroups = nullptr;
KIconThemeNode *mpThemeRoot = nullptr;
QStringList searchPaths;
#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
KIconEffect mpEffect;
#endif
QList<KIconThemeNode *> links;
// This caches rendered QPixmaps in just this process.
QCache<QString, PixmapWithPath> mPixmapCache;
bool extraDesktopIconsLoaded : 1;
// lazy loading: initIconThemes() is only needed when the "links" list is needed
// mIconThemeInited is used inside initIconThemes() to init only once
bool mIconThemeInited : 1;
QString m_appname;
void drawOverlays(const KIconLoader *loader, KIconLoader::Group group, int state, QPixmap &pix, const QStringList &overlays);
QHash<QString, QString> mIconAvailability; // icon name -> actual icon name (not null if known to be available)
QElapsedTimer mLastUnknownIconCheck; // recheck for unknown icons after kiconloader_ms_between_checks
// the colors used to recolor svg icons stylesheets
KIconColors mColors;
QPalette mPalette;
// to keep track if we are using a custom palette or just falling back to qApp;
bool mCustomColors = false;
};
#endif // KICONLOADER_P_H
@@ -0,0 +1,846 @@
/*
kicontheme.cpp: Lowlevel icon theme handling.
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kicontheme.h"
#include "debug.h"
#include <KColorSchemeManager>
#include <KConfigGroup>
#include <KLocalizedString> // KLocalizedString::localizedFilePath. Need such functionality in, hmm, QLocale? QStandardPaths?
#include <KSharedConfig>
#include <QAction>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QMap>
#include <QResource>
#include <QSet>
#include <QTimer>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
#include <qplatformdefs.h>
#include <array>
#include <cmath>
#include "config.h"
Q_GLOBAL_STATIC(QString, _themeOverride)
#if !USE_BreezeIcons
// on Android icon theme loading works differently and is managed by code in Kirigami
// so don't actually touch anything icon-related here
static void initThemeHelper()
{
// postpone until QGuiApplication applies initial palette
QTimer::singleShot(0, [] {
// follow the system color, construct the global manager for that
(void)KColorSchemeManager::instance();
});
}
void KIconTheme::initTheme()
{
}
#else
#include <BreezeIcons>
// do init only once and avoid later helpers to mess with it again
static bool initThemeUsed = false;
// startup function to set theme once the app got constructed
static void initThemeHelper()
{
// make sure we add application install path to search path, for e.g. bundles on Windows
if (initThemeUsed) {
// do that similar to QCoreApplicationPrivate::appendApplicationPathToLibraryPaths() with minimal extra API use
QString path = QCoreApplication::applicationFilePath();
path.truncate(path.lastIndexOf(QLatin1Char('/')));
if (const QString ourPath = path + QStringLiteral("/kiconthemes6"); QFile::exists(ourPath)) {
QCoreApplication::addLibraryPath(ourPath);
}
}
// Makes sure the icon theme fallback is set to breeze or one of its
// variants. Most of our apps use "lots" of icons that most of the times
// are only available with breeze, we still honour the user icon theme
// but if the icon is not found there, we go to breeze since it's almost
// sure it'll be there
BreezeIcons::initIcons();
// ensure lib call above did the job
Q_ASSERT(!QIcon::fallbackThemeName().isEmpty());
// only do further stuff if we requested it
if (!initThemeUsed) {
return;
}
// do nothing if we have the proper platform theme already
if (QGuiApplicationPrivate::platformTheme() && QGuiApplicationPrivate::platformTheme()->name() == QLatin1String("kde")) {
return;
}
// get config, with fallback to kdeglobals
const auto config = KSharedConfig::openConfig();
// enforce the theme configured by the user, with kdeglobals fallback
// if not set, use Breeze
const QString themeToUse = KConfigGroup(config, "Icons").readEntry("Theme", QStringLiteral("breeze"));
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
// set our theme, Qt internally will still not fully use our engine and lookup
QIcon::setThemeName(themeToUse);
#else
// use Qt API to really fully override the engine, if we set KIconEngine the Key in our plugin will
// enforce that our engine is used
// https://codereview.qt-project.org/c/qt/qtbase/+/563241
QIcon::setThemeName(QStringLiteral("KIconEngine"));
#endif
// Tell KIconTheme about the theme, in case KIconLoader is used directly
*_themeOverride() = themeToUse;
qCDebug(KICONTHEMES) << "KIconTheme::initTheme() enforces the icon theme:" << themeToUse;
// postpone until QGuiApplication applies initial palette
QTimer::singleShot(0, [] {
// follow the system color, construct the global manager for that
(void)KColorSchemeManager::instance();
});
}
void KIconTheme::initTheme()
{
// inject paths only once
if (!initThemeUsed) {
// inject our icon engine in the search path
// it will be used as the first found engine for a suffix will be taken
// this must be done before the QCoreApplication is constructed
const auto paths = QCoreApplication::libraryPaths();
for (const auto &path : paths) {
if (const QString ourPath = path + QStringLiteral("/kiconthemes6"); QFile::exists(ourPath)) {
QCoreApplication::addLibraryPath(ourPath);
}
}
}
// initThemeHelper will do the remaining work via Q_COREAPP_STARTUP_FUNCTION(initThemeHelper) above
initThemeUsed = true;
}
#endif
Q_COREAPP_STARTUP_FUNCTION(initThemeHelper)
class KIconThemeDir;
class KIconThemePrivate
{
public:
QString example, screenshot;
bool hidden;
KSharedConfig::Ptr sharedConfig;
struct GroupInfo {
KIconLoader::Group type;
const char *name;
int defaultSize;
QList<int> availableSizes{};
};
std::array<GroupInfo, KIconLoader::LastGroup> m_iconGroups = {{
{KIconLoader::Desktop, "Desktop", 32},
{KIconLoader::Toolbar, "Toolbar", 22},
{KIconLoader::MainToolbar, "MainToolbar", 22},
{KIconLoader::Small, "Small", 16},
{KIconLoader::Panel, "Panel", 48},
{KIconLoader::Dialog, "Dialog", 32},
}};
int mDepth;
QString mDir, mName, mInternalName, mDesc;
QStringList mInherits;
QStringList mExtensions;
QList<KIconThemeDir *> mDirs;
QList<KIconThemeDir *> mScaledDirs;
bool followsColorScheme : 1;
/// Searches the given dirs vector for a matching icon
QString iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const;
};
Q_GLOBAL_STATIC(QString, _theme)
Q_GLOBAL_STATIC(QStringList, _theme_list)
/**
* A subdirectory in an icon theme.
*/
class KIconThemeDir
{
public:
KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config);
bool isValid() const
{
return mbValid;
}
QString iconPath(const QString &name) const;
QStringList iconList() const;
QString constructFileName(const QString &file) const
{
return mBaseDir + mThemeDir + QLatin1Char('/') + file;
}
KIconLoader::Context context() const
{
return mContext;
}
KIconLoader::Type type() const
{
return mType;
}
int size() const
{
return mSize;
}
int scale() const
{
return mScale;
}
int minSize() const
{
return mMinSize;
}
int maxSize() const
{
return mMaxSize;
}
int threshold() const
{
return mThreshold;
}
private:
bool mbValid = false;
KIconLoader::Type mType = KIconLoader::Fixed;
KIconLoader::Context mContext;
int mSize = 0;
int mScale = 1;
int mMinSize = 1;
int mMaxSize = 50;
int mThreshold = 2;
const QString mBaseDir;
const QString mThemeDir;
};
QString KIconThemePrivate::iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const
{
QString path;
QString tempPath; // used to cache icon path if it exists
int delta = -INT_MAX; // current icon size delta of 'icon'
int dw = INT_MAX; // icon size delta of current directory
// Rather downsample than upsample
int integerScale = std::ceil(scale);
// Search the directory that contains the icon which matches best to the requested
// size. If there is no directory which matches exactly to the requested size, the
// following criteria get applied:
// - Take a directory having icons with a minimum difference to the requested size.
// - Prefer directories that allow a downscaling even if the difference to
// the requested size is bigger than a directory where an upscaling is required.
for (KIconThemeDir *dir : dirs) {
if (dir->scale() != integerScale) {
continue;
}
if (match == KIconLoader::MatchExact) {
if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size)) {
continue;
}
if ((dir->type() == KIconLoader::Scalable) //
&& ((size < dir->minSize()) || (size > dir->maxSize()))) {
continue;
}
if ((dir->type() == KIconLoader::Threshold) //
&& (abs(dir->size() - size) > dir->threshold())) {
continue;
}
} else {
// dw < 0 means need to scale up to get an icon of the requested size.
// Upscaling should only be done if no larger icon is available.
if (dir->type() == KIconLoader::Fixed) {
dw = dir->size() - size;
} else if (dir->type() == KIconLoader::Scalable) {
if (size < dir->minSize()) {
dw = dir->minSize() - size;
} else if (size > dir->maxSize()) {
dw = dir->maxSize() - size;
} else {
dw = 0;
}
} else if (dir->type() == KIconLoader::Threshold) {
if (size < dir->size() - dir->threshold()) {
dw = dir->size() - dir->threshold() - size;
} else if (size > dir->size() + dir->threshold()) {
dw = dir->size() + dir->threshold() - size;
} else {
dw = 0;
}
}
// Usually if the delta (= 'dw') of the current directory is
// not smaller than the delta (= 'delta') of the currently best
// matching icon, this candidate can be skipped. But skipping
// the candidate may only be done, if this does not imply
// in an upscaling of the icon (it is OK to use a directory with
// smaller icons that what we've already found, however).
if ((abs(dw) >= abs(delta)) && ((dw < 0) || (delta > 0))) {
continue;
}
if (match == KIconLoader::MatchBestOrGreaterSize && dw < 0) {
continue;
}
}
// cache the result of iconPath() call which checks if file exists
tempPath = dir->iconPath(name);
if (tempPath.isEmpty()) {
continue;
}
path = tempPath;
// if we got in MatchExact that far, we find no better
if (match == KIconLoader::MatchExact) {
return path;
}
delta = dw;
if (delta == 0) {
return path; // We won't find a better match anyway
}
}
return path;
}
KIconTheme::KIconTheme(const QString &name, const QString &appName, const QString &basePathHint)
: d(new KIconThemePrivate)
{
d->mInternalName = name;
QStringList themeDirs;
// Applications can have local additions to the global "locolor" and
// "hicolor" icon themes. For these, the _global_ theme description
// files are used..
/* clang-format off */
if (!appName.isEmpty()
&& (name == defaultThemeName()
|| name == QLatin1String("hicolor")
|| name == QLatin1String("locolor"))) { /* clang-format on */
const QString suffix = QLatin1Char('/') + appName + QLatin1String("/icons/") + name + QLatin1Char('/');
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
for (auto &cDir : dataDirs) {
cDir += suffix;
if (QFileInfo::exists(cDir)) {
themeDirs += cDir;
}
}
if (!basePathHint.isEmpty()) {
// Checks for dir existing are done below
themeDirs += basePathHint + QLatin1Char('/') + name + QLatin1Char('/');
}
}
// Find the theme description file. These are either locally in the :/icons resource path or global.
QStringList icnlibs;
// local embedded icons have preference
icnlibs << QStringLiteral(":/icons");
#ifdef Q_OS_ANDROID
// Android icon theme installed by Kirigami
icnlibs << QStringLiteral("assets:/qml/org/kde/kirigami");
#endif
// global icons
icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
// These are not in the icon spec, but e.g. GNOME puts some icons there anyway.
icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory);
QString fileName;
QString mainSection;
const QString pathSuffix = QLatin1Char('/') + name + QLatin1Char('/');
const QLatin1String indexTheme("index.theme");
const QLatin1String indexDesktop("theme.desktop");
for (auto &iconDir : icnlibs) {
iconDir += pathSuffix;
const QFileInfo fi(iconDir);
if (!fi.exists() || !fi.isDir()) {
continue;
}
themeDirs.append(iconDir);
if (d->mDir.isEmpty()) {
QString possiblePath;
if (possiblePath = iconDir + indexTheme; QFileInfo::exists(possiblePath)) {
d->mDir = iconDir;
fileName = possiblePath;
mainSection = QStringLiteral("Icon Theme");
} else if (possiblePath = iconDir + indexDesktop; QFileInfo::exists(possiblePath)) {
d->mDir = iconDir;
fileName = possiblePath;
mainSection = QStringLiteral("KDE Icon Theme");
}
}
}
if (d->mDir.isEmpty()) {
qCDebug(KICONTHEMES) << "Icon theme" << name << "not found.";
return;
}
// Use KSharedConfig to avoid parsing the file many times, from each component.
// Need to keep a ref to it to make this useful
d->sharedConfig = KSharedConfig::openConfig(fileName, KConfig::SimpleConfig);
KConfigGroup cfg(d->sharedConfig, mainSection);
d->mName = cfg.readEntry("Name");
d->mDesc = cfg.readEntry("Comment");
d->mDepth = cfg.readEntry("DisplayDepth", 32);
d->mInherits = cfg.readEntry("Inherits", QStringList());
if (name != defaultThemeName()) {
for (auto &inheritedTheme : d->mInherits) {
if (inheritedTheme == QLatin1String("default")) {
inheritedTheme = defaultThemeName();
}
}
}
d->hidden = cfg.readEntry("Hidden", false);
d->followsColorScheme = cfg.readEntry("FollowsColorScheme", false);
d->example = cfg.readPathEntry("Example", QString());
d->screenshot = cfg.readPathEntry("ScreenShot", QString());
d->mExtensions =
cfg.readEntry("KDE-Extensions", QStringList{QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm")});
QSet<QString> addedDirs; // Used for avoiding duplicates.
const QStringList dirs = cfg.readPathEntry("Directories", QStringList()) + cfg.readPathEntry("ScaledDirectories", QStringList());
for (const auto &dirName : dirs) {
KConfigGroup cg(d->sharedConfig, dirName);
for (const auto &themeDir : std::as_const(themeDirs)) {
const QString currentDir(themeDir + dirName + QLatin1Char('/'));
if (!addedDirs.contains(currentDir) && QFileInfo::exists(currentDir)) {
addedDirs.insert(currentDir);
KIconThemeDir *dir = new KIconThemeDir(themeDir, dirName, cg);
if (dir->isValid()) {
if (dir->scale() > 1) {
d->mScaledDirs.append(dir);
} else {
d->mDirs.append(dir);
}
} else {
delete dir;
}
}
}
}
KConfigGroup cg(d->sharedConfig, mainSection);
for (auto &iconGroup : d->m_iconGroups) {
iconGroup.defaultSize = cg.readEntry(iconGroup.name + QLatin1String("Default"), iconGroup.defaultSize);
iconGroup.availableSizes = cg.readEntry(iconGroup.name + QLatin1String("Sizes"), QList<int>());
}
}
KIconTheme::~KIconTheme()
{
qDeleteAll(d->mDirs);
qDeleteAll(d->mScaledDirs);
}
QString KIconTheme::name() const
{
return d->mName;
}
QString KIconTheme::internalName() const
{
return d->mInternalName;
}
QString KIconTheme::description() const
{
return d->mDesc;
}
QString KIconTheme::example() const
{
return d->example;
}
QString KIconTheme::screenshot() const
{
return d->screenshot;
}
QString KIconTheme::dir() const
{
return d->mDir;
}
QStringList KIconTheme::inherits() const
{
return d->mInherits;
}
bool KIconTheme::isValid() const
{
return !d->mDirs.isEmpty() || !d->mScaledDirs.isEmpty();
}
bool KIconTheme::isHidden() const
{
return d->hidden;
}
int KIconTheme::depth() const
{
return d->mDepth;
}
int KIconTheme::defaultSize(KIconLoader::Group group) const
{
if (group < 0 || group >= KIconLoader::LastGroup) {
qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
return -1;
}
return d->m_iconGroups[group].defaultSize;
}
QList<int> KIconTheme::querySizes(KIconLoader::Group group) const
{
if (group < 0 || group >= KIconLoader::LastGroup) {
qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
return QList<int>();
}
return d->m_iconGroups[group].availableSizes;
}
static bool isAnyOrDirContext(const KIconThemeDir *dir, KIconLoader::Context context)
{
return context == KIconLoader::Any || context == dir->context();
}
QStringList KIconTheme::queryIcons(int size, KIconLoader::Context context) const
{
// Try to find exact match
QStringList result;
const QList<KIconThemeDir *> listDirs = d->mDirs + d->mScaledDirs;
for (const KIconThemeDir *dir : listDirs) {
if (!isAnyOrDirContext(dir, context)) {
continue;
}
const int dirSize = dir->size();
if ((dir->type() == KIconLoader::Fixed && dirSize == size) //
|| (dir->type() == KIconLoader::Scalable && size >= dir->minSize() && size <= dir->maxSize())
|| (dir->type() == KIconLoader::Threshold && abs(size - dirSize) < dir->threshold())) {
result += dir->iconList();
}
}
return result;
}
QStringList KIconTheme::queryIconsByContext(int size, KIconLoader::Context context) const
{
int dw;
// We want all the icons for a given context, but we prefer icons
// of size "size" . Note that this may (will) include duplicate icons
// QStringList iconlist[34]; // 33 == 48-16+1
QStringList iconlist[128]; // 33 == 48-16+1
// Usually, only the 0, 6 (22-16), 10 (32-22), 16 (48-32 or 32-16),
// 26 (48-22) and 32 (48-16) will be used, but who knows if someone
// will make icon themes with different icon sizes.
const auto listDirs = d->mDirs + d->mScaledDirs;
for (KIconThemeDir *dir : listDirs) {
if (!isAnyOrDirContext(dir, context)) {
continue;
}
dw = abs(dir->size() - size);
iconlist[(dw < 127) ? dw : 127] += dir->iconList();
}
QStringList iconlistResult;
for (int i = 0; i < 128; i++) {
iconlistResult += iconlist[i];
}
return iconlistResult;
}
bool KIconTheme::hasContext(KIconLoader::Context context) const
{
const auto listDirs = d->mDirs + d->mScaledDirs;
for (KIconThemeDir *dir : listDirs) {
if (isAnyOrDirContext(dir, context)) {
return true;
}
}
return false;
}
QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match) const
{
return iconPathByName(iconName, size, match, 1 /*scale*/);
}
QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const
{
for (const QString &current : std::as_const(d->mExtensions)) {
const QString path = iconPath(iconName + current, size, match, scale);
if (!path.isEmpty()) {
return path;
}
}
return QString();
}
bool KIconTheme::followsColorScheme() const
{
return d->followsColorScheme;
}
QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match) const
{
return iconPath(name, size, match, 1 /*scale*/);
}
QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const
{
// first look for a scaled image at exactly the requested size
QString path = d->iconPath(d->mScaledDirs, name, size, scale, KIconLoader::MatchExact);
// then look for an unscaled one but request it at larger size so it doesn't become blurry
if (path.isEmpty()) {
path = d->iconPath(d->mDirs, name, size * scale, 1, match);
}
return path;
}
// static
QString KIconTheme::current()
{
// Static pointers because of unloading problems wrt DSO's.
if (_themeOverride && !_themeOverride->isEmpty()) {
*_theme() = *_themeOverride();
}
if (!_theme()->isEmpty()) {
return *_theme();
}
QString theme;
// Check application specific config for a theme setting.
KConfigGroup app_cg(KSharedConfig::openConfig(QString(), KConfig::NoGlobals), "Icons");
theme = app_cg.readEntry("Theme", QString());
if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
// No theme, try to use Qt's. A Platform plugin might have set
// a good theme there.
theme = QIcon::themeName();
}
if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
// Still no theme, try config with kdeglobals.
KConfigGroup cg(KSharedConfig::openConfig(), "Icons");
theme = cg.readEntry("Theme", QStringLiteral("breeze"));
}
if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
// Still no good theme, use default.
theme = defaultThemeName();
}
*_theme() = theme;
return *_theme();
}
void KIconTheme::forceThemeForTests(const QString &themeName)
{
*_themeOverride() = themeName;
_theme()->clear(); // ::current sets this again based on conditions
}
// static
QStringList KIconTheme::list()
{
// Static pointer because of unloading problems wrt DSO's.
if (!_theme_list()->isEmpty()) {
return *_theme_list();
}
// Find the theme description file. These are either locally in the :/icons resource path or global.
QStringList icnlibs;
// local embedded icons have preference
icnlibs << QStringLiteral(":/icons");
// global icons
icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
// These are not in the icon spec, but e.g. GNOME puts some icons there anyway.
icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory);
for (const QString &iconDir : std::as_const(icnlibs)) {
QDir dir(iconDir);
const QStringList themeDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const auto &theme : themeDirs) {
if (theme.startsWith(QLatin1String("default."))) {
continue;
}
const QString prefix = iconDir + QLatin1Char('/') + theme;
if (!QFileInfo::exists(prefix + QLatin1String("/index.desktop")) //
&& !QFileInfo::exists(prefix + QLatin1String("/index.theme"))) {
continue;
}
if (!KIconTheme(theme).isValid()) {
continue;
}
if (!_theme_list()->contains(theme)) {
_theme_list()->append(theme);
}
}
}
return *_theme_list();
}
// static
void KIconTheme::reconfigure()
{
_theme()->clear();
_theme_list()->clear();
}
// static
QString KIconTheme::defaultThemeName()
{
return QStringLiteral("hicolor");
}
/*** KIconThemeDir ***/
KIconThemeDir::KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config)
: mSize(config.readEntry("Size", 0))
, mScale(config.readEntry("Scale", 1))
, mBaseDir(basedir)
, mThemeDir(themedir)
{
if (mSize == 0) {
return;
}
QString tmp = config.readEntry("Context", QString());
if (tmp == QLatin1String("Devices")) {
mContext = KIconLoader::Device;
} else if (tmp == QLatin1String("MimeTypes")) {
mContext = KIconLoader::MimeType;
} else if (tmp == QLatin1String("Applications")) {
mContext = KIconLoader::Application;
} else if (tmp == QLatin1String("Actions")) {
mContext = KIconLoader::Action;
} else if (tmp == QLatin1String("Animations")) {
mContext = KIconLoader::Animation;
} else if (tmp == QLatin1String("Categories")) {
mContext = KIconLoader::Category;
} else if (tmp == QLatin1String("Emblems")) {
mContext = KIconLoader::Emblem;
} else if (tmp == QLatin1String("Emotes")) {
mContext = KIconLoader::Emote;
} else if (tmp == QLatin1String("International")) {
mContext = KIconLoader::International;
} else if (tmp == QLatin1String("Places")) {
mContext = KIconLoader::Place;
} else if (tmp == QLatin1String("Status")) {
mContext = KIconLoader::StatusIcon;
} else if (tmp == QLatin1String("Stock")) { // invalid, but often present context, skip warning
return;
} else if (tmp == QLatin1String("FileSystems")) { // invalid, but present context for hicolor, skip warning
return;
} else if (tmp == QLatin1String("Legacy")) { // invalid, but often present context for Adwaita, skip warning
return;
} else if (tmp == QLatin1String("UI")) { // invalid, but often present context for Adwaita, skip warning
return;
} else if (tmp.isEmpty()) {
// do nothing. key not required
} else {
qCDebug(KICONTHEMES) << "Invalid Context=" << tmp << "line for icon theme: " << constructFileName(QString());
return;
}
tmp = config.readEntry("Type", QStringLiteral("Threshold"));
if (tmp == QLatin1String("Fixed")) {
mType = KIconLoader::Fixed;
} else if (tmp == QLatin1String("Scalable")) {
mType = KIconLoader::Scalable;
} else if (tmp == QLatin1String("Threshold")) {
mType = KIconLoader::Threshold;
} else {
qCDebug(KICONTHEMES) << "Invalid Type=" << tmp << "line for icon theme: " << constructFileName(QString());
return;
}
if (mType == KIconLoader::Scalable) {
mMinSize = config.readEntry("MinSize", mSize);
mMaxSize = config.readEntry("MaxSize", mSize);
} else if (mType == KIconLoader::Threshold) {
mThreshold = config.readEntry("Threshold", 2);
}
mbValid = true;
}
QString KIconThemeDir::iconPath(const QString &name) const
{
if (!mbValid) {
return QString();
}
const QString file = constructFileName(name);
if (QFileInfo::exists(file)) {
return KLocalizedString::localizedFilePath(file);
}
return QString();
}
QStringList KIconThemeDir::iconList() const
{
const QDir icondir = constructFileName(QString());
const QStringList formats = QStringList() << QStringLiteral("*.png") << QStringLiteral("*.svg") << QStringLiteral("*.svgz") << QStringLiteral("*.xpm");
const QStringList lst = icondir.entryList(formats, QDir::Files);
QStringList result;
result.reserve(lst.size());
for (const QString &file : lst) {
result += constructFileName(file);
}
return result;
}
@@ -0,0 +1,282 @@
/*
This file is part of the KDE project, module kdecore.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONTHEME_H
#define KICONTHEME_H
#include <kiconthemes_export.h>
#include <QList>
#include <QString>
#include <QStringList>
#include <memory>
#include "kiconloader.h"
class QAction;
/**
* @internal
* Class to use/access icon themes in KDE. This class is used by the
* iconloader but can be used by others too.
* @warning You should not use this class externally. This class is exported because
* the KCM needs it.
* @see KIconLoader
*/
class KICONTHEMES_EXPORT KIconTheme
{
public:
/**
* Load an icon theme by name.
* @param name the name of the theme (e.g. "hicolor" or "keramik")
* @param appName the name of the application. Can be null. This argument
* allows applications to have themed application icons.
* @param basePathHint optional hint where to search the app themes.
* This is appended at the end of the search paths
*/
explicit KIconTheme(const QString &name, const QString &appName = QString(), const QString &basePathHint = QString());
~KIconTheme();
KIconTheme(const KIconTheme &) = delete;
KIconTheme &operator=(const KIconTheme &) = delete;
/**
* The stylized name of the icon theme.
* @return the (human-readable) name of the theme
*/
QString name() const;
/**
* The internal name of the icon theme (same as the name argument passed
* to the constructor).
* @return the internal name of the theme
*/
QString internalName() const;
/**
* A description for the icon theme.
* @return a human-readable description of the theme, QString()
* if there is none
*/
QString description() const;
/**
* Return the name of the "example" icon. This can be used to
* present the theme to the user.
* @return the name of the example icon, QString() if there is none
*/
QString example() const;
/**
* Return the name of the screenshot.
* @return the name of the screenshot, QString() if there is none
*/
QString screenshot() const;
/**
* Returns the toplevel theme directory.
* @return the directory of the theme
*/
QString dir() const;
/**
* The themes this icon theme falls back on.
* @return a list of icon themes that are used as fall-backs
*/
QStringList inherits() const;
/**
* The icon theme exists?
* @return true if the icon theme is valid
*/
bool isValid() const;
/**
* The icon theme should be hidden to the user?
* @return true if the icon theme is hidden
*/
bool isHidden() const;
/**
* The minimum display depth required for this theme. This can either
* be 8 or 32.
* @return the minimum bpp (8 or 32)
*/
int depth() const;
/**
* The default size of this theme for a certain icon group.
* @param group The icon group. See KIconLoader::Group.
* @return The default size in pixels for the given icon group.
*/
int defaultSize(KIconLoader::Group group) const;
/**
* Query available sizes for a group.
* @param group The icon group. See KIconLoader::Group.
* @return a list of available sizes for the given group
*/
QList<int> querySizes(KIconLoader::Group group) const;
/**
* Query available icons for a size and context.
* @param size the size of the icons
* @param context the context of the icons
* @return the list of icon names
*/
QStringList queryIcons(int size, KIconLoader::Context context = KIconLoader::Any) const;
/**
* Query available icons for a context and preferred size.
* @param size the size of the icons
* @param context the context of the icons
* @return the list of icon names
*/
QStringList queryIconsByContext(int size, KIconLoader::Context context = KIconLoader::Any) const;
/**
* Lookup an icon in the theme.
* @param name The name of the icon, with extension.
* @param size The desired size of the icon.
* @param match The matching mode. KIconLoader::MatchExact returns an icon
* only if matches exactly. KIconLoader::MatchBest returns the best matching
* icon.
* @return An absolute path to the file of the icon if it's found, QString() otherwise.
* @see KIconLoader::isValid will return true, and false otherwise.
*/
QString iconPath(const QString &name, int size, KIconLoader::MatchType match) const;
/**
* Lookup an icon in the theme.
* @param name The name of the icon, with extension.
* @param size The desired size of the icon.
* @param match The matching mode. KIconLoader::MatchExact returns an icon
* only if matches exactly. KIconLoader::MatchBest returns the best matching
* icon.
* @param scale The scale of the icon group.
* @return An absolute path to the file of the icon if it's found, QString() otherwise.
* @see KIconLoader::isValid will return true, and false otherwise.
* @since 5.48
*/
// TODO KF6 merge iconPath() with and without "scale" and move that argument after "size"
QString iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const;
/**
* Lookup an icon in the theme.
* @param name The name of the icon, without extension.
* @param size The desired size of the icon.
* @param match The matching mode. KIconLoader::MatchExact returns an icon
* only if matches exactly. KIconLoader::MatchBest returns the best matching
* icon.
* @return An absolute path to the file of the icon if it's found, QString() otherwise.
* @see KIconLoader::isValid will return true, and false otherwise.
*
* @since 5.22
*/
QString iconPathByName(const QString &name, int size, KIconLoader::MatchType match) const;
/**
* Lookup an icon in the theme.
* @param name The name of the icon, without extension.
* @param size The desired size of the icon.
* @param match The matching mode. KIconLoader::MatchExact returns an icon
* only if matches exactly. KIconLoader::MatchBest returns the best matching
* icon.
* @param scale The scale of the icon group.
* @return An absolute path to the file of the icon if it's found, QString() otherwise.
* @see KIconLoader::isValid will return true, and false otherwise.
*
* @since 5.48
*/
// TODO KF6 merge iconPathByName() with and without "scale" and move that argument after "size"
QString iconPathByName(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const;
/**
* Returns true if the theme has any icons for the given context.
*/
bool hasContext(KIconLoader::Context context) const;
/**
* If true, this theme is made of SVG icons that will be colorized following the system
* color scheme. This is necessary for monochrome themes that should look visible on both
* light and dark color schemes.
* @return true if the SVG will be colorized with a stylesheet.
* @since 5.22
*/
bool followsColorScheme() const;
/**
* List all icon themes installed on the system, global and local.
* @return the list of all icon themes
*/
static QStringList list();
/**
* Returns the current icon theme.
* @return the name of the current theme
*/
static QString current();
/**
* Force a current theme and disable automatic resolution of the current
* theme in favor of the forced theme.
*
* To reset a forced theme, simply set an empty themeName.
*
* @param themeName name of the theme to force as current theme, or an
* empty string to unset theme forcing.
*
* @note This should only be used when a very precise expectation about
* the current theme is present. Most notably in unit tests
* this can be used to force a given theme.
*
* @warning A forced theme persists across reconfigure() calls!
*
* @see current
* @since 5.23
*/
static void forceThemeForTests(const QString &themeName);
/**
* Reconfigure the theme.
*/
static void reconfigure();
/**
* Returns the default icon theme.
* @return the name of the default theme name
*/
static QString defaultThemeName();
/**
* Enforces the Breeze icon theme (including our KIconEngine for re-coloring).
*
* If the user has configured a different icon set, that one will be taken.
*
* (following the settings in the application configuration with fallback to kdeglobals)
*
* Must be called before the construction of the first Q(Core)Application, as it
* might need to modify plugin loading and installs a startup function.
*
* If the application is already using the KDE platform theme, the icon set will not
* be touched and the platform theme will ensure proper theming.
*
* Does setup the needed KColorSchemeManager to follow the system color mode starting
* with version 6.6.
*
* @since 6.3
*/
static void initTheme();
private:
std::unique_ptr<class KIconThemePrivate> const d;
};
#endif
@@ -0,0 +1,79 @@
/*
SPDX-FileCopyrightText: 2011 Artur Duque de Souza <asouza@kde.org>
SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KQUICK_ICON_PROVIDER_H
#define KQUICK_ICON_PROVIDER_H
#include <KIconEffect>
#include <KIconLoader>
#include <QIcon>
#include <QPixmap>
#include <QQuickImageProvider>
#include <QSize>
/**
* Class which exposes the KIcon* functioality to QML.
* For dependency reasons, this is a header-only class.
*
* This needs to be registered in the engine using the following code:
* @code
* engine->addImageProvider(QStringLiteral("icon"), new KQuickIconProvider);
* @endcode
* @since 5.98
*/
class KQuickIconProvider : public QQuickImageProvider
{
public:
KQuickIconProvider()
: QQuickImageProvider(QQuickImageProvider::Pixmap)
{
}
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
{
// We need to handle QIcon::state
const QStringList source = id.split(QLatin1Char('/'));
QPixmap pixmap;
if (requestedSize.isValid()) {
pixmap = QIcon::fromTheme(source.at(0)).pixmap(requestedSize);
} else if (size->isValid()) {
pixmap = QIcon::fromTheme(source.at(0)).pixmap(*size);
} else {
pixmap = QIcon::fromTheme(source.at(0)).pixmap(KIconLoader::global()->currentSize(KIconLoader::Desktop));
}
if (source.size() == 2) {
const QString state(source.at(1));
int finalState = KIconLoader::DefaultState;
if (state == QLatin1String("active")) {
finalState = KIconLoader::ActiveState;
} else if (state == QLatin1String("disabled")) {
finalState = KIconLoader::DisabledState;
} else if (state == QLatin1String("last")) {
finalState = KIconLoader::LastState;
}
// apply the effect for state
if (finalState == KIconLoader::ActiveState) {
KIconEffect::toActive(pixmap);
}
if (finalState == KIconLoader::DisabledState) {
KIconEffect::toDisabled(pixmap);
}
}
if (!pixmap.isNull() && size) {
*size = pixmap.size();
}
return pixmap;
}
};
#endif
@@ -0,0 +1,5 @@
ecm_add_qml_module(iconthemesplugin URI "org.kde.iconthemes" VERSION 1.0 GENERATE_PLUGIN_SOURCE)
target_sources(iconthemesplugin PRIVATE icondialog.cpp)
target_link_libraries(iconthemesplugin PRIVATE Qt6::Qml Qt6::Quick KF6::IconWidgets)
ecm_finalize_qml_module(iconthemesplugin DESTINATION ${KDE_INSTALL_QMLDIR})
@@ -0,0 +1,173 @@
/*
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "icondialog_p.h"
#include <QQuickItem>
#include <QQuickWindow>
#include <KIconDialog>
#include <KIconLoader>
#include <QApplication>
IconDialog::IconDialog(QObject *parent)
: QObject(parent)
, m_dialog(nullptr)
, m_iconSize(0)
, m_user(false)
, m_modality(Qt::WindowModal)
{
if (qobject_cast<QApplication *>(QCoreApplication::instance())) {
m_dialog.reset(new KIconDialog());
connect(m_dialog.data(), &KIconDialog::newIconName, this, [this](const QString &newIconName) {
if (m_iconName != newIconName) {
m_iconName = newIconName;
Q_EMIT iconNameChanged(newIconName);
}
});
m_dialog->installEventFilter(this);
}
}
IconDialog::~IconDialog()
{
if (m_dialog) {
m_dialog->close();
}
}
QString IconDialog::iconName() const
{
return m_iconName;
}
int IconDialog::iconSize() const
{
return m_iconSize;
}
void IconDialog::setIconSize(int iconSize)
{
if (m_dialog->iconSize() != iconSize) {
m_iconSize = iconSize;
Q_EMIT iconSizeChanged(iconSize);
}
}
QString IconDialog::title() const
{
return m_dialog->windowTitle();
}
void IconDialog::setTitle(const QString &title)
{
if (m_dialog->windowTitle() != title) {
m_dialog->setWindowTitle(title);
Q_EMIT titleChanged(title);
}
}
bool IconDialog::user() const
{
return m_user;
}
void IconDialog::setUser(bool user)
{
if (m_user != user) {
m_user = user;
Q_EMIT userChanged(user);
}
}
QString IconDialog::customLocation() const
{
return m_customLocation;
}
void IconDialog::setCustomLocation(const QString &customLocation)
{
if (m_customLocation != customLocation) {
m_dialog->setCustomLocation(customLocation);
m_customLocation = customLocation;
Q_EMIT customLocationChanged(customLocation);
}
}
Qt::WindowModality IconDialog::modality() const
{
return m_modality;
}
void IconDialog::setModality(Qt::WindowModality modality)
{
if (m_modality != modality) {
m_modality = modality;
Q_EMIT modalityChanged(modality);
}
}
bool IconDialog::visible() const
{
return m_dialog->isVisible();
}
void IconDialog::setVisible(bool visible)
{
if (visible) {
open();
} else {
close();
}
}
void IconDialog::open()
{
if (m_dialog->isVisible()) {
return;
}
QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
QQuickWindow *parentWindow = (parentItem ? parentItem->window() : qobject_cast<QQuickWindow *>(parent()));
if (m_modality == Qt::NonModal) {
m_dialog->setModal(false);
} else if (m_modality == Qt::WindowModal) {
m_dialog->winId(); // needed to get the windowHandle prior to showing
m_dialog->windowHandle()->setTransientParent(parentWindow);
m_dialog->setModal(false); // WindowModal does not unset the overall modality
} else if (m_modality == Qt::ApplicationModal) {
m_dialog->setModal(true);
}
m_dialog->setWindowModality(m_modality);
m_dialog->setup(KIconLoader::Desktop, KIconLoader::Application, false, m_iconSize, m_user);
m_dialog->show();
}
void IconDialog::close()
{
if (!m_dialog->isVisible()) {
return;
}
m_dialog->hide();
}
bool IconDialog::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_dialog.data() && (event->type() == QEvent::Show || event->type() == QEvent::Hide)) {
Q_EMIT visibleChanged();
}
return false;
}
#include "moc_icondialog_p.cpp"
@@ -0,0 +1,99 @@
/*
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef ICONDIALOG_H
#define ICONDIALOG_H
#include <QObject>
#include <QString>
#include <qqmlregistration.h>
class KIconDialog;
class IconDialog : public QObject
{
Q_OBJECT
QML_ELEMENT
/**
* The name or path of the icon the user has selected
*/
Q_PROPERTY(QString iconName READ iconName NOTIFY iconNameChanged)
/**
* The desired size of icons
*/
Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
/**
* The title to use for the dialog
*/
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
/**
* Begin with the "user icons" instead of "system icons"
*/
Q_PROPERTY(bool user READ user WRITE setUser NOTIFY userChanged)
/**
* Use a custom location, only local directory paths are allowed
*/
Q_PROPERTY(QString customLocation READ customLocation WRITE setCustomLocation NOTIFY customLocationChanged)
/**
* Window modality, default is Qt.NonModal
*/
Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged)
/**
* Whether the dialog is currently visible, setting this property to true
* is the same as calling show()
*/
Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)
public:
explicit IconDialog(QObject *parent = nullptr);
~IconDialog() override;
QString iconName() const;
int iconSize() const;
void setIconSize(int iconSize);
QString title() const;
void setTitle(const QString &title);
bool user() const;
void setUser(bool user);
QString customLocation() const;
void setCustomLocation(const QString &customLocation);
Qt::WindowModality modality() const;
void setModality(Qt::WindowModality modality);
bool visible() const;
void setVisible(bool visible);
Q_INVOKABLE void open();
Q_INVOKABLE void close();
Q_SIGNALS:
void iconNameChanged(const QString &iconName);
void iconSizeChanged(int iconSize);
void titleChanged(const QString &title);
void userChanged(bool user);
void customLocationChanged(const QString &customLocation);
void modalityChanged(Qt::WindowModality modality);
void visibleChanged();
private:
bool eventFilter(QObject *watched, QEvent *event) override;
QScopedPointer<KIconDialog> m_dialog;
QString m_iconName;
int m_iconSize;
bool m_user;
QString m_customLocation;
Qt::WindowModality m_modality;
};
#endif // ICONDIALOG_H
@@ -0,0 +1,5 @@
add_executable(kiconfinder6 kiconfinder.cpp)
ecm_mark_nongui_executable(kiconfinder6)
target_link_libraries(kiconfinder6 Qt6::Gui KF6IconThemes)
install(TARGETS kiconfinder6 ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
@@ -0,0 +1,41 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include <kiconloader.h>
#include <kiconthemes_version.h>
#include <QCommandLineParser>
#include <QGuiApplication>
#include <stdio.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
app.setApplicationName(QStringLiteral("kiconfinder"));
app.setApplicationVersion(QStringLiteral(KICONTHEMES_VERSION_STRING));
QCommandLineParser parser;
parser.setApplicationDescription(app.translate("main", "Finds an icon based on its name"));
parser.addPositionalArgument(QStringLiteral("iconname"), app.translate("main", "The icon name to look for"));
parser.addHelpOption();
parser.process(app);
if (parser.positionalArguments().isEmpty()) {
parser.showHelp();
}
for (const QString &iconName : parser.positionalArguments()) {
const QString icon = KIconLoader::global()->iconPath(iconName, KIconLoader::Desktop /*TODO configurable*/, true);
if (!icon.isEmpty()) {
printf("%s\n", icon.toLocal8Bit().constData());
} else {
return 1; // error
}
}
return 0;
}
@@ -0,0 +1,5 @@
add_executable(ksvg2icns ksvg2icns.cpp)
ecm_mark_nongui_executable(ksvg2icns)
target_link_libraries(ksvg2icns Qt6::Gui Qt6::Svg)
install(TARGETS ksvg2icns ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
@@ -0,0 +1,144 @@
/*
SPDX-FileCopyrightText: 2014 Harald Fernengel <harry@kdevelop.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
/* This tool converts an svg to a Mac OS X icns file.
* Note: Requires the 'iconutil' Mac OS X binary
*/
#include <QCommandLineParser>
#include <QFileInfo>
#include <QProcess>
#include <QTemporaryDir>
#include <QGuiApplication>
#include <QPainter>
#include <QStandardPaths>
#include <QSvgRenderer>
#include "../../kiconthemes_version.h"
#include <stdio.h>
/* clang-format off */
#define EXIT_ON_ERROR(isOk, ...) \
do { \
if (!(isOk)) { \
fprintf(stderr, __VA_ARGS__); \
return 1; \
} \
} while (false);
/* clang-format on */
static bool writeImage(QSvgRenderer &svg, int size, const QString &outFile1, const QString &outFile2 = QString())
{
QImage out(size, size, QImage::Format_ARGB32);
out.fill(Qt::transparent);
QPainter painter(&out);
svg.setAspectRatioMode(Qt::KeepAspectRatio);
svg.render(&painter);
painter.end();
if (!out.save(outFile1)) {
fprintf(stderr, "Unable to write %s\n", qPrintable(outFile1));
return false;
}
if (!outFile2.isEmpty() && !out.save(outFile2)) {
fprintf(stderr, "Unable to write %s\n", qPrintable(outFile2));
return false;
}
return true;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
app.setApplicationName(QStringLiteral("ksvg2icns"));
app.setApplicationVersion(QStringLiteral(KICONTHEMES_VERSION_STRING));
QCommandLineParser parser;
parser.setApplicationDescription(app.translate("main", "Creates an icns file from an svg image"));
parser.addPositionalArgument("iconname", app.translate("main", "The svg icon to convert"));
parser.addHelpOption();
parser.process(app);
if (parser.positionalArguments().isEmpty()) {
parser.showHelp();
return 1;
}
bool isOk;
// create a temporary dir to create an iconset
QTemporaryDir tmpDir(QDir::tempPath() + QStringLiteral("/ksvg2icns"));
isOk = tmpDir.isValid();
EXIT_ON_ERROR(isOk, "Unable to create temporary directory\n");
const QString outPath = tmpDir.filePath(QStringLiteral("out.iconset"));
isOk = QDir(tmpDir.path()).mkpath(outPath);
EXIT_ON_ERROR(isOk, "Unable to create out.iconset directory\n");
const QStringList &args = app.arguments();
EXIT_ON_ERROR(args.size() == 2, "Usage: %s svg-image\n", qPrintable(args.value(0)));
const QString &svgFileName = args.at(1);
// open the actual svg file
QSvgRenderer svg;
isOk = svg.load(svgFileName);
EXIT_ON_ERROR(isOk, "Unable to load %s\n", qPrintable(svgFileName));
// The sizes are from:
// https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html
struct OutFiles {
int size;
QString out1;
QString out2;
};
// create the pngs in various resolutions
const OutFiles outFiles[] = {{1024, outPath + QStringLiteral("/icon_512x512@2x.png"), QString()},
{512, outPath + QStringLiteral("/icon_512x512.png"), outPath + QStringLiteral("/icon_256x256@2x.png")},
{256, outPath + QStringLiteral("/icon_256x256.png"), outPath + QStringLiteral("/icon_128x128@2x.png")},
{128, outPath + QStringLiteral("/icon_128x128.png"), QString()},
{64, outPath + QStringLiteral("/icon_32x32@2x.png"), QString()},
{32, outPath + QStringLiteral("/icon_32x32.png"), outPath + QStringLiteral("/icon_16x16@2x.png")},
{16, outPath + QStringLiteral("/icon_16x16.png"), QString()}};
for (const OutFiles &outFile : outFiles) {
isOk = writeImage(svg, outFile.size, outFile.out1, outFile.out2);
if (!isOk) {
return 1;
}
}
// convert the iconset to icns using the "iconutil" command
const QString iconutilExec = QStandardPaths::findExecutable(QStringLiteral("iconutil"));
if (iconutilExec.isEmpty()) {
EXIT_ON_ERROR(false, "Could not find iconutil executable in PATH.\n");
}
const QString outIcns = QFileInfo(svgFileName).baseName() + QStringLiteral(".icns");
const QStringList utilArgs = QStringList() << "-c"
<< "icns"
<< "-o" << outIcns << outPath;
QProcess iconUtil;
iconUtil.start(iconutilExec, utilArgs, QIODevice::ReadOnly);
isOk = iconUtil.waitForFinished(-1);
EXIT_ON_ERROR(isOk, "Unable to launch iconutil: %s\n", qPrintable(iconUtil.errorString()));
EXIT_ON_ERROR(iconUtil.exitStatus() == QProcess::NormalExit, "iconutil crashed!\n");
EXIT_ON_ERROR(iconUtil.exitCode() == 0, "iconutil returned %d\n", iconUtil.exitCode());
return 0;
}
@@ -0,0 +1,97 @@
add_library(KF6IconWidgets)
add_library(KF6::IconWidgets ALIAS KF6IconWidgets)
set_target_properties(KF6IconWidgets PROPERTIES
VERSION ${KICONTHEMES_VERSION}
SOVERSION ${KICONTHEMES_SOVERSION}
EXPORT_NAME IconWidgets
)
target_sources(KF6IconWidgets PRIVATE
kiconbutton.cpp
kiconbutton.h
kicondialog.cpp
kicondialog.h
kicondialogmodel_p.h
kicondialog_p.h
kpixmapsequenceloader.h
kpixmapsequenceloader.cpp
)
ki18n_wrap_ui(KF6IconWidgets kicondialog.ui)
ecm_generate_export_header(KF6IconWidgets
BASE_NAME KIconWidgets
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
VERSION_BASE_NAME KIconThemes
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
target_include_directories(KF6IconWidgets INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIconWidgets>")
target_link_libraries(KF6IconWidgets
PUBLIC
Qt6::Widgets
KF6::IconThemes
PRIVATE
Qt6::GuiPrivate
Qt6::Svg
KF6::I18n # for i18n in KIconDialog
KF6::ConfigGui # for KStandardActions
KF6::WidgetsAddons # for KPixmapSequence
)
ecm_generate_headers(KIconWidgets_HEADERS
HEADER_NAMES
KIconButton
KIconDialog
KPixmapSequenceLoader
REQUIRED_HEADERS KIconWidgets_HEADERS
)
install(TARGETS KF6IconWidgets EXPORT KF6IconThemesTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/kiconwidgets_export.h
${KIconWidgets_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIconWidgets COMPONENT Devel
)
if(BUILD_DESIGNERPLUGIN)
add_subdirectory(designer)
endif()
if(BUILD_QCH)
ecm_add_qch(
KF6IconWidgets_QCH
NAME KIconWidgets
BASE_NAME KF6IconWidgets
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${KIconWidgets_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
LINK_QCHS
Qt6Widgets_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KICONWIDGETS_EXPORT
KICONWIDGETS_DEPRECATED
KICONWIDGETS_DEPRECATED_EXPORT
"KICONWIDGETS_DEPRECATED_VERSION(x, y, t)"
"KICONWIDGETS_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
"KICONWIDGETS_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
"KICONWIDGETS_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
@@ -0,0 +1,17 @@
include(ECMAddQtDesignerPlugin)
ecm_qtdesignerplugin_widget(KIconButton
TOOLTIP "Button for selecting an icon (KF6)"
GROUP "Buttons (KF6)"
)
ecm_add_qtdesignerplugin(kiconthemeswidgets
NAME KIconThemesWidgets
OUTPUT_NAME kiconthemes6widgets
WIDGETS
KIconButton
LINK_LIBRARIES
KF6::IconWidgets
INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer"
COMPONENT Devel
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,188 @@
/*
This file is part of the KDE project, module kfile.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kiconbutton.h"
#include <QFileInfo>
#include <KLocalizedString>
#include "kicondialog.h"
class KIconButtonPrivate
{
public:
KIconButtonPrivate(KIconButton *qq, KIconLoader *loader);
~KIconButtonPrivate();
KIconDialog *dialog();
// slots
void _k_slotChangeIcon();
void _k_newIconName(const QString &);
KIconButton *q;
int iconSize;
int buttonIconSize;
bool m_bStrictIconSize;
bool mbUser;
KIconLoader::Group mGroup;
KIconLoader::Context mContext;
QString mIcon;
KIconDialog *mpDialog;
KIconLoader *mpLoader;
};
/*
* KIconButton: A "choose icon" pushbutton.
*/
KIconButton::KIconButton(QWidget *parent)
: QPushButton(parent)
, d(new KIconButtonPrivate(this, KIconLoader::global()))
{
QPushButton::setIconSize(QSize(48, 48));
}
KIconButtonPrivate::KIconButtonPrivate(KIconButton *qq, KIconLoader *loader)
: q(qq)
{
m_bStrictIconSize = false;
iconSize = 0; // let KIconLoader choose the default
buttonIconSize = -1; // When buttonIconSize is -1, iconSize will be used for the button
mGroup = KIconLoader::Desktop;
mContext = KIconLoader::Application;
mbUser = false;
mpLoader = loader;
mpDialog = nullptr;
QObject::connect(q, &KIconButton::clicked, q, [this]() {
_k_slotChangeIcon();
});
q->setToolTip(i18nc("@info:tooltip", "Select Icon…"));
}
KIconButtonPrivate::~KIconButtonPrivate()
{
delete mpDialog;
}
KIconDialog *KIconButtonPrivate::dialog()
{
if (!mpDialog) {
mpDialog = new KIconDialog(q);
QObject::connect(mpDialog, &KIconDialog::newIconName, q, [this](const QString &iconName) {
_k_newIconName(iconName);
});
}
return mpDialog;
}
KIconButton::~KIconButton() = default;
void KIconButton::setStrictIconSize(bool b)
{
d->m_bStrictIconSize = b;
}
bool KIconButton::strictIconSize() const
{
return d->m_bStrictIconSize;
}
void KIconButton::setIconSize(int size)
{
if (d->buttonIconSize == -1) {
QPushButton::setIconSize(QSize(size, size));
}
d->iconSize = size;
}
int KIconButton::iconSize() const
{
return d->iconSize;
}
void KIconButton::setButtonIconSize(int size)
{
QPushButton::setIconSize(QSize(size, size));
d->buttonIconSize = size;
}
int KIconButton::buttonIconSize() const
{
return QPushButton::iconSize().height();
}
void KIconButton::setIconType(KIconLoader::Group group, KIconLoader::Context context, bool user)
{
d->mGroup = group;
d->mContext = context;
d->mbUser = user;
}
void KIconButton::setIcon(const QString &icon)
{
d->mIcon = icon;
setIcon(QIcon::fromTheme(d->mIcon));
if (d->mbUser) {
d->dialog()->setCustomLocation(QFileInfo(d->mpLoader->iconPath(d->mIcon, d->mGroup, true)).absolutePath());
}
}
void KIconButton::setIcon(const QIcon &icon)
{
QPushButton::setIcon(icon);
}
void KIconButton::resetIcon()
{
d->mIcon.clear();
setIcon(QIcon());
}
const QString &KIconButton::icon() const
{
return d->mIcon;
}
void KIconButtonPrivate::_k_slotChangeIcon()
{
dialog()->setup(mGroup, mContext, m_bStrictIconSize, iconSize, mbUser);
dialog()->setSelectedIcon(mIcon);
dialog()->showDialog();
}
void KIconButtonPrivate::_k_newIconName(const QString &name)
{
if (name.isEmpty()) {
return;
}
q->setIcon(QIcon::fromTheme(name));
mIcon = name;
if (mbUser) {
mpDialog->setCustomLocation(QFileInfo(mpLoader->iconPath(mIcon, mGroup, true)).absolutePath());
}
Q_EMIT q->iconChanged(name);
}
#include "moc_kiconbutton.cpp"
@@ -0,0 +1,123 @@
/*
This file is part of the KDE project, module kfile.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONBUTTON_H
#define KICONBUTTON_H
#include "kiconwidgets_export.h"
#include <QPushButton>
#include <memory>
#include <kiconloader.h>
/**
* @class KIconButton kiconbutton.h KIconButton
*
* A pushbutton for choosing an icon. Pressing on the button will open a
* KIconDialog for the user to select an icon. The current icon will be
* displayed on the button.
*
* @see KIconDialog
* @short A push button that allows selection of an icon.
*/
class KICONWIDGETS_EXPORT KIconButton : public QPushButton
{
Q_OBJECT
Q_PROPERTY(QString icon READ icon WRITE setIcon RESET resetIcon NOTIFY iconChanged USER true)
Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize)
Q_PROPERTY(bool strictIconSize READ strictIconSize WRITE setStrictIconSize)
public:
/**
* Constructs a KIconButton using the global icon loader.
*
* @param parent The parent widget.
*/
explicit KIconButton(QWidget *parent = nullptr);
/**
* Destructs the button.
*/
~KIconButton() override;
/**
* Sets a strict icon size policy for allowed icons. When true,
* only icons of the specified group's size in setIconType() are allowed,
* and only icons of that size will be shown in the icon dialog.
*/
void setStrictIconSize(bool b);
/**
* Returns true if a strict icon size policy is set.
*/
bool strictIconSize() const;
/**
* Sets the icon group and context. Use KIconLoader::NoGroup if you want to
* allow icons for any group in the given context.
*/
void setIconType(KIconLoader::Group group, KIconLoader::Context context, bool user = false);
/**
* Sets the button's initial icon.
*/
void setIcon(const QString &icon);
void setIcon(const QIcon &icon);
/**
* Resets the icon (reverts to an empty button).
*/
void resetIcon();
/**
* Returns the name of the selected icon.
*/
const QString &icon() const;
/**
* Sets the size of the icon to be shown / selected.
* @see KIconLoader::StdSizes
* @see iconSize
*/
void setIconSize(int size);
/**
* Returns the icon size set via setIconSize() or 0, if the default
* icon size will be used.
*/
int iconSize() const;
/**
* Sets the size of the icon to be shown on the button.
* @see KIconLoader::StdSizes
* @see buttonIconSize
* @since 4.1
*/
void setButtonIconSize(int size);
/**
* Returns the button's icon size.
* @since 4.1
*/
int buttonIconSize() const;
Q_SIGNALS:
/**
* Emitted when the icon has changed.
*/
void iconChanged(const QString &icon);
private:
std::unique_ptr<class KIconButtonPrivate> const d;
Q_DISABLE_COPY(KIconButton)
};
#endif // KICONBUTTON_H
@@ -0,0 +1,754 @@
/*
This file is part of the KDE project, module kfile.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kicondialog.h"
#include "kicondialog_p.h"
#include "kicondialogmodel_p.h"
#include <KLazyLocalizedString>
#include <KLocalizedString>
#include <KStandardActions>
#include <QAbstractListModel>
#include <QActionGroup>
#include <QApplication>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFileInfo>
#include <QGraphicsOpacityEffect>
#include <QLabel>
#include <QList>
#include <QMenu>
#include <QPainter>
#include <QScrollBar>
#include <QSortFilterProxyModel>
#include <QStandardItemModel> // for manipulatig QComboBox
#include <QStandardPaths>
#include <QSvgRenderer>
#include <algorithm>
#include <math.h>
static const int s_edgePad = 3;
class KIconDialogSortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit KIconDialogSortFilterProxyModel(QObject *parent);
enum SymbolicIcons { AllSymbolicIcons, OnlySymbolicIcons, NoSymbolicIcons };
void setSymbolicIcons(SymbolicIcons symbolicIcons);
void setHasSymbolicIcon(bool hasSymbolicIcon);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
SymbolicIcons m_symbolicIcons = AllSymbolicIcons;
bool m_hasSymbolicIcon = false;
};
KIconDialogSortFilterProxyModel::KIconDialogSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
void KIconDialogSortFilterProxyModel::setSymbolicIcons(SymbolicIcons symbolicIcons)
{
if (m_symbolicIcons == symbolicIcons) {
return;
}
m_symbolicIcons = symbolicIcons;
invalidateFilter();
}
void KIconDialogSortFilterProxyModel::setHasSymbolicIcon(bool hasSymbolicIcon)
{
if (m_hasSymbolicIcon == hasSymbolicIcon) {
return;
}
m_hasSymbolicIcon = hasSymbolicIcon;
invalidateFilter();
}
bool KIconDialogSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (m_hasSymbolicIcon) {
if (m_symbolicIcons == OnlySymbolicIcons || m_symbolicIcons == NoSymbolicIcons) {
const QString display = sourceModel()->index(source_row, 0, source_parent).data(Qt::DisplayRole).toString();
const bool isSymbolic = display.endsWith(KIconDialogModel::symbolicSuffix());
if ((m_symbolicIcons == OnlySymbolicIcons && !isSymbolic) || (m_symbolicIcons == NoSymbolicIcons && isSymbolic)) {
return false;
}
}
}
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
KIconDialogModel::KIconDialogModel(KIconLoader *loader, QObject *parent)
: QAbstractListModel(parent)
, m_loader(loader)
{
}
KIconDialogModel::~KIconDialogModel() = default;
qreal KIconDialogModel::devicePixelRatio() const
{
return m_dpr;
}
void KIconDialogModel::setDevicePixelRatio(qreal dpr)
{
m_dpr = dpr;
}
QSize KIconDialogModel::iconSize() const
{
return m_iconSize;
}
void KIconDialogModel::setIconSize(const QSize &iconSize)
{
m_iconSize = iconSize;
}
QLatin1String KIconDialogModel::symbolicSuffix()
{
return QLatin1String("-symbolic");
}
bool KIconDialogModel::hasSymbolicIcon() const
{
return m_hasSymbolicIcon;
}
void KIconDialogModel::load(const QStringList &paths)
{
beginResetModel();
const bool oldSymbolic = m_hasSymbolicIcon;
m_hasSymbolicIcon = false;
m_data.clear();
m_data.reserve(paths.count());
for (const QString &path : paths) {
const QFileInfo fi(path);
KIconDialogModelData item;
item.name = fi.completeBaseName();
item.path = path;
// pixmap is created on demand
if (!m_hasSymbolicIcon && item.name.endsWith(symbolicSuffix())) {
m_hasSymbolicIcon = true;
}
m_data.append(item);
}
endResetModel();
if (oldSymbolic != m_hasSymbolicIcon) {
Q_EMIT hasSymbolicIconChanged(m_hasSymbolicIcon);
}
}
int KIconDialogModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return m_data.count();
}
QVariant KIconDialogModel::data(const QModelIndex &index, int role) const
{
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
return QVariant();
}
const auto &item = m_data.at(index.row());
switch (role) {
case Qt::DisplayRole:
return item.name;
case Qt::DecorationRole:
if (item.pixmap.isNull()) {
const_cast<KIconDialogModel *>(this)->loadPixmap(index);
}
return item.pixmap;
case Qt::ToolTipRole:
return item.name;
case PathRole:
return item.path;
}
return QVariant();
}
void KIconDialogModel::loadPixmap(const QModelIndex &index)
{
Q_ASSERT(index.isValid());
auto &item = m_data[index.row()];
Q_ASSERT(item.pixmap.isNull());
const auto dpr = devicePixelRatio();
item.pixmap = m_loader->loadScaledIcon(item.path, KIconLoader::Desktop, dpr, iconSize(), KIconLoader::DefaultState, {}, nullptr, true);
item.pixmap.setDevicePixelRatio(dpr);
}
/**
* Qt allocates very little horizontal space for the icon name,
* even if the gridSize width is large. This delegate allocates
* the gridSize width (minus some padding) for the icon and icon name.
*/
class KIconCanvasDelegate : public QAbstractItemDelegate
{
Q_OBJECT
public:
KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate);
~KIconCanvasDelegate() override
{
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QAbstractItemDelegate *m_defaultDelegate = nullptr;
};
KIconCanvasDelegate::KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate)
: QAbstractItemDelegate(parent)
{
m_defaultDelegate = defaultDelegate;
}
void KIconCanvasDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
auto *canvas = static_cast<QListView *>(parent());
const int gridWidth = canvas->gridSize().width();
QStyleOptionViewItem newOption = option;
newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop;
newOption.features.setFlag(QStyleOptionViewItem::WrapText);
// Manipulate the width available.
newOption.rect.setX((option.rect.x() / gridWidth) * gridWidth + s_edgePad);
newOption.rect.setY(option.rect.y() + s_edgePad);
newOption.rect.setWidth(gridWidth - 2 * s_edgePad);
newOption.rect.setHeight(option.rect.height() - 2 * s_edgePad);
m_defaultDelegate->paint(painter, newOption, index);
}
QSize KIconCanvasDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
auto *canvas = static_cast<QListView *>(parent());
// TODO can we set wrap text and display alignment somewhere globally?
QStyleOptionViewItem newOption = option;
newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop;
newOption.features.setFlag(QStyleOptionViewItem::WrapText);
QSize size = m_defaultDelegate->sizeHint(newOption, index);
const int gridWidth = canvas->gridSize().width();
const int gridHeight = canvas->gridSize().height();
size.setWidth(gridWidth - 2 * s_edgePad);
size.setHeight(gridHeight - 2 * s_edgePad);
QFontMetrics metrics(option.font);
size.setHeight(gridHeight + metrics.height() * 3);
return size;
}
KIconDialogPrivate::KIconDialogPrivate(KIconDialog *qq)
: q(qq)
, mpLoader(KIconLoader::global())
, model(new KIconDialogModel(mpLoader, qq))
, proxyModel(new KIconDialogSortFilterProxyModel(qq))
, filterSymbolicAction(new QAction(qq))
, filterSymbolicGroup(new QActionGroup(qq))
{
proxyModel->setSourceModel(model);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
filterSymbolicGroup->setExclusive(true);
QObject::connect(model, &KIconDialogModel::hasSymbolicIconChanged, filterSymbolicAction, &QAction::setVisible);
QObject::connect(model, &KIconDialogModel::hasSymbolicIconChanged, proxyModel, &KIconDialogSortFilterProxyModel::setHasSymbolicIcon);
}
/*
* KIconDialog: Dialog for selecting icons. Both system and user
* specified icons can be chosen.
*/
KIconDialog::KIconDialog(QWidget *parent)
: QDialog(parent)
, d(new KIconDialogPrivate(this))
{
setModal(true);
d->init();
}
void KIconDialogPrivate::init()
{
mGroupOrSize = KIconLoader::Desktop;
mContext = KIconLoader::Any;
ui.setupUi(q);
auto updatePlaceholder = [this] {
updatePlaceholderLabel();
};
QObject::connect(proxyModel, &QSortFilterProxyModel::modelReset, q, updatePlaceholder);
QObject::connect(proxyModel, &QSortFilterProxyModel::rowsInserted, q, updatePlaceholder);
QObject::connect(proxyModel, &QSortFilterProxyModel::rowsRemoved, q, updatePlaceholder);
QAction *findAction = KStandardActions::find(ui.searchLine, qOverload<>(&QWidget::setFocus), q);
q->addAction(findAction);
QMenu *filterSymbolicMenu = new QMenu(q);
QAction *filterSymbolicAll = filterSymbolicMenu->addAction(i18nc("@item:inmenu All icons", "All"));
filterSymbolicAll->setData(KIconDialogSortFilterProxyModel::AllSymbolicIcons);
filterSymbolicAll->setChecked(true); // Start with "All" icons.
filterSymbolicAll->setCheckable(true);
QAction *filterSymbolicOnly = filterSymbolicMenu->addAction(i18nc("@item:inmenu Show only symbolic icons", "Only Symbolic"));
filterSymbolicOnly->setData(KIconDialogSortFilterProxyModel::OnlySymbolicIcons);
filterSymbolicOnly->setCheckable(true);
QAction *filterSymbolicNone = filterSymbolicMenu->addAction(i18nc("@item:inmenu Hide symbolic icons", "No Symbolic"));
filterSymbolicNone->setData(KIconDialogSortFilterProxyModel::NoSymbolicIcons);
filterSymbolicNone->setCheckable(true);
filterSymbolicAction->setIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
filterSymbolicAction->setCheckable(true);
filterSymbolicAction->setChecked(true);
filterSymbolicAction->setMenu(filterSymbolicMenu);
filterSymbolicGroup->addAction(filterSymbolicAll);
filterSymbolicGroup->addAction(filterSymbolicOnly);
filterSymbolicGroup->addAction(filterSymbolicNone);
QObject::connect(filterSymbolicGroup, &QActionGroup::triggered, q, [this](QAction *action) {
proxyModel->setSymbolicIcons(static_cast<KIconDialogSortFilterProxyModel::SymbolicIcons>(action->data().toInt()));
});
ui.searchLine->addAction(filterSymbolicAction, QLineEdit::TrailingPosition);
QObject::connect(ui.searchLine, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterFixedString);
static const KLazyLocalizedString context_text[] = {
kli18n("All"),
kli18n("Actions"),
kli18n("Applications"),
kli18n("Categories"),
kli18n("Devices"),
kli18n("Emblems"),
kli18n("Emotes"),
kli18n("Mimetypes"),
kli18n("Places"),
kli18n("Status"),
};
static const KIconLoader::Context context_id[] = {
KIconLoader::Any,
KIconLoader::Action,
KIconLoader::Application,
KIconLoader::Category,
KIconLoader::Device,
KIconLoader::Emblem,
KIconLoader::Emote,
KIconLoader::MimeType,
KIconLoader::Place,
KIconLoader::StatusIcon,
};
const int cnt = sizeof(context_text) / sizeof(context_text[0]);
for (int i = 0; i < cnt; ++i) {
if (mpLoader->hasContext(context_id[i])) {
ui.contextCombo->addItem(context_text[i].toString(), context_id[i]);
if (i == 0) {
ui.contextCombo->insertSeparator(i + 1);
}
}
}
ui.contextCombo->insertSeparator(ui.contextCombo->count());
ui.contextCombo->addItem(i18nc("Other icons", "Other"));
ui.contextCombo->setMaxVisibleItems(ui.contextCombo->count());
ui.contextCombo->setFixedSize(ui.contextCombo->sizeHint());
QObject::connect(ui.contextCombo, qOverload<int>(&QComboBox::activated), q, [this]() {
const auto currentData = ui.contextCombo->currentData();
if (currentData.isValid()) {
mContext = static_cast<KIconLoader::Context>(ui.contextCombo->currentData().toInt());
} else {
mContext = static_cast<KIconLoader::Context>(-1);
}
showIcons();
});
auto *delegate = new KIconCanvasDelegate(ui.canvas, ui.canvas->itemDelegate());
ui.canvas->setItemDelegate(delegate);
ui.canvas->setModel(proxyModel);
QObject::connect(ui.canvas, &QAbstractItemView::activated, q, [this]() {
custom.clear();
q->slotOk();
});
// You can't just stack widgets on top of each other in Qt Designer
auto *placeholderLayout = new QVBoxLayout(ui.canvas);
placeholderLabel = new QLabel();
QFont placeholderLabelFont;
// To match the size of a level 2 Heading/KTitleWidget
placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
placeholderLabel->setFont(placeholderLabelFont);
placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction);
placeholderLabel->setWordWrap(true);
placeholderLabel->setAlignment(Qt::AlignCenter);
// Match opacity of QML placeholder label component
auto *effect = new QGraphicsOpacityEffect(placeholderLabel);
effect->setOpacity(0.5);
placeholderLabel->setGraphicsEffect(effect);
placeholderLayout->addWidget(placeholderLabel);
placeholderLayout->setAlignment(placeholderLabel, Qt::AlignCenter);
updatePlaceholderLabel();
// TODO I bet there is a KStandardAction for that?
browseButton = new QPushButton(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Browse…"));
// TODO does this have implicatons? I just want the "Browse" button on the left side :)
ui.buttonBox->addButton(browseButton, QDialogButtonBox::HelpRole);
QObject::connect(browseButton, &QPushButton::clicked, q, [this] {
browse();
});
QObject::connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &KIconDialog::slotOk);
QObject::connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
q->adjustSize();
}
KIconDialog::~KIconDialog() = default;
static bool sortByFileName(const QString &path1, const QString &path2)
{
const QString fileName1 = path1.mid(path1.lastIndexOf(QLatin1Char('/')) + 1);
const QString fileName2 = path2.mid(path2.lastIndexOf(QLatin1Char('/')) + 1);
return QString::compare(fileName1, fileName2, Qt::CaseInsensitive) < 0;
}
void KIconDialogPrivate::showIcons()
{
QStringList filelist;
if (isSystemIconsContext()) {
if (m_bStrictIconSize) {
filelist = mpLoader->queryIcons(mGroupOrSize, mContext);
} else {
filelist = mpLoader->queryIconsByContext(mGroupOrSize, mContext);
}
} else if (!customLocation.isEmpty()) {
filelist = mpLoader->queryIconsByDir(customLocation);
} else {
// List PNG files found directly in the kiconload search paths.
const QStringList pngNameFilter(QStringLiteral("*.png"));
for (const QString &relDir : KIconLoader::global()->searchPaths()) {
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, relDir, QStandardPaths::LocateDirectory);
for (const QString &dir : dirs) {
const auto files = QDir(dir).entryList(pngNameFilter);
for (const QString &fileName : files) {
filelist << dir + QLatin1Char('/') + fileName;
}
}
}
}
std::sort(filelist.begin(), filelist.end(), sortByFileName);
// The KIconCanvas has uniformItemSizes set which really expects
// all added icons to be the same size, otherwise weirdness ensues :)
// Ensure all SVGs are scaled to the desired size and that as few icons
// need to be padded as possible by specifying a sensible size.
if (mGroupOrSize < -1) {
// mGroupOrSize can be -1 if NoGroup is chosen.
// Explicit size.
ui.canvas->setIconSize(QSize(-mGroupOrSize, -mGroupOrSize));
} else {
// Icon group.
int groupSize = mpLoader->currentSize(static_cast<KIconLoader::Group>(mGroupOrSize));
ui.canvas->setIconSize(QSize(groupSize, groupSize));
}
// Try to make room for three lines of text...
QFontMetrics metrics(ui.canvas->font());
const int frameHMargin = ui.canvas->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, ui.canvas) + 1;
const int lineCount = 3;
ui.canvas->setGridSize(QSize(100, ui.canvas->iconSize().height() + lineCount * metrics.height() + 3 * frameHMargin));
// Set a minimum size of 6x3 icons
const int columnCount = 6;
const int rowCount = 3;
QStyleOption opt;
opt.initFrom(ui.canvas);
int width = columnCount * ui.canvas->gridSize().width();
width += ui.canvas->verticalScrollBar()->sizeHint().width() + 1;
width += 2 * ui.canvas->frameWidth();
if (ui.canvas->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, ui.canvas)) {
width += ui.canvas->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, ui.canvas);
}
int height = rowCount * ui.canvas->gridSize().height() + 1;
height += 2 * ui.canvas->frameWidth();
ui.canvas->setMinimumSize(QSize(width, height));
model->setIconSize(ui.canvas->iconSize());
model->setDevicePixelRatio(q->devicePixelRatioF());
model->load(filelist);
if (!pendingSelectedIcon.isEmpty()) {
selectIcon(pendingSelectedIcon);
pendingSelectedIcon.clear();
}
}
bool KIconDialogPrivate::selectIcon(const QString &iconName)
{
for (int i = 0; i < proxyModel->rowCount(); ++i) {
const QModelIndex idx = proxyModel->index(i, 0);
QString name = idx.data(KIconDialogModel::PathRole).toString();
if (!name.isEmpty() && isSystemIconsContext()) {
const QFileInfo fi(name);
name = fi.completeBaseName();
}
if (iconName == name) {
ui.canvas->setCurrentIndex(idx);
return true;
}
}
return false;
}
void KIconDialog::setStrictIconSize(bool b)
{
d->m_bStrictIconSize = b;
}
bool KIconDialog::strictIconSize() const
{
return d->m_bStrictIconSize;
}
void KIconDialog::setIconSize(int size)
{
// see KIconLoader, if you think this is weird
if (size == 0) {
d->mGroupOrSize = KIconLoader::Desktop; // default Group
} else {
d->mGroupOrSize = -size; // yes, KIconLoader::queryIconsByContext is weird
}
}
int KIconDialog::iconSize() const
{
// 0 or any other value ==> mGroupOrSize is a group, so we return 0
return (d->mGroupOrSize < 0) ? -d->mGroupOrSize : 0;
}
void KIconDialog::setSelectedIcon(const QString &iconName)
{
// TODO Update live when dialog is already open
d->pendingSelectedIcon = iconName;
}
void KIconDialog::setup(KIconLoader::Group group, KIconLoader::Context context, bool strictIconSize, int iconSize, bool user, bool lockUser, bool lockCustomDir)
{
d->m_bStrictIconSize = strictIconSize;
d->m_bLockUser = lockUser;
d->m_bLockCustomDir = lockCustomDir;
if (iconSize == 0) {
if (group == KIconLoader::NoGroup) {
// NoGroup has numeric value -1, which should
// not really be used with KIconLoader::queryIcons*(...);
// pick a proper group.
d->mGroupOrSize = KIconLoader::Small;
} else {
d->mGroupOrSize = group;
}
} else {
d->mGroupOrSize = -iconSize;
}
if (user) {
d->ui.contextCombo->setCurrentIndex(d->ui.contextCombo->count() - 1);
} else {
d->setContext(context);
}
d->ui.contextCombo->setEnabled(!user || !lockUser);
// Disable "Other" entry when user is locked
auto *model = qobject_cast<QStandardItemModel *>(d->ui.contextCombo->model());
auto *otherItem = model->item(model->rowCount() - 1);
auto flags = otherItem->flags();
flags.setFlag(Qt::ItemIsEnabled, !lockUser);
otherItem->setFlags(flags);
// Only allow browsing when explicitly allowed and user icons are allowed
// An app may not expect a path when asking only about system icons
d->browseButton->setVisible(!lockCustomDir && (!user || !lockUser));
}
void KIconDialogPrivate::setContext(KIconLoader::Context context)
{
mContext = context;
const int index = ui.contextCombo->findData(context);
if (index > -1) {
ui.contextCombo->setCurrentIndex(index);
}
}
void KIconDialogPrivate::updatePlaceholderLabel()
{
if (proxyModel->rowCount() > 0) {
placeholderLabel->hide();
return;
}
if (!ui.searchLine->text().isEmpty()) {
placeholderLabel->setText(i18n("No icons matching the search"));
} else {
placeholderLabel->setText(i18n("No icons in this category"));
}
placeholderLabel->show();
}
void KIconDialog::setCustomLocation(const QString &location)
{
d->customLocation = location;
}
QString KIconDialog::openDialog()
{
if (exec() == Accepted) {
if (!d->custom.isEmpty()) {
return d->custom;
}
const QString name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString();
if (name.isEmpty() || !d->ui.contextCombo->currentData().isValid()) {
return name;
}
QFileInfo fi(name);
return fi.completeBaseName();
}
return QString();
}
void KIconDialog::showDialog()
{
setModal(false);
show();
}
void KIconDialog::slotOk()
{
QString name;
if (!d->custom.isEmpty()) {
name = d->custom;
} else {
name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString();
if (!name.isEmpty() && d->isSystemIconsContext()) {
const QFileInfo fi(name);
name = fi.completeBaseName();
}
}
Q_EMIT newIconName(name);
QDialog::accept();
}
void KIconDialog::showEvent(QShowEvent *event)
{
QDialog::showEvent(event);
d->showIcons();
d->ui.searchLine->setFocus();
}
QString KIconDialog::getIcon(KIconLoader::Group group,
KIconLoader::Context context,
bool strictIconSize,
int iconSize,
bool user,
QWidget *parent,
const QString &title)
{
KIconDialog dlg(parent);
dlg.setup(group, context, strictIconSize, iconSize, user);
if (!title.isEmpty()) {
dlg.setWindowTitle(title);
}
return dlg.openDialog();
}
void KIconDialogPrivate::browse()
{
if (browseDialog) {
browseDialog.data()->show();
browseDialog.data()->raise();
return;
}
// Create a file dialog to select an ICO, PNG, XPM or SVG file,
// with the image previewer shown.
QFileDialog *dlg = new QFileDialog(q, i18n("Select Icon"), QString(), i18n("*.ico *.png *.xpm *.svg *.svgz|Icon Files (*.ico *.png *.xpm *.svg *.svgz)"));
// TODO This was deliberately modal before, why? Or just because "window modal" wasn't a thing?
dlg->setWindowModality(Qt::WindowModal);
dlg->setFileMode(QFileDialog::ExistingFile);
QObject::connect(dlg, &QFileDialog::fileSelected, q, [this](const QString &path) {
if (!path.isEmpty()) {
custom = path;
if (isSystemIconsContext()) {
customLocation = QFileInfo(custom).absolutePath();
}
q->slotOk();
}
});
browseDialog = dlg;
dlg->show();
}
bool KIconDialogPrivate::isSystemIconsContext() const
{
return ui.contextCombo->currentData().isValid();
}
#include "kicondialog.moc"
#include "moc_kicondialog.cpp"
#include "moc_kicondialogmodel_p.cpp"
@@ -0,0 +1,163 @@
/*
This file is part of the KDE project, module kfile.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONDIALOG_H
#define KICONDIALOG_H
#include "kiconwidgets_export.h"
#include <QDialog>
#include <QPushButton>
#include <memory>
#include <kiconloader.h>
/**
* @class KIconDialog kicondialog.h KIconDialog
*
* Dialog for interactive selection of icons. Use the function
* getIcon() to let the user select an icon.
*
* @short An icon selection dialog.
*/
class KICONWIDGETS_EXPORT KIconDialog : public QDialog
{
Q_OBJECT
public:
/**
* Constructs an icon selection dialog using the global icon loader.
*
* @param parent The parent widget.
*/
explicit KIconDialog(QWidget *parent = nullptr);
/**
* Destructs the dialog.
*/
~KIconDialog() override;
/**
* Sets a strict icon size policy for allowed icons.
*
* @param policy When true, only icons of the specified group's
* size in getIcon() are shown.
* When false, icons not available at the desired group's size will
* also be selectable.
*/
void setStrictIconSize(bool policy);
/**
* Returns true if a strict icon size policy is set.
*/
bool strictIconSize() const;
/**
* Sets the location of the custom icon directory. Only local directory
* paths are allowed.
*/
void setCustomLocation(const QString &location);
/**
* Sets the size of the icons to be shown / selected.
* @see KIconLoader::StdSizes
* @see iconSize
*/
void setIconSize(int size);
/**
* Returns the icon size set via setIconSize() or 0, if the default
* icon size will be used.
*/
int iconSize() const;
/**
* Sets the icon that is initially selected in the dialog.
*
* @note Changing this after the dialog has been shown has no effect.
* @note If the given icon cannot be found in the current context,
* no icon will be selected.
* @param iconName The name of the icon to select
* @since 5.89
*/
void setSelectedIcon(const QString &iconName);
/**
* Allows you to set the same parameters as in the class method
* getIcon(), as well as two additional parameters to lock
* the choice between system and user directories and to lock the
* custom icon directory itself.
*/
void setup(KIconLoader::Group group,
KIconLoader::Context context = KIconLoader::Application,
bool strictIconSize = false,
int iconSize = 0,
bool user = false,
bool lockUser = false,
bool lockCustomDir = false);
/**
* exec()utes this modal dialog and returns the name of the selected icon,
* or QString() if the dialog was aborted.
* @returns the name of the icon, suitable for loading with KIconLoader.
* @see getIcon
*/
QString openDialog();
/**
* show()s this dialog and emits a newIconName(const QString&) signal when
* successful. QString() will be emitted if the dialog was aborted.
*/
void showDialog();
/**
* Pops up the dialog an lets the user select an icon.
*
* @param group The icon group this icon is intended for. Providing the
* group shows the icons in the dialog with the same appearance as when
* used outside the dialog.
* @param context The initial icon context. Initially, the icons having
* this context are shown in the dialog. The user can change this.
* @param strictIconSize When true, only icons of the specified group's size
* are shown, otherwise icon not available in the desired group's size
* will also be selectable.
* @param iconSize the size of the icons -- the default of the icon group
* if set to 0
* @param user Begin with the "user icons" instead of "system icons".
* @param parent The parent widget of the dialog.
* @param title The title to use for the dialog.
* @return The name of the icon, suitable for loading with KIconLoader.
*/
static QString getIcon(KIconLoader::Group group = KIconLoader::Desktop,
KIconLoader::Context context = KIconLoader::Application,
bool strictIconSize = false,
int iconSize = 0,
bool user = false,
QWidget *parent = nullptr,
const QString &title = QString());
Q_SIGNALS:
void newIconName(const QString &iconName);
protected:
void showEvent(QShowEvent *event) override;
protected Q_SLOTS:
void slotOk();
private:
std::unique_ptr<class KIconDialogPrivate> const d;
friend class KIconDialogPrivate;
Q_DISABLE_COPY(KIconDialog)
};
#endif // KICONDIALOG_H
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>IconDialog</class>
<widget class="QWidget" name="IconDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Icon</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="contextCombo">
<property name="accessibleName">
<string>Icon category</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="searchLine">
<property name="placeholderText">
<string>Search Icons...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="canvas">
<property name="iconSize">
<size>
<width>60</width>
<height>60</height>
</size>
</property>
<property name="movement">
<enum>QListView::Static</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,74 @@
/*
This file is part of the KDE project, module kfile.
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KICONDIALOG_P_H
#define KICONDIALOG_P_H
#include <QFileDialog>
#include <QPointer>
#include <QSortFilterProxyModel>
#include <kiconloader.h>
#include "kicondialogmodel_p.h"
#include "ui_kicondialog.h"
class QLabel;
class QPushButton;
class KIconDialog;
class KIconDialogSortFilterProxyModel;
class KIconDialogPrivate
{
public:
KIconDialogPrivate(KIconDialog *qq);
void init();
void showIcons();
bool selectIcon(const QString &iconName);
void setContext(KIconLoader::Context context);
void updatePlaceholderLabel();
void browse();
bool isSystemIconsContext() const;
KIconDialog *q;
KIconLoader *mpLoader;
KIconDialogModel *model;
KIconDialogSortFilterProxyModel *proxyModel;
int mGroupOrSize;
KIconLoader::Context mContext;
QLabel *placeholderLabel;
QPushButton *browseButton;
QAction *filterSymbolicAction;
QActionGroup *filterSymbolicGroup;
bool m_bStrictIconSize = true;
bool m_bLockUser = false;
bool m_bLockCustomDir = false;
QString custom;
QString customLocation;
QString pendingSelectedIcon;
QPointer<QFileDialog> browseDialog;
Ui::IconDialog ui;
};
#endif // KICONDIALOG_P_H
@@ -0,0 +1,64 @@
/*
SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KICONDIALOGMODEL_P_H
#define KICONDIALOGMODEL_P_H
#include <QAbstractListModel>
#include <QList>
#include <QPixmap>
#include <QSize>
#include <QString>
#include <QStringList>
class KIconLoader;
struct KIconDialogModelData {
QString name;
QString path;
QPixmap pixmap;
};
Q_DECLARE_TYPEINFO(KIconDialogModelData, Q_RELOCATABLE_TYPE);
class KIconDialogModel : public QAbstractListModel
{
Q_OBJECT
public:
KIconDialogModel(KIconLoader *loader, QObject *parent);
~KIconDialogModel() override;
enum Roles { PathRole = Qt::UserRole };
qreal devicePixelRatio() const;
void setDevicePixelRatio(qreal dpr);
QSize iconSize() const;
void setIconSize(const QSize &iconSize);
static QLatin1String symbolicSuffix();
bool hasSymbolicIcon() const;
void load(const QStringList &paths);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
Q_SIGNALS:
void hasSymbolicIconChanged(bool hasSymbolicIcon);
private:
void loadPixmap(const QModelIndex &index);
QList<KIconDialogModelData> m_data;
KIconLoader *m_loader;
qreal m_dpr = 1;
QSize m_iconSize;
bool m_hasSymbolicIcon = false;
};
#endif // KICONDIALOGMODEL_P_H
@@ -0,0 +1,20 @@
/*
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kpixmapsequenceloader.h"
#include <KIconLoader>
namespace KPixmapSequenceLoader
{
KPixmapSequence load(const QString &iconName, int size)
{
return KPixmapSequence(KIconLoader::global()->iconPath(iconName, -size), size);
}
}
@@ -0,0 +1,29 @@
/*
SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KPIXMAPSEQUENCELOADER_H
#define KPIXMAPSEQUENCELOADER_H
#include <KPixmapSequence>
#include "kiconwidgets_export.h"
namespace KPixmapSequenceLoader
{
/**
* Loads a pixmapSequence given the xdg icon name
*
* @param iconName The name of the icon, without extension.
* @param size the size to be used
* @since 6.0
*/
KICONWIDGETS_EXPORT KPixmapSequence load(const QString &iconName, int size);
};
#endif