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