Merge branch 'issue-1573' into calamares
This commit is contained in:
commit
ff66eacd0d
@ -139,32 +139,33 @@ public:
|
|||||||
RunInTarget
|
RunInTarget
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** @brief Runs a command in the host or the target (select explicitly)
|
||||||
* Runs the specified command in the chroot of the target system.
|
*
|
||||||
* @param args the command with arguments, as a string list.
|
* @param location whether to run in the host or the target
|
||||||
* @param workingPath the current working directory for the QProcess
|
* @param args the command with arguments, as a string list.
|
||||||
* call (optional).
|
* @param workingPath the current working directory for the QProcess
|
||||||
* @param stdInput the input string to send to the running process as
|
* call (optional).
|
||||||
* standard input (optional).
|
* @param stdInput the input string to send to the running process as
|
||||||
* @param timeoutSec the timeout after which the process will be
|
* standard input (optional).
|
||||||
* killed (optional, default is 0 i.e. no timeout).
|
* @param timeoutSec the timeout after which the process will be
|
||||||
*
|
* killed (optional, default is 0 i.e. no timeout).
|
||||||
* @returns the program's exit code and its output (if any). Special
|
*
|
||||||
* exit codes (which will never have any output) are:
|
* @returns the program's exit code and its output (if any). Special
|
||||||
* Crashed = QProcess crash
|
* exit codes (which will never have any output) are:
|
||||||
* FailedToStart = QProcess cannot start
|
* Crashed = QProcess crash
|
||||||
* NoWorkingDirectory = bad arguments
|
* FailedToStart = QProcess cannot start
|
||||||
* TimedOut = QProcess timeout
|
* NoWorkingDirectory = bad arguments
|
||||||
*/
|
* TimedOut = QProcess timeout
|
||||||
|
*/
|
||||||
static DLLEXPORT ProcessResult runCommand( RunLocation location,
|
static DLLEXPORT ProcessResult runCommand( RunLocation location,
|
||||||
const QStringList& args,
|
const QStringList& args,
|
||||||
const QString& workingPath = QString(),
|
const QString& workingPath = QString(),
|
||||||
const QString& stdInput = QString(),
|
const QString& stdInput = QString(),
|
||||||
std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) );
|
std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) );
|
||||||
|
|
||||||
/** @brief Convenience wrapper for runCommand()
|
/** @brief Convenience wrapper for runCommand() in the host
|
||||||
*
|
*
|
||||||
* Runs the given command-line @p args in the host in the current direcory
|
* Runs the given command-line @p args in the **host** in the current direcory
|
||||||
* with no input, and the given @p timeoutSec for completion.
|
* with no input, and the given @p timeoutSec for completion.
|
||||||
*/
|
*/
|
||||||
static inline ProcessResult runCommand( const QStringList& args, std::chrono::seconds timeoutSec )
|
static inline ProcessResult runCommand( const QStringList& args, std::chrono::seconds timeoutSec )
|
||||||
@ -173,10 +174,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Convenience wrapper for runCommand().
|
/** @brief Convenience wrapper for runCommand().
|
||||||
* Runs the command in the location specified through the boolean
|
*
|
||||||
* doChroot(), which is what you usually want for running commands
|
* Runs the command in the location specified through the boolean
|
||||||
* during installation.
|
* doChroot(), which is what you usually want for running commands
|
||||||
*/
|
* during installation.
|
||||||
|
*/
|
||||||
inline ProcessResult targetEnvCommand( const QStringList& args,
|
inline ProcessResult targetEnvCommand( const QStringList& args,
|
||||||
const QString& workingPath = QString(),
|
const QString& workingPath = QString(),
|
||||||
const QString& stdInput = QString(),
|
const QString& stdInput = QString(),
|
||||||
|
@ -10,16 +10,18 @@ find_package(ECM ${ECM_VERSION} REQUIRED NO_MODULE)
|
|||||||
set( lnf_ver 5.41 )
|
set( lnf_ver 5.41 )
|
||||||
|
|
||||||
find_package( KF5Config ${lnf_ver} )
|
find_package( KF5Config ${lnf_ver} )
|
||||||
find_package( KF5Plasma ${lnf_ver} )
|
|
||||||
find_package( KF5Package ${lnf_ver} )
|
|
||||||
set_package_properties(
|
set_package_properties(
|
||||||
KF5Config PROPERTIES
|
KF5Config PROPERTIES
|
||||||
PURPOSE "For finding default Plasma Look-and-Feel"
|
PURPOSE "For finding default Plasma Look-and-Feel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package( KF5Plasma ${lnf_ver} )
|
||||||
set_package_properties(
|
set_package_properties(
|
||||||
KF5Plasma PROPERTIES
|
KF5Plasma PROPERTIES
|
||||||
PURPOSE "For Plasma Look-and-Feel selection"
|
PURPOSE "For Plasma Look-and-Feel selection"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package( KF5Package ${lnf_ver} )
|
||||||
set_package_properties(
|
set_package_properties(
|
||||||
KF5Package PROPERTIES
|
KF5Package PROPERTIES
|
||||||
PURPOSE "For Plasma Look-and-Feel selection"
|
PURPOSE "For Plasma Look-and-Feel selection"
|
||||||
@ -32,10 +34,11 @@ if ( KF5Plasma_FOUND AND KF5Package_FOUND )
|
|||||||
COMPILE_DEFINITIONS
|
COMPILE_DEFINITIONS
|
||||||
${option_defs}
|
${option_defs}
|
||||||
SOURCES
|
SOURCES
|
||||||
|
Config.cpp
|
||||||
PlasmaLnfViewStep.cpp
|
PlasmaLnfViewStep.cpp
|
||||||
PlasmaLnfPage.cpp
|
PlasmaLnfPage.cpp
|
||||||
PlasmaLnfJob.cpp
|
PlasmaLnfJob.cpp
|
||||||
ThemeWidget.cpp
|
ThemeInfo.cpp
|
||||||
RESOURCES
|
RESOURCES
|
||||||
page_plasmalnf.qrc
|
page_plasmalnf.qrc
|
||||||
UI
|
UI
|
||||||
|
164
src/modules/plasmalnf/Config.cpp
Normal file
164
src/modules/plasmalnf/Config.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include "PlasmaLnfJob.h"
|
||||||
|
#include "ThemeInfo.h"
|
||||||
|
|
||||||
|
#include "utils/CalamaresUtilsSystem.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Variant.h"
|
||||||
|
|
||||||
|
#ifdef WITH_KCONFIG
|
||||||
|
#include <KConfigGroup>
|
||||||
|
#include <KSharedConfig>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
static QString
|
||||||
|
currentPlasmaTheme()
|
||||||
|
{
|
||||||
|
#ifdef WITH_KCONFIG
|
||||||
|
KConfigGroup cg( KSharedConfig::openConfig( QStringLiteral( "kdeglobals" ) ), "KDE" );
|
||||||
|
return cg.readEntry( "LookAndFeelPackage", QString() );
|
||||||
|
#else
|
||||||
|
cWarning() << "No KConfig support, cannot determine Plasma theme.";
|
||||||
|
return QString();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Config( QObject* parent )
|
||||||
|
: QObject( parent )
|
||||||
|
, m_themeModel( new ThemesModel( this ) )
|
||||||
|
{
|
||||||
|
auto* filter = new QSortFilterProxyModel( m_themeModel );
|
||||||
|
filter->setFilterRole( ThemesModel::ShownRole );
|
||||||
|
filter->setFilterFixedString( QStringLiteral( "true" ) );
|
||||||
|
filter->setSourceModel( m_themeModel );
|
||||||
|
filter->setSortRole( ThemesModel::LabelRole );
|
||||||
|
filter->sort( 0 );
|
||||||
|
|
||||||
|
m_filteredModel = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||||
|
{
|
||||||
|
m_lnfPath = CalamaresUtils::getString( configurationMap, "lnftool" );
|
||||||
|
|
||||||
|
if ( m_lnfPath.isEmpty() )
|
||||||
|
{
|
||||||
|
cWarning() << "no lnftool given for plasmalnf module.";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_liveUser = CalamaresUtils::getString( configurationMap, "liveuser" );
|
||||||
|
|
||||||
|
QString preselect = CalamaresUtils::getString( configurationMap, "preselect" );
|
||||||
|
if ( preselect == QStringLiteral( "*" ) )
|
||||||
|
{
|
||||||
|
preselect = currentPlasmaTheme();
|
||||||
|
}
|
||||||
|
m_preselectThemeId = preselect;
|
||||||
|
|
||||||
|
if ( configurationMap.contains( "themes" ) && configurationMap.value( "themes" ).type() == QVariant::List )
|
||||||
|
{
|
||||||
|
QMap< QString, QString > listedThemes;
|
||||||
|
auto themeList = configurationMap.value( "themes" ).toList();
|
||||||
|
// Create the ThemInfo objects for the listed themes; information
|
||||||
|
// about the themes from Plasma (e.g. human-readable name and description)
|
||||||
|
// are filled in by update_names() in PlasmaLnfPage.
|
||||||
|
for ( const auto& i : themeList )
|
||||||
|
if ( i.type() == QVariant::Map )
|
||||||
|
{
|
||||||
|
auto iv = i.toMap();
|
||||||
|
listedThemes.insert( iv.value( "theme" ).toString(), iv.value( "image" ).toString() );
|
||||||
|
}
|
||||||
|
else if ( i.type() == QVariant::String )
|
||||||
|
{
|
||||||
|
listedThemes.insert( i.toString(), QString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( listedThemes.count() == 1 )
|
||||||
|
{
|
||||||
|
cWarning() << "only one theme enabled in plasmalnf";
|
||||||
|
}
|
||||||
|
m_themeModel->setThemeImage( listedThemes );
|
||||||
|
|
||||||
|
bool showAll = CalamaresUtils::getBool( configurationMap, "showAll", false );
|
||||||
|
if ( !listedThemes.isEmpty() && !showAll )
|
||||||
|
{
|
||||||
|
m_themeModel->showOnlyThemes( listedThemes );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_themeModel->select( m_preselectThemeId );
|
||||||
|
}
|
||||||
|
|
||||||
|
Calamares::JobList
|
||||||
|
Config::createJobs() const
|
||||||
|
{
|
||||||
|
Calamares::JobList l;
|
||||||
|
|
||||||
|
cDebug() << "Creating Plasma LNF jobs ..";
|
||||||
|
if ( !theme().isEmpty() )
|
||||||
|
{
|
||||||
|
if ( !lnfToolPath().isEmpty() )
|
||||||
|
{
|
||||||
|
l.append( Calamares::job_ptr( new PlasmaLnfJob( lnfToolPath(), theme() ) ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cWarning() << "no lnftool given for plasmalnf module.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setTheme( const QString& id )
|
||||||
|
{
|
||||||
|
if ( m_themeId == id )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_themeId = id;
|
||||||
|
if ( lnfToolPath().isEmpty() )
|
||||||
|
{
|
||||||
|
cWarning() << "no lnftool given for plasmalnf module.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QStringList command;
|
||||||
|
if ( !m_liveUser.isEmpty() )
|
||||||
|
{
|
||||||
|
command << "sudo"
|
||||||
|
<< "-E"
|
||||||
|
<< "-H"
|
||||||
|
<< "-u" << m_liveUser;
|
||||||
|
}
|
||||||
|
command << lnfToolPath() << "--resetLayout"
|
||||||
|
<< "--apply" << id;
|
||||||
|
auto r = CalamaresUtils::System::instance()->runCommand( command, std::chrono::seconds( 10 ) );
|
||||||
|
|
||||||
|
if ( r.getExitCode() )
|
||||||
|
{
|
||||||
|
cWarning() << "Failed (" << r.getExitCode() << ')';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cDebug() << "Plasma look-and-feel applied" << id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_themeModel->select( id );
|
||||||
|
emit themeChanged( id );
|
||||||
|
}
|
77
src/modules/plasmalnf/Config.h
Normal file
77
src/modules/plasmalnf/Config.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PLASMALNF_CONFIG_H
|
||||||
|
#define PLASMALNF_CONFIG_H
|
||||||
|
|
||||||
|
#include "Job.h"
|
||||||
|
|
||||||
|
#include "ThemeInfo.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class Config : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY( QString preselectedTheme READ preselectedTheme CONSTANT )
|
||||||
|
Q_PROPERTY( QString theme READ theme WRITE setTheme NOTIFY themeChanged )
|
||||||
|
Q_PROPERTY( QAbstractItemModel* themeModel READ themeModel CONSTANT )
|
||||||
|
|
||||||
|
public:
|
||||||
|
Config( QObject* parent = nullptr );
|
||||||
|
virtual ~Config() override = default; // QObject cleans up the model pointer
|
||||||
|
|
||||||
|
void setConfigurationMap( const QVariantMap& );
|
||||||
|
Calamares::JobList createJobs() const;
|
||||||
|
|
||||||
|
/** @brief Full path to the lookandfeeltool (if it exists)
|
||||||
|
*
|
||||||
|
* This can be configured, or defaults to `lookandfeeltool` to find
|
||||||
|
* it in $PATH.
|
||||||
|
*/
|
||||||
|
QString lnfToolPath() const { return m_lnfPath; }
|
||||||
|
/** @brief For OEM mode, the name of the (current) live user
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString liveUser() const { return m_liveUser; }
|
||||||
|
|
||||||
|
/** @brief The id (in reverse-DNS notation) of the current theme.
|
||||||
|
*/
|
||||||
|
QString theme() const { return m_themeId; }
|
||||||
|
|
||||||
|
/** @brief The theme we start with
|
||||||
|
*
|
||||||
|
* This can be configured, or is taken from the live environment
|
||||||
|
* if the environment is (also) KDE Plasma.
|
||||||
|
*/
|
||||||
|
QString preselectedTheme() const { return m_preselectThemeId; }
|
||||||
|
|
||||||
|
/** @brief The (list) model of available themes.
|
||||||
|
*/
|
||||||
|
QAbstractItemModel* themeModel() const { return m_filteredModel; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setTheme( const QString& id );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void themeChanged( const QString& id );
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_lnfPath; // Path to the lnf tool
|
||||||
|
QString m_liveUser; // Name of the live user (for OEM mode)
|
||||||
|
|
||||||
|
QString m_preselectThemeId;
|
||||||
|
QString m_themeId; // Id of selected theme
|
||||||
|
|
||||||
|
QAbstractItemModel* m_filteredModel = nullptr;
|
||||||
|
ThemesModel* m_themeModel = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -10,49 +10,67 @@
|
|||||||
|
|
||||||
#include "PlasmaLnfPage.h"
|
#include "PlasmaLnfPage.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
#include "ui_page_plasmalnf.h"
|
#include "ui_page_plasmalnf.h"
|
||||||
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QHeaderView>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
#include <KPackage/Package>
|
class ThemeDelegate : public QStyledItemDelegate
|
||||||
#include <KPackage/PackageLoader>
|
|
||||||
|
|
||||||
ThemeInfo::ThemeInfo( const KPluginMetaData& data )
|
|
||||||
: id( data.pluginId() )
|
|
||||||
, name( data.name() )
|
|
||||||
, description( data.description() )
|
|
||||||
, widget( nullptr )
|
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
using QStyledItemDelegate::QStyledItemDelegate;
|
||||||
|
|
||||||
|
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const override;
|
||||||
|
// The size of the item is constant
|
||||||
|
QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSize
|
||||||
|
ThemeDelegate::sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const
|
||||||
|
{
|
||||||
|
QSize image( ThemesModel::imageSize() );
|
||||||
|
return { 3 * image.width(), image.height() };
|
||||||
}
|
}
|
||||||
|
|
||||||
static ThemeInfoList
|
void
|
||||||
plasma_themes()
|
ThemeDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
|
||||||
{
|
{
|
||||||
ThemeInfoList packages;
|
auto label = index.data( ThemesModel::LabelRole ).toString();
|
||||||
|
auto description = index.data( ThemesModel::DescriptionRole ).toString();
|
||||||
|
auto selected = index.data( ThemesModel::SelectedRole ).toBool() ? QStyle::State_On : QStyle::State_Off;
|
||||||
|
auto image_v = index.data( ThemesModel::ImageRole );
|
||||||
|
QPixmap image = image_v.canConvert< QPixmap >() ? qvariant_cast< QPixmap >( image_v ) : QPixmap();
|
||||||
|
|
||||||
QList< KPluginMetaData > pkgs = KPackage::PackageLoader::self()->listPackages( "Plasma/LookAndFeel" );
|
// The delegate paints three "columns", each of which takes 1/3
|
||||||
|
// of the space: label, description and screenshot.
|
||||||
|
QRect labelRect( option.rect );
|
||||||
|
labelRect.setWidth( labelRect.width() / 3 );
|
||||||
|
|
||||||
for ( const KPluginMetaData& data : pkgs )
|
QStyleOptionButton rbOption;
|
||||||
{
|
rbOption.state |= QStyle::State_Enabled | selected;
|
||||||
if ( data.isValid() && !data.isHidden() && !data.name().isEmpty() )
|
rbOption.rect = labelRect;
|
||||||
{
|
rbOption.text = label;
|
||||||
packages << ThemeInfo { data };
|
option.widget->style()->drawControl( QStyle::CE_RadioButton, &rbOption, painter, option.widget );
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return packages;
|
labelRect.moveLeft( labelRect.width() );
|
||||||
|
option.widget->style()->drawItemText(
|
||||||
|
painter, labelRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, option.palette, false, description );
|
||||||
|
|
||||||
|
labelRect.moveLeft( 2 * labelRect.width() );
|
||||||
|
option.widget->style()->drawItemPixmap( painter, labelRect, Qt::AlignHCenter | Qt::AlignVCenter, image );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PlasmaLnfPage::PlasmaLnfPage( QWidget* parent )
|
PlasmaLnfPage::PlasmaLnfPage( Config* config, QWidget* parent )
|
||||||
: QWidget( parent )
|
: QWidget( parent )
|
||||||
, ui( new Ui::PlasmaLnfPage )
|
, ui( new Ui::PlasmaLnfPage )
|
||||||
, m_showAll( false )
|
, m_config( config )
|
||||||
, m_buttonGroup( nullptr )
|
|
||||||
{
|
{
|
||||||
ui->setupUi( this );
|
ui->setupUi( this );
|
||||||
CALAMARES_RETRANSLATE( {
|
CALAMARES_RETRANSLATE( {
|
||||||
@ -67,134 +85,28 @@ PlasmaLnfPage::PlasmaLnfPage( QWidget* parent )
|
|||||||
"You can also skip this step and configure the look-and-feel "
|
"You can also skip this step and configure the look-and-feel "
|
||||||
"once the system is installed. Clicking on a look-and-feel "
|
"once the system is installed. Clicking on a look-and-feel "
|
||||||
"selection will give you a live preview of that look-and-feel." ) );
|
"selection will give you a live preview of that look-and-feel." ) );
|
||||||
updateThemeNames();
|
|
||||||
fillUi();
|
|
||||||
} )
|
} )
|
||||||
}
|
|
||||||
|
auto* view = new QListView( this );
|
||||||
void
|
view->setModel( m_config->themeModel() );
|
||||||
PlasmaLnfPage::setLnfPath( const QString& path )
|
view->setItemDelegate( new ThemeDelegate( view ) );
|
||||||
{
|
view->setUniformItemSizes( true );
|
||||||
m_lnfPath = path;
|
view->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
||||||
}
|
ui->verticalLayout->addWidget( view );
|
||||||
|
|
||||||
void
|
connect( view->selectionModel(),
|
||||||
PlasmaLnfPage::setEnabledThemes( const ThemeInfoList& themes, bool showAll )
|
&QItemSelectionModel::selectionChanged,
|
||||||
{
|
[this]( const QItemSelection& selected, const QItemSelection& ) {
|
||||||
m_enabledThemes = themes;
|
auto i = selected.indexes();
|
||||||
|
if ( !i.isEmpty() )
|
||||||
if ( showAll )
|
{
|
||||||
{
|
auto row = i.first().row();
|
||||||
auto plasmaThemes = plasma_themes();
|
auto* model = m_config->themeModel();
|
||||||
for ( auto& installed_theme : plasmaThemes )
|
auto id = model->data( model->index( row, 0 ), ThemesModel::KeyRole ).toString();
|
||||||
if ( !m_enabledThemes.findById( installed_theme.id ) )
|
if ( !id.isEmpty() )
|
||||||
{
|
{
|
||||||
m_enabledThemes.append( installed_theme );
|
m_config->setTheme( id );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} );
|
||||||
updateThemeNames();
|
|
||||||
winnowThemes();
|
|
||||||
fillUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfPage::setEnabledThemesAll()
|
|
||||||
{
|
|
||||||
// Don't need to set showAll=true, because we're already passing in
|
|
||||||
// the complete list of installed themes.
|
|
||||||
setEnabledThemes( plasma_themes(), false );
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfPage::setPreselect( const QString& id )
|
|
||||||
{
|
|
||||||
m_preselect = id;
|
|
||||||
if ( !m_enabledThemes.isEmpty() )
|
|
||||||
{
|
|
||||||
fillUi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfPage::updateThemeNames()
|
|
||||||
{
|
|
||||||
auto plasmaThemes = plasma_themes();
|
|
||||||
for ( auto& enabled_theme : m_enabledThemes )
|
|
||||||
{
|
|
||||||
ThemeInfo* t = plasmaThemes.findById( enabled_theme.id );
|
|
||||||
if ( t != nullptr )
|
|
||||||
{
|
|
||||||
enabled_theme.name = t->name;
|
|
||||||
enabled_theme.description = t->description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfPage::winnowThemes()
|
|
||||||
{
|
|
||||||
auto plasmaThemes = plasma_themes();
|
|
||||||
bool winnowed = true;
|
|
||||||
int winnow_index = 0;
|
|
||||||
while ( winnowed )
|
|
||||||
{
|
|
||||||
winnowed = false;
|
|
||||||
winnow_index = 0;
|
|
||||||
|
|
||||||
for ( auto& enabled_theme : m_enabledThemes )
|
|
||||||
{
|
|
||||||
ThemeInfo* t = plasmaThemes.findById( enabled_theme.id );
|
|
||||||
if ( t == nullptr )
|
|
||||||
{
|
|
||||||
cDebug() << "Removing" << enabled_theme.id;
|
|
||||||
winnowed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++winnow_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( winnowed )
|
|
||||||
{
|
|
||||||
m_enabledThemes.removeAt( winnow_index );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfPage::fillUi()
|
|
||||||
{
|
|
||||||
if ( m_enabledThemes.isEmpty() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !m_buttonGroup )
|
|
||||||
{
|
|
||||||
m_buttonGroup = new QButtonGroup( this );
|
|
||||||
m_buttonGroup->setExclusive( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
int c = 1; // After the general explanation
|
|
||||||
for ( auto& theme : m_enabledThemes )
|
|
||||||
{
|
|
||||||
if ( !theme.widget )
|
|
||||||
{
|
|
||||||
ThemeWidget* w = new ThemeWidget( theme );
|
|
||||||
m_buttonGroup->addButton( w->button() );
|
|
||||||
ui->verticalLayout->insertWidget( c, w );
|
|
||||||
connect( w, &ThemeWidget::themeSelected, this, &PlasmaLnfPage::plasmaThemeSelected );
|
|
||||||
theme.widget = w;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
theme.widget->updateThemeName( theme );
|
|
||||||
}
|
|
||||||
if ( theme.id == m_preselect )
|
|
||||||
{
|
|
||||||
const QSignalBlocker b( theme.widget->button() );
|
|
||||||
theme.widget->button()->setChecked( true );
|
|
||||||
}
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ namespace Ui
|
|||||||
class PlasmaLnfPage;
|
class PlasmaLnfPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Config;
|
||||||
|
|
||||||
/** @brief Page for selecting a Plasma Look-and-Feel theme.
|
/** @brief Page for selecting a Plasma Look-and-Feel theme.
|
||||||
*
|
*
|
||||||
* You must call setEnabledThemes -- either overload -- once
|
* You must call setEnabledThemes -- either overload -- once
|
||||||
@ -34,40 +36,11 @@ class PlasmaLnfPage : public QWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit PlasmaLnfPage( QWidget* parent = nullptr );
|
explicit PlasmaLnfPage( Config* config, QWidget* parent = nullptr );
|
||||||
|
|
||||||
void setLnfPath( const QString& path );
|
|
||||||
/** @brief enable only the listed themes.
|
|
||||||
*
|
|
||||||
* Shows the listed @p themes with full information (e.g. screenshot).
|
|
||||||
* If @p showAll is true, then also show all installed themes
|
|
||||||
* not explicitly listed (without a screenshot).
|
|
||||||
*/
|
|
||||||
void setEnabledThemes( const ThemeInfoList& themes, bool showAll );
|
|
||||||
/** @brief enable all installed plasma themes. */
|
|
||||||
void setEnabledThemesAll();
|
|
||||||
/** @brief set which theme is to be preselected. */
|
|
||||||
void setPreselect( const QString& id );
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void plasmaThemeSelected( const QString& id );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** @brief Intersect the list of enabled themes with the installed ones. */
|
|
||||||
void winnowThemes();
|
|
||||||
/** @brief Get the translated names for all enabled themes. */
|
|
||||||
void updateThemeNames();
|
|
||||||
/** @brief show enabled themes in the UI. */
|
|
||||||
void fillUi();
|
|
||||||
|
|
||||||
Ui::PlasmaLnfPage* ui;
|
Ui::PlasmaLnfPage* ui;
|
||||||
QString m_lnfPath;
|
Config* m_config;
|
||||||
QString m_preselect;
|
|
||||||
bool m_showAll; // If true, don't winnow according to enabledThemes
|
|
||||||
ThemeInfoList m_enabledThemes;
|
|
||||||
|
|
||||||
QButtonGroup* m_buttonGroup;
|
|
||||||
QList< ThemeWidget* > m_widgets;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //PLASMALNFPAGE_H
|
#endif //PLASMALNFPAGE_H
|
||||||
|
@ -8,40 +8,22 @@
|
|||||||
*/
|
*/
|
||||||
#include "PlasmaLnfViewStep.h"
|
#include "PlasmaLnfViewStep.h"
|
||||||
|
|
||||||
#include "PlasmaLnfJob.h"
|
#include "Config.h"
|
||||||
#include "PlasmaLnfPage.h"
|
#include "PlasmaLnfPage.h"
|
||||||
#include "ThemeInfo.h"
|
#include "ThemeInfo.h"
|
||||||
|
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Variant.h"
|
#include "utils/Variant.h"
|
||||||
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
#ifdef WITH_KCONFIG
|
|
||||||
#include <KConfigGroup>
|
|
||||||
#include <KSharedConfig>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( PlasmaLnfViewStepFactory, registerPlugin< PlasmaLnfViewStep >(); )
|
CALAMARES_PLUGIN_FACTORY_DEFINITION( PlasmaLnfViewStepFactory, registerPlugin< PlasmaLnfViewStep >(); )
|
||||||
|
|
||||||
static QString
|
|
||||||
currentPlasmaTheme()
|
|
||||||
{
|
|
||||||
#ifdef WITH_KCONFIG
|
|
||||||
KConfigGroup cg( KSharedConfig::openConfig( QStringLiteral( "kdeglobals" ) ), "KDE" );
|
|
||||||
return cg.readEntry( "LookAndFeelPackage", QString() );
|
|
||||||
#else
|
|
||||||
cWarning() << "No KConfig support, cannot determine Plasma theme.";
|
|
||||||
return QString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaLnfViewStep::PlasmaLnfViewStep( QObject* parent )
|
PlasmaLnfViewStep::PlasmaLnfViewStep( QObject* parent )
|
||||||
: Calamares::ViewStep( parent )
|
: Calamares::ViewStep( parent )
|
||||||
, m_widget( new PlasmaLnfPage )
|
, m_config( new Config( this ) )
|
||||||
|
, m_widget( new PlasmaLnfPage( m_config ) )
|
||||||
{
|
{
|
||||||
connect( m_widget, &PlasmaLnfPage::plasmaThemeSelected, this, &PlasmaLnfViewStep::themeSelected );
|
|
||||||
emit nextStatusChanged( false );
|
emit nextStatusChanged( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,112 +88,12 @@ PlasmaLnfViewStep::onLeave()
|
|||||||
Calamares::JobList
|
Calamares::JobList
|
||||||
PlasmaLnfViewStep::jobs() const
|
PlasmaLnfViewStep::jobs() const
|
||||||
{
|
{
|
||||||
Calamares::JobList l;
|
return m_config->createJobs();
|
||||||
|
|
||||||
cDebug() << "Creating Plasma LNF jobs ..";
|
|
||||||
if ( !m_themeId.isEmpty() )
|
|
||||||
{
|
|
||||||
if ( !m_lnfPath.isEmpty() )
|
|
||||||
{
|
|
||||||
l.append( Calamares::job_ptr( new PlasmaLnfJob( m_lnfPath, m_themeId ) ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cWarning() << "no lnftool given for plasmalnf module.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PlasmaLnfViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
PlasmaLnfViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||||
{
|
{
|
||||||
m_lnfPath = CalamaresUtils::getString( configurationMap, "lnftool" );
|
m_config->setConfigurationMap( configurationMap );
|
||||||
m_widget->setLnfPath( m_lnfPath );
|
|
||||||
|
|
||||||
if ( m_lnfPath.isEmpty() )
|
|
||||||
{
|
|
||||||
cWarning() << "no lnftool given for plasmalnf module.";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_liveUser = CalamaresUtils::getString( configurationMap, "liveuser" );
|
|
||||||
|
|
||||||
QString preselect = CalamaresUtils::getString( configurationMap, "preselect" );
|
|
||||||
if ( preselect == QStringLiteral( "*" ) )
|
|
||||||
{
|
|
||||||
preselect = currentPlasmaTheme();
|
|
||||||
}
|
|
||||||
if ( !preselect.isEmpty() )
|
|
||||||
{
|
|
||||||
m_widget->setPreselect( preselect );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showAll = CalamaresUtils::getBool( configurationMap, "showAll", false );
|
|
||||||
|
|
||||||
if ( configurationMap.contains( "themes" ) && configurationMap.value( "themes" ).type() == QVariant::List )
|
|
||||||
{
|
|
||||||
ThemeInfoList listedThemes;
|
|
||||||
auto themeList = configurationMap.value( "themes" ).toList();
|
|
||||||
// Create the ThemInfo objects for the listed themes; information
|
|
||||||
// about the themes from Plasma (e.g. human-readable name and description)
|
|
||||||
// are filled in by update_names() in PlasmaLnfPage.
|
|
||||||
for ( const auto& i : themeList )
|
|
||||||
if ( i.type() == QVariant::Map )
|
|
||||||
{
|
|
||||||
auto iv = i.toMap();
|
|
||||||
listedThemes.append( ThemeInfo( iv.value( "theme" ).toString(), iv.value( "image" ).toString() ) );
|
|
||||||
}
|
|
||||||
else if ( i.type() == QVariant::String )
|
|
||||||
{
|
|
||||||
listedThemes.append( ThemeInfo( i.toString() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( listedThemes.length() == 1 )
|
|
||||||
{
|
|
||||||
cWarning() << "only one theme enabled in plasmalnf";
|
|
||||||
}
|
|
||||||
m_widget->setEnabledThemes( listedThemes, showAll );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_widget->setEnabledThemesAll(); // All of them
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PlasmaLnfViewStep::themeSelected( const QString& id )
|
|
||||||
{
|
|
||||||
m_themeId = id;
|
|
||||||
if ( m_lnfPath.isEmpty() )
|
|
||||||
{
|
|
||||||
cWarning() << "no lnftool given for plasmalnf module.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QProcess lnftool;
|
|
||||||
if ( !m_liveUser.isEmpty() )
|
|
||||||
lnftool.start( "sudo", { "-E", "-H", "-u", m_liveUser, m_lnfPath, "--resetLayout", "--apply", id } );
|
|
||||||
else
|
|
||||||
lnftool.start( m_lnfPath, { "--resetLayout", "--apply", id } );
|
|
||||||
|
|
||||||
if ( !lnftool.waitForStarted( 1000 ) )
|
|
||||||
{
|
|
||||||
cWarning() << "could not start look-and-feel" << m_lnfPath;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( !lnftool.waitForFinished() )
|
|
||||||
{
|
|
||||||
cWarning() << m_lnfPath << "timed out.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ( lnftool.exitCode() == 0 ) && ( lnftool.exitStatus() == QProcess::NormalExit ) )
|
|
||||||
{
|
|
||||||
cDebug() << "Plasma look-and-feel applied" << id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cWarning() << "could not apply look-and-feel" << id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "utils/PluginFactory.h"
|
#include "utils/PluginFactory.h"
|
||||||
#include "viewpages/ViewStep.h"
|
#include "viewpages/ViewStep.h"
|
||||||
|
|
||||||
|
class Config;
|
||||||
class PlasmaLnfPage;
|
class PlasmaLnfPage;
|
||||||
|
|
||||||
class PLUGINDLLEXPORT PlasmaLnfViewStep : public Calamares::ViewStep
|
class PLUGINDLLEXPORT PlasmaLnfViewStep : public Calamares::ViewStep
|
||||||
@ -40,14 +41,9 @@ public:
|
|||||||
|
|
||||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||||
|
|
||||||
public slots:
|
|
||||||
void themeSelected( const QString& id );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Config* m_config;
|
||||||
PlasmaLnfPage* m_widget;
|
PlasmaLnfPage* m_widget;
|
||||||
QString m_lnfPath; // Path to the lnf tool
|
|
||||||
QString m_themeId; // Id of selected theme
|
|
||||||
QString m_liveUser; // Name of the live user (for OEM mode)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( PlasmaLnfViewStepFactory )
|
CALAMARES_PLUGIN_FACTORY_DECLARATION( PlasmaLnfViewStepFactory )
|
||||||
|
319
src/modules/plasmalnf/ThemeInfo.cpp
Normal file
319
src/modules/plasmalnf/ThemeInfo.cpp
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "ThemeInfo.h"
|
||||||
|
|
||||||
|
#include "Branding.h"
|
||||||
|
#include "utils/CalamaresUtilsGui.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
#include <KPackage/Package>
|
||||||
|
#include <KPackage/PackageLoader>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
/** @brief describes a single plasma LnF theme.
|
||||||
|
*
|
||||||
|
* A theme description has an id, which is really the name of the desktop
|
||||||
|
* file (e.g. org.kde.breeze.desktop), a name which is human-readable and
|
||||||
|
* translated, and an optional image Page, which points to a local screenshot
|
||||||
|
* of that theme.
|
||||||
|
*/
|
||||||
|
struct ThemeInfo
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
QString name;
|
||||||
|
QString description;
|
||||||
|
QString imagePath;
|
||||||
|
mutable QPixmap pixmap;
|
||||||
|
bool show = true;
|
||||||
|
bool selected = false;
|
||||||
|
|
||||||
|
ThemeInfo() {}
|
||||||
|
|
||||||
|
explicit ThemeInfo( const QString& _id )
|
||||||
|
: id( _id )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ThemeInfo( const QString& _id, const QString& image )
|
||||||
|
: id( _id )
|
||||||
|
, imagePath( image )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ThemeInfo( const KPluginMetaData& );
|
||||||
|
|
||||||
|
bool isValid() const { return !id.isEmpty(); }
|
||||||
|
|
||||||
|
/// @brief Fill in the pixmap member based on imagePath
|
||||||
|
QPixmap loadImage() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThemeInfoList : public QList< ThemeInfo >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::pair< int, const ThemeInfo* > indexById( const QString& id ) const
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for ( const ThemeInfo& i : *this )
|
||||||
|
{
|
||||||
|
if ( i.id == id )
|
||||||
|
{
|
||||||
|
return { index, &i };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { -1, nullptr };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair< int, ThemeInfo* > indexById( const QString& id )
|
||||||
|
{
|
||||||
|
// Call the const version and then munge the types
|
||||||
|
auto [ i, p ] = const_cast< const ThemeInfoList* >( this )->indexById( id );
|
||||||
|
return { i, const_cast< ThemeInfo* >( p ) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */
|
||||||
|
ThemeInfo* findById( const QString& id )
|
||||||
|
{
|
||||||
|
auto [ i, p ] = indexById( id );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */
|
||||||
|
const ThemeInfo* findById( const QString& id ) const
|
||||||
|
{
|
||||||
|
auto [ i, p ] = indexById( id );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Checks if a given @p id is in the list of themes. */
|
||||||
|
bool contains( const QString& id ) const { return findById( id ) != nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
ThemesModel::ThemesModel( QObject* parent )
|
||||||
|
: QAbstractListModel( parent )
|
||||||
|
, m_themes( new ThemeInfoList )
|
||||||
|
{
|
||||||
|
auto packages = KPackage::PackageLoader::self()->listPackages( "Plasma/LookAndFeel" );
|
||||||
|
m_themes->reserve( packages.length() );
|
||||||
|
|
||||||
|
for ( const auto& p : packages )
|
||||||
|
{
|
||||||
|
m_themes->append( ThemeInfo { p } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ThemesModel::rowCount( const QModelIndex& ) const
|
||||||
|
{
|
||||||
|
return m_themes->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant
|
||||||
|
ThemesModel::data( const QModelIndex& index, int role ) const
|
||||||
|
{
|
||||||
|
if ( !index.isValid() )
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
if ( index.row() < 0 || index.row() >= m_themes->count() )
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& item = m_themes->at( index.row() );
|
||||||
|
switch ( role )
|
||||||
|
{
|
||||||
|
case LabelRole:
|
||||||
|
return item.name;
|
||||||
|
case KeyRole:
|
||||||
|
return item.id;
|
||||||
|
case ShownRole:
|
||||||
|
return item.show;
|
||||||
|
case SelectedRole:
|
||||||
|
return item.selected;
|
||||||
|
case DescriptionRole:
|
||||||
|
return item.description;
|
||||||
|
case ImageRole:
|
||||||
|
return item.loadImage();
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash< int, QByteArray >
|
||||||
|
ThemesModel::roleNames() const
|
||||||
|
{
|
||||||
|
return { { LabelRole, "label" },
|
||||||
|
{ KeyRole, "key" },
|
||||||
|
{ SelectedRole, "selected" },
|
||||||
|
{ ShownRole, "show" },
|
||||||
|
{ ImageRole, "image" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThemesModel::setThemeImage( const QString& id, const QString& imagePath )
|
||||||
|
{
|
||||||
|
auto [ i, theme ] = m_themes->indexById( id );
|
||||||
|
if ( theme )
|
||||||
|
{
|
||||||
|
theme->imagePath = imagePath;
|
||||||
|
emit dataChanged( index( i, 0 ), index( i, 0 ), { ImageRole } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThemesModel::setThemeImage( const QMap< QString, QString >& images )
|
||||||
|
{
|
||||||
|
if ( m_themes->isEmpty() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't emit signals from each call, aggregate to one call (below this block)
|
||||||
|
{
|
||||||
|
QSignalBlocker b( this );
|
||||||
|
for ( auto k = images.constKeyValueBegin(); k != images.constKeyValueEnd(); ++k )
|
||||||
|
{
|
||||||
|
setThemeImage( k->first, k->second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit dataChanged( index( 0, 0 ), index( m_themes->count() - 1 ), { ImageRole } );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThemesModel::showTheme( const QString& id, bool show )
|
||||||
|
{
|
||||||
|
auto [ i, theme ] = m_themes->indexById( id );
|
||||||
|
if ( theme )
|
||||||
|
{
|
||||||
|
theme->show = show;
|
||||||
|
emit dataChanged( index( i, 0 ), index( i, 0 ), { ShownRole } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThemesModel::showOnlyThemes( const QMap< QString, QString >& onlyThese )
|
||||||
|
{
|
||||||
|
if ( m_themes->isEmpty() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No signal blocker block needed here because we're not calling showTheme()
|
||||||
|
// QSignalBlocker b( this );
|
||||||
|
for ( auto& t : *m_themes )
|
||||||
|
{
|
||||||
|
t.show = onlyThese.contains( t.id );
|
||||||
|
}
|
||||||
|
emit dataChanged( index( 0, 0 ), index( m_themes->count() - 1 ), { ShownRole } );
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize
|
||||||
|
ThemesModel::imageSize()
|
||||||
|
{
|
||||||
|
return { qMax( 12 * CalamaresUtils::defaultFontHeight(), 120 ),
|
||||||
|
qMax( 8 * CalamaresUtils::defaultFontHeight(), 80 ) };
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThemesModel::select( const QString& themeId )
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for ( auto& t : *m_themes )
|
||||||
|
{
|
||||||
|
if ( t.selected && t.id != themeId )
|
||||||
|
{
|
||||||
|
t.selected = false;
|
||||||
|
emit dataChanged( index( i, 0 ), index( i, 0 ), { SelectedRole } );
|
||||||
|
}
|
||||||
|
if ( !t.selected && t.id == themeId )
|
||||||
|
{
|
||||||
|
t.selected = true;
|
||||||
|
emit dataChanged( index( i, 0 ), index( i, 0 ), { SelectedRole } );
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Massage the given @p path to the most-likely
|
||||||
|
* path that actually contains a screenshot. For
|
||||||
|
* empty image paths, returns the QRC path for an
|
||||||
|
* empty screenshot. Returns blank if the path
|
||||||
|
* doesn't exist anywhere in the search paths.
|
||||||
|
*/
|
||||||
|
static QString
|
||||||
|
munge_imagepath( const QString& path )
|
||||||
|
{
|
||||||
|
if ( path.isEmpty() )
|
||||||
|
{
|
||||||
|
return ":/view-preview.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( path.startsWith( '/' ) )
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( QFileInfo::exists( path ) )
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fi( QDir( Calamares::Branding::instance()->componentDirectory() ), path );
|
||||||
|
if ( fi.exists() )
|
||||||
|
{
|
||||||
|
return fi.absoluteFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeInfo::ThemeInfo( const KPluginMetaData& data )
|
||||||
|
: id( data.pluginId() )
|
||||||
|
, name( data.name() )
|
||||||
|
, description( data.description() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap
|
||||||
|
ThemeInfo::loadImage() const
|
||||||
|
{
|
||||||
|
if ( pixmap.isNull() )
|
||||||
|
{
|
||||||
|
|
||||||
|
const QSize image_size( ThemesModel::imageSize() );
|
||||||
|
|
||||||
|
const QString path = munge_imagepath( imagePath );
|
||||||
|
cDebug() << "Loading initial image for" << id << imagePath << "->" << path;
|
||||||
|
QPixmap image( path );
|
||||||
|
if ( image.isNull() )
|
||||||
|
{
|
||||||
|
// Not found or not specified, so convert the name into some (horrible, likely)
|
||||||
|
// color instead.
|
||||||
|
image = QPixmap( image_size );
|
||||||
|
auto hash_color = qHash( imagePath.isEmpty() ? id : imagePath );
|
||||||
|
cDebug() << Logger::SubEntry << "Theme image" << imagePath << "not found, hash" << hash_color;
|
||||||
|
image.fill( QColor( QRgb( hash_color ) ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cDebug() << Logger::SubEntry << "Theme image" << image.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
pixmap = image.scaled( image_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
|
||||||
|
}
|
||||||
|
return pixmap;
|
||||||
|
}
|
@ -10,82 +10,66 @@
|
|||||||
#ifndef PLASMALNF_THEMEINFO_H
|
#ifndef PLASMALNF_THEMEINFO_H
|
||||||
#define PLASMALNF_THEMEINFO_H
|
#define PLASMALNF_THEMEINFO_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class KPluginMetaData;
|
class ThemeInfoList;
|
||||||
class ThemeWidget;
|
|
||||||
|
|
||||||
/** @brief describes a single plasma LnF theme.
|
class ThemesModel : public QAbstractListModel
|
||||||
*
|
|
||||||
* A theme description has an id, which is really the name of the desktop
|
|
||||||
* file (e.g. org.kde.breeze.desktop), a name which is human-readable and
|
|
||||||
* translated, and an optional image Page, which points to a local screenshot
|
|
||||||
* of that theme.
|
|
||||||
*/
|
|
||||||
struct ThemeInfo
|
|
||||||
{
|
{
|
||||||
QString id;
|
Q_OBJECT
|
||||||
QString name;
|
|
||||||
QString description;
|
|
||||||
QString imagePath;
|
|
||||||
ThemeWidget* widget;
|
|
||||||
|
|
||||||
ThemeInfo()
|
|
||||||
: widget( nullptr )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit ThemeInfo( const QString& _id )
|
|
||||||
: id( _id )
|
|
||||||
, widget( nullptr )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit ThemeInfo( const QString& _id, const QString& image )
|
|
||||||
: id( _id )
|
|
||||||
, imagePath( image )
|
|
||||||
, widget( nullptr )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defined in PlasmaLnfPage.cpp
|
|
||||||
explicit ThemeInfo( const KPluginMetaData& );
|
|
||||||
|
|
||||||
bool isValid() const { return !id.isEmpty(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThemeInfoList : public QList< ThemeInfo >
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */
|
enum
|
||||||
ThemeInfo* findById( const QString& id )
|
|
||||||
{
|
{
|
||||||
for ( ThemeInfo& i : *this )
|
LabelRole = Qt::DisplayRole,
|
||||||
{
|
KeyRole = Qt::UserRole,
|
||||||
if ( i.id == id )
|
ShownRole, // Should theme be displayed
|
||||||
{
|
SelectedRole, // Is theme selected
|
||||||
return &i;
|
DescriptionRole,
|
||||||
}
|
ImageRole
|
||||||
}
|
};
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */
|
explicit ThemesModel( QObject* parent );
|
||||||
const ThemeInfo* findById( const QString& id ) const
|
|
||||||
{
|
|
||||||
for ( const ThemeInfo& i : *this )
|
|
||||||
{
|
|
||||||
if ( i.id == id )
|
|
||||||
{
|
|
||||||
return &i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Checks if a given @p id is in the list of themes. */
|
int rowCount( const QModelIndex& = QModelIndex() ) const override;
|
||||||
bool contains( const QString& id ) const { return findById( id ) != nullptr; }
|
QVariant data( const QModelIndex& index, int role ) const override;
|
||||||
|
|
||||||
|
QHash< int, QByteArray > roleNames() const override;
|
||||||
|
|
||||||
|
/// @brief Set the screenshot to go with the given @p id
|
||||||
|
void setThemeImage( const QString& id, const QString& imagePath );
|
||||||
|
|
||||||
|
/// @brief Call setThemeImage( key, value ) for all keys in @p images
|
||||||
|
void setThemeImage( const QMap< QString, QString >& images );
|
||||||
|
|
||||||
|
/// @brief Set whether to show the given theme @p id (or not)
|
||||||
|
void showTheme( const QString& id, bool show = true );
|
||||||
|
|
||||||
|
/// @brief Shows the keys in the @p onlyThese map, and hides the rest
|
||||||
|
void showOnlyThemes( const QMap< QString, QString >& onlyThese );
|
||||||
|
|
||||||
|
/** @brief Mark the @p themeId as current / selected
|
||||||
|
*
|
||||||
|
* One theme can be selected at a time; this will emit data
|
||||||
|
* changed signals for any (one) theme already selected, and
|
||||||
|
* the newly-selected theme. If @p themeId does not name any
|
||||||
|
* theme, none are selected.
|
||||||
|
*/
|
||||||
|
void select( const QString& themeId );
|
||||||
|
|
||||||
|
/** @brief The size of theme Images
|
||||||
|
*
|
||||||
|
* The size is dependent on the font size used by Calamares,
|
||||||
|
* and is constant within one run of Calamares, but may change
|
||||||
|
* if the font settings do between runs.
|
||||||
|
*/
|
||||||
|
static QSize imageSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThemeInfoList* m_themes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,19 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -24,10 +24,18 @@ lnftool: "/usr/bin/lookandfeeltool"
|
|||||||
#
|
#
|
||||||
# liveuser: "live"
|
# liveuser: "live"
|
||||||
|
|
||||||
|
# If *showAll* is true, then all installed themes are shown in the
|
||||||
|
# UI for selection, even if they are not listed in *themes* (below).
|
||||||
|
# This allows selection of all themes even while not all of them are
|
||||||
|
# listed in *themes* -- which is useful to show screenshots for those
|
||||||
|
# you do have a screenshot for. If *themes* is empty or missing,
|
||||||
|
# the value of *showAll* is treated as `true`.
|
||||||
|
showAll: false
|
||||||
|
|
||||||
# You can limit the list of Plasma look-and-feel themes by listing ids
|
# You can limit the list of Plasma look-and-feel themes by listing ids
|
||||||
# here. If this key is not present, all of the installed themes are listed.
|
# here. If this key is not present, all of the installed themes are listed.
|
||||||
# If the key is present, only installed themes that are **also** included
|
# If the key is present, only installed themes that are **also** included
|
||||||
# in the list are shown (could be none!). See the *showAll* key, below,
|
# in the list are shown (could be none!). See the *showAll* key, above,
|
||||||
# to change that.
|
# to change that.
|
||||||
#
|
#
|
||||||
# Themes may be listed by id, (e.g. fluffy-bunny, below) or as a theme
|
# Themes may be listed by id, (e.g. fluffy-bunny, below) or as a theme
|
||||||
@ -58,13 +66,6 @@ themes:
|
|||||||
image: "breeze-dark.png"
|
image: "breeze-dark.png"
|
||||||
- org.kde.fluffy-bunny.desktop
|
- org.kde.fluffy-bunny.desktop
|
||||||
|
|
||||||
# If *showAll* is true, then all installed themes are shown in the
|
|
||||||
# UI for selection, even if they are not listed in *themes*. This
|
|
||||||
# allows selection of all themes even while not all of them are
|
|
||||||
# listed in *themes* -- which is useful to show screenshots for those
|
|
||||||
# you do have a screenshot for.
|
|
||||||
showAll: false
|
|
||||||
|
|
||||||
# You can pre-select one of the themes; it is not applied
|
# You can pre-select one of the themes; it is not applied
|
||||||
# immediately, but its radio-button is switched on to indicate
|
# immediately, but its radio-button is switched on to indicate
|
||||||
# that that is the theme (that is most likely) currently in use.
|
# that that is the theme (that is most likely) currently in use.
|
||||||
|
Loading…
Reference in New Issue
Block a user