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,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