diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,15 +5,14 @@ set(QT_MIN_VERSION "5.7.0") set(KF5_MIN_VERSION "5.30") -remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) - find_package(ECM 1.2.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS diff --git a/app/kdesystemsettings.desktop b/app/kdesystemsettings.desktop --- a/app/kdesystemsettings.desktop +++ b/app/kdesystemsettings.desktop @@ -122,10 +122,13 @@ Comment[el]=Εργαλεία διαμόρφωσης για τον υπολογιστή σας Comment[es]=Herramientas de configuración para su equipo Comment[eu]=Zure ordenagailurako konfiguratzeko tresnak +Comment[fr]=Outils de configuration pour votre ordinateur Comment[it]=Strumenti di configurazione per il tuo computer Comment[nl]=Hulpmiddelen voor configuratie voor uw computer Comment[pl]=Narzędzia ustawień dla twojego komputera Comment[pt]=Ferramentas de configuração para o seu computador +Comment[pt_BR]=Ferramentas de configuração para seu computador +Comment[ru]=Инструменты для настройки компьютера Comment[sr]=Алатке за подешавање рачунара Comment[sr@ijekavian]=Алатке за подешавање рачунара Comment[sr@ijekavianlatin]=Alatke za podešavanje računara diff --git a/app/systemsettings.desktop b/app/systemsettings.desktop --- a/app/systemsettings.desktop +++ b/app/systemsettings.desktop @@ -179,10 +179,13 @@ Comment[el]=Εργαλεία διαμόρφωσης για τον υπολογιστή σας Comment[es]=Herramientas de configuración para su equipo Comment[eu]=Zure ordenagailurako konfiguratzeko tresnak +Comment[fr]=Outils de configuration pour votre ordinateur Comment[it]=Strumenti di configurazione per il tuo computer Comment[nl]=Hulpmiddelen voor configuratie voor uw computer Comment[pl]=Narzędzia ustawień dla twojego komputera Comment[pt]=Ferramentas de configuração para o seu computador +Comment[pt_BR]=Ferramentas de configuração para seu computador +Comment[ru]=Инструменты для настройки компьютера Comment[sr]=Алатке за подешавање рачунара Comment[sr@ijekavian]=Алатке за подешавање рачунара Comment[sr@ijekavianlatin]=Alatke za podešavanje računara diff --git a/categories/old/settings-window-behaviour.desktop b/categories/old/settings-window-behaviour.desktop --- a/categories/old/settings-window-behaviour.desktop +++ b/categories/old/settings-window-behaviour.desktop @@ -25,7 +25,7 @@ Name[el]=Συμπεριφορά παραθύρων Name[en_GB]=Window Behaviour Name[eo]=Fenestrokonduto -Name[es]=Comportamiento de la ventana +Name[es]=Comportamiento de las ventanas Name[et]=Akende käitumine Name[eu]=Leihoaren portaera Name[fa]=رفتار پنجره diff --git a/classic/settings-classic-view.desktop b/classic/settings-classic-view.desktop --- a/classic/settings-classic-view.desktop +++ b/classic/settings-classic-view.desktop @@ -68,20 +68,23 @@ Name[el]=Προβολή δέντρου Name[es]=Vista de árbol Name[eu]=Zuhaitz-ikuspegia +Name[fr]=Vue arborescente Name[gl]=Vista en árbore Name[it]=Vista ad albero Name[nl]=Boomstructuurweergave Name[pl]=Widok drzewa Name[pt]=Vista em Árvore +Name[pt_BR]=Exibição em árvore +Name[ru]=Дерево Name[sk]=Stromové zobrazenie Name[sr]=Приказ стабла Name[sr@ijekavian]=Приказ стабла Name[sr@ijekavianlatin]=Prikaz stabla Name[sr@latin]=Prikaz stabla Name[sv]=Trädvy Name[uk]=Ієрархічний перегляд Name[x-test]=xxTree Viewxx -Name[zh_CN]=树形视图 +Name[zh_CN]=树状视图 Name[zh_TW]=樹狀檢視 Comment=A Classic KDE 3 KControl style system settings view. Comment[ar]=عرض إعدادات النّظام لنمط KControl في كدي 3 التّقليديّ. diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -21,13 +21,25 @@ #define SIDEBARMODE_H #include "BaseMode.h" +#include class ModuleView; class KAboutData; class QModelIndex; class QAbstractItemView; class QAbstractItemModel; +class FocusHackWidget : public QWidget { + Q_OBJECT +public: + FocusHackWidget(QWidget *parent = nullptr); + ~FocusHackWidget(); + +public Q_SLOTS: + void focusNext(); + void focusPrevious(); +}; + class SidebarMode : public BaseMode { Q_OBJECT diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -63,6 +63,22 @@ K_PLUGIN_FACTORY( SidebarModeFactory, registerPlugin(); ) +FocusHackWidget::FocusHackWidget(QWidget *parent) + : QWidget(parent) +{} +FocusHackWidget::~FocusHackWidget() +{} + +void FocusHackWidget::focusNext() +{ + focusNextChild(); +} + +void FocusHackWidget::focusPrevious() +{ + focusNextPrevChild(false); +} + class SubcategoryModel : public QStandardItemModel { public: @@ -221,7 +237,7 @@ KPackage::Package package; SubcategoryModel * subCategoryModel; MostUsedModel * mostUsedModel; - QWidget * mainWidget; + FocusHackWidget * mainWidget; QQuickWidget * placeHolderWidget; QHBoxLayout * mainLayout; KDeclarative::KDeclarative kdeclarative; @@ -320,7 +336,7 @@ d->mostUsedModel = new MostUsedModel( this ); d->subCategoryModel = new SubcategoryModel( d->searchModel, this ); - d->mainWidget = new QWidget(); + d->mainWidget = new FocusHackWidget(); d->mainWidget->installEventFilter(this); d->mainLayout = new QHBoxLayout(d->mainWidget); d->mainLayout->setContentsMargins(0, 0, 0, 0); @@ -435,15 +451,17 @@ } d->quickWidget = new QQuickWidget(d->mainWidget); + d->quickWidget->quickWindow()->setTitle(i18n("Sidebar")); d->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + d->quickWidget->engine()->rootContext()->setContextProperty("systemsettings", this); d->package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML"); d->package.setPath(QStringLiteral("org.kde.systemsettings.sidebar")); d->kdeclarative.setDeclarativeEngine(d->quickWidget->engine()); d->kdeclarative.setupBindings(); - d->quickWidget->setSource(d->package.filePath("mainscript")); + d->quickWidget->setSource(QUrl::fromLocalFile(d->package.filePath("mainscript"))); const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt(); if (rootImplicitWidth != 0) { @@ -460,16 +478,22 @@ d->quickWidget->setFixedWidth(240); } }); + connect(d->quickWidget->rootObject(), SIGNAL(focusNextRequest()), d->mainWidget, SLOT(focusNext())); + connect(d->quickWidget->rootObject(), SIGNAL(focusPreviousRequest()), d->mainWidget, SLOT(focusPrevious())); d->quickWidget->installEventFilter(this); d->toolTipManager = new ToolTipManager(d->searchModel, d->quickWidget); d->placeHolderWidget = new QQuickWidget(d->mainWidget); + d->placeHolderWidget->quickWindow()->setTitle(i18n("Most Used")); d->placeHolderWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); d->placeHolderWidget->engine()->rootContext()->setContextObject(new KLocalizedContext(d->placeHolderWidget)); d->placeHolderWidget->engine()->rootContext()->setContextProperty("systemsettings", this); - d->placeHolderWidget->setSource(d->package.filePath("ui", "introPage.qml")); + d->placeHolderWidget->setSource(QUrl::fromLocalFile(d->package.filePath("ui", "introPage.qml"))); + connect(d->placeHolderWidget->rootObject(), SIGNAL(focusNextRequest()), d->mainWidget, SLOT(focusNext())); + connect(d->placeHolderWidget->rootObject(), SIGNAL(focusPreviousRequest()), d->mainWidget, SLOT(focusPrevious())); + d->placeHolderWidget->installEventFilter(this); d->mainLayout->addWidget( d->quickWidget ); d->moduleView->hide(); @@ -482,8 +506,28 @@ bool SidebarMode::eventFilter(QObject* watched, QEvent* event) { - //TODO: patch Qt - if (watched == d->quickWidget && event->type() == QEvent::Leave) { + //FIXME: those are all workarounds around the QQuickWidget brokeness + if ((watched == d->quickWidget || watched == d->placeHolderWidget) + && event->type() == QEvent::KeyPress) { + //allow tab navigation inside the qquickwidget + QKeyEvent *ke = static_cast(event); + QQuickWidget *qqw = static_cast(watched); + if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { + QCoreApplication::sendEvent(qqw->quickWindow(), event); + return true; + } + } else if ((watched == d->quickWidget || watched == d->placeHolderWidget) + && event->type() == QEvent::FocusIn) { + QFocusEvent *fe = static_cast(event); + QQuickWidget *qqw = static_cast(watched); + if (qqw && qqw->rootObject()) { + if (fe->reason() == Qt::TabFocusReason) { + QMetaObject::invokeMethod(qqw->rootObject(), "focusFirstChild"); + } else if (fe->reason() == Qt::BacktabFocusReason) { + QMetaObject::invokeMethod(qqw->rootObject(), "focusLastChild"); + } + } + } else if (watched == d->quickWidget && event->type() == QEvent::Leave) { QCoreApplication::sendEvent(d->quickWidget->quickWindow(), event); } else if (watched == d->mainWidget && event->type() == QEvent::Resize) { emit widthChanged(); diff --git a/sidebar/ToolTips/tooltipmanager.cpp b/sidebar/ToolTips/tooltipmanager.cpp --- a/sidebar/ToolTips/tooltipmanager.cpp +++ b/sidebar/ToolTips/tooltipmanager.cpp @@ -128,7 +128,11 @@ QWidget * tip = createTipContent( menuItem ); - d->tooltip->showBelow(d->itemRect, tip, d->view->nativeParentWidget()->windowHandle()); + if (qApp->isRightToLeft()) { + d->tooltip->showAt(d->itemRect.topLeft() - QPoint(d->tooltip->width(), 0), tip, d->view->nativeParentWidget()->windowHandle()); + } else { + d->tooltip->showAt(d->itemRect.topRight(), tip, d->view->nativeParentWidget()->windowHandle()); + } connect(d->tooltip, &KToolTipWidget::hidden, tip, &QObject::deleteLater); diff --git a/sidebar/package/contents/ui/CategoriesPage.qml b/sidebar/package/contents/ui/CategoriesPage.qml --- a/sidebar/package/contents/ui/CategoriesPage.qml +++ b/sidebar/package/contents/ui/CategoriesPage.qml @@ -29,10 +29,9 @@ header: Item { width: mainColumn.width - height: searchLayout.implicitHeight + Kirigami.Units.smallSpacing * 2 + height: Kirigami.Units.gridUnit * 2.5 RowLayout { id: searchLayout - height: menuButton.height spacing: Kirigami.Units.smallSpacing anchors { fill: parent @@ -43,6 +42,9 @@ iconName: "application-menu" Layout.maximumWidth: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.smallSpacing * 2 Layout.maximumHeight: width + Keys.onBacktabPressed: { + root.focusPreviousRequest() + } menu: QtControls.Menu { id: globalMenu QtControls.MenuItem { @@ -132,6 +134,16 @@ model: systemsettings.categoryModel currentIndex: systemsettings.activeCategory onContentYChanged: systemsettings.hideToolTip(); + activeFocusOnTab: true + keyNavigationWraps: true + Accessible.role: Accessible.List + Keys.onTabPressed: { + if (applicationWindow().wideScreen) { + subCategoryColumn.focus = true; + } else { + root.focusNextRequest(); + } + } section { property: "categoryDisplayRole" delegate: Kirigami.AbstractListItem { @@ -160,8 +172,9 @@ icon: model.decoration label: model.display separatorVisible: false - activeFocusOnTab: root.pageStack.currentIndex == 0 highlighted: focus + Accessible.role: Accessible.ListItem + Accessible.name: model.display onClicked: { if (systemsettings.activeCategory == index) { root.pageStack.currentIndex = 1; @@ -173,26 +186,16 @@ onHoveredChanged: { if (hovered) { systemsettings.requestToolTip(index, delegate.mapToItem(root, 0, 0, width, height)); + } else { + systemsettings.hideToolTip(); } } onFocusChanged: { if (focus) { onCurrentIndexChanged: categoryView.positionViewAtIndex(index, ListView.Contain); } } checked: systemsettings.activeCategory == index - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Up: - delegate.nextItemInFocusChain(false).forceActiveFocus(); - break; - case Qt.Key_Down: - delegate.nextItemInFocusChain(true).forceActiveFocus(); - break; - default: - break; - } - } } } } diff --git a/sidebar/package/contents/ui/IntroIcon.qml b/sidebar/package/contents/ui/IntroIcon.qml --- a/sidebar/package/contents/ui/IntroIcon.qml +++ b/sidebar/package/contents/ui/IntroIcon.qml @@ -23,7 +23,7 @@ MouseArea { - id: root + id: item property alias icon: iconItem.source property alias text: label.text property string module @@ -33,29 +33,56 @@ cursorShape: Qt.PointingHandCursor Layout.fillWidth: true Layout.alignment: Qt.AlignTop + activeFocusOnTab: true onClicked: systemsettings.loadMostUsed(index); + + Keys.onTabPressed: { + if (index < (mostUsedRepeater.count-1)) { + event.accepted = false; + } else { + root.focusNextRequest(); + } + } + Keys.onBacktabPressed: { + if (index > 0) { + event.accepted = false; + } else { + root.focusPreviousRequest(); + } + } + Kirigami.Separator { + anchors{ + left: parent.left + right: parent.right + bottom: parent.bottom + } + visible: item.activeFocus + color: Kirigami.Theme.highlightColor + } ColumnLayout { id: column width: parent.width Kirigami.Icon { id: iconItem Layout.alignment: Qt.AlignHCenter - Layout.minimumWidth: root.iconSize + Layout.minimumWidth: item.iconSize Layout.minimumHeight: Layout.minimumWidth height: width } QQC2.Label { id: label Layout.fillWidth: true - Layout.maximumWidth: root.width + Layout.maximumWidth: item.width Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } } + Accessible.role: Accessible.Button Accessible.name: label.text + Accessible.description: i18n("Most used modeule number %1", index+1) Accessible.onPressAction: systemsettings.loadMostUsed(index); } diff --git a/sidebar/package/contents/ui/SubCategoryPage.qml b/sidebar/package/contents/ui/SubCategoryPage.qml --- a/sidebar/package/contents/ui/SubCategoryPage.qml +++ b/sidebar/package/contents/ui/SubCategoryPage.qml @@ -27,7 +27,7 @@ id: subCategoryColumn header: MouseArea { width: subCategoryColumn.width - height: toolButtonIcon.height + Kirigami.Units.smallSpacing * 4 + height: Kirigami.Units.gridUnit * 2.5 enabled: !applicationWindow().wideScreen onClicked: root.pageStack.currentIndex = 0; Accessible.role: Accessible.Button @@ -37,54 +37,47 @@ id: headerControls anchors.fill: parent - anchors.margins: Kirigami.Units.smallSpacing QtControls.ToolButton { id: backButton anchors.fill: parent visible: !applicationWindow().wideScreen onClicked: root.pageStack.currentIndex = 0; - Item { + RowLayout { anchors.fill: parent opacity: 0.3 Kirigami.Icon { id: toolButtonIcon - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - height: Kirigami.Units.iconSizes.small - width: height + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: Kirigami.Units.iconSizes.small + Layout.preferredWidth: Layout.preferredHeight + source: "go-previous" } - Kirigami.Label { - anchors { - left: toolButtonIcon.right - right: parent.right - leftMargin: Kirigami.Units.smallSpacing - verticalCenter: parent.verticalCenter - } + QtControls2.Label { + Layout.fillWidth: true + Layout.fillHeight: true + height: toolButtonIcon.height text: subCategoryColumn.title + verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - //FIXME: kirigami bug, why? + //FIXME: QtControls bug, why? Component.onCompleted: font.bold = true } } } - Kirigami.Label { - anchors { - fill: parent - leftMargin: Kirigami.Units.smallSpacing - } + QtControls2.Label { + anchors.verticalCenter: parent.verticalCenter + x: y text: subCategoryColumn.title elide: Text.ElideRight visible: !backButton.visible opacity: 0.3 - //FIXME: kirigami bug, why? + //FIXME: QtControls bug, why? Component.onCompleted: font.bold = true } } @@ -105,6 +98,13 @@ anchors.fill: parent model: systemsettings.subCategoryModel currentIndex: systemsettings.activeSubCategory + activeFocusOnTab: true + keyNavigationWraps: true + Accessible.role: Accessible.List + Keys.onTabPressed: root.focusNextRequest(); + Keys.onBacktabPressed: { + mainColumn.focus = true; + } onCountChanged: { if (count > 1) { if (root.pageStack.depth < 2) { @@ -127,7 +127,6 @@ icon: model.decoration label: model.display separatorVisible: false - activeFocusOnTab: root.pageStack.currentIndex == 1 highlighted: focus onClicked: systemsettings.activeSubCategory = index onFocusChanged: { @@ -139,23 +138,6 @@ //checkable: false //FIXME: Qt 5.7 doesn't have checkable, this way fails at runtime but still works correctly on 5.7 Component.onCompleted: delegate.checkable = true; - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Up: - delegate.nextItemInFocusChain(false).forceActiveFocus(); - break; - case Qt.Key_Down: - delegate.nextItemInFocusChain(true).forceActiveFocus(); - break; - case Qt.Key_left: - case Qt.Key_Backspace: - root.pageStack.currentIndex = 0; - mainColumn.forceActiveFocus(); - break; - default: - break; - } - } } } } diff --git a/sidebar/package/contents/ui/introPage.qml b/sidebar/package/contents/ui/introPage.qml --- a/sidebar/package/contents/ui/introPage.qml +++ b/sidebar/package/contents/ui/introPage.qml @@ -23,6 +23,18 @@ Rectangle { id: root color: Kirigami.Theme.backgroundColor + + signal focusNextRequest() + signal focusPreviousRequest() + + function focusFirstChild() { + iconsRow.children[0].focus = true; + } + + function focusLastChild() { + iconsRow.children[iconsRow.children.length-1].focus = true; + } + ColumnLayout { anchors { bottom: separator.top @@ -70,6 +82,7 @@ : (iconsRow.spaceForIcon >= Kirigami.Units.iconSizes.large ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium) Repeater { + id: mostUsedRepeater model: systemsettings.mostUsedModel delegate: IntroIcon { icon: model.decoration diff --git a/sidebar/package/contents/ui/main.qml b/sidebar/package/contents/ui/main.qml --- a/sidebar/package/contents/ui/main.qml +++ b/sidebar/package/contents/ui/main.qml @@ -23,10 +23,21 @@ Kirigami.ApplicationItem { id: root - implicitWidth: wideScreen ? Kirigami.Units.gridUnit * 24 : Kirigami.Units.gridUnit * 12 + implicitWidth: wideScreen ? Kirigami.Units.gridUnit * 30 : Kirigami.Units.gridUnit * 15 pageStack.initialPage: mainColumn pageStack.defaultColumnWidth: wideScreen ? root.width / 2 : root.width + signal focusNextRequest() + signal focusPreviousRequest() + + function focusFirstChild() { + mainColumn.focus = true; + } + + function focusLastChild() { + subCategoryColumn.focus = true; + } + wideScreen: pageStack.depth > 1 && systemsettings.width > Kirigami.Units.gridUnit * 70 CategoriesPage { id: mainColumn diff --git a/sidebar/package/metadata.desktop b/sidebar/package/metadata.desktop --- a/sidebar/package/metadata.desktop +++ b/sidebar/package/metadata.desktop @@ -18,6 +18,8 @@ Name[pa]=ਝਲਕ ਬਾਹੀ Name[pl]=Widok paska bocznego Name[pt]=Vista da Barra Lateral +Name[pt_BR]=Exibição em barra lateral +Name[ru]=Боковая панель Name[sk]=Pohľad bočného panelu Name[sl]=Pogled stranske vrstice Name[sr]=Приказ бочне траке @@ -48,6 +50,8 @@ Comment[nl]=Gecategoriseerde zijbalkstijl Comment[pl]=Skategoryzowany wygląd paska bocznego Comment[pt]=Estilo de barra lateral por categorias +Comment[pt_BR]=Estilo da barra lateral categorizada +Comment[ru]=Список в боковой панели, разбитый на категории Comment[sk]=Kategorizovaný štýl bočného panelu Comment[sl]=Kategoriziran slog stranske vrstice Comment[sr]=Стиле бочне траке по категоријама diff --git a/sidebar/settings-sidebar-view.desktop b/sidebar/settings-sidebar-view.desktop --- a/sidebar/settings-sidebar-view.desktop +++ b/sidebar/settings-sidebar-view.desktop @@ -80,6 +80,8 @@ Name[pa]=ਝਲਕ ਬਾਹੀ Name[pl]=Widok paska bocznego Name[pt]=Vista da Barra Lateral +Name[pt_BR]=Exibição em barra lateral +Name[ru]=Боковая панель Name[sk]=Pohľad bočného panelu Name[sl]=Pogled stranske vrstice Name[sr]=Приказ бочне траке @@ -110,6 +112,8 @@ Comment[nl]=Gecategoriseerde zijbalkstijl Comment[pl]=Skategoryzowany wygląd paska bocznego Comment[pt]=Estilo de barra lateral por categorias +Comment[pt_BR]=Estilo da barra lateral categorizada +Comment[ru]=Список в боковой панели, разбитый на категории Comment[sk]=Kategorizovaný štýl bočného panelu Comment[sl]=Kategoriziran slog stranske vrstice Comment[sr]=Стиле бочне траке по категоријама