diff --git a/CMakeLists.txt b/CMakeLists.txt
index fafbb8d..d7c71a6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,7 @@ include_directories(src/appdata)
include_directories(src/settings)
include_directories(src/uiconfig)
include_directories(src/windows)
+include_directories(src/utils)
# 用于Qt Creator识别自定义qml模块的导入路径
list(APPEND QML_MODULE_DIRS "${PROJECT_SOURCE_DIR}/qml")
@@ -92,6 +93,7 @@ set(SOURCE_FILES
src/extension/menu-extension.cpp src/extension/menu-extension.h
src/extension/extensions/folder-extension.cpp src/extension/extensions/folder-extension.h
src/extension/extensions/recent-file-extension.cpp src/extension/extensions/recent-file-extension.h
+ src/utils/app-page-header-utils.cpp src/utils/app-page-header-utils.h
src/utils/power-button.cpp src/utils/power-button.h
)
diff --git a/qml/AppUI/AppPage.qml b/qml/AppUI/AppPage.qml
index 3f02d99..4cd8818 100644
--- a/qml/AppUI/AppPage.qml
+++ b/qml/AppUI/AppPage.qml
@@ -31,6 +31,11 @@ AppControls2.StyleBackground {
anchors.topMargin: parent.radius
spacing: 4
+ AppPageHeader {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 40
+ }
+
AppList {
id: appList
Layout.fillWidth: true
diff --git a/qml/AppUI/AppPageHeader.qml b/qml/AppUI/AppPageHeader.qml
new file mode 100644
index 0000000..4efe42f
--- /dev/null
+++ b/qml/AppUI/AppPageHeader.qml
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023, KylinSoft Co., Ltd.
+ *
+ * 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 3 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.0
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.5
+
+import AppControls2 1.0 as AppControls2
+import org.ukui.menu.core 1.0
+import org.ukui.menu.utils 1.0
+
+Item {
+ id: appPageHeader
+ property string title: ""
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 16
+ Text {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ verticalAlignment: Qt.AlignVCenter
+ text: appPageHeader.title
+ }
+
+ ListView {
+ Layout.preferredWidth: childrenRect.width
+ Layout.preferredHeight: 32
+ Layout.alignment: Qt.AlignVCenter
+
+ clip: true
+ orientation: ListView.Horizontal
+
+ model: appPageHeaderUtils.model(PluginGroup.Button)
+ delegate: Item {
+ width: 32
+ height: ListView.view ? ListView.view.height : 0
+ Image {
+ anchors.fill: parent
+ source: model.icon
+ }
+ }
+ }
+
+ Row {
+ Layout.preferredWidth: childrenRect.width
+ Layout.preferredHeight: 32
+ Layout.rightMargin: 8
+ Layout.alignment: Qt.AlignVCenter
+
+ Image {
+ id: providerIcon
+ width: 32
+ height: width
+ anchors.verticalCenter: parent.verticalCenter
+ source: "image://appicon/ukui-selected"
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ sortMenu.sortMenuModel.autoSwitchProvider();
+ }
+ }
+ }
+
+ Image {
+ id: providerSelector
+ width: 16
+ height: width
+ anchors.verticalCenter: parent.verticalCenter
+ source: "image://appicon/ukui-down.symbolic"
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ sortMenu.show();
+ }
+ }
+ }
+
+ function updateProviderIcon() {
+ providerIcon.source = sortMenu.sortMenuModel.currentProviderIcon();
+ }
+
+ Component.onCompleted: {
+ updateProviderIcon();
+ sortMenu.sortMenuModel.currentIndexChanged.connect(updateProviderIcon);
+ }
+ }
+ }
+
+ Menu {
+ id: sortMenu
+ width: 128
+ height: 112
+ clip: true
+ property var sortMenuModel: appPageHeaderUtils.model(PluginGroup.SortMenuItem)
+
+ function show() {
+ popup();
+ }
+
+ onOpened: {
+ providerSelector.source = "image://appicon/ukui-up.symbolic";
+ }
+
+ onClosed: {
+ providerSelector.source = "image://appicon/ukui-down.symbolic";
+ }
+
+ // TODO 添加边框阴影
+ background: AppControls2.StyleBackground {
+ paletteRole: Palette.Window
+ useStyleTransparent: false
+ radius: 8
+ }
+
+ contentItem: ListView {
+ clip: true
+ model: sortMenu.sortMenuModel
+ delegate: Item {
+ width: ListView.view ? ListView.view.width : 0
+ height: 48
+ AppControls2.StyleBackground {
+ anchors.fill: parent
+ anchors.margins: 8
+ radius: 4
+ paletteRole: (model.isChecked || mouseArea.isHoverd) ? Palette.Text : Palette.Window
+ useStyleTransparent: false
+ alpha: (model.isChecked || mouseArea.isHoverd) ? 0.2 : 1
+
+ Item {
+ anchors.fill: parent
+ anchors.margins: 8
+ Image {
+ visible: model.isChecked
+ anchors.verticalCenter: parent.verticalCenter
+ width: 16
+ height: 16
+ source: "image://appicon/object-select.symbolic"
+ }
+ AppControls2.StyleText {
+ x: 24
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width - x
+ height: parent.height
+ text: model.name
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ property bool isHoverd: false
+ onClicked: {
+ if (model.isChecked) {
+ return;
+ }
+ appPageHeaderUtils.activateProvider(model.id);
+ }
+ onEntered: {
+ isHoverd = true;
+ }
+ onExited: {
+ isHoverd = false;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/qml/AppUI/Sidebar.qml b/qml/AppUI/Sidebar.qml
index bff5f7e..832b204 100644
--- a/qml/AppUI/Sidebar.qml
+++ b/qml/AppUI/Sidebar.qml
@@ -1,9 +1,29 @@
+/*
+ * Copyright (C) 2023, KylinSoft Co., Ltd.
+ *
+ * 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 3 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 QtQml 2.12
import QtQuick 2.0
import QtQuick.Layouts 1.12
-import org.ukui.menu.utils 1.0
import QtQuick.Controls 2.5
+
import AppControls2 1.0 as AppControls2
import org.ukui.menu.core 1.0
+import org.ukui.menu.utils 1.0
Item {
ColumnLayout {
diff --git a/qml/AppUI/qmldir b/qml/AppUI/qmldir
index ebe7e45..9c5c00f 100644
--- a/qml/AppUI/qmldir
+++ b/qml/AppUI/qmldir
@@ -4,3 +4,4 @@ AppList 1.0 AppList.qml
Sidebar 1.0 Sidebar.qml
NormalUI 1.0 NormalUI.qml
FullScreenUI 1.0 FullScreenUI.qml
+AppPageHeader 1.0 AppPageHeader.qml
diff --git a/qml/qml.qrc b/qml/qml.qrc
index a328b2e..dc9f9f3 100644
--- a/qml/qml.qrc
+++ b/qml/qml.qrc
@@ -10,6 +10,7 @@
AppUI/AppPage.qml
AppUI/Sidebar.qml
AppUI/AppList.qml
+ AppUI/AppPageHeader.qml
AppControls2/qmldir
AppControls2/App.qml
AppControls2/ScrollBar.qml
diff --git a/src/appdata/data-provider-manager.cpp b/src/appdata/data-provider-manager.cpp
index 63d87ea..0385691 100644
--- a/src/appdata/data-provider-manager.cpp
+++ b/src/appdata/data-provider-manager.cpp
@@ -104,7 +104,7 @@ QVector DataProviderManager::providers(PluginGroup::Group group) c
void DataProviderManager::activateProvider(const QString &id)
{
- if (!m_providers.contains(id)) {
+ if (!m_providers.contains(id) || m_activatedPlugin == id) {
return;
}
diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp
index d44c6a8..9a9b360 100644
--- a/src/ukui-menu-application.cpp
+++ b/src/ukui-menu-application.cpp
@@ -25,7 +25,8 @@
#include "app-icon-provider.h"
#include "menu-main-window.h"
#include "extension/menu-extension.h"
-#include "utils/power-button.h"
+#include "app-page-header-utils.h"
+#include "power-button.h"
#include
#include
@@ -79,6 +80,7 @@ void UkuiMenuApplication::initQmlEngine()
context->setContextProperty("menuSetting", MenuSetting::instance());
context->setContextProperty("modelManager", new ModelManager(this));
context->setContextProperty("extensionManager", MenuExtension::instance());
+ context->setContextProperty("appPageHeaderUtils", new AppPageHeaderUtils(this));
// MenuMainWindow
// const QUrl url(QStringLiteral("qrc:/qml/MenuMainWindow.qml"));
diff --git a/src/utils/app-page-header-utils.cpp b/src/utils/app-page-header-utils.cpp
new file mode 100644
index 0000000..5c2a077
--- /dev/null
+++ b/src/utils/app-page-header-utils.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023, KylinSoft Co., Ltd.
+ *
+ * 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 3 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 .
+ *
+ */
+
+#include "app-page-header-utils.h"
+#include "data-provider-manager.h"
+
+#include
+#include
+#include
+#include
+
+namespace UkuiMenu {
+
+class ProviderModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum Roles {Id = 0, Name, Icon, Title, IsChecked};
+ explicit ProviderModel(QVector, QObject *parent = nullptr);
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+ void updateCurrentPId(const QString &providerId);
+ //自动在各个插件之间切换
+ Q_INVOKABLE void autoSwitchProvider();
+ Q_INVOKABLE QString currentProviderIcon();
+
+Q_SIGNALS:
+ void currentIndexChanged();
+
+private:
+ int indexOfProvider(const QString &providerId);
+
+private:
+ int m_currentIndex = 0;
+ QString m_currentId;
+ QVector m_providers;
+ QHash m_roleNames;
+};
+
+// ====== ProviderModel ======
+ProviderModel::ProviderModel(QVector infos, QObject *parent)
+: QAbstractListModel(parent), m_providers(std::move(infos))
+{
+ std::sort(m_providers.begin(), m_providers.end(), [=](const ProviderInfo &a, const ProviderInfo &b) {
+ return a.index < b.index;
+ });
+
+ m_roleNames.insert(Id, "id");
+ m_roleNames.insert(Name, "name");
+ m_roleNames.insert(Icon, "icon");
+ m_roleNames.insert(Title, "title");
+ m_roleNames.insert(IsChecked, "isChecked");
+}
+
+int ProviderModel::rowCount(const QModelIndex &parent) const
+{
+ return m_providers.size();
+}
+
+QVariant ProviderModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ if (row < 0 || row > m_providers.size()) {
+ return {};
+ }
+
+ switch (role) {
+ case Id:
+ return m_providers.at(row).id;
+ case Name:
+ return m_providers.at(row).name;
+ case Icon:
+ return m_providers.at(row).icon;
+ case Title:
+ return m_providers.at(row).title;
+ case IsChecked:
+ return (m_currentId == m_providers.at(row).id);
+ default:
+ break;
+ }
+
+ return {};
+}
+
+QHash ProviderModel::roleNames() const
+{
+ return m_roleNames;
+}
+
+void ProviderModel::updateCurrentPId(const QString &providerId)
+{
+ int index = indexOfProvider(providerId);
+ if (index < 0) {
+ return;
+ }
+
+ m_currentId = providerId;
+ m_currentIndex = index;
+
+ Q_EMIT beginResetModel();
+ Q_EMIT endResetModel();
+ Q_EMIT currentIndexChanged();
+}
+
+int ProviderModel::indexOfProvider(const QString &providerId)
+{
+ for (int i = 0; i < m_providers.size(); ++i) {
+ if(providerId == m_providers.at(i).id) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void ProviderModel::autoSwitchProvider()
+{
+ m_currentIndex = (m_currentIndex + 1) % m_providers.size();
+ DataProviderManager::instance()->activateProvider(m_providers.at(m_currentIndex).id);
+}
+
+QString ProviderModel::currentProviderIcon()
+{
+ return data(createIndex(m_currentIndex, 0), Icon).toString();
+}
+
+// ====== AppPageHeaderUtils ======
+AppPageHeaderUtils::AppPageHeaderUtils(QObject *parent) : QObject(parent)
+{
+ qRegisterMetaType("ProviderModel*");
+ qRegisterMetaType("PluginGroup::Group");
+ qmlRegisterUncreatableType("org.ukui.menu.utils", 1, 0, "PluginGroup", "Use enums only.");
+
+ for (int i = PluginGroup::Button; i <= PluginGroup::SortMenuItem; ++i) {
+ //qDebug() << "==PluginGroup==" << PluginGroup::Group(i);
+ auto *model = new ProviderModel(DataProviderManager::instance()->providers(PluginGroup::Group(i)), this);
+ model->updateCurrentPId(DataProviderManager::instance()->activatedProvider());
+ m_models.insert(PluginGroup::Group(i), model);
+ }
+
+ connect(DataProviderManager::instance(), &DataProviderManager::pluginChanged,
+ this, &AppPageHeaderUtils::onPluginChanged);
+}
+
+void AppPageHeaderUtils::onPluginChanged(const QString &id, PluginGroup::Group group)
+{
+ m_models.value(group)->updateCurrentPId(id);
+}
+
+ProviderModel *AppPageHeaderUtils::model(PluginGroup::Group group)
+{
+ return m_models.value(group);
+}
+
+void AppPageHeaderUtils::activateProvider(const QString &name)
+{
+ DataProviderManager::instance()->activateProvider(name);
+}
+
+} // UkuiMenu
+
+#include "app-page-header-utils.moc"
diff --git a/src/utils/app-page-header-utils.h b/src/utils/app-page-header-utils.h
new file mode 100644
index 0000000..0636930
--- /dev/null
+++ b/src/utils/app-page-header-utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023, KylinSoft Co., Ltd.
+ *
+ * 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 3 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_APP_PAGE_HEADER_UTILS_H
+#define UKUI_MENU_APP_PAGE_HEADER_UTILS_H
+
+#include
+#include
+
+#include "data-provider-plugin-iface.h"
+
+namespace UkuiMenu {
+
+class ProviderModel;
+
+class AppPageHeaderUtils : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AppPageHeaderUtils(QObject *parent = nullptr);
+
+ // 激活某插件
+ Q_INVOKABLE void activateProvider(const QString &name);
+ // 获取不同的model
+ Q_INVOKABLE ProviderModel *model(PluginGroup::Group group);
+
+private Q_SLOTS:
+ void onPluginChanged(const QString &id, PluginGroup::Group group);
+
+private:
+ QHash m_models;
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_APP_PAGE_HEADER_UTILS_H