Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 6c5869af authored by Eike Hein's avatar Eike Hein

Port Language KCM to Qt Quick

Summary:
* Changed the overall design from two lists to one list with a modal
  sheet to add more languages.
* Replaced a modal "You need to relogin for changes" dialog with a
  MessageType.Positive InlineMessage.
* Reworked the way missing languages are handled: The old KCM silently
  rewrote config and showed a warning. The new design shows an
  informative warning and removes the missing languages on the next
  save. Until then they're flagged as missing in the list.
* Manages Apply button state correctly (or rather at all ...).

This depends on D12097.

This implements T7247.

This is currently not final code. It's a WIP upload to give Marco
something to work with to fix various Kirigami and SimpleKCM problems.

Currently known issues:
* Does not save (code is from old KCM, might have been broken there)
* Disabled SwipeListItem actions do not show disabled
* Placement of actions button in SwipeListItem is wonky if the
  contentItem is a RowLayout
* SwipeListItem spews errors about positionAnimation after using an
  action
* SwipeListItem is awkward to use, we need a drag-reorderable list
  delegate
* OverlaySheet spews numerous warnings about not being able to find
  applicationWindow and activeFocusItem
* The sheet is parented to the SimpleKCM's parent since there's no
  app window to be modal too
* The footer inside an OverlaySheet sometimes moves up above the
  content instead of staying down
* Even though SimpleKCM is just a Kirigami.ScrollablePage like
  Kirigami Gallery pages, an InlineMessage that fills the page width
  gets cut off on the left and right, so wonky code to insert margins
  next to them
* List has window bg color as background instead of view background
  color

Reviewers: #kirigami, mart

Subscribers: plasma-devel

Tags: #plasma, #kirigami

Differential Revision: https://phabricator.kde.org/D12102
parent 55234f0d
include(ECMQMLModules)
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
# KI18N Translation Domain for this library.
add_definitions(-DTRANSLATION_DOMAIN=\"kcmtranslations\")
set(kcm_translations_PART_SRCS kcmtranslations.cpp)
########### next target ###############
ki18n_wrap_ui(kcm_translations_PART_SRCS kcmtranslationswidget.ui)
set(kcm_translations_PART_SRCS translations.cpp translationsmodel.cpp)
add_library(kcm_translations ${kcm_translations_PART_SRCS})
add_library(kcm_translations MODULE ${kcm_translations_PART_SRCS})
target_link_libraries(kcm_translations
Qt5::Widgets
KF5::WidgetsAddons
KF5::KCMUtils
KF5::I18n
KF5::KCMUtils
KF5::QuickAddons
)
kcoreaddons_desktop_to_json(kcm_translations "kcm_translations.desktop")
########### install files ###############
install(TARGETS kcm_translations DESTINATION ${KDE_INSTALL_PLUGINDIR})
install(FILES translations.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(TARGETS kcm_translations DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install(FILES kcm_translations.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
kpackage_install_package(package kcm_translations kcms)
#! /usr/bin/env bash
$EXTRACTRC *.ui >> rc.cpp
$XGETTEXT -ktranslate:1,1t -ktranslate:1c,2,2t *.cpp -o $podir/kcmtranslations.pot
rm -f rc.cpp
$XGETTEXT `find . -name \*.cpp -o -name \*.qml` -o $podir/kcmtranslations.pot
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KCMTranslationsWidget</class>
<widget class="QWidget" name="KCMTranslationsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>333</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="KActionSelector" name="m_selectTranslations">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="availableLabel">
<string>Available &amp;Translations:</string>
</property>
<property name="selectedLabel">
<string>Preferred Trans&amp;lations:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_buttonTranslationsInstall">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Install more translations</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>
<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>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KActionSelector</class>
<extends>QWidget</extends>
<header>kactionselector.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
/*
* Copyright (C) 2015 Marco Martin <mart@kde.org>
* Copyright (C) 2018 Eike Hein <hein@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
import QtQuick 2.1
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3 as QtControls
import org.kde.kirigami 2.4 as Kirigami
import org.kde.plasma.core 2.1 as PlasmaCore
import org.kde.kcm 1.1
SimpleKCM {
id: root
ConfigModule.quickHelp: i18n("Language")
topPadding: 0
leftPadding: 0
rightPadding: 0
bottomPadding: 0
Kirigami.OverlaySheet {
id: addLanguagesSheet
parent: root.parent
topPadding: 0
leftPadding: 0
rightPadding: 0
bottomPadding: 0
header: Kirigami.Heading { text: i18n("Add Languages") }
property var selectedLanguages: []
ListView {
implicitWidth: Kirigami.Units.gridUnit * 18
implicitHeight: Kirigami.Units.gridUnit * 15
model: PlasmaCore.SortFilterModel {
id: availableLanguagesModel
sourceModel: addLanguagesSheet.sheetOpen ? kcm.translationsModel : null
filterRole: "IsSelected"
filterCallback: function(source_row, value) { return !value; }
sortRole: "display"
}
delegate: Kirigami.BasicListItem {
Layout.fillWidth: true
property string languageCode: model.LanguageCode
reserveSpaceForIcon: false
label: model.display
checkable: true
onCheckedChanged: {
if (checked) {
addLanguagesSheet.selectedLanguages.push(index);
} else {
addLanguagesSheet.selectedLanguages = addLanguagesSheet.selectedLanguages.filter(function(item) { return item !== index })
}
}
}
}
footer: QtControls.Button {
width: parent.width
text: i18n("Add")
onClicked: {
var langs = [];
addLanguagesSheet.selectedLanguages.sort().forEach(function(index) {
langs.push(availableLanguagesModel.get(index).LanguageCode);
});
if (addLanguagesSheet.selectedLanguages.length) {
if (kcm.translationsModel.selectedLanguages.length) {
kcm.translationsModel.selectedLanguages = kcm.translationsModel.selectedLanguages.concat(langs);
} else {
kcm.translationsModel.selectedLanguages = langs;
}
}
addLanguagesSheet.sheetOpen = false;
}
}
}
header: QtControls.Control {
width: parent.width
height: messagesLayout.implicitHeight + (messagesLayout.implicitHeight ? bottomPadding : 0)
bottomPadding: Kirigami.Units.smallSpacing
ColumnLayout {
id: messagesLayout
width: parent.width
spacing: Kirigami.Units.largeSpacing
Kirigami.InlineMessage {
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
type: kcm.everSaved ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information
text: (kcm.everSaved ? i18n("Your changes will take effect the next time you log in.")
: i18n("There are currently no preferred languages configured."))
visible: !languagesList.count || kcm.everSaved
}
Kirigami.InlineMessage {
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
type: Kirigami.MessageType.Error
text: i18ncp("%2 is the language code",
"The translation files for the language with the code '%2' could not be found. The language will be removed from your configuration. If you want to add it back, please install the localization files for it and add the language again.",
"The translation files for the languages with the codes '%2' could not be found. These languages will be removed from your configuration. If you want to add them back, please install the localization files for it and the languages again.",
kcm.translationsModel.missingLanguages.length,
kcm.translationsModel.missingLanguages.join("', '"))
visible: kcm.translationsModel.missingLanguages.length
}
}
}
ListView {
id: languagesList
Layout.fillWidth: true
model: PlasmaCore.SortFilterModel {
sourceModel: kcm.translationsModel
filterRole: "IsSelected"
filterCallback: function(source_row, value) { return value; }
sortRole: "SelectedPriority"
}
delegate: Kirigami.SwipeListItem {
id: listItem
Layout.fillWidth: true
Layout.margins: 0
contentItem: RowLayout {
width: implicitWidth
height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium)
anchors.verticalCenter: parent.verticalCenter
Kirigami.Icon {
visible: model.IsMissing
Layout.alignment: Qt.AlignVCenter
width: Kirigami.Units.iconSizes.smallMedium
height: width
source: "error"
color: Kirigami.Theme.negativeTextColor
}
QtControls.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
text: model.display
color: (model.IsMissing ? Kirigami.Theme.negativeTextColor
: (listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate)
? listItem.activeTextColor : listItem.textColor))
}
}
actions: [
Kirigami.Action {
enabled: !model.IsMissing && index > 0
iconName: "arrow-up"
onTriggered: kcm.translationsModel.selectedLanguages.splice(index - 1, 0, kcm.translationsModel.selectedLanguages.splice(index, 1)[0])
},
Kirigami.Action {
enabled: !model.IsMissing && index < (languagesList.count - 1)
iconName: "arrow-down"
onTriggered: kcm.translationsModel.selectedLanguages.splice(index + 1, 0, kcm.translationsModel.selectedLanguages.splice(index, 1)[0])
},
Kirigami.Action {
enabled: !model.IsMissing
iconName: "list-remove"
onTriggered: kcm.translationsModel.selectedLanguages = kcm.translationsModel.selectedLanguages.filter(function(item) { return item !== model.LanguageCode })
}]
}
}
footer: QtControls.Control {
width: parent.width
height: footerLayout.implicitHeight + topPadding
topPadding: Kirigami.Units.smallSpacing
leftPadding: Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.smallSpacing
RowLayout {
id: footerLayout
width: parent.width
QtControls.Button {
Layout.alignment: Qt.AlignHCenter
text: i18n("Add languages...")
onClicked: addLanguagesSheet.sheetOpen = !addLanguagesSheet.sheetOpen
checkable: true
checked: addLanguagesSheet.sheetOpen
}
}
}
}
[Desktop Entry]
Name=Language
Name[ca]=Idioma
Name[ca@valencia]=Idioma
Name[cs]=Jazyk
Name[da]=Sprog
Name[de]=Sprache
Name[el]=Γλώσσα
Name[en_GB]=Language
Name[es]=Idioma
Name[et]=Keel
Name[eu]=Hizkuntza
Name[fi]=Kieli
Name[fr]=Langage
Name[gl]=Idioma
Name[he]=שפה
Name[hu]=Nyelv
Name[id]=Bahasa
Name[it]=Lingua
Name[ja]=言語
Name[ko]=언어
Name[lt]=Kalba
Name[nl]=Taal
Name[nn]=Språk
Name[pa]=ਭਾਸ਼ਾ
Name[pl]=Język
Name[pt]=Língua
Name[pt_BR]=Idioma
Name[ru]=Язык
Name[sk]=Jazyk
Name[sl]=Jezik
Name[sr]=Језик
Name[sr@ijekavian]=Језик
Name[sr@ijekavianlatin]=Jezik
Name[sr@latin]=Jezik
Name[sv]=Språk
Name[tr]=Dil
Name[uk]=Мова
Name[x-test]=xxLanguagexx
Name[zh_CN]=语言
Name[zh_TW]=語言
Icon=preferences-desktop-locale
Type=Service
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-Name=kcm_translations
X-KDE-ServiceTypes=Plasma/Generic
X-Plasma-API=declarativeappletscript
X-Plasma-MainScript=ui/main.qml
/*
* Copyright (C) 2014 John Layt <john@layt.net>
* Copyright (C) 2018 Eike Hein <hein@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "translations.h"
#include "translationsmodel.h"
#include "../formats/writeexports.h"
#include <KAboutData>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KSharedConfig>
K_PLUGIN_FACTORY_WITH_JSON(TranslationsFactory, "kcm_translations.json", registerPlugin<Translations>();)
Translations::Translations(QObject *parent, const QVariantList &args)
: KQuickAddons::ConfigModule(parent, args)
, m_translationsModel(new TranslationsModel(this))
, m_everSaved(false)
{
KAboutData *about = new KAboutData("kcm_translations",
i18n("Configure Plasma translations"),
"2.0", QString(), KAboutLicense::LGPL);
setAboutData(about);
setButtons(Apply | Default);
m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations");
connect(m_translationsModel, &TranslationsModel::selectedLanguagesChanged,
this, &Translations::selectedLanguagesChanged);
connect(m_translationsModel, &TranslationsModel::missingLanguagesChanged,
this, &Translations::missingLanguagesChanged);
}
Translations::~Translations()
{
}
QAbstractItemModel* Translations::translationsModel() const
{
return m_translationsModel;
}
bool Translations::everSaved() const
{
return m_everSaved;
}
void Translations::load()
{
m_configuredLanguages = m_config.readEntry(lcLanguage,
QString()).split(':', QString::SkipEmptyParts);
m_translationsModel->setSelectedLanguages(m_configuredLanguages);
}
void Translations::save()
{
m_everSaved = true;
emit everSavedChanged();
m_configuredLanguages = m_translationsModel->selectedLanguages();
qDebug() << m_configuredLanguages;
for (const QString& lang : m_translationsModel->missingLanguages()) {
m_configuredLanguages.removeOne(lang);
}
m_config.writeEntry(lcLanguage, m_configuredLanguages.join(QStringLiteral(":")),
KConfig::Persistent | KConfig::Global);
m_config.sync();
m_translationsModel->setSelectedLanguages(m_configuredLanguages);
}
void Translations::defaults()
{
KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats");
QString lang = formatsConfig.readEntry("LANG", QString());
if (lang.isEmpty()
|| !KLocalizedString::availableDomainTranslations("systemsettings").contains(lang)) {
lang = QLocale::system().name();
}
if (!KLocalizedString::availableDomainTranslations("systemsettings").contains(lang)) {
lang = QStringLiteral("en_US");
}
QStringList languages;
languages << lang;
m_translationsModel->setSelectedLanguages(languages);
}
void Translations::selectedLanguagesChanged()
{
setNeedsSave(m_configuredLanguages != m_translationsModel->selectedLanguages());
}
void Translations::missingLanguagesChanged()
{
if (m_translationsModel->missingLanguages().count()) {
setNeedsSave(true);
}
}
#include "translations.moc"
/* This file is part of the KDE's Plasma workspace
* Copyright 2014 John Layt <john@layt.net>
/*
* Copyright (C) 2014 John Layt <john@layt.net>
* Copyright (C) 2018 Eike Hein <hein@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -17,68 +18,51 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef KCMTRANSLATIONS_H
#define KCMTRANSLATIONS_H
#ifndef TRANSLATIONS_H
#define TRANSLATIONS_H
#include <KQuickAddons/ConfigModule>
#include <KCModule>
#include <KSharedConfig>
#include <KConfigGroup>
class QListWidgetItem;
class KMessageWidget;
class TranslationsModel;
namespace Ui
{
class KCMTranslationsWidget;
}
class QAbstractListModel;
/**
* @short A KCM to configure KDE Gui Translations
*
* This module is for changing the User's Gui Translations settings.
*/
class KCMTranslations : public KCModule
class Translations : public KQuickAddons::ConfigModule
{
Q_OBJECT
public:
KCMTranslations(QWidget *parent, const QVariantList &);
~KCMTranslations() override;
void load() override;
void save() override;
void defaults() override;
QString quickHelp() const override;
Q_PROPERTY(QAbstractItemModel* translationsModel READ translationsModel CONSTANT)
Q_PROPERTY(bool everSaved READ everSaved NOTIFY everSavedChanged)
private Q_SLOTS:
public:
explicit Translations(QObject* parent = 0, const QVariantList &list = QVariantList());
~Translations() override;
void changedTranslationsAvailable(QListWidgetItem *item);
void changedTranslationsSelected(QListWidgetItem *item);
QAbstractItemModel* translationsModel() const;
void installTranslations();
bool everSaved() const;
private:
public Q_SLOTS:
void load();
void save();
void defaults();
void loadTranslations();
void changedTranslations();
Q_SIGNALS:
void everSavedChanged() const;
void initWidgets();
void initTranslations();
void initTranslationsInstall();
private Q_SLOTS:
void selectedLanguagesChanged();
void missingLanguagesChanged();
// The list of translations currently set in the KCM
QStringList m_kcmTranslations;
// The currently saved list of user translations, used to check if value changed
QString m_configTranslations;
// The currently installed translations, used to check if users translations are valid
QStringList m_installedTranslations;
private:
TranslationsModel *m_translationsModel;
KConfigGroup m_config;
KConfigGroup m_config;
QStringList m_configuredLanguages;
Ui::KCMTranslationsWidget *m_ui;
KMessageWidget *m_messageWidget;
bool m_everSaved;
};
#endif //KCMTRANSLATIONS_H
#endif
/*
* Copyright (C) 2014 John Layt <john@layt.net>
* Copyright (C) 2018 Eike Hein <hein@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "translationsmodel.h"
#include <KLocalizedString>
#include <QDebug>