diff --git a/qml/AppUI/NormalUI.qml b/qml/AppUI/NormalUI.qml index 5db239f..85b58ae 100644 --- a/qml/AppUI/NormalUI.qml +++ b/qml/AppUI/NormalUI.qml @@ -1,6 +1,5 @@ import QtQuick 2.12 import QtQuick.Controls 2.0 as QQC2 -import org.ukui.menu.extension 1.0 Item { Row { @@ -12,19 +11,7 @@ Item { } Sidebar { - width: parent.width - 400; - height: parent.height; - - MouseArea { - anchors.fill: parent; - onClicked: { - mainWindow.isFullScreen = !mainWindow.isFullScreen; - } - } - } - - UkuiMenuExtension { - width: 100; + width: parent.width - 300; height: parent.height; } } diff --git a/qml/AppUI/Sidebar.qml b/qml/AppUI/Sidebar.qml index e668f72..7443747 100644 --- a/qml/AppUI/Sidebar.qml +++ b/qml/AppUI/Sidebar.qml @@ -1,8 +1,93 @@ import QtQuick 2.0 -import AppControls2 1.0 as AppControls2 +import QtQuick.Layouts 1.12 Item { - AppControls2.AppTest { - anchors.fill: parent; + ColumnLayout { + anchors.fill: parent + Item { + Layout.fillWidth: true + Layout.preferredHeight: 30 + + ListView { + id: extensionListView + anchors.fill: parent + + orientation: ListView.Horizontal + model: extensionManager.extensionModel() + delegate: headerDelegate + + function send(data) { + if (currentItem !== null) { + model.send(currentIndex, data); + } + } + + onCurrentIndexChanged: { + if (currentItem !== null) { + currentItem.select(); + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Loader { + id: extensionLoader + anchors.fill: parent + clip: true + focus: true + onLoaded: { + item.send.connect(extensionListView.send); + } + } + } + } + + Component { + id: headerDelegate + Item { + property var extensionData: model.data + width: 100 + height: ListView.view ? ListView.view.height : 0 + + onExtensionDataChanged: { + if (extensionLoader.source === model.url) { + extensionLoader.item.extensionData = extensionData; + } + } + + function select() { + if (extensionLoader.source !== model.url) { + extensionLoader.setSource(model.url, {extensionData: extensionData}); + } + } + + Text { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.bold: true + wrapMode: Text.ElideRight + + color: parent.ListView.isCurrentItem ? "blue" : "black" + text: model.name + } + + MouseArea { + anchors.fill: parent + onClicked: { + parent.ListView.view.currentIndex = model.index; + } + } + } + } + + Component.onCompleted: { + if (extensionListView.count > 0) { + extensionListView.currentIndex = 0; + } } } diff --git a/qml/main.qml b/qml/main.qml index 204e2e5..d383f54 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2022, 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.12 import org.ukui.menu.core 1.0 @@ -17,18 +35,7 @@ MenuMainWindow { } AppUI.NormalUI { - anchors.fill: mainWindow.contentItem; - transitions: Transition { - NumberAnimation { - easing.type: Easing.OutBounce; - properties: "width,height"; - duration: 4000; - } - } - Behavior on width { - PropertyAnimation { - - } - } + parent: mainWindow.contentItem + anchors.fill: parent } } diff --git a/qml/org/ukui/menu/extension/UkuiMenuExtension.qml b/qml/org/ukui/menu/extension/UkuiMenuExtension.qml index 4c59cc3..ef817fe 100644 --- a/qml/org/ukui/menu/extension/UkuiMenuExtension.qml +++ b/qml/org/ukui/menu/extension/UkuiMenuExtension.qml @@ -1,7 +1,6 @@ import QtQuick 2.0 Item { - id: extensionView; - property var data; + property var extensionData; signal send(var data); } diff --git a/src/extension/menu-extension-iface.h b/src/extension/menu-extension-iface.h index ff854cc..3df7903 100644 --- a/src/extension/menu-extension-iface.h +++ b/src/extension/menu-extension-iface.h @@ -21,6 +21,7 @@ #include +#include #include #include @@ -30,9 +31,10 @@ class MenuExtensionIFace : public QObject { Q_OBJECT public: + explicit MenuExtensionIFace(QObject *parent = nullptr) : QObject(parent) {}; virtual int index() = 0; virtual QString name() = 0; - virtual QString url() = 0; + virtual QUrl url() = 0; virtual QVariantMap data() = 0; virtual void receive(QVariantMap data) = 0; diff --git a/src/extension/menu-extension.cpp b/src/extension/menu-extension.cpp index bd80a56..a0cc168 100644 --- a/src/extension/menu-extension.cpp +++ b/src/extension/menu-extension.cpp @@ -18,7 +18,132 @@ #include "menu-extension.h" +#include +#include + namespace UkuiMenu { +MenuExtension *MenuExtension::instance() +{ + static MenuExtension menuExtension; + return &menuExtension; +} + +MenuExtension::MenuExtension() +{ + qRegisterMetaType("ExtensionModel*"); + + // TODO load extension from filesystem. + + // register extension. + + initModel(); +} + +void MenuExtension::registerExtension(MenuExtensionIFace *extension) +{ + if (!extension) { + return; + } + + extension->setParent(this); + if (m_extensions.contains(extension->name())) { + return; + } + + m_extensions.insert(extension->name(), extension); +} + +ExtensionModel *MenuExtension::extensionModel() +{ + return m_model; +} + +void MenuExtension::initModel() +{ + QVector data; + + QMap::const_iterator iterator = m_extensions.constBegin(); + for (; iterator != m_extensions.constEnd(); ++iterator) { + MenuExtensionIFace* extension = iterator.value(); + data.append(extension); + } + + std::sort(data.begin(), data.end(), [](MenuExtensionIFace* a, MenuExtensionIFace* b) { + return a->index() <= b->index(); + }); + + m_model = new ExtensionModel(data, this); + if (!m_model) { + qWarning() << "MenuExtension: unable to init the model of menu-extension."; + } +} + +ExtensionModel::ExtensionModel(const QVector &extensions, QObject *parent) + : QAbstractListModel(parent), m_extensions(extensions) +{ + m_roleNames.insert(Name, "name"); + m_roleNames.insert(Url, "url"); + m_roleNames.insert(Data, "data"); + + for (int i = 0; i < m_extensions.size(); ++i) { + connect(m_extensions.at(i), &MenuExtensionIFace::dataUpdated, this, [this, i] { + extensionDataChanged(i); + }); + } +} + +int ExtensionModel::rowCount(const QModelIndex &parent) const +{ + return m_extensions.size(); +} + +QVariant ExtensionModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + + if (row < 0 || row >= m_extensions.size()) { + return {}; + } + + switch (role) { + case Name: + return m_extensions.at(row)->name(); + case Url: + return m_extensions.at(row)->url(); + case Data: { + return m_extensions.at(row)->data(); + } + default: + break; + } + + return {}; +} + +QHash ExtensionModel::roleNames() const +{ + return m_roleNames; +} + +void ExtensionModel::send(int index, QVariantMap data) +{ + if (index < 0 || index >= m_extensions.size()) { + return; + } + + m_extensions.at(index)->receive(std::move(data)); +} + +void ExtensionModel::extensionDataChanged(int index) +{ + if (index < 0 || index >= m_extensions.size()) { + return; + } + + QVector roles(1, Data); + QModelIndex modelIndex = createIndex(index, 0); + Q_EMIT dataChanged(modelIndex, modelIndex, roles); +} } // UkuiMenu diff --git a/src/extension/menu-extension.h b/src/extension/menu-extension.h index 5918a4c..4e2c779 100644 --- a/src/extension/menu-extension.h +++ b/src/extension/menu-extension.h @@ -20,15 +20,59 @@ #define UKUI_MENU_MENU_EXTENSION_H #include +#include +#include +#include +#include +#include + +#include "menu-extension-iface.h" namespace UkuiMenu { +class ExtensionModel; + class MenuExtension : public QObject { Q_OBJECT public: + static MenuExtension *instance(); + void registerExtension(MenuExtensionIFace *extension); + Q_INVOKABLE ExtensionModel *extensionModel(); +private: + MenuExtension(); + void initModel(); +private: + ExtensionModel *m_model{nullptr}; + QMap m_extensions; +}; + +class ExtensionModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Name { + Name, + Url, + Data + }; + + explicit ExtensionModel(const QVector &extensions, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const override; + + Q_INVOKABLE void send(int index, QVariantMap data); + +private: + void extensionDataChanged(int index); + +private: + QHash m_roleNames; + QVector m_extensions; }; } // UkuiMenu diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp index 4ff7669..fdcd789 100644 --- a/src/ukui-menu-application.cpp +++ b/src/ukui-menu-application.cpp @@ -24,6 +24,7 @@ #include "theme-palette.h" #include "app-icon-provider.h" #include "menu-main-window.h" +#include "extension/menu-extension.h" #include #include @@ -77,6 +78,7 @@ void UkuiMenuApplication::initQmlEngine() m_applicationEngine->rootContext()->setContextProperty("themePalette", ThemePalette::getInstance()); m_applicationEngine->rootContext()->setContextProperty("menuSetting", MenuSetting::instance()); m_applicationEngine->rootContext()->setContextProperty("modelManager", new ModelManager(this)); + m_applicationEngine->rootContext()->setContextProperty("extensionManager", MenuExtension::instance()); QObject::connect(m_applicationEngine, &QQmlApplicationEngine::objectCreated, this, [url](QObject *obj, const QUrl &objUrl) {