diff --git a/applets/icontasks/metadata.desktop b/applets/icontasks/metadata.desktop --- a/applets/icontasks/metadata.desktop +++ b/applets/icontasks/metadata.desktop @@ -18,12 +18,13 @@ Name[gl]=Xestor de tarefas de só iconas Name[he]=מנהל משימות של סמלים בלבד Name[hu]=Ikonos feladatkezelő -Name[id]=Pengelola Tugas Hanya Ikon +Name[id]=Task Manager Hanya Ikon Name[is]=Verkefnastjóri einungis með táknmyndum Name[it]=Gestore dei processi solo icone Name[ja]=アイコンだけのタスクマネージャ Name[ko]=아이콘만 있는 작업 관리자 Name[lt]=Tik piktogramų užduočių tvarkytuvė +Name[ml]=ചിഹ്നങ്ങൾക്കായുള്ള ടാസ്ക് മാനേജര്‍ Name[mr]=फक्त-चिन्ह कार्य व्यवस्थापक Name[nb]=Oppgavebehandler med bare ikoner Name[nds]=Lüttbild-Opgavenpleger diff --git a/applets/kicker/package/metadata.desktop b/applets/kicker/package/metadata.desktop --- a/applets/kicker/package/metadata.desktop +++ b/applets/kicker/package/metadata.desktop @@ -26,6 +26,7 @@ Name[ja]=アプリケーションメニュー Name[ko]=프로그램 메뉴 Name[lt]=Programų meniu +Name[ml]=പ്രയോഗസൂചിക Name[mr]=अनुप्रयोग मेन्यु Name[nb]=Programmeny Name[nds]=Programmmenü diff --git a/applets/kickoff/package/contents/config/main.xml b/applets/kickoff/package/contents/config/main.xml --- a/applets/kickoff/package/contents/config/main.xml +++ b/applets/kickoff/package/contents/config/main.xml @@ -12,7 +12,7 @@ - false + true diff --git a/applets/kickoff/package/contents/ui/Header.qml b/applets/kickoff/package/contents/ui/Header.qml --- a/applets/kickoff/package/contents/ui/Header.qml +++ b/applets/kickoff/package/contents/ui/Header.qml @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import QtQuick 2.0 +import QtQuick 2.12 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras diff --git a/applets/kimpanel/backend/ibus/CMakeLists.txt b/applets/kimpanel/backend/ibus/CMakeLists.txt --- a/applets/kimpanel/backend/ibus/CMakeLists.txt +++ b/applets/kimpanel/backend/ibus/CMakeLists.txt @@ -4,41 +4,37 @@ find_package(GObject) if(IBUS_FOUND AND GLIB2_FOUND AND GIO_FOUND AND GOBJECT_FOUND) - configure_file(config-kimpanel.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kimpanel.h) - include_directories(${IBUS_INCLUDE_DIR}) - include_directories(${GIO_INCLUDE_DIR}) - include_directories(${GOBJECT_INCLUDE_DIR}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") + include_directories(${IBUS_INCLUDE_DIR} ${GLIB2_INCLUDE_DIR}) + add_subdirectory(emojier) + configure_file(config-kimpanel.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kimpanel.h) find_package(Qt5X11Extras) find_package(XCB COMPONENTS XCB KEYSYMS) - add_subdirectory(emojier) - if (Qt5X11Extras_FOUND AND XCB_XCB_FOUND AND XCB_KEYSYMS_FOUND) + include_directories(${GIO_INCLUDE_DIR}) + include_directories(${GOBJECT_INCLUDE_DIR}) include_directories(${XCB_XCB_INCLUDE_DIRS}) include_directories(${XCB_KEYSYMS_INCLUDE_DIRS}) - set(kimpanel_ibus_panel_SRCS + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") + add_executable(kimpanel-ibus-panel ibus15/xkblayoutmanager.cpp ibus15/gtkaccelparse_p.c ibus15/gdkkeynames_p.c ibus15/app.cpp ibus15/enginemanager.cpp ibus15/main.cpp ibus15/panel.cpp ibus15/propertymanager.cpp) - add_definitions(-DQT_NO_KEYWORDS) - add_executable(kimpanel-ibus-panel ${kimpanel_ibus_panel_SRCS}) + target_compile_definitions(kimpanel-ibus-panel PRIVATE -DQT_NO_KEYWORDS) target_link_libraries(kimpanel-ibus-panel ${IBUS_LIBRARIES} GLIB2::GLIB2 ${GIO_LIBRARIES} ${GOBJECT_LIBRARIES} Qt5::Core Qt5::DBus Qt5::Gui Qt5::X11Extras XCB::KEYSYMS) # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kimpanel.xml.in ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml @ONLY) # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ibus/component) target_include_directories(kimpanel-ibus-panel PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) add_executable(kimpanel-ibus-panel-launcher launcher.cpp) - set_target_properties(kimpanel-ibus-panel-launcher PROPERTIES AUTOMOC TRUE) target_link_libraries(kimpanel-ibus-panel-launcher Qt5::Core Qt5::DBus) - target_include_directories(kimpanel-ibus-panel-launcher PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) install(TARGETS kimpanel-ibus-panel kimpanel-ibus-panel-launcher DESTINATION ${KDE_INSTALL_LIBEXECDIR}) endif() endif() diff --git a/applets/kimpanel/backend/ibus/README b/applets/kimpanel/backend/ibus/README --- a/applets/kimpanel/backend/ibus/README +++ b/applets/kimpanel/backend/ibus/README @@ -2,4 +2,4 @@ ibus>=1.1.0 is needed, not 0.1.1 version START: -ibus-daemon --panel=//panel.py +ibus-daemon --panel=/...prefix.../lib/libexec/kimpanel-ibus-panel diff --git a/applets/kimpanel/backend/ibus/emojier/emojier.cpp b/applets/kimpanel/backend/ibus/emojier/emojier.cpp --- a/applets/kimpanel/backend/ibus/emojier/emojier.cpp +++ b/applets/kimpanel/backend/ibus/emojier/emojier.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "emojiersettings.h" #include "config-workspace.h" @@ -129,35 +130,56 @@ EmojiModel() { QLocale locale; - const QString dictName = "ibus/dicts/emoji-" + locale.bcp47Name().replace(QLatin1Char('-'), QLatin1Char('_')) + ".dict"; + const auto bcp = locale.bcp47Name(); + const QString dictName = "ibus/dicts/emoji-" + QString(bcp).replace(QLatin1Char('-'), QLatin1Char('_')) + ".dict"; const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, dictName); if (path.isEmpty()) { qWarning() << "could not find" << dictName; return; } - GSList *list = ibus_emoji_data_load (path.toUtf8().constData()); - m_emoji.reserve(g_slist_length(list)); + const QString genericDictName = "ibus/dicts/emoji-" + bcp.left(bcp.indexOf(QLatin1Char('-'))) + ".dict"; + const QString genericPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, genericDictName); + + QVector dicts = {path}; + if (!genericPath.isEmpty()) { + dicts << genericPath; + } + QSet categories; - for (GSList *l = list; l; l = l->next) { - IBusEmojiData *data = (IBusEmojiData *) l->data; - if (!IBUS_IS_EMOJI_DATA (data)) { - qWarning() << "Your dict format is no longer supported.\n" - "Need to create the dictionaries again."; - g_slist_free (list); - return; + QSet processedEmoji; + for (const auto &dictPath : qAsConst(dicts)) { + GSList *list = ibus_emoji_data_load (dictPath.toUtf8().constData()); + m_emoji.reserve(g_slist_length(list)); + for (GSList *l = list; l; l = l->next) { + IBusEmojiData *data = (IBusEmojiData *) l->data; + if (!IBUS_IS_EMOJI_DATA (data)) { + qWarning() << "Your dict format is no longer supported.\n" + "Need to create the dictionaries again."; + g_slist_free (list); + return; + } + + const QString emoji = QString::fromUtf8(ibus_emoji_data_get_emoji(data)); + const QString description = ibus_emoji_data_get_description(data); + qDebug() << "ooo" << dictPath << emoji << description << processedEmoji.contains(emoji); + if (description == QString::fromUtf8("↑↑↑") || description.isEmpty() || processedEmoji.contains(emoji)) { + continue; + } + + const QString category = QString::fromUtf8(ibus_emoji_data_get_category(data)); + categories.insert(category); + m_emoji += { emoji, description, category }; + processedEmoji << emoji; } - - const QString category = QString::fromUtf8(ibus_emoji_data_get_category(data)); - categories.insert(category); - m_emoji += { QString::fromUtf8(ibus_emoji_data_get_emoji(data)), ibus_emoji_data_get_description(data), category }; + g_slist_free (list); } + categories.remove({}); m_categories = categories.values(); m_categories.sort(); m_categories.prepend({}); m_categories.prepend(QStringLiteral(":recent:")); - g_slist_free (list); } Q_SCRIPTABLE QString findFirstEmojiForCategory(const QString &category) { @@ -306,6 +328,7 @@ int main(int argc, char** argv) { + QGuiApplication::setFallbackSessionManagementEnabled(false); QApplication app(argc, argv); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-emoticons"))); @@ -322,6 +345,12 @@ about.setProgramLogo(app.windowIcon()); KAboutData::setApplicationData(about); + auto disableSessionManagement = [](QSessionManager &sm) { + sm.setRestartHint(QSessionManager::RestartNever); + }; + QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); + QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); + KDBusService::StartupOptions startup = nullptr; { QCommandLineParser parser; diff --git a/applets/kimpanel/backend/ibus/ibus15/README b/applets/kimpanel/backend/ibus/ibus15/README deleted file mode 100644 --- a/applets/kimpanel/backend/ibus/ibus15/README +++ /dev/null @@ -1,5 +0,0 @@ -WARINGING: -ibus>=1.1.0 is needed, not 0.1.1 version - -START: -ibus-daemon --panel=//panel.py diff --git a/applets/kimpanel/backend/ibus/ibus15/panel.cpp b/applets/kimpanel/backend/ibus/ibus15/panel.cpp --- a/applets/kimpanel/backend/ibus/ibus15/panel.cpp +++ b/applets/kimpanel/backend/ibus/ibus15/panel.cpp @@ -535,8 +535,7 @@ IBusEngineDesc** engines = ibus_bus_get_engines_by_names(impanel->bus, engine_names); impanel->engineManager->setEngines(engines); - - if (engines && engines[0] && !contains(engine_names, ibus_engine_desc_get_name(ibus_bus_get_global_engine(impanel->bus)))) { + if (engines && engines[0] && (!ibus_bus_get_global_engine(impanel->bus) || !contains(engine_names, ibus_engine_desc_get_name(ibus_bus_get_global_engine(impanel->bus))))) { ibus_bus_set_global_engine(impanel->bus, ibus_engine_desc_get_name(engines[0])); } g_strfreev(engine_names); diff --git a/applets/minimizeall/package/metadata.desktop b/applets/minimizeall/package/metadata.desktop --- a/applets/minimizeall/package/metadata.desktop +++ b/applets/minimizeall/package/metadata.desktop @@ -16,6 +16,7 @@ Name[it]=Minimizza tutte le finestre Name[ko]=모든 창 최소화 Name[lt]=Suskleisti visus langus +Name[ml]=എല്ലാ ജാലകങ്ങളും ചെറുതാക്കുക Name[nl]=Alle vensters minimaliseren Name[nn]=Minimer alle vindauge Name[pl]=Minimalizacja wszystkich okien diff --git a/applets/showActivityManager/package/metadata.desktop b/applets/showActivityManager/package/metadata.desktop --- a/applets/showActivityManager/package/metadata.desktop +++ b/applets/showActivityManager/package/metadata.desktop @@ -32,6 +32,7 @@ Name[ko]=활동 Name[lt]=Veiklos Name[lv]=Aktivitātes +Name[ml]=പ്രവര്‍ത്തികള്‍ Name[mr]=कार्यपध्दती Name[nb]=Aktiviteter Name[nds]=Aktiviteten diff --git a/applets/showdesktop/package/metadata.desktop b/applets/showdesktop/package/metadata.desktop --- a/applets/showdesktop/package/metadata.desktop +++ b/applets/showdesktop/package/metadata.desktop @@ -16,6 +16,7 @@ Name[it]=Mostra il desktop Name[ko]=바탕 화면 표시 Name[lt]=Rodyti darbalaukį +Name[ml]=പണിയിടം കാണിക്കുക Name[nl]=Bureaublad tonen Name[nn]=Vis skrivebord Name[pl]=Pokaż pulpit @@ -47,6 +48,7 @@ Comment[it]=Mostra il desktop di Plasma Comment[ko]=Plasma 바탕 화면 표시 Comment[lt]=Rodyti Plasma darbalaukį +Comment[ml]=പ്ലാസ്മ പണിയിടം കാണിക്കുക Comment[nl]=Toont het Plasma-bureaublad Comment[nn]=Vis Plasma-skrivebordet Comment[pl]=Pokaż pulpit Plazmy diff --git a/applets/taskmanager/package/contents/ui/ConfigBehavior.qml b/applets/taskmanager/package/contents/ui/ConfigBehavior.qml --- a/applets/taskmanager/package/contents/ui/ConfigBehavior.qml +++ b/applets/taskmanager/package/contents/ui/ConfigBehavior.qml @@ -48,7 +48,6 @@ // TODO: port to QQC2 version once we've fixed https://bugs.kde.org/show_bug.cgi?id=403153 QQC1.ComboBox { id: groupingStrategy - visible: (plasmoid.pluginName !== "org.kde.plasma.icontasks") Kirigami.FormData.label: i18n("Group:") Layout.fillWidth: true model: [i18n("Do not group"), i18n("By program name")] @@ -144,7 +143,6 @@ CheckBox { id: showOnlyMinimized - visible: (plasmoid.pluginName !== "org.kde.plasma.icontasks") text: i18n("Show only tasks that are minimized") } } diff --git a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml --- a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml +++ b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml @@ -132,15 +132,22 @@ } // Count badge. - Badge { + Item { Layout.alignment: Qt.AlignRight | Qt.AlignTop - height: units.iconSizes.smallMedium - visible: flatIndex === 0 && smartLauncherCountVisible - number: smartLauncherCount + Layout.preferredHeight: closeButton.height + Layout.preferredWidth: closeButton.width + + Badge { + anchors.centerIn: parent + height: units.iconSizes.smallMedium + visible: flatIndex === 0 && smartLauncherCountVisible + number: smartLauncherCount + } } // close button PlasmaComponents.ToolButton { + id: closeButton Layout.alignment: Qt.AlignRight | Qt.AlignTop visible: isWin iconSource: "window-close" @@ -171,8 +178,15 @@ // TODO: this causes XCB error message when being visible the first time property int winId: isWin && windows[flatIndex] !== undefined ? windows[flatIndex] : 0 + PlasmaComponents.Highlight { + anchors.fill: parent + visible: hoverHandler.containsMouse + pressed: hoverHandler.containsPress + } + PlasmaCore.WindowThumbnail { anchors.fill: parent + anchors.margins: units.smallSpacing visible: !albumArtImage.visible && !thumbnailSourceItem.isMinimized winId: thumbnailSourceItem.winId @@ -220,6 +234,7 @@ } ToolTipWindowMouseArea { + id: hoverHandler anchors.fill: parent rootTask: parentTask modelIndex: submodelIndex diff --git a/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml b/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml --- a/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml +++ b/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml @@ -29,6 +29,7 @@ property Item rootTask acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton + cursorShape: Qt.PointingHandCursor hoverEnabled: true enabled: winId != 0 diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml --- a/applets/taskmanager/package/contents/ui/main.qml +++ b/applets/taskmanager/package/contents/ui/main.qml @@ -138,8 +138,7 @@ return true; } - groupMode: iconsOnly ? TaskManager.TasksModel.GroupApplications - : groupModeEnumValue(plasmoid.configuration.groupingStrategy) + groupMode: groupModeEnumValue(plasmoid.configuration.groupingStrategy) groupInline: !plasmoid.configuration.groupPopups groupingWindowTasksThreshold: (plasmoid.configuration.onlyGroupWhenFull && !iconsOnly ? LayoutManager.optimumCapacity(width, height) + 1 : -1) diff --git a/applets/taskmanager/package/metadata.desktop b/applets/taskmanager/package/metadata.desktop --- a/applets/taskmanager/package/metadata.desktop +++ b/applets/taskmanager/package/metadata.desktop @@ -33,7 +33,7 @@ Name[hsb]=Rjadowar nadawkow Name[hu]=Feladatkezelő Name[ia]=Gerente de carga -Name[id]=Pengelola Tugas +Name[id]=Task Manager Name[is]=Verkefnastjóri Name[it]=Gestore dei processi Name[ja]=タスクマネージャ diff --git a/applets/window-list/metadata.desktop b/applets/window-list/metadata.desktop --- a/applets/window-list/metadata.desktop +++ b/applets/window-list/metadata.desktop @@ -27,6 +27,7 @@ Name[kk]=Терезе тізімі Name[ko]=창 목록 Name[lt]=Langų sąrašas +Name[ml]=ജാലകപട്ടിക Name[mr]=चौकट यादी Name[nb]=Vindusliste Name[nds]=Finsterlist @@ -85,7 +86,7 @@ Comment[lt]=Plasma įskiepis, skirtas rodyti atvertų langų sąrašą. Comment[lv]=Plazmoīds, kas rāda atvērto logu sarakstu. Comment[mk]=Плазмоид за прикажување листа од отворените прозорци -Comment[ml]=തുറന്ന ജാലകങ്ങളുടെ പട്ടിക കാണിയ്ക്കാനുള്ള പ്ലാസ്മോയിഡ് +Comment[ml]=തുറന്ന ജാലകങ്ങളുടെ പട്ടിക കാണിയ്ക്കാനുള്ള പ്ലാസ്മോയിഡ്. Comment[mr]=उघडलेल्या चौकटींची यादी दर्शविणारे प्लाज्मोइड Comment[nb]=Plasmoide som viser en liste over åpne vinduer. Comment[nds]=Plasma-Lüttprogramm, dat de List mit opmaakt Finstern wiest diff --git a/containments/desktop/package/contents/ui/ActionButton.qml b/containments/desktop/package/contents/ui/ActionButton.qml --- a/containments/desktop/package/contents/ui/ActionButton.qml +++ b/containments/desktop/package/contents/ui/ActionButton.qml @@ -52,7 +52,7 @@ id: toolTip text: button.qAction ? button.qAction.text : "" delay: 0 - visible: button.hovered + visible: button.hovered && text.length > 0 x: button.width y: button.height/2 - height/2 } diff --git a/containments/desktop/package/contents/ui/ConfigOverlay.qml b/containments/desktop/package/contents/ui/ConfigOverlay.qml --- a/containments/desktop/package/contents/ui/ConfigOverlay.qml +++ b/containments/desktop/package/contents/ui/ConfigOverlay.qml @@ -98,7 +98,7 @@ id: rotateButton svg: configIconsSvg elementId: "rotate" - toolTip: i18n("Rotate") + toolTip: !rotateHandle.pressed ? i18n("Rotate") : "" iconSize: overlay.iconSize action: (applet) ? applet.action("rotate") : null down: !rotateHandle.pressed @@ -228,7 +228,7 @@ Layout.minimumHeight: units.gridUnit * 3 Layout.fillHeight: true Layout.fillWidth: true - cursorShape: Qt.DragMoveCursor + cursorShape: containsPress? Qt.DragMoveCursor : Qt.OpenHandCursor hoverEnabled: true onPressed: appletsLayout.releaseSpace(overlay.itemContainer); onPositionChanged: { diff --git a/containments/panel/contents/ui/ConfigOverlay.qml b/containments/panel/contents/ui/ConfigOverlay.qml --- a/containments/panel/contents/ui/ConfigOverlay.qml +++ b/containments/panel/contents/ui/ConfigOverlay.qml @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ -import QtQuick 2.1 +import QtQuick 2.5 import QtQuick.Layouts 1.0 import org.kde.plasma.core 2.0 as PlasmaCore @@ -64,7 +64,11 @@ } } } else { - configurationArea.cursorShape = Qt.ArrowCursor; + if (configurationArea.containsPress) { + configurationArea.cursorShape = Qt.ClosedHandCursor; + } else { + configurationArea.cursorShape = Qt.OpenHandCursor; + } } if (pressed) { diff --git a/kaccess/kaccess.notifyrc b/kaccess/kaccess.notifyrc --- a/kaccess/kaccess.notifyrc +++ b/kaccess/kaccess.notifyrc @@ -63,7 +63,7 @@ Comment[oc]=Accessibilitat Comment[or]=ଅଭିଗମ୍ୟତା Comment[pa]=ਸਹੂਲਤਾਂ -Comment[pl]=Ułatwienia dostępu +Comment[pl]=Ułatwienia Comment[pt]=Acessibilidade Comment[pt_BR]=Acessibilidade Comment[ro]=Accesibilitate diff --git a/kcms/access/kcmaccess.desktop b/kcms/access/kcmaccess.desktop --- a/kcms/access/kcmaccess.desktop +++ b/kcms/access/kcmaccess.desktop @@ -67,7 +67,7 @@ Name[lv]=Pieejamība Name[mai]=अभिगम्यता Name[mk]=Пристапливост -Name[ml]=സാമീപ്യത +Name[ml]=പ്രാപ്യത Name[mr]=सुलभता Name[ms]=Kebolehcapaian Name[nb]=Tilgjengelighet @@ -78,7 +78,7 @@ Name[oc]=Accessibilitat Name[or]=ଅଭିଗମ୍ୟତା Name[pa]=ਸਹੂਲਤਾਂ -Name[pl]=Ułatwienia dostępu +Name[pl]=Ułatwienia Name[pt]=Acessibilidade Name[pt_BR]=Acessibilidade Name[ro]=Accesibilitate @@ -132,6 +132,7 @@ Comment[ja]=アクセシビリティオプション Comment[ko]=내게 필요한 설정 Comment[lt]=Prieinamumo parinktys +Comment[ml]=പ്രാപ്യതാസാധ്യതകൾ Comment[mr]=सुलभता पर्याय Comment[nb]=Valg for tilgjengelighet Comment[nds]=Toganghülp-Optschonen diff --git a/kcms/activities/kcm_activities.desktop b/kcms/activities/kcm_activities.desktop --- a/kcms/activities/kcm_activities.desktop +++ b/kcms/activities/kcm_activities.desktop @@ -45,6 +45,7 @@ Name[ko]=활동 Name[lt]=Veiklos Name[lv]=Aktivitātes +Name[ml]=പ്രവര്‍ത്തികള്‍ Name[mr]=कार्यपध्दती Name[nb]=Aktiviteter Name[nds]=Aktiviteten diff --git a/kcms/activities/qml/activitiesTab/ActivitiesView.qml b/kcms/activities/qml/activitiesTab/ActivitiesView.qml --- a/kcms/activities/qml/activitiesTab/ActivitiesView.qml +++ b/kcms/activities/qml/activitiesTab/ActivitiesView.qml @@ -52,6 +52,8 @@ height: Kirigami.Units.iconSizes.medium width: height source: model.icon + sourceSize.width: model.icon.width + sourceSize.height: model.icon.height } QQC2.Label { diff --git a/kcms/autostart/autostart.desktop b/kcms/autostart/autostart.desktop --- a/kcms/autostart/autostart.desktop +++ b/kcms/autostart/autostart.desktop @@ -125,6 +125,7 @@ Comment[ja]=自動的に起動されるアプリケーション Comment[ko]=자동으로 시작되는 프로그램 Comment[lt]=Automatiškai paleidžiamos programos +Comment[ml]=താനേ തുടങ്ങിയ പ്രയോഗങ്ങൾ Comment[mr]=स्वप्रारंभअनुप्रयोग Comment[nb]=Programmer som startes automatisk Comment[nds]=Automaatsch opropen Programmen diff --git a/kcms/baloo/kcm_baloofile.desktop b/kcms/baloo/kcm_baloofile.desktop --- a/kcms/baloo/kcm_baloofile.desktop +++ b/kcms/baloo/kcm_baloofile.desktop @@ -36,6 +36,7 @@ Name[ja]=ファイルの検索 Name[ko]=파일 검색 Name[lt]=Failų paieška +Name[ml]=ഫയൽ തിരയുക Name[mr]=फाईल शोध Name[nl]=Bestanden zoeken Name[nn]=Filsøk @@ -81,6 +82,7 @@ Comment[ja]=ファイルの検索を設定 Comment[ko]=파일 검색 설정 Comment[lt]=Konfigūruoti failų paiešką +Comment[ml]=ഫയൽ തിരച്ചിൽ ക്രമീകരിക്കുക Comment[mr]=फाईल शोध संयोजीत करा Comment[nl]=Bestanden zoeken instellen Comment[nn]=Set opp filsøk diff --git a/kcms/baloo/package/metadata.desktop b/kcms/baloo/package/metadata.desktop --- a/kcms/baloo/package/metadata.desktop +++ b/kcms/baloo/package/metadata.desktop @@ -13,6 +13,7 @@ Name[id]=Pencarian Name[it]=Ricerca Name[lt]=Paieška +Name[ml]=തിരയുക Name[nl]=Zoeken Name[nn]=Søk Name[pl]=Szukaj @@ -39,6 +40,7 @@ Comment[id]=Konfigurasikan Pengindeks File Comment[it]=Configura l'indicizzatore dei file Comment[lt]=Konfigūruoti failų indeksatorių +Comment[ml]=ഫയൽ സൂചിക ക്രമീകരിക്കുക Comment[nl]=De bestandenindexering configureren Comment[nn]=Set opp filindeksering Comment[pl]=Ustawienia indeksera plików diff --git a/kcms/componentchooser/EXAMPLE.desktop b/kcms/componentchooser/EXAMPLE.desktop --- a/kcms/componentchooser/EXAMPLE.desktop +++ b/kcms/componentchooser/EXAMPLE.desktop @@ -133,7 +133,7 @@ Comment[lv]=Noderīgs saskarnes apraksts augšējai labajai informācijas rūtij Comment[mai]=ऊपरका दहिन्ना कोना केर सूचना बाक्स केर इंटरफेस क' उपयोगी वर्णन. Comment[mk]=Опис со објаснување за интерфејсот за горното десно поле за инфо. -Comment[ml]=വലത്തു് മുകളിലായുള്ള വിവരം നല്‍കുന്ന കളത്തില്‍ കാണിയ്ക്കാനായി നിങ്ങളുടെ വിനിമയതലത്തിനുള്ള സഹായകരമായ ഒരു വിവരണം. +Comment[ml]=വലത്തു് മുകളിലായുള്ള വിവരം നല്‍കുന്ന കളത്തില്‍ കാണിയ്ക്കാനായി നിങ്ങളുടെ വിനിമയതലത്തിനുള്ള സഹായകരമായ ഒരു വിവരണം Comment[mr]=वरील उजव्या कोपऱ्या वरील माहिती बॉक्स संवादपट करिता उपयोगी वर्णन Comment[ms]=Huraian antara muka yang sangat membantu untuk kotak maklumat kanan atas Comment[nb]=Beskrivelse av grensesnittet for infoboksen øverst til høyre diff --git a/kcms/componentchooser/componentchooser.desktop b/kcms/componentchooser/componentchooser.desktop --- a/kcms/componentchooser/componentchooser.desktop +++ b/kcms/componentchooser/componentchooser.desktop @@ -14,7 +14,7 @@ Name=Default Applications Name[af]=Verstektoepassings Name[ar]=التّطبيقات الافتراضيّة -Name[ast]=Aplicaicones predeterminaes +Name[ast]=Aplicaciones predeterminaes Name[be]=Праграмы па змаўчанні Name[be@latin]=Zmoŭčanyja aplikacyi Name[bg]=Стандартни програми @@ -98,7 +98,7 @@ Name[zh_TW]=預設應用程式 Comment=Default Applications Comment[ar]=التّطبيقات الافتراضيّة -Comment[ast]=Aplicaicones predeterminaes +Comment[ast]=Aplicaciones predeterminaes Comment[bs]=Podrazumijevani programi Comment[ca]=Aplicacions per omissió Comment[ca@valencia]=Aplicacions per omissió diff --git a/kcms/desktoptheme/CMakeLists.txt b/kcms/desktoptheme/CMakeLists.txt --- a/kcms/desktoptheme/CMakeLists.txt +++ b/kcms/desktoptheme/CMakeLists.txt @@ -3,6 +3,8 @@ set(kcm_desktoptheme_SRCS kcm.cpp + themesmodel.cpp + filterproxymodel.cpp ) kconfig_add_kcfg_files(kcm_desktoptheme_SRCS desktopthemesettings.kcfgc GENERATE_MOC) diff --git a/kcms/desktoptheme/filterproxymodel.h b/kcms/desktoptheme/filterproxymodel.h new file mode 100644 --- /dev/null +++ b/kcms/desktoptheme/filterproxymodel.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 Kai Uwe Broulik + * Copyright (c) 2019 David Redondo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "kcm.h" + +class FilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + + enum ThemeFilter { + AllThemes, + LightThemes, + DarkThemes, + ThemesFollowingColors + }; + Q_ENUM(ThemeFilter) + + Q_PROPERTY(QString selectedTheme READ selectedTheme WRITE setSelectedTheme NOTIFY selectedThemeChanged) + Q_PROPERTY(int selectedThemeIndex READ selectedThemeIndex NOTIFY selectedThemeIndexChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(ThemeFilter filter READ filter WRITE setFilter NOTIFY filterChanged) + + FilterProxyModel(QObject *parent = nullptr); + ~FilterProxyModel() override; + + QString selectedTheme() const; + void setSelectedTheme(const QString &pluginName); + + int selectedThemeIndex() const; + + QString query() const; + void setQuery(const QString &query); + + ThemeFilter filter() const; + void setFilter(ThemeFilter filter); + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +Q_SIGNALS: + void filterChanged(); + void queryChanged(); + + void selectedThemeChanged(); + void selectedThemeIndexChanged(); + +private: + void emitSelectedThemeIndexChange(); + + QString m_selectedTheme; + QString m_query; + ThemeFilter m_filter = AllThemes; +}; diff --git a/kcms/desktoptheme/filterproxymodel.cpp b/kcms/desktoptheme/filterproxymodel.cpp new file mode 100644 --- /dev/null +++ b/kcms/desktoptheme/filterproxymodel.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 Kai Uwe Broulik + * Copyright (c) 2019 David Redondo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "filterproxymodel.h" + +#include "themesmodel.h" + +FilterProxyModel::FilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + +} + +FilterProxyModel::~FilterProxyModel() = default; + +QString FilterProxyModel::selectedTheme() const +{ + return m_selectedTheme; +} + +void FilterProxyModel::setSelectedTheme(const QString &pluginName) +{ + if (m_selectedTheme == pluginName) { + return; + } + + const bool firstTime = m_selectedTheme.isNull(); + m_selectedTheme = pluginName; + + if (!firstTime) { + emit selectedThemeChanged(); + } + emit selectedThemeIndexChanged(); +} + +int FilterProxyModel::selectedThemeIndex() const +{ + // We must search in the source model and then map the index to our proxy model. + const auto results = sourceModel()->match(sourceModel()->index(0, 0), ThemesModel::PluginNameRole, m_selectedTheme); + + if (results.count() == 1) { + const QModelIndex result = mapFromSource(results.first()); + if (result.isValid()) { + return result.row(); + } + } + + return -1; +} + +QString FilterProxyModel::query() const +{ + return m_query; +} + +void FilterProxyModel::setQuery(const QString &query) +{ + if (m_query != query) { + const int oldIndex = selectedThemeIndex(); + + m_query = query; + invalidateFilter(); + + emit queryChanged(); + + if (selectedThemeIndex() != oldIndex) { + emit selectedThemeIndexChanged(); + } + } +} + +FilterProxyModel::ThemeFilter FilterProxyModel::filter() const +{ + return m_filter; +} + +void FilterProxyModel::setFilter(ThemeFilter filter) +{ + if (m_filter != filter) { + const int oldIndex = selectedThemeIndex(); + + m_filter = filter; + invalidateFilter(); + + emit filterChanged(); + + if (selectedThemeIndex() != oldIndex) { + emit selectedThemeIndexChanged(); + } + } +} + +bool FilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + + if (!m_query.isEmpty()) { + if (!idx.data(Qt::DisplayRole).toString().contains(m_query, Qt::CaseInsensitive) + && !idx.data(ThemesModel::PluginNameRole).toString().contains(m_query, Qt::CaseInsensitive)) { + return false; + } + } + + const auto type = idx.data(ThemesModel::ColorTypeRole).value(); + switch (m_filter) { + case AllThemes: + return true; + case LightThemes: + return type == ThemesModel::LightTheme; + case DarkThemes: + return type == ThemesModel::DarkTheme; + case ThemesFollowingColors: + return type == ThemesModel::FollowsColorTheme; + } + + return true; +} diff --git a/kcms/desktoptheme/kcm.h b/kcms/desktoptheme/kcm.h --- a/kcms/desktoptheme/kcm.h +++ b/kcms/desktoptheme/kcm.h @@ -39,44 +39,36 @@ } class QQuickItem; -class QStandardItemModel; class DesktopThemeSettings; +class FilterProxyModel; +class ThemesModel; class KCMDesktopTheme : public KQuickAddons::ManagedConfigModule { Q_OBJECT + Q_PROPERTY(DesktopThemeSettings *desktopThemeSettings READ desktopThemeSettings CONSTANT) - Q_PROPERTY(QStandardItemModel *desktopThemeModel READ desktopThemeModel CONSTANT) + Q_PROPERTY(FilterProxyModel *filteredModel READ filteredModel CONSTANT) + Q_PROPERTY(ThemesModel *desktopThemeModel READ desktopThemeModel CONSTANT) Q_PROPERTY(bool downloadingFile READ downloadingFile NOTIFY downloadingFileChanged) Q_PROPERTY(bool canEditThemes READ canEditThemes CONSTANT) public: - enum Roles { - PluginNameRole = Qt::UserRole + 1, - ThemeNameRole, - DescriptionRole, - IsLocalRole, - PendingDeletionRole - }; - Q_ENUM(Roles) KCMDesktopTheme(QObject *parent, const QVariantList &args); ~KCMDesktopTheme() override; DesktopThemeSettings *desktopThemeSettings() const; - QStandardItemModel *desktopThemeModel() const; - - Q_INVOKABLE int pluginIndex(const QString &pluginName) const; + ThemesModel *desktopThemeModel() const; + FilterProxyModel *filteredModel() const; bool downloadingFile() const; bool canEditThemes() const; Q_INVOKABLE void getNewStuff(QQuickItem *ctx); Q_INVOKABLE void installThemeFromFile(const QUrl &url); - Q_INVOKABLE void setPendingDeletion(int index, bool pending); - Q_INVOKABLE void applyPlasmaTheme(QQuickItem *item, const QString &themeName); Q_INVOKABLE void editTheme(const QString &themeName); @@ -101,8 +93,8 @@ DesktopThemeSettings *m_settings; - QStandardItemModel *m_model; - QStringList m_pendingRemoval; + ThemesModel *m_model; + FilterProxyModel *m_filteredModel; QHash m_themes; bool m_haveThemeExplorerInstalled; diff --git a/kcms/desktoptheme/kcm.cpp b/kcms/desktoptheme/kcm.cpp --- a/kcms/desktoptheme/kcm.cpp +++ b/kcms/desktoptheme/kcm.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -44,35 +43,45 @@ #include #include "desktopthemesettings.h" +#include "filterproxymodel.h" +#include "themesmodel.h" Q_LOGGING_CATEGORY(KCM_DESKTOP_THEME, "kcm_desktoptheme") K_PLUGIN_FACTORY_WITH_JSON(KCMDesktopThemeFactory, "kcm_desktoptheme.json", registerPlugin();) KCMDesktopTheme::KCMDesktopTheme(QObject *parent, const QVariantList &args) : KQuickAddons::ManagedConfigModule(parent, args) , m_settings(new DesktopThemeSettings(this)) + , m_model(new ThemesModel(this)) + , m_filteredModel(new FilterProxyModel(this)) , m_haveThemeExplorerInstalled(false) { qmlRegisterType(); - qmlRegisterType(); + qmlRegisterUncreatableType("org.kde.private.kcms.desktoptheme", 1, 0, "ThemesModel", "Cannot create ThemesModel"); + qmlRegisterUncreatableType("org.kde.private.kcms.desktoptheme", 1, 0, "FilterProxyModel", "Cannot create FilterProxyModel"); KAboutData* about = new KAboutData(QStringLiteral("kcm_desktoptheme"), i18n("Plasma Style"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("David Rosca"), QString(), QStringLiteral("nowrep@gmail.com")); setAboutData(about); setButtons(Apply | Default | Help); - m_model = new QStandardItemModel(this); - QHash roles = m_model->roleNames(); - roles[PluginNameRole] = QByteArrayLiteral("pluginName"); - roles[ThemeNameRole] = QByteArrayLiteral("themeName"); - roles[DescriptionRole] = QByteArrayLiteral("description"); - roles[IsLocalRole] = QByteArrayLiteral("isLocal"); - roles[PendingDeletionRole] = QByteArrayLiteral("pendingDeletion"); - m_model->setItemRoleNames(roles); - m_haveThemeExplorerInstalled = !QStandardPaths::findExecutable(QStringLiteral("plasmathemeexplorer")).isEmpty(); + + connect(m_model, &ThemesModel::pendingDeletionsChanged, this, &KCMDesktopTheme::settingsChanged); + + connect(m_model, &ThemesModel::selectedThemeChanged, this, [this](const QString &pluginName) { + m_settings->setName(pluginName); + }); + + connect(m_settings, &DesktopThemeSettings::nameChanged, this, [this] { + m_model->setSelectedTheme(m_settings->name()); + }); + + connect(m_model, &ThemesModel::selectedThemeChanged, m_filteredModel, &FilterProxyModel::setSelectedTheme); + + m_filteredModel->setSourceModel(m_model); } KCMDesktopTheme::~KCMDesktopTheme() @@ -84,41 +93,21 @@ return m_settings; } -QStandardItemModel *KCMDesktopTheme::desktopThemeModel() const +ThemesModel *KCMDesktopTheme::desktopThemeModel() const { return m_model; } -int KCMDesktopTheme::pluginIndex(const QString &pluginName) const +FilterProxyModel *KCMDesktopTheme::filteredModel() const { - const auto results = m_model->match(m_model->index(0, 0), PluginNameRole, pluginName); - if (results.count() == 1) { - return results.first().row(); - } - - return -1; + return m_filteredModel; } bool KCMDesktopTheme::downloadingFile() const { return m_tempCopyJob; } -void KCMDesktopTheme::setPendingDeletion(int index, bool pending) -{ - QModelIndex idx = m_model->index(index, 0); - - m_model->setData(idx, pending, PendingDeletionRole); - - if (pending && pluginIndex(m_settings->name()) == index) { - // move to the next non-pending theme - const auto nonPending = m_model->match(idx, PendingDeletionRole, false); - m_settings->setName(nonPending.first().data(PluginNameRole).toString()); - } - - settingsChanged(); -} - void KCMDesktopTheme::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { @@ -222,58 +211,8 @@ void KCMDesktopTheme::load() { ManagedConfigModule::load(); - - m_pendingRemoval.clear(); - - // Get all desktop themes - QStringList themes; - const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/desktoptheme"), QStandardPaths::LocateDirectory); - Q_FOREACH (const QString &ppath, packs) { - const QDir cd(ppath); - const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot); - Q_FOREACH (const QString &pack, entries) { - const QString _metadata = ppath + QLatin1Char('/') + pack + QStringLiteral("/metadata.desktop"); - if (QFile::exists(_metadata)) { - themes << _metadata; - } - } - } - - m_model->clear(); - - Q_FOREACH (const QString &theme, themes) { - int themeSepIndex = theme.lastIndexOf(QLatin1Char('/'), -1); - const QString themeRoot = theme.left(themeSepIndex); - int themeNameSepIndex = themeRoot.lastIndexOf(QLatin1Char('/'), -1); - const QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); - - KDesktopFile df(theme); - - if (df.noDisplay()) { - continue; - } - - QString name = df.readName(); - if (name.isEmpty()) { - name = packageName; - } - const bool isLocal = QFileInfo(theme).isWritable(); - - if (m_model->findItems(packageName).isEmpty()) { - QStandardItem *item = new QStandardItem; - item->setText(packageName); - item->setData(packageName, PluginNameRole); - item->setData(name, ThemeNameRole); - item->setData(df.readComment(), DescriptionRole); - item->setData(isLocal, IsLocalRole); - item->setData(false, PendingDeletionRole); - m_model->appendRow(item); - } - } - - m_model->setSortRole(ThemeNameRole); // FIXME the model should really be just using Qt::DisplayRole - m_model->sort(0 /*column*/); - + m_model->load(); + m_model->setSelectedTheme(m_settings->name()); // Model has been cleared so pretend the theme name changed to force view update emit m_settings->nameChanged(); } @@ -290,9 +229,9 @@ ManagedConfigModule::defaults(); // can this be done more elegantly? - const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true); + const auto pendingDeletions = m_model->match(m_model->index(0, 0), ThemesModel::PendingDeletionRole, true); for (const QModelIndex &idx : pendingDeletions) { - m_model->setData(idx, false, PendingDeletionRole); + m_model->setData(idx, false, ThemesModel::PendingDeletionRole); } } @@ -308,23 +247,23 @@ bool KCMDesktopTheme::isSaveNeeded() const { - return !m_model->match(m_model->index(0, 0), PendingDeletionRole, true).isEmpty(); + return !m_model->match(m_model->index(0, 0), ThemesModel::PendingDeletionRole, true).isEmpty(); } void KCMDesktopTheme::processPendingDeletions() { const QString program = QStringLiteral("plasmapkg2"); - const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true, -1 /*all*/); + const auto pendingDeletions = m_model->match(m_model->index(0, 0), ThemesModel::PendingDeletionRole, true, -1 /*all*/); QVector persistentPendingDeletions; // turn into persistent model index so we can delete as we go std::transform(pendingDeletions.begin(), pendingDeletions.end(), std::back_inserter(persistentPendingDeletions), [](const QModelIndex &idx) { return QPersistentModelIndex(idx); }); for (const QPersistentModelIndex &idx : persistentPendingDeletions) { - const QString pluginName = idx.data(PluginNameRole).toString(); + const QString pluginName = idx.data(ThemesModel::PluginNameRole).toString(); const QString displayName = idx.data(Qt::DisplayRole).toString(); Q_ASSERT(pluginName != m_settings->name()); @@ -340,7 +279,7 @@ } else { emit showErrorMessage(i18n("Removing theme failed: %1", QString::fromLocal8Bit(process->readAllStandardOutput().trimmed()))); - m_model->setData(idx, false, PendingDeletionRole); + m_model->setData(idx, false, ThemesModel::PendingDeletionRole); } process->deleteLater(); }); diff --git a/kcms/desktoptheme/package/contents/ui/ThemePreview.qml b/kcms/desktoptheme/package/contents/ui/ThemePreview.qml --- a/kcms/desktoptheme/package/contents/ui/ThemePreview.qml +++ b/kcms/desktoptheme/package/contents/ui/ThemePreview.qml @@ -20,6 +20,7 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kirigami 2.4 as Kirigami import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.private.kcms.desktoptheme 1.0 as Private Item { id: root @@ -52,6 +53,7 @@ RowLayout { id: contents + spacing: 0 anchors { fill: parent topMargin: background.generalMargin @@ -84,16 +86,13 @@ } } - Item { - Layout.fillWidth: true - } - // Analog clock Item { id: clock Layout.fillHeight: true + Layout.fillWidth: true Layout.preferredWidth: height - + Layout.alignment: Qt.AlignHCenter property int hours: 9 property int minutes: 5 @@ -177,6 +176,13 @@ height: naturalSize.height * clock.svgScale } } + Kirigami.Icon { + visible: model.colorType == Private.ThemesModel.FollowsColorTheme + source: "color-profile" + width: Kirigami.Units.iconSizes.smallMedium + height: width + Layout.alignment: Qt.AlignRight && Qt.AlignTop + } } Component.onCompleted: { diff --git a/kcms/desktoptheme/package/contents/ui/main.qml b/kcms/desktoptheme/package/contents/ui/main.qml --- a/kcms/desktoptheme/package/contents/ui/main.qml +++ b/kcms/desktoptheme/package/contents/ui/main.qml @@ -26,12 +26,26 @@ import org.kde.kirigami 2.4 as Kirigami import org.kde.kconfig 1.0 // for KAuthorized import org.kde.kcm 1.1 as KCM +import org.kde.private.kcms.desktoptheme 1.0 as Private + KCM.GridViewKCM { KCM.ConfigModule.quickHelp: i18n("This module lets you choose the Plasma style.") - view.model: kcm.desktopThemeModel - view.currentIndex: kcm.pluginIndex(kcm.desktopThemeSettings.name) + view.model: kcm.filteredModel + view.currentIndex: kcm.filteredModel.selectedThemeIndex + + Binding { + target: kcm.filteredModel + property: "query" + value: searchField.text + } + + Binding { + target: kcm.filteredModel + property: "filter" + value: filterCombo.model[filterCombo.currentIndex].filter + } enabled: !kcm.downloadingFile && !kcm.desktopThemeSettings.isImmutable("name") @@ -44,12 +58,80 @@ } onDropped: kcm.installThemeFromFile(drop.urls[0]) } + header: RowLayout { + Layout.fillWidth: true + + QtControls.TextField { + id: searchField + Layout.fillWidth: true + placeholderText: i18n("Search...") + leftPadding: LayoutMirroring.enabled ? clearButton.width : undefined + rightPadding: LayoutMirroring.enabled ? undefined : clearButton.width + // this could be useful as a component + MouseArea { + id: clearButton + anchors { + top: parent.top + topMargin: parent.topPadding + right: parent.right + // the TextField's padding is taking into account the clear button's size + // so we just use the opposite one for positioning the clear button + rightMargin: LayoutMirroring.enabled ? parent.rightPadding: parent.leftPadding + bottom: parent.bottom + bottomMargin: parent.bottomPadding + } + width: height + + opacity: searchField.length > 0 ? 1 : 0 + onClicked: searchField.clear() + + Kirigami.Icon { + anchors.fill: parent + active: parent.pressed + source: "edit-clear-locationbar-" + (LayoutMirroring.enabled ? "ltr" : "rtl") + } + + Behavior on opacity { + NumberAnimation { duration: Kirigami.Units.longDuration } + } + } + } + QtControls.ComboBox { + id: filterCombo + textRole: "text" + model: [ + {text: i18n("All Themes"), filter: Private.FilterProxyModel.AllThemes}, + {text: i18n("Light Themes"), filter: Private.FilterProxyModel.LightThemes}, + {text: i18n("Dark Themes"), filter: Private.FilterProxyModel.DarkThemes}, + {text: i18n("Themes following color scheme"), filter: Private.FilterProxyModel.ThemesFollowingColors} + ] + + // HACK QQC2 doesn't support icons, so we just tamper with the desktop style ComboBox's background + // and inject a nice little filter icon. + Component.onCompleted: { + if (!background || !background.hasOwnProperty("properties")) { + // not a KQuickStyleItem + return; + } + + var props = background.properties || {}; + + background.properties = Qt.binding(function() { + var newProps = props; + newProps.currentIcon = "view-filter"; + newProps.iconColor = Kirigami.Theme.textColor; + return newProps; + }); + } + } + } view.delegate: KCM.GridDelegate { id: delegate - text: model.themeName - toolTip: model.description || model.themeName + text: model.display + subtitle: model.colorType == Private.ThemesModel.FollowsColorTheme && view.model.filter != Private.FilterProxyModel.ThemesFollowingColors ? i18n("Follows color scheme") : "" + toolTip: model.description || model.display opacity: model.pendingDeletion ? 0.3 : 1 Behavior on opacity { @@ -76,13 +158,13 @@ tooltip: i18n("Remove Theme") enabled: model.isLocal visible: !model.pendingDeletion - onTriggered: kcm.setPendingDeletion(model.index, true); + onTriggered: model.pendingDeletion = true; }, Kirigami.Action { iconName: "edit-undo" tooltip: i18n("Restore Theme") visible: model.pendingDeletion - onTriggered: kcm.setPendingDeletion(model.index, false); + onTriggered: model.pendingDeletion = false; } ] diff --git a/kcms/desktoptheme/themesmodel.h b/kcms/desktoptheme/themesmodel.h new file mode 100644 --- /dev/null +++ b/kcms/desktoptheme/themesmodel.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Jeremy Whiting + * Copyright (C) 2016 Olivier Churlaud + * Copyright (C) 2019 Kai Uwe Broulik + * Copyright (C) 2019 David Redondo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +struct ThemesModelData; + +class ThemesModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QString selectedTheme READ selectedTheme WRITE setSelectedTheme NOTIFY selectedThemeChanged) + Q_PROPERTY(int selectedThemeIndex READ selectedThemeIndex NOTIFY selectedThemeChanged) + +public: + ThemesModel(QObject *parent); + ~ThemesModel() override; + + enum Roles { + PluginNameRole = Qt::UserRole + 1, + ThemeNameRole, + DescriptionRole, + FollowsSystemColorsRole, + ColorTypeRole, + IsLocalRole, + PendingDeletionRole + }; + Q_ENUM(Roles) + enum ColorType { + LightTheme, + DarkTheme, + FollowsColorTheme + }; + Q_ENUM(ColorType) + + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QHash roleNames() const override; + + QString selectedTheme() const; + void setSelectedTheme(const QString &pluginName); + + int pluginIndex(const QString &pluginName) const; + int selectedThemeIndex() const; + + QStringList pendingDeletions() const; + void removeRow(int row); + + void load(); + +Q_SIGNALS: + void selectedThemeChanged(const QString &pluginName); + void selectedThemeIndexChanged(); + + void pendingDeletionsChanged(); + +private: + QString m_selectedTheme; + //Can't use QVector because unique_ptr causes deletion of copy-ctor + QVector m_data; + +}; + +struct ThemesModelData +{ + QString display; + QString pluginName; + QString description; + ThemesModel::ColorType type; + bool isLocal; + bool pendingDeletion; +}; diff --git a/kcms/desktoptheme/themesmodel.cpp b/kcms/desktoptheme/themesmodel.cpp new file mode 100644 --- /dev/null +++ b/kcms/desktoptheme/themesmodel.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Jeremy Whiting + * Copyright (C) 2016 Olivier Churlaud + * Copyright (C) 2019 Kai Uwe Broulik + * Copyright (C) 2019 David Redondo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "themesmodel.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +ThemesModel::ThemesModel(QObject *parent) : QAbstractListModel(parent) +{ + +} + +ThemesModel::~ThemesModel() = default; + +int ThemesModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_data.count(); +} + +QVariant ThemesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_data.count()) { + return QVariant(); + } + + const auto &item = m_data.at(index.row()); + + switch (role) { + case Qt::DisplayRole: return item.display; + case PluginNameRole: return item.pluginName; + case DescriptionRole: return item.description; + case ColorTypeRole: return item.type; + case IsLocalRole: return item.isLocal; + case PendingDeletionRole: return item.pendingDeletion; + } + return QVariant(); +} + +bool ThemesModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || index.row() >= m_data.count()) { + return false; + } + + if (role == PendingDeletionRole) { + auto &item = m_data[index.row()]; + + const bool pendingDeletion = value.toBool(); + + if (item.pendingDeletion != pendingDeletion) { + item.pendingDeletion = pendingDeletion; + emit dataChanged(index, index, {PendingDeletionRole}); + + // move to the next non-pending theme + const auto nonPending = match(index, PendingDeletionRole, false); + if (!nonPending.isEmpty()) { + setSelectedTheme(nonPending.first().data(PluginNameRole).toString()); + } + + emit pendingDeletionsChanged(); + return true; + } + } + + return false; +} + +QHash ThemesModel::roleNames() const +{ + return { + {Qt::DisplayRole, QByteArrayLiteral("display")}, + {PluginNameRole, QByteArrayLiteral("pluginName")}, + {DescriptionRole, QByteArrayLiteral("description")}, + {ColorTypeRole, QByteArrayLiteral("colorType")}, + {IsLocalRole, QByteArrayLiteral("isLocal")}, + {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")} + }; +} + +QString ThemesModel::selectedTheme() const +{ + return m_selectedTheme; +} + +void ThemesModel::setSelectedTheme(const QString &pluginName) +{ + if (m_selectedTheme == pluginName) { + return; + } + + m_selectedTheme = pluginName; + + emit selectedThemeChanged(pluginName); + + emit selectedThemeIndexChanged(); +} + +int ThemesModel::pluginIndex(const QString &pluginName) const +{ + const auto results = match(index(0, 0), PluginNameRole, pluginName); + if (results.count() == 1) { + return results.first().row(); + } + + return -1; +} + +int ThemesModel::selectedThemeIndex() const +{ + return pluginIndex(m_selectedTheme); +} + +void ThemesModel::load() +{ + beginResetModel(); + + const int oldCount = m_data.count(); + + m_data.clear(); + + // Get all desktop themes + QStringList themes; + const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/desktoptheme"), QStandardPaths::LocateDirectory); + for(const QString &ppath : packs) { + const QDir cd(ppath); + const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot); + Q_FOREACH (const QString &pack, entries) { + const QString _metadata = ppath + QLatin1Char('/') + pack + QStringLiteral("/metadata.desktop"); + if (QFile::exists(_metadata)) { + themes << _metadata; + } + } + } + + for (const QString &theme : themes) { + int themeSepIndex = theme.lastIndexOf(QLatin1Char('/'), -1); + const QString themeRoot = theme.left(themeSepIndex); + int themeNameSepIndex = themeRoot.lastIndexOf(QLatin1Char('/'), -1); + const QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); + + KDesktopFile df(theme); + + if (df.noDisplay()) { + continue; + } + + QString name = df.readName(); + if (name.isEmpty()) { + name = packageName; + } + const bool isLocal = QFileInfo(theme).isWritable(); + bool hasPluginName = std::any_of(m_data.begin(), m_data.end(), [&] (const ThemesModelData &item) { + return item.pluginName == packageName; + }); + if (!hasPluginName) { + // Plasma Theme creates a KColorScheme out of the "color" file and falls back to system colors if there is none + const QString colorsPath = themeRoot + QLatin1String("/colors"); + const bool followsSystemColors = !QFileInfo::exists(colorsPath); + ColorType type = FollowsColorTheme; + if (!followsSystemColors) { + const KSharedConfig::Ptr config = KSharedConfig::openConfig(colorsPath); + const QPalette palette = KColorScheme::createApplicationPalette(config); + const int windowBackgroundGray = qGray(palette.window().color().rgb()); + if (windowBackgroundGray < 192) { + type = DarkTheme; + } else { + type = LightTheme; + } + } + ThemesModelData item { + name, + packageName, + df.readComment(), + type, + isLocal, + false + }; + m_data.append(item); + } + } + + QCollator collator; + std::sort(m_data.begin(), m_data.end(), [&collator](const ThemesModelData &a, const ThemesModelData &b) { + return collator.compare(a.display, b.display) < 0; + }); + + endResetModel(); + + // an item might have been added before the currently selected one + if (oldCount != m_data.count()) { + emit selectedThemeIndexChanged(); + } +} + +QStringList ThemesModel::pendingDeletions() const +{ + QStringList pendingDeletions; + + for (const auto &item : m_data) { + if (item.pendingDeletion) { + pendingDeletions.append(item.pluginName); + } + } + + return pendingDeletions; +} + +void ThemesModel::removeRow(int row) +{ + beginRemoveRows(QModelIndex(), row, row); + m_data.erase(m_data.begin() + row); + endRemoveRows(); +} diff --git a/kcms/icons/main.cpp b/kcms/icons/main.cpp --- a/kcms/icons/main.cpp +++ b/kcms/icons/main.cpp @@ -474,7 +474,7 @@ {QStringLiteral("folder")}, {QStringLiteral("document"), QStringLiteral("text-x-generic")}, {QStringLiteral("user-trash"), QStringLiteral("user-trash-empty")}, - {QStringLiteral("system-help"), QStringLiteral("help-about"), QStringLiteral("help-contents")}, + {QStringLiteral("help-browser"), QStringLiteral("system-help"), QStringLiteral("help-about"), QStringLiteral("help-contents")}, {QStringLiteral("preferences-system"), QStringLiteral("systemsettings"), QStringLiteral("configure")}, {QStringLiteral("text-html")}, diff --git a/kcms/kfontinst/dbus/fontinst.actions b/kcms/kfontinst/dbus/fontinst.actions --- a/kcms/kfontinst/dbus/fontinst.actions +++ b/kcms/kfontinst/dbus/fontinst.actions @@ -130,7 +130,7 @@ Name[lt]=Tvarkyti sistemos masto šriftus. Name[lv]=Pārvaldīt sistēmas fontus. Name[mk]=Менаџирање на системските фонтови. -Name[ml]=സിസ്റ്റത്തിനു് മുഴുവനായുള്ള അക്ഷരസഞ്ചയങ്ങളുടെ നോക്കിനടത്തല്‍ +Name[ml]=സിസ്റ്റത്തിനു് മുഴുവനായുള്ള അക്ഷരസഞ്ചയങ്ങളുടെ നോക്കിനടത്തല്‍. Name[mr]=प्रणालीतील फॉन्ट व्यवस्थापन Name[nb]=Håndter skrifter for hele systemet. Name[nds]=Systeemschriftoorden plegen diff --git a/kcms/launch/launchfeedback.h b/kcms/launch/launchfeedback.h --- a/kcms/launch/launchfeedback.h +++ b/kcms/launch/launchfeedback.h @@ -44,9 +44,6 @@ LaunchFeedbackSettings *launchFeedbackSettings() const; -public Q_SLOTS: - void save() override; - private: LaunchFeedbackSettings *m_settings; }; diff --git a/kcms/launch/launchfeedback.cpp b/kcms/launch/launchfeedback.cpp --- a/kcms/launch/launchfeedback.cpp +++ b/kcms/launch/launchfeedback.cpp @@ -53,14 +53,4 @@ return m_settings; } -void LaunchFeedback::save() -{ - ManagedConfigModule::save(); - - org::kde::kwin::Effects kwin(QStringLiteral("org.kde.KWin"), - QStringLiteral("/Effects"), - QDBusConnection::sessionBus()); - kwin.reconfigureEffect(QStringLiteral("startupfeedback")); -} - #include "launchfeedback.moc" diff --git a/kcms/launch/launchfeedbacksettings.kcfgc b/kcms/launch/launchfeedbacksettings.kcfgc --- a/kcms/launch/launchfeedbacksettings.kcfgc +++ b/kcms/launch/launchfeedbacksettings.kcfgc @@ -4,3 +4,4 @@ DefaultValueGetters=true GenerateProperties=true ParentInConstructor=true +Notifiers=true diff --git a/kcms/lookandfeel/kcm_lookandfeel.desktop b/kcms/lookandfeel/kcm_lookandfeel.desktop --- a/kcms/lookandfeel/kcm_lookandfeel.desktop +++ b/kcms/lookandfeel/kcm_lookandfeel.desktop @@ -61,7 +61,7 @@ Comment[lt]=Pasirinkti visuotinę išvaizdą ir turinį Comment[nl]=Globaal uiterlijk en gedrag kiezen Comment[nn]=Vel globalt tema for utsjånad og åtferd -Comment[pl]=Wybierz globalny wygląd oraz wrażenia wzrokowe i dotykowe +Comment[pl]=Nadaj wygląd globalnie Comment[pt]=Escolher a aparência e comportamento globais Comment[pt_BR]=Escolha a aparência e comportamento globais Comment[ru]=Выбор оформления рабочей среды Plasma diff --git a/kcms/lookandfeel/package/metadata.desktop b/kcms/lookandfeel/package/metadata.desktop --- a/kcms/lookandfeel/package/metadata.desktop +++ b/kcms/lookandfeel/package/metadata.desktop @@ -49,7 +49,7 @@ Comment[lt]=Pasirinkti visuotinę išvaizdą ir turinį Comment[nl]=Globaal uiterlijk en gedrag kiezen Comment[nn]=Vel globalt tema for utsjånad og åtferd -Comment[pl]=Wybierz globalny wygląd oraz wrażenia wzrokowe i dotykowe +Comment[pl]=Nadaj wygląd globalnie Comment[pt]=Escolher a aparência e comportamento globais Comment[pt_BR]=Escolha a aparência e comportamento globais Comment[ru]=Выбор оформления рабочей среды Plasma diff --git a/kcms/mouse/kcm/libinput/main.qml b/kcms/mouse/kcm/libinput/main.qml --- a/kcms/mouse/kcm/libinput/main.qml +++ b/kcms/mouse/kcm/libinput/main.qml @@ -33,8 +33,8 @@ spacing: Kirigami.Units.smallSpacing - property size sizeHint: Qt.size(formLayout.width, formLayout.height) - property size minimumSizeHint: Qt.size(formLayout.width/2, deviceSelector.height) + property size sizeHint: Qt.size(formLayout.width, Math.round(1.3 * formLayout.height)) + property size minimumSizeHint: Qt.size(formLayout.width/2, Math.round(1.3 * formLayout.height)) property alias deviceIndex: deviceSelector.currentIndex signal changeSignal() diff --git a/kcms/mouse/kcm/libinput/main_deviceless.qml b/kcms/mouse/kcm/libinput/main_deviceless.qml --- a/kcms/mouse/kcm/libinput/main_deviceless.qml +++ b/kcms/mouse/kcm/libinput/main_deviceless.qml @@ -33,8 +33,8 @@ spacing: Kirigami.Units.smallSpacing - property size sizeHint: Qt.size(formLayout.width, Math.round(1.1 * formLayout.height)) - property size minimumSizeHint: Qt.size(formLayout.width, Math.round(1.1 * formLayout.height)) + property size sizeHint: Qt.size(formLayout.width, Math.round(1.3 * formLayout.height)) + property size minimumSizeHint: Qt.size(formLayout.width, Math.round(1.3 * formLayout.height)) signal changeSignal() diff --git a/kcms/nightcolor/kcm_nightcolor.desktop b/kcms/nightcolor/kcm_nightcolor.desktop --- a/kcms/nightcolor/kcm_nightcolor.desktop +++ b/kcms/nightcolor/kcm_nightcolor.desktop @@ -14,6 +14,7 @@ X-KDE-Weight=120 Name=Night Color +Name[ast]=Color nocherniegu Name[ca]=Color de nit Name[ca@valencia]=Color de nit Name[cs]=Noční barva diff --git a/kcms/nightcolor/package/contents/ui/LocationsAutoView.qml b/kcms/nightcolor/package/contents/ui/LocationsAutoView.qml deleted file mode 100644 --- a/kcms/nightcolor/package/contents/ui/LocationsAutoView.qml +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************** -Copyright 2017 Roman Gilg - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ -import QtQuick 2.1 -import org.kde.kirigami 2.5 as Kirigami - -Kirigami.FormLayout { - twinFormLayouts: parentLayout - enabled: activator.checked - - property double latitude - property double longitude - - onLatitudeChanged: latitudeField.backend = latitude; - onLongitudeChanged: longitudeField.backend = longitude; - - NumberField { - id: latitudeField - Kirigami.FormData.label: i18n("Latitude:") - backend: locator.latitude - enabled: false - } - - NumberField { - id: longitudeField - Kirigami.FormData.label: i18n("Longitude:") - backend: locator.longitude - enabled: false - } -} diff --git a/kcms/nightcolor/package/contents/ui/LocationsFixedView.qml b/kcms/nightcolor/package/contents/ui/LocationsFixedView.qml --- a/kcms/nightcolor/package/contents/ui/LocationsFixedView.qml +++ b/kcms/nightcolor/package/contents/ui/LocationsFixedView.qml @@ -15,12 +15,12 @@ along with this program. If not, see . *********************************************************************/ import QtQuick 2.1 -import org.kde.kirigami 2.5 as Kirigami +import QtQuick.Layouts 1.1 import QtQuick.Controls 2.5 as QQC2 +import org.kde.kirigami 2.5 as Kirigami Kirigami.FormLayout { twinFormLayouts: parentLayout - enabled: activator.checked Connections { target: root @@ -32,37 +32,40 @@ longitudeFixedField.backend = cA.longitudeFixed; } + QQC2.Button { + text: i18n("Detect Location") + // Match combobox width + Layout.minimumWidth: modeSwitcher.width + icon.name: "find-location" + onClicked: { + latitudeFixedField.backend = locator.latitude; + longitudeFixedField.backend = locator.longitude; + } + } + NumberField { id: latitudeFixedField + // Match combobox width + Layout.minimumWidth: modeSwitcher.width Kirigami.FormData.label: i18n("Latitude:") backend: cA.latitudeFixedStaged validator: DoubleValidator {bottom: -90; top: 90; decimals: 10} onBackendChanged: { cA.latitudeFixedStaged = backend; - manualLocationsViewRow.change(); calcNeedsSave(); } } NumberField { id: longitudeFixedField + // Match combobox width + Layout.minimumWidth: modeSwitcher.width Kirigami.FormData.label: i18n("Longitude:") backend: cA.longitudeFixedStaged validator: DoubleValidator {bottom: -180; top: 180; decimals: 10} onBackendChanged: { cA.longitudeFixedStaged = backend; - manualLocationsViewRow.change(); calcNeedsSave(); } } - - QQC2.Button { - text: i18n("Detect Location") - implicitWidth: longitudeFixedField.width // TODO: see if there is a smarter way for doing this - icon.name: "edit-paste-in-place" - onClicked: { - latitudeFixedField.backend = locator.latitude; - longitudeFixedField.backend = locator.longitude; - } - } } diff --git a/kcms/nightcolor/package/contents/ui/TimingsView.qml b/kcms/nightcolor/package/contents/ui/TimingsView.qml --- a/kcms/nightcolor/package/contents/ui/TimingsView.qml +++ b/kcms/nightcolor/package/contents/ui/TimingsView.qml @@ -20,7 +20,6 @@ Kirigami.FormLayout { twinFormLayouts: parentLayout - enabled: activator.checked property double latitude property double longitude @@ -33,31 +32,33 @@ eveningTimings = sunCalc.getEveningTimings(latitude, longitude); } - TimeField { - id: mornBeginField - Kirigami.FormData.label: i18n("Sunrise begins:") - backend: morningTimings.begin - enabled: false + function prettyTime(date) { + return date.toLocaleString(Qt.locale(), "h:mm"); } - TimeField { - id: mornEndField - Kirigami.FormData.label: i18n("...and ends:") - backend: morningTimings.end - enabled: false + Kirigami.Separator { + Kirigami.FormData.isSection: true } - TimeField { - id: evenBeginField - Kirigami.FormData.label: i18n("Sunset begins:") - backend: eveningTimings.begin - enabled: false + QQC2.Label { + wrapMode: Text.Wrap + text: i18n("Night Color begins at %1", prettyTime(eveningTimings.begin)) + } + QQC2.Label { + wrapMode: Text.Wrap + text: i18n("Color fully changed at %1", prettyTime(eveningTimings.end)) + } + + Item { + Kirigami.FormData.isSection: true } - TimeField { - id: evenEndField - Kirigami.FormData.label: i18n("...and ends:") - backend: eveningTimings.end - enabled: false + QQC2.Label { + wrapMode: Text.Wrap + text: i18n("Night Color begins changing back at %1", prettyTime(morningTimings.begin)) + } + QQC2.Label { + wrapMode: Text.Wrap + text: i18n("Normal coloration restored by %1", prettyTime(morningTimings.end)) } } diff --git a/kcms/nightcolor/package/contents/ui/main.qml b/kcms/nightcolor/package/contents/ui/main.qml --- a/kcms/nightcolor/package/contents/ui/main.qml +++ b/kcms/nightcolor/package/contents/ui/main.qml @@ -28,6 +28,7 @@ property int error: cA.error property bool defaultRequested: false implicitHeight: Kirigami.Units.gridUnit * 29 + implicitWidth: Kirigami.Units.gridUnit * 35 CC.CompositorAdaptor { id: cA @@ -108,13 +109,22 @@ Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: Math.round(root.width * 0.5) - text: i18n("Night Color makes the colors on the screen warmer to reduce eye strain.") + text: i18n("Night Color makes the colors on the screen warmer to reduce eye strain at the time of your choosing.") wrapMode: Text.WordWrap } Kirigami.FormLayout { id: parentLayout + Connections { + target: root + onReset: { + mornBeginManField.backend = cA.morningBeginFixed; + evenBeginManField.backend = cA.eveningBeginFixed; + transTimeField.value = cA.transitionTime; + } + } + QQC2.CheckBox { id: activator text: i18n("Activate Night Color") @@ -132,17 +142,18 @@ } GridLayout { - Kirigami.FormData.label: i18n("Night Color temperature:") + Kirigami.FormData.label: i18n("Night Color Temperature:") Kirigami.FormData.buddyFor: tempSlider enabled: activator.checked columns: 4 QQC2.Slider { id: tempSlider + // Match combobox width + Layout.minimumWidth: modeSwitcher.width enabled: activator.checked from: cA.minimalTemperature - implicitWidth: modeSwitcher.width to: cA.neutralTemperature value: cA.nightTemperature stepSize: 100 @@ -176,201 +187,128 @@ QQC2.ComboBox { id: modeSwitcher - Kirigami.FormData.label: i18n("Operation mode:") + // Work around https://bugs.kde.org/show_bug.cgi?id=403153 + Layout.minimumWidth: Kirigami.Units.gridUnit * 17 + Kirigami.FormData.label: i18n("Activation time:") enabled: activator.checked model: [ - i18n("Automatic"), - i18n("Location"), - i18n("Times"), - i18n("Constant") + i18n("Sunset to sunrise at current location"), + i18n("Sunset to sunrise at manual location"), + i18n("Custom time"), + i18n("Always on") ] currentIndex: cA.mode onCurrentIndexChanged: { cA.modeStaged = currentIndex; - advancedControlLoader.updatePage(currentIndex); calcNeedsSave(); } } - } - Kirigami.FormLayout { + // Show current location in auto mode + QQC2.Label { + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeAutomatic + enabled: activator.checked + wrapMode: Text.Wrap + text: i18n("Latitude: %1 Longitude: %2", locator.latitude, locator.longitude) + } - Loader { - id: advancedControlLoader - - - function updatePage(index) { - switch (index) { - case CC.CompositorAdaptor.ModeAutomatic: - sourceComponent = automaticView; - break; - case CC.CompositorAdaptor.ModeLocation: - sourceComponent = manualLocationsView; - break; - case CC.CompositorAdaptor.ModeTimings: - sourceComponent = manualTimingsView; - break; - case CC.CompositorAdaptor.ModeConstant: - default: - sourceComponent = undefined; - break; - } + // Show time entry fields in manual timings mode + TimeField { + id: evenBeginManField + // Match combobox width + Layout.minimumWidth: modeSwitcher.width + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeTimings + Kirigami.FormData.label: i18n("Turn on at:") + backend: cA.eveningBeginFixedStaged + onBackendChanged: {cA.eveningBeginFixedStaged = backend; + calcNeedsSave(); } - } - Component { - id: automaticView + QQC2.ToolTip { + text: i18n("Input format: HH:MM") + } + } - ColumnLayout { + TimeField { + id: mornBeginManField + // Match combobox width + Layout.minimumWidth: modeSwitcher.width + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeTimings + Kirigami.FormData.label: i18n("Turn off at:") + backend: cA.morningBeginFixedStaged + onBackendChanged: {cA.morningBeginFixedStaged = backend; + calcNeedsSave(); + } - Loader { - sourceComponent: TimingsView { - latitude: locator.latitude - longitude: locator.longitude - } - } + QQC2.ToolTip { + text: i18n("Input format: HH:MM") + } + } - Kirigami.Separator { - Layout.fillWidth: true - Kirigami.FormData.isSection: true - } + QQC2.SpinBox { + id: transTimeField + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeTimings + // Match width of combobox and input fields + Layout.minimumWidth: modeSwitcher.width + Kirigami.FormData.label: i18n("Transition duration:") + from: 1 + to: 600 // less than 12 hours (in minutes: 720) + value: cA.transitionTimeStaged + editable: true + onValueModified: { + cA.transitionTimeStaged = value; + calcNeedsSave(); + } + textFromValue: function(value, locale) { + return i18np("%1 minute", "%1 minutes", value) + } + valueFromText: function(text, locale) { + return parseInt(text); + } - Loader { - sourceComponent: LocationsAutoView { - latitude: locator.latitude - longitude: locator.longitude - } - } + QQC2.ToolTip { + text: i18n("Input minutes - min. 1, max. 600") } } - Component { - id: manualLocationsView - - ColumnLayout { - id: manualLocationsViewRow - signal change() - - Loader { - sourceComponent: TimingsView { - latitude: cA.latitudeFixedStaged - longitude: cA.longitudeFixedStaged - - Connections { - target: manualLocationsViewRow - onChange: { - reset(); - } - } - } - } + QQC2.Label { + id: manualTimingsError1 + visible: evenBeginManField.getNormedDate() - mornBeginManField.getNormedDate() <= 0 + font.italic: true + text: i18n("Error: Morning is before evening.") + } - Kirigami.Separator { - Layout.fillWidth: true - Kirigami.FormData.isSection: true + QQC2.Label { + id: manualTimingsError2 + visible: { + if (manualTimingsError1.visible) { + return false; } + var trTime = transTimeField.backend * 60 * 1000; + var mor = mornBeginManField.getNormedDate(); + var eve = evenBeginManField.getNormedDate(); - Loader { - sourceComponent: LocationsFixedView {} - } + return eve - mor <= trTime || eve - mor >= 86400000 - trTime; } + font.italic: true + text: i18n("Error: Transition time overlaps.") } + } - Component { - id: manualTimingsView - - ColumnLayout { - Loader { - sourceComponent: Kirigami.FormLayout { - twinFormLayouts: parentLayout - enabled: activator.checked && cA.timingsEnabled - - Connections { - target: root - onReset: { - mornBeginManField.backend = cA.morningBeginFixed; - evenBeginManField.backend = cA.eveningBeginFixed; - transTimeField.backend = cA.transitionTime; - } - } - - TimeField { - id: mornBeginManField - Kirigami.FormData.label: i18n("Sunrise begins:") - backend: cA.morningBeginFixedStaged - onBackendChanged: {cA.morningBeginFixedStaged = backend; - calcNeedsSave(); - } - - QQC2.ToolTip { - text: i18n("(Input format: HH:MM)") - } - } - - TimeField { - id: evenBeginManField - Kirigami.FormData.label: i18n("Sunset begins:") - backend: cA.eveningBeginFixedStaged - onBackendChanged: {cA.eveningBeginFixedStaged = backend; - calcNeedsSave(); - } - - QQC2.ToolTip { - text: i18n("Input format: HH:MM") - } - } - - QQC2.SpinBox { - id: transTimeField - // Match width of other text fields - Layout.minimumWidth: 200 - Kirigami.FormData.label: i18n("Transition duration:") - from: 1 - to: 600 // less than 12 hours (in minutes: 720) - value: cA.transitionTimeStaged - editable: true - onValueModified: { - cA.transitionTimeStaged = value; - calcNeedsSave(); - } - textFromValue: function(value, locale) { - return i18np("%1 minute", "%1 minutes", value) - } - valueFromText: function(text, locale) { - return parseInt(text); - } - - QQC2.ToolTip { - text: i18n("Input minutes - min. 1, max. 600") - } - } - - QQC2.Label { - id: manualTimingsError1 - visible: evenBeginManField.getNormedDate() - mornBeginManField.getNormedDate() <= 0 - font.italic: true - text: i18n("Error: Morning is before evening.") - } - - QQC2.Label { - id: manualTimingsError2 - visible: { - if (manualTimingsError1.visible) { - return false; - } - var trTime = transTimeField.backend * 60 * 1000; - var mor = mornBeginManField.getNormedDate(); - var eve = evenBeginManField.getNormedDate(); - - return eve - mor <= trTime || eve - mor >= 86400000 - trTime; - } - font.italic: true - text: i18n("Error: Transition time overlaps.") - } - } - } - } - } + // Show location chooser in manual location mode + LocationsFixedView { + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeLocation + enabled: activator.checked + } + + // Show start/end times in automatic and manual location modes + TimingsView { + id: timings + visible: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeAutomatic || + modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeLocation + enabled: activator.checked + latitude: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeAutomatic ? locator.latitude : cA.latitudeFixedStaged + longitude: modeSwitcher.currentIndex === CC.CompositorAdaptor.ModeAutomatic ? locator.longitude : cA.longitudeFixedStaged } } } diff --git a/kcms/nightcolor/package/metadata.desktop b/kcms/nightcolor/package/metadata.desktop --- a/kcms/nightcolor/package/metadata.desktop +++ b/kcms/nightcolor/package/metadata.desktop @@ -1,5 +1,6 @@ [Desktop Entry] Name=Night Color +Name[ast]=Color nocherniegu Name[ca]=Color de nit Name[ca@valencia]=Color de nit Name[cs]=Noční barva diff --git a/kcms/notifications/package/contents/ui/main.qml b/kcms/notifications/package/contents/ui/main.qml --- a/kcms/notifications/package/contents/ui/main.qml +++ b/kcms/notifications/package/contents/ui/main.qml @@ -33,6 +33,14 @@ // Sidebar on SourcesPage is 1/3 of the width at a minimum of 12, so assume 3 * 12 = 36 as preferred implicitWidth: Kirigami.Units.gridUnit * 36 + readonly property string ourServerVendor: "KDE" + readonly property string ourServerName: "Plasma" + + readonly property NotificationManager.ServerInfo currentOwnerInfo: NotificationManager.Server.currentOwner + + readonly property bool notificationsAvailable: currentOwnerInfo.status === NotificationManager.ServerInfo.Running + && currentOwnerInfo.vendor === ourServerVendor && currentOwnerInfo.name === ourServerName + function openSourcesSettings() { // TODO would be nice to re-use the current SourcesPage instead of pushing a new one that lost all state // but there's no pageAt(index) method in KConfigModuleQml @@ -46,37 +54,67 @@ } Kirigami.FormLayout { + Kirigami.InlineMessage { + Kirigami.FormData.isSection: true + Layout.fillWidth: true + type: Kirigami.MessageType.Error + text: i18n("Could not find a 'Notifications' widget which is required for displaying notifications."); + visible: currentOwnerInfo.status === NotificationManager.ServerInfo.NotRunning + } + + Kirigami.InlineMessage { + Kirigami.FormData.isSection: true + Layout.fillWidth: true + type: Kirigami.MessageType.Information + text: { + if (currentOwnerInfo.vendor && currentOwnerInfo.name) { + return i18nc("Vendor and product name", + "Notifications are currently provided by '%1 %2' instead of Plasma.", + currentOwnerInfo.vendor, currentOwnerInfo.name); + } + + return i18n("Notifications are currently not provided by Plasma."); + } + visible: root.currentOwnerInfo.status === NotificationManager.ServerInfo.Running + && (currentOwnerInfo.vendor !== root.ourServerVendor || currentOwnerInfo.name !== root.ourServerName) + } + QtControls.CheckBox { Kirigami.FormData.label: i18n("Do not disturb:") text: i18nc("Do not disturb when screens are mirrored", "When screens are mirrored") checked: kcm.settings.inhibitNotificationsWhenScreensMirrored onClicked: kcm.settings.inhibitNotificationsWhenScreensMirrored = checked + enabled: root.notificationsAvailable } QtControls.CheckBox { text: i18n("Show critical notifications") checked: kcm.settings.criticalPopupsInDoNotDisturbMode onClicked: kcm.settings.criticalPopupsInDoNotDisturbMode = checked + enabled: root.notificationsAvailable } QtControls.CheckBox { Kirigami.FormData.label: i18n("Critical notifications:") text: i18n("Always keep on top") checked: kcm.settings.keepCriticalAlwaysOnTop onClicked: kcm.settings.keepCriticalAlwaysOnTop = checked + enabled: root.notificationsAvailable } QtControls.CheckBox { Kirigami.FormData.label: i18n("Low priority notifications:") text: i18n("Show popup") checked: kcm.settings.lowPriorityPopups onClicked: kcm.settings.lowPriorityPopups = checked + enabled: root.notificationsAvailable } QtControls.CheckBox { text: i18n("Show in history") checked: kcm.settings.lowPriorityHistory onClicked: kcm.settings.lowPriorityHistory = checked + enabled: root.notificationsAvailable } QtControls.ButtonGroup { @@ -90,10 +128,13 @@ text: i18nc("Popup position near notification plasmoid", "Near the notification icon") // "widget" checked: kcm.settings.popupPosition === NotificationManager.Settings.CloseToWidget onClicked: kcm.settings.popupPosition = NotificationManager.Settings.CloseToWidget + enabled: root.notificationsAvailable } RowLayout { spacing: 0 + enabled: root.notificationsAvailable + QtControls.RadioButton { id: positionCustomPosition checked: kcm.settings.popupPosition !== NotificationManager.Settings.CloseToWidget @@ -126,6 +167,7 @@ to: 120000 // 2 minutes stepSize: 1000 value: kcm.settings.popupTimeout + enabled: root.notificationsAvailable editable: true valueFromText: function(text, locale) { return parseInt(text) * 1000; @@ -180,6 +222,7 @@ Kirigami.FormData.label: i18n("Applications:") text: i18n("Configure...") icon.name: "configure" + enabled: root.notificationsAvailable onClicked: root.openSourcesSettings() } } diff --git a/kcms/solid_actions/device-actions/solid-device-Battery.desktop b/kcms/solid_actions/device-actions/solid-device-Battery.desktop --- a/kcms/solid_actions/device-actions/solid-device-Battery.desktop +++ b/kcms/solid_actions/device-actions/solid-device-Battery.desktop @@ -39,7 +39,7 @@ Name[lv]=Uzlādes procenti Name[mai]=चार्ज प्रतिशत Name[mk]=Процент на полнеж -Name[ml]=ചാര്‍ജ്ജ് ശതമാനം +Name[ml]=ചാര്‍ജ്ജ് ശതമാനം Name[mr]=चार्ज टक्केवारी Name[nb]=Prosent lading Name[nds]=Oplaadperzent diff --git a/kcms/spellchecking/spellchecking.desktop b/kcms/spellchecking/spellchecking.desktop --- a/kcms/spellchecking/spellchecking.desktop +++ b/kcms/spellchecking/spellchecking.desktop @@ -36,6 +36,7 @@ Name[ja]=スペルチェック Name[ko]=맞춤법 검사 Name[lt]=Rašybos tikrinimas +Name[ml]=അക്ഷരത്തെറ്റു് പരിശോധക Name[mr]=शब्द तपासक Name[nb]=Stavekontroll Name[nds]=Klookschriever diff --git a/kcms/style/package/contents/ui/main.qml b/kcms/style/package/contents/ui/main.qml --- a/kcms/style/package/contents/ui/main.qml +++ b/kcms/style/package/contents/ui/main.qml @@ -68,10 +68,24 @@ toolTip: model.description thumbnailAvailable: thumbnailItem.valid - thumbnail: Private.PreviewItem { - id: thumbnailItem + thumbnail: Item { anchors.fill: parent - styleName: model.styleName + clip: thumbnailItem.implicitWidth * thumbnailItem.scale > width + + Private.PreviewItem { + id: thumbnailItem + anchors.centerIn: parent + // Fit the widget's width in the grid view + // Round up to the nearest 0.05, so that 0.95 gets scaled up to 1.0 + // to avoid blurriness in case the widget is only slightly bigger + scale: (Math.ceil(Math.min(1, parent.width / implicitWidth) * 20) / 20).toFixed(2) + width: Math.max(parent.width, implicitWidth) + // Scale the height back up as the background color comes from the style + // and we don't want to leave an ugly gap above/below it + height: parent.height / scale + smooth: true + styleName: model.styleName + } Connections { target: kcm diff --git a/kcms/workspaceoptions/workspaceoptions_kdeglobalssettings.kcfgc b/kcms/workspaceoptions/workspaceoptions_kdeglobalssettings.kcfgc --- a/kcms/workspaceoptions/workspaceoptions_kdeglobalssettings.kcfgc +++ b/kcms/workspaceoptions/workspaceoptions_kdeglobalssettings.kcfgc @@ -4,3 +4,4 @@ DefaultValueGetters=true GenerateProperties=true ParentInConstructor=true +Notifiers=true diff --git a/org.kde.plasmashell.metainfo.xml b/org.kde.plasmashell.metainfo.xml --- a/org.kde.plasmashell.metainfo.xml +++ b/org.kde.plasmashell.metainfo.xml @@ -135,9 +135,9 @@ + - diff --git a/toolboxes/desktoptoolbox/metadata.desktop b/toolboxes/desktoptoolbox/metadata.desktop --- a/toolboxes/desktoptoolbox/metadata.desktop +++ b/toolboxes/desktoptoolbox/metadata.desktop @@ -16,7 +16,7 @@ Name[gl]=Caixa de ferramentas declarativa Name[hu]=Deklaratív eszközdoboz Name[ia]=Instrumentario declarative -Name[id]=Kotak-Alat Deklaratif +Name[id]=Perkakas Deklaratif Name[is]=Skilgreininga-verkfærasafn Name[it]=Strumento dichiarativo Name[kk]=Мәлімдеме құралдары diff --git a/toolboxes/paneltoolbox/metadata.desktop b/toolboxes/paneltoolbox/metadata.desktop --- a/toolboxes/paneltoolbox/metadata.desktop +++ b/toolboxes/paneltoolbox/metadata.desktop @@ -18,7 +18,7 @@ Name[he]=ארגז־הכלים של הלוח Name[hu]=Panel eszköztár Name[ia]=Instrumentario de pannello -Name[id]=Kotak-Alat Panel +Name[id]=Perkakas Panel Name[is]=Verkfærasafn fyrir spjöld Name[it]=Strumento per pannelli Name[ja]=パネルツールボックス @@ -75,6 +75,7 @@ Comment[kk]=Plasma панелін басқару Comment[ko]=Plasma 패널 관리 Comment[lt]=Tvarkyti Plasma skydelį +Comment[ml]=പ്ലാസ്മയുടെ സഹായക്കുറിപ്പു് Comment[mr]=प्लाज्मा पटल व्यवस्थापन Comment[nb]=Håndter Plasma-panel Comment[nds]=Plasmapaneel plegen diff --git a/toolboxes/plasma-toolbox-paneltoolbox.desktop b/toolboxes/plasma-toolbox-paneltoolbox.desktop --- a/toolboxes/plasma-toolbox-paneltoolbox.desktop +++ b/toolboxes/plasma-toolbox-paneltoolbox.desktop @@ -20,7 +20,7 @@ Comment[hr]=Zadana panelna alatna kutija za Plasma ljusku radne površine Comment[hu]=Alapértelmezett panel eszközkészlet a Plasma asztali héjhoz Comment[ia]=Instrumentario de pannello pro le shell de scriptorio de Plasma -Comment[id]=Toolbar panel baku untuk shell desktop Plasma +Comment[id]=Perkakas panel baku untuk shell desktop Plasma Comment[is]=Sjálfgefið verkfærasafn með Plasma skjáborðsskelinni Comment[it]=Strumento predefinito del pannello per la shell del desktop di Plasma Comment[ja]=Plasma デスクトップシェルの標準パネルツールボックス @@ -78,7 +78,7 @@ Name[hr]=Alatna kutija panela Name[hu]=Panel eszköztár Name[ia]=Instrumentario de pannello -Name[id]=Kotak-alat panel +Name[id]=Perkakas panel Name[is]=Verkfærasafn fyrir spjöld Name[it]=Strumento per pannelli Name[ja]=パネルツールボックス