diff --git a/frame/model/collaborationdevmodel.cpp b/frame/model/collaborationdevmodel.cpp new file mode 100644 index 000000000..81be2d560 --- /dev/null +++ b/frame/model/collaborationdevmodel.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 "collaborationdevmodel.h" + +#include +#include +#include +#include +#include +#include + +#include + +DGUI_USE_NAMESPACE +DCORE_USE_NAMESPACE + +static const QString CollaborationService = "com.deepin.Cooperation"; +static const QString CollaborationPath = "/com/deepin/Cooperation"; +static const QString CollaborationInterface = "com.deepin.Cooperation"; +static const QString ColPropertiesInterface = "org.freedesktop.DBus.Properties"; + +CollaborationDevModel::CollaborationDevModel(QObject *parent) + : QObject(parent) + , m_timer(new QTimer(this)) + , m_colDbusInter(new QDBusInterface(CollaborationService, CollaborationPath, CollaborationInterface, QDBusConnection::sessionBus(), this)) +{ + if (m_colDbusInter->isValid()) { + QList paths = m_colDbusInter->property("Machines").value>(); + for (const QDBusObjectPath& path : paths) { + CollaborationDevice *device = new CollaborationDevice(path.path(), this); + if (device->isValid()) + m_devices[path.path()] = device; + else + device->deleteLater(); + } + } else { + qWarning() << CollaborationService << " is invalid"; + } + + m_colDbusInter->connection().connect(CollaborationService, CollaborationPath, ColPropertiesInterface, + "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertyChanged(QDBusMessage))); + + connect(m_timer, &QTimer::timeout, this, &CollaborationDevModel::callScanMethod); +} + +void CollaborationDevModel::scanDevice() +{ + callScanMethod(); + m_timer->start(30 * 1000); // 30s +} + +void CollaborationDevModel::stopScanDevice() +{ + if (m_timer->isActive()) + m_timer->stop(); +} + +QList CollaborationDevModel::devices() const +{ + return m_devices.values(); +} + +void CollaborationDevModel::onPropertyChanged(const QDBusMessage &msg) +{ + QList arguments = msg.arguments(); + if (3 != arguments.count()) + return; + + QString interfaceName = msg.arguments().at(0).toString(); + if (interfaceName != CollaborationInterface) + return; + + QVariantMap changedProps = qdbus_cast(arguments.at(1).value()); + if (changedProps.contains("Machines")) { + QStringList devPaths = changedProps.value("Machines").toStringList(); + updateDevice(devPaths); + } +} + +void CollaborationDevModel::callScanMethod() +{ + // TODO 该功能目前不可用 + // m_dbusInter->asyncCall("Scan"); +} + +void CollaborationDevModel::updateDevice(const QStringList &devPaths) +{ + if (devPaths.isEmpty()) { + qDeleteAll(m_devices); + m_devices.clear(); + } else { + // 清除已不存在的设备 + QMapIterator it(m_devices); + while (it.hasNext()) { + it.next(); + if (!devPaths.contains(it.key())) { + it.value()->deleteLater(); + m_devices.remove(it.key()); + } + } + + // 添加新增设备 + for (const QString &path : devPaths) { + if (!m_devices.contains(path)) { + CollaborationDevice *device = new CollaborationDevice(path, this); + if (device->isValid()) + m_devices[path] = device; + else + device->deleteLater(); + } + } + } + + emit devicesChanged(); +} + +const CollaborationDevice *CollaborationDevModel::getDevice(const QString &uuid) +{ + QList devices = m_devices.values(); + for (const CollaborationDevice *device : devices) { + if (device->uuid() == uuid) { + return device; + } + } + + return nullptr; +} + +CollaborationDevice::CollaborationDevice(const QString &devPath, QObject *parent) + : QObject(parent) + , m_path(devPath) + , m_OS(-1) + , m_isPaired(false) + , m_isCooperated(false) + , m_isValid(false) + , m_devDbusInter(new QDBusInterface(CollaborationService, devPath, CollaborationInterface + QString(".Machine"), + QDBusConnection::sessionBus(), this)) +{ + if (m_devDbusInter->isValid()) { + m_name = m_devDbusInter->property("Name").toString(); + m_OS = m_devDbusInter->property("OS").toInt(); + m_isPaired = m_devDbusInter->property("Paired").toBool(); + m_isCooperated = m_devDbusInter->property("Cooperating").toBool(); + m_uuid = m_devDbusInter->property("UUID").toString(); + m_isValid = true; + } else { + qWarning() << "CollaborationDevice devPath:" << devPath << " is invalid and get properties failed"; + } + + m_devDbusInter->connection().connect(CollaborationService, m_path, ColPropertiesInterface, "PropertiesChanged", + this, SLOT(onPropertyChanged(QDBusMessage))); +} + +bool CollaborationDevice::isValid() const +{ + return m_isValid; +} + +QString CollaborationDevice::name() const +{ + return m_name; +} + +QString CollaborationDevice::uuid() const +{ + return m_uuid; +} + +QString CollaborationDevice::deviceIcon() const +{ + switch (m_OS) { + case DeviceType::Android: { + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + return QString(":/icons/resources/ICON_Device_Headphone_dark.svg"); + + return QString(":/icons/resources/ICON_Device_Headphone.svg"); + } + default: { + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + return QString(":/icons/resources/ICON_Device_Laptop_dark.svg"); + + return QString(":/icons/resources/ICON_Device_Laptop.svg"); + } + } +} + +bool CollaborationDevice::isPaired() const +{ + return m_isPaired; +} + +bool CollaborationDevice::isCooperated() const +{ + return m_isCooperated; +} + +void CollaborationDevice::onPropertyChanged(const QDBusMessage &msg) +{ + QList arguments = msg.arguments(); + if (3 != arguments.count()) + return; + + QString interfaceName = msg.arguments().at(0).toString(); + if (interfaceName != QString("%1.Machine").arg(CollaborationInterface)) + return; + + QVariantMap changedProps = qdbus_cast(arguments.at(1).value()); + if (changedProps.contains("Paired")) { + bool isPaired = changedProps.value("Paired").value(); + m_isPaired = isPaired; + if (isPaired) { + // paired 成功之后再去请求cooperate + requestCooperate(); + } else { + Q_EMIT pairedStateChanged(false); + } + } else if (changedProps.contains("Cooperating")) { + m_isCooperated = changedProps.value("Cooperating").value(); + + Q_EMIT pairedStateChanged(m_isCooperated); + } +} + +void CollaborationDevice::requestCooperate() const +{ + callMethod("RequestCooperate"); +} + +void CollaborationDevice::disconnectDevice() const +{ + callMethod("Disconnect"); +} + +void CollaborationDevice::pair() const +{ + callMethod("Pair"); +} + +QDBusMessage CollaborationDevice::callMethod(const QString &methodName) const +{ + if (m_devDbusInter->isValid()) { + QDBusMessage msg = m_devDbusInter->call(methodName); + qInfo() << "CollaborationDevice callMethod:" << methodName << " " << msg.errorMessage(); + return msg; + } + + qWarning() << "CollaborationDevice callMethod: " << methodName << " failed"; + return QDBusMessage(); +} diff --git a/frame/model/collaborationdevmodel.h b/frame/model/collaborationdevmodel.h new file mode 100644 index 000000000..8a2ca1487 --- /dev/null +++ b/frame/model/collaborationdevmodel.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 COLLABORATION_DEV_MODEL_H +#define COLLABORATION_DEV_MODEL_H + +#include +#include + +class QTimer; +class QDBusInterface; +class QDBusMessage; +class CollaborationDevice; + +/*! + * \brief The CollaborationDevModel class + * 协同设备model + */ +class CollaborationDevModel : public QObject +{ + Q_OBJECT +public: + explicit CollaborationDevModel(QObject *parent = nullptr); + +signals: + void devicesChanged(); + +public: + void scanDevice(); + void stopScanDevice(); + QList devices() const; + const CollaborationDevice *getDevice(const QString &uuid); + +private slots: + void onPropertyChanged(const QDBusMessage &msg); + +private: + void callScanMethod(); + void updateDevice(const QStringList &devPaths); + +private: + QTimer *m_timer; // 定时扫描设备 + QDBusInterface *m_colDbusInter; + // machine path : device object + QMap m_devices; + +}; + +/*! + * \brief The CollaborationDevice class + * 协同设备类 + */ +class CollaborationDevice : public QObject +{ + Q_OBJECT +public: + explicit CollaborationDevice(const QString &devPath, QObject *parent = nullptr); + +signals: + void pairedStateChanged(bool); + +public: + bool isValid() const; + void pair() const; + void requestCooperate() const; + void disconnectDevice() const; + + QString name() const; + QString uuid() const; + QString deviceIcon() const; + bool isPaired() const; + bool isCooperated() const; + +private slots: + void onPropertyChanged(const QDBusMessage &msg); + +private: + QDBusMessage callMethod(const QString &methodName) const; + +private: + enum DeviceType { + Other = 0, + UOS, + Linux, + Windows, + MacOS, + Android + }; + + QString m_path; + QString m_name; + QString m_uuid; + int m_OS; + + bool m_isPaired; + bool m_isCooperated; + bool m_isValid; + + QDBusInterface *m_devDbusInter; +}; + +#endif // COLLABORATION_DEV_MODEL_H diff --git a/frame/window/components/brightnessadjwidget.cpp b/frame/window/components/brightnessadjwidget.cpp new file mode 100644 index 000000000..3ae544459 --- /dev/null +++ b/frame/window/components/brightnessadjwidget.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 "brightnessadjwidget.h" +#include "brightnessmodel.h" +#include "slidercontainer.h" +#include "imageutil.h" + +#include + +BrightnessAdjWidget::BrightnessAdjWidget(QWidget *parent) + : QWidget(parent) + , m_mainLayout(new QVBoxLayout(this)) + , m_brightnessModel(new BrightnessModel(this)) +{ + m_mainLayout->setSpacing(5); + loadBrightnessItem(); +} + +void BrightnessAdjWidget::loadBrightnessItem() +{ + QList monitors = m_brightnessModel->monitors(); + for (BrightMonitor *monitor : monitors) { + SliderContainer *sliderContainer = new SliderContainer(this); + if (monitors.count() > 1) + sliderContainer->setTitle(monitor->name()); + + QPixmap leftPixmap = ImageUtil::loadSvg(":/icons/resources/brightnesslow", QSize(20, 20)); + QPixmap rightPixmap = ImageUtil::loadSvg(":/icons/resources/brightnesshigh", QSize(20, 20)); + sliderContainer->setIcon(SliderContainer::IconPosition::LeftIcon,leftPixmap, QSize(), 12); + sliderContainer->setIcon(SliderContainer::IconPosition::RightIcon, rightPixmap, QSize(), 12); + + sliderContainer->setFixedWidth(310); + sliderContainer->setFixedHeight(monitors.count() > 1 ? 56 : 30); + sliderContainer->updateSliderValue(monitor->brightness()); + + SliderProxyStyle *proxy = new SliderProxyStyle(SliderProxyStyle::Normal); + sliderContainer->setSliderProxyStyle(proxy); + m_mainLayout->addWidget(sliderContainer); + + connect(monitor, &BrightMonitor::brightnessChanged, sliderContainer, &SliderContainer::updateSliderValue); + connect(sliderContainer, &SliderContainer::sliderValueChanged, monitor, &BrightMonitor::setBrightness); + } +} + diff --git a/frame/window/components/brightnessadjwidget.h b/frame/window/components/brightnessadjwidget.h new file mode 100644 index 000000000..74482c7d8 --- /dev/null +++ b/frame/window/components/brightnessadjwidget.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 BRIGHTNESS_ADJUSTMENT_WIDGET_H +#define BRIGHTNESS_ADJUSTMENT_WIDGET_H + +#include + +class QVBoxLayout; +class BrightnessModel; + +/*! + * \brief The BrightnessAdjWidget class + * 显示器亮度调整页面 + */ +class BrightnessAdjWidget : public QWidget +{ + Q_OBJECT +public: + explicit BrightnessAdjWidget(QWidget *parent = nullptr); + +private: + void loadBrightnessItem(); + +private: + QVBoxLayout *m_mainLayout; + BrightnessModel *m_brightnessModel; +}; + + +#endif // BRIGHTNESS_ADJUSTMENT_WIDGET_H diff --git a/frame/window/components/devcollaborationwidget.cpp b/frame/window/components/devcollaborationwidget.cpp new file mode 100644 index 000000000..e9855f322 --- /dev/null +++ b/frame/window/components/devcollaborationwidget.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 "devcollaborationwidget.h" +#include "collaborationdevmodel.h" +#include "devitemdelegate.h" + +#include + +#include +#include +#include +#include +#include + +#define TITLE_HEIGHT 16 +#define ITME_WIDTH 310 +#define ITEM_HEIGHT 36 +#define LISTVIEW_ITEM_SPACE 2 +#define ITME_SPACE 10 +#define PER_DEGREE 14 + +DevCollaborationWidget::DevCollaborationWidget(QWidget *parent) + : QWidget(parent) + , m_deviceModel(new CollaborationDevModel(this)) + , m_deviceListView(new DListView(this)) + , m_viewItemModel(new QStandardItemModel(m_deviceListView)) + , m_refreshTimer(new QTimer(this)) +{ + initUI(); + loadDevice(); + + connect(m_deviceModel, &CollaborationDevModel::devicesChanged, this, &DevCollaborationWidget::loadDevice); + connect(m_deviceListView, &DListView::clicked, this, &DevCollaborationWidget::itemClicked); + connect(m_refreshTimer, &QTimer::timeout, this, &DevCollaborationWidget::refreshViewItem); +} + +void DevCollaborationWidget::showEvent(QShowEvent *event) +{ + m_deviceModel->scanDevice(); + + QWidget::showEvent(event); +} + +void DevCollaborationWidget::hideEvent(QHideEvent *event) +{ + m_deviceModel->stopScanDevice(); + + QWidget::hideEvent(event); +} + +void DevCollaborationWidget::initUI() +{ + m_deviceListView->setModel(m_viewItemModel); + + QLabel *title = new QLabel(tr("Cross-end Collaboration"), this); + title->setFixedHeight(TITLE_HEIGHT); + + QHBoxLayout *hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(10, 0, 0, 0); + hLayout->addWidget(title); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(ITME_SPACE); + mainLayout->addLayout(hLayout); + mainLayout->addWidget(m_deviceListView); + + setLayout(mainLayout); + + m_deviceListView->setContentsMargins(0, 0, 0, 0); + m_deviceListView->setFrameShape(QFrame::NoFrame); + m_deviceListView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_deviceListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_deviceListView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_deviceListView->setResizeMode(QListView::Adjust); + m_deviceListView->setViewportMargins(0, 0, 0, 0); + m_deviceListView->setSpacing(LISTVIEW_ITEM_SPACE); + m_deviceListView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_deviceListView->setItemDelegate(new DevItemDelegate(this)); +} + +void DevCollaborationWidget::loadDevice() +{ + if (!m_deviceListView->count()) { + for (CollaborationDevice *device : m_deviceModel->devices()) { + addItem(device); + } + } else { + updateDeviceListView(); + } + + if(!m_deviceListView->count()) { + m_deviceListView->hide(); + } else { + if (!m_deviceListView->isVisible()) + m_deviceListView->setVisible(true); + + m_deviceListView->setFixedSize(ITME_WIDTH, m_deviceListView->count() * ITEM_HEIGHT + LISTVIEW_ITEM_SPACE * (m_deviceListView->count() * 2)); + } + + resetWidgetSize(); +} + +void DevCollaborationWidget::addItem(const CollaborationDevice *device) +{ + if (!device) + return; + + QStandardItem *item = new QStandardItem(); + DevItemDelegate::DevItemData data; + data.checkedIconPath = device->deviceIcon(); // TODO + data.text = device->name(); + data.iconPath = device->deviceIcon(); + int resultState = device->isCooperated() ? DevItemDelegate::Connected : DevItemDelegate::None; + + item->setData(QVariant::fromValue(data), DevItemDelegate::StaticDataRole); + item->setData(device->uuid(), DevItemDelegate::UUIDDataRole); + item->setData(0, DevItemDelegate::DegreeDataRole); + item->setData(resultState, DevItemDelegate::ResultDataRole); + + m_viewItemModel->appendRow(item); + m_uuidItemMap[device->uuid()] = item; + + connect(device, &CollaborationDevice::pairedStateChanged, this, &DevCollaborationWidget::itemStatusChanged); +} + +void DevCollaborationWidget::updateDeviceListView() +{ + QList devices = m_deviceModel->devices(); + if (devices.isEmpty()) { + m_deviceListView->removeItems(0, m_deviceListView->count()); + return; + } + + // 删除不存在设备 + for (int row = 0; row < m_deviceListView->count(); row++) { + QStandardItem *item = m_viewItemModel->item(row); + if (!item) + continue; + + QString uuid = item->data(DevItemDelegate::UUIDDataRole).toString(); + if (m_deviceModel->getDevice(uuid)) + continue; + + m_deviceListView->removeItem(row); + + if (m_uuidItemMap.contains(uuid)) { + m_uuidItemMap.remove(uuid); + } + + if (m_connectingDevices.contains(uuid)) { + m_connectingDevices.removeAll(uuid); + } + } + + // 处理新增 + for (CollaborationDevice *device : devices) { + if (!m_uuidItemMap.contains(device->uuid())) { + addItem(device); + } + } +} + +void DevCollaborationWidget::resetWidgetSize() +{ + int height = TITLE_HEIGHT + ITME_SPACE + (m_deviceListView->count() ? m_deviceListView->height() : 0); + + setFixedSize(ITME_WIDTH, height); +} + +void DevCollaborationWidget::itemClicked(const QModelIndex &index) +{ + QString uuid = index.data(DevItemDelegate::UUIDDataRole).toString(); + const CollaborationDevice *device = m_deviceModel->getDevice(uuid); + if (!device) + return; + + if (!device->isPaired()) { + device->pair(); + m_connectingDevices.append(uuid); + } else if (!device->isCooperated()) { + device->requestCooperate(); + m_connectingDevices.append(uuid); + } else if (device->isCooperated()) { + device->disconnectDevice(); + } + + if (!m_connectingDevices.isEmpty() && !m_refreshTimer->isActive()) + m_refreshTimer->start(30); +} + +void DevCollaborationWidget::itemStatusChanged() +{ + CollaborationDevice *device = qobject_cast(sender()); + if (!device) + return; + + QString uuid = device->uuid(); + if (m_uuidItemMap.contains(uuid) && m_uuidItemMap[uuid]) { + // 更新item的连接状态 + int resultState = device->isCooperated() ? DevItemDelegate::Connected : DevItemDelegate::None; + m_uuidItemMap[uuid]->setData(resultState, DevItemDelegate::ResultDataRole); + if (device->isCooperated()) + m_uuidItemMap[uuid]->setData(0, DevItemDelegate::DegreeDataRole); + + m_deviceListView->update(m_uuidItemMap[uuid]->index()); + + if (resultState == DevItemDelegate::Connected && m_connectingDevices.contains(uuid)) { + m_connectingDevices.removeAll(uuid); + } + } +} + +void DevCollaborationWidget::refreshViewItem() +{ + if (m_connectingDevices.isEmpty()) { + m_refreshTimer->stop(); + return; + } + + for (const QString &uuid : m_connectingDevices) { + if (m_uuidItemMap.contains(uuid) && m_uuidItemMap[uuid]) { + int degree = m_uuidItemMap[uuid]->data(DevItemDelegate::DegreeDataRole).toInt(); + degree += PER_DEGREE; // 递进值 + m_uuidItemMap[uuid]->setData(DevItemDelegate::Connecting, DevItemDelegate::ResultDataRole); + m_uuidItemMap[uuid]->setData(degree, DevItemDelegate::DegreeDataRole); + m_deviceListView->update(m_uuidItemMap[uuid]->index()); + } + } +} diff --git a/frame/window/components/devcollaborationwidget.h b/frame/window/components/devcollaborationwidget.h new file mode 100644 index 000000000..bef33c479 --- /dev/null +++ b/frame/window/components/devcollaborationwidget.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 DEVICE_COLLABORATION_WIDGET_H +#define DEVICE_COLLABORATION_WIDGET_H + +#include +#include + +DWIDGET_USE_NAMESPACE + +class CollaborationDevice; +class CollaborationDevModel; + +/*! + * \brief The DevCollaborationWidget class + * 设备跨端协同子页面 + */ +class DevCollaborationWidget : public QWidget +{ + Q_OBJECT +public: + explicit DevCollaborationWidget(QWidget *parent = nullptr); + +protected: + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + +private slots: + void loadDevice(); + void itemClicked(const QModelIndex &index); + void itemStatusChanged(); + void refreshViewItem(); + +private: + void initUI(); + void updateDeviceListView(); + + void addItem(const CollaborationDevice *device); + void resetWidgetSize(); + +private: + CollaborationDevModel *m_deviceModel; + DListView *m_deviceListView; + QStandardItemModel *m_viewItemModel; + QMap m_uuidItemMap; + QStringList m_connectingDevices; + + QTimer *m_refreshTimer; +}; + +#endif // DEVICE_COLLABORATION_WIDGET_H diff --git a/frame/window/components/devitemdelegate.cpp b/frame/window/components/devitemdelegate.cpp new file mode 100644 index 000000000..876cde1cf --- /dev/null +++ b/frame/window/components/devitemdelegate.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 "devitemdelegate.h" + +#include +#include +#include + +#include + +#define RADIUS_VALUE 10 +#define ITEM_SPACE 10 +#define ICON_WIDTH 16 +#define ICON_HEIGHT 16 +#define TEXT_RECT_HEIGHT 20 +#define ITEM_HEIGHT 36 +#define INDICATOR_SHADOW_OFFSET 10 + +DevItemDelegate::DevItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + +} + +void DevItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (!index.isValid()) + return; + + painter->setRenderHint(QPainter::Antialiasing); + QVariant var = index.data(StaticDataRole); + DevItemData itemData = var.value(); + QRect rect = option.rect; + QPen pen; + pen.setWidth(2); + + // 鼠标悬停 + if (option.state.testFlag(QStyle::State_MouseOver)) { + pen.setColor(QColor("#EBECED")); + painter->setPen(pen); + painter->setBrush(QColor("#EBECED")); + painter->drawRoundedRect(rect, RADIUS_VALUE, RADIUS_VALUE); + } + + // 选中背景(连接上和选中) + int result = index.data(ResultDataRole).toInt(); + if (option.state.testFlag(QStyle::State_Selected) && result == Connected) { + pen.setColor(QColor("#0081FF")); + painter->setPen(pen); + painter->setBrush(QColor("#0081FF")); + painter->drawRoundedRect(rect, RADIUS_VALUE, RADIUS_VALUE); + } else { + // 绘制默认背景 + pen.setColor(QColor("#EBECED")); + painter->setPen(pen); + painter->setBrush(QColor("#EBECED")); + painter->drawRoundedRect(rect, RADIUS_VALUE, RADIUS_VALUE); + } + + bool selected = (option.state.testFlag(QStyle::State_Selected) && result == Connected); + + // 绘制Icon + QString imagePath = selected ? itemData.checkedIconPath : itemData.iconPath; + QRect iconRect = QRect(rect.left() + ITEM_SPACE, rect.top() + rect.height() / 2 - ICON_HEIGHT / 2, + ICON_WIDTH, ICON_HEIGHT); + painter->drawImage(iconRect, QImage(imagePath)); + + // 绘制text + QFont font = Dtk::Widget::DFontSizeManager::instance()->t4(); + painter->setFont(font); + pen.setColor(selected ? Qt::white : Qt::black); + painter->setPen(pen); + + int textRectWidth = rect.width() - ITEM_SPACE - iconRect.width() - iconRect.width() - ITEM_SPACE; + QRect textRect = QRect(iconRect.right() + ITEM_SPACE, rect.top() + 2, + textRectWidth, rect.height()); + + QFontMetrics fm(font); + QString itemText = fm.elidedText(itemData.text, Qt::ElideRight, textRectWidth); + painter->drawText(textRect, itemText); + + switch (result) { + case ResultState::Connected: + drawResultState(painter, rect); + break; + case ResultState::Connecting: + drawWaitingState(painter, rect, index.data(DegreeDataRole).toInt()); + break; + default: + break; + } +} + +QSize DevItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + return QSize(option.rect.width(), ITEM_HEIGHT); +} + +void DevItemDelegate::drawWaitingState(QPainter *painter, const QRect &rect, int degree) const +{ + int left = rect.width() - ITEM_SPACE; + int top = rect.top() + rect.height() / 2 - ICON_HEIGHT / 2; + QRect newRect(left, top, ICON_WIDTH, ICON_HEIGHT); + + painter->setRenderHint(QPainter::Antialiasing, true); + QList> indicatorColors; + for (int i = 0; i < 3; i++) + indicatorColors << createDefaultIndicatorColorList(QColor("#0081FF")); + + double radius = 16 * 0.66; + auto center = QRectF(newRect).center(); + auto indicatorRadius = radius / 2 / 2 * 1.1; + auto indicatorDegreeDelta = 360 / indicatorColors.count(); + + for (int i = 0; i < indicatorColors.count(); ++i) { + QList colors = indicatorColors.value(i); + for (int j = 0; j < colors.count(); ++j) { + double degreeCurrent = degree - j * INDICATOR_SHADOW_OFFSET + indicatorDegreeDelta * i; + auto x = (radius - indicatorRadius) * qCos(qDegreesToRadians(degreeCurrent)); + auto y = (radius - indicatorRadius) * qSin(qDegreesToRadians(degreeCurrent)); + + x = center.x() + x; + y = center.y() + y; + auto tl = QPointF(x - 1 * indicatorRadius, y - 1 * indicatorRadius); + QRectF rf(tl.x(), tl.y(), indicatorRadius * 2, indicatorRadius * 2); + + QPainterPath path; + path.addEllipse(rf); + + painter->fillPath(path, colors.value(j)); + } + } +} + +void DevItemDelegate::drawResultState(QPainter *painter, const QRect &rect) const +{ + // 绘制对勾,14x12 + int left = rect.width() - ITEM_SPACE; + int top = rect.top() + rect.height() / 2 - 6; + + QPainterPath path; + path.moveTo(left, top + 6); + path.lineTo(left + 4, top + 11); + path.lineTo(left + 12, top + 1); + + painter->drawPath(path); +} + +QList DevItemDelegate::createDefaultIndicatorColorList(QColor color) const +{ + QList colors; + QList opacitys; + opacitys << 100 << 30 << 15 << 10 << 5 << 4 << 3 << 2 << 1; + for (int i = 0; i < opacitys.count(); ++i) { + color.setAlpha(255 * opacitys.value(i) / 100); + colors << color; + } + + return colors; +} diff --git a/frame/window/components/devitemdelegate.h b/frame/window/components/devitemdelegate.h new file mode 100644 index 000000000..266d07a2a --- /dev/null +++ b/frame/window/components/devitemdelegate.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 DEVITEMDELEGATE_H +#define DEVITEMDELEGATE_H + +#include + +/*! + * \brief The DevItemDelegate class + */ +class DevItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + enum DevItemDataRole { + StaticDataRole = Qt::UserRole + 1, // 静态信息 + UUIDDataRole = Qt::UserRole + 2, // uuid, 可唯一代表一个设备 + DegreeDataRole = Qt::UserRole + 3, // degree 绘制waiting使用的参数 + ResultDataRole = Qt::UserRole + 4 // 连接结果 + }; + + enum ResultState { + None, + Connecting, + Connected + }; + + struct DevItemData { + QString checkedIconPath; + QString iconPath; + QString text; + }; + +public: + explicit DevItemDelegate(QObject *parent = nullptr); + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; + +private: + void drawWaitingState(QPainter *painter, const QRect &rect, int degree) const; + void drawResultState(QPainter *painter, const QRect &rect) const; + QList createDefaultIndicatorColorList(QColor color) const; +}; + +Q_DECLARE_METATYPE(DevItemDelegate::DevItemData) + +#endif // DEVITEMDELEGATE_H diff --git a/frame/window/components/displaysettingwidget.cpp b/frame/window/components/displaysettingwidget.cpp new file mode 100644 index 000000000..510108faa --- /dev/null +++ b/frame/window/components/displaysettingwidget.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 "displaysettingwidget.h" +#include "brightnessadjwidget.h" +#include "devcollaborationwidget.h" + +#include +#include + +#include + +DisplaySettingWidget::DisplaySettingWidget(QWidget *parent) + : QWidget(parent) + , m_brightnessAdjWidget(new BrightnessAdjWidget(this)) + , m_collaborationWidget(new DevCollaborationWidget(this)) + , m_settingBtn(new QPushButton(this)) +{ + initUI(); + + connect(m_settingBtn, &QPushButton::clicked, this, [ this ](){ + DDBusSender().service("org.deepin.dde.ControlCenter1") + .path("/org/deepin/dde/ControlCenter1") + .interface("org.deepin.dde.ControlCenter1") + .method("ShowPage").arg(QString("display")).call(); + hide(); + }); +} + +void DisplaySettingWidget::initUI() +{ + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->setMargin(0); + + mainLayout->addWidget(m_brightnessAdjWidget); + mainLayout->addWidget(m_collaborationWidget); + mainLayout->addWidget(m_settingBtn); + mainLayout->addStretch(); + + setLayout(mainLayout); +} diff --git a/frame/window/components/displaysettingwidget.h b/frame/window/components/displaysettingwidget.h new file mode 100644 index 000000000..ae8005808 --- /dev/null +++ b/frame/window/components/displaysettingwidget.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 ~ 2022 Uniontech Software Technology Co.,Ltd. + * + * Author: zhaoyingzhen + * + * Maintainer: zhaoyingzhen + * + * 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 + * 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 DISPLAY_SETTING_WIDGET_H +#define DISPLAY_SETTING_WIDGET_H + +#include + +class QPushButton; +class BrightnessAdjWidget; +class DevCollaborationWidget; + +/*! + * \brief The DisplaySettingWidget class + * 显示设置页面,快捷设置面板-->亮度调节栏右边显示按钮-->此页面 + */ +class DisplaySettingWidget : public QWidget +{ + Q_OBJECT +public: + explicit DisplaySettingWidget(QWidget *parent = nullptr); + +private: + void initUI(); + +private: + BrightnessAdjWidget *m_brightnessAdjWidget; // 亮度调整 + DevCollaborationWidget *m_collaborationWidget; // 跨端协同 + QPushButton *m_settingBtn; // 设置按钮 +}; + + +#endif // DISPLAY_SETTING_WIDGET_H diff --git a/frame/window/quicksettingcontainer.cpp b/frame/window/quicksettingcontainer.cpp index 0cc5141db..bf318e80f 100644 --- a/frame/window/quicksettingcontainer.cpp +++ b/frame/window/quicksettingcontainer.cpp @@ -32,6 +32,7 @@ #include "pluginchildpage.h" #include "volumemodel.h" #include "utils.h" +#include "displaysettingwidget.h" #include #include @@ -68,6 +69,7 @@ QuickSettingContainer::QuickSettingContainer(QWidget *parent) , m_volumnWidget(new VolumeWidget(m_volumeModel, m_componentWidget)) , m_brihtnessWidget(new BrightnessWidget(m_brightnessModel, m_componentWidget)) , m_volumeSettingWidget(new VolumeDevicesWidget(m_volumeModel, this)) + , m_displaySettingWidget(new DisplaySettingWidget(this)) , m_childPage(new PluginChildPage(this)) , m_dragPluginPosition(QPoint(0, 0)) { @@ -341,7 +343,7 @@ void QuickSettingContainer::initConnection() connect(m_brihtnessWidget->sliderContainer(), &SliderContainer::iconClicked, this, [ this ](const SliderContainer::IconPosition &iconPosition) { if (iconPosition == SliderContainer::RightIcon) { // 点击右侧的按钮,弹出具体的调节的界面 - // showWidget(m_brightSettingWidget, tr("brightness")); + showWidget(m_displaySettingWidget, tr("brightness")); resizeView(); } }); diff --git a/frame/window/quicksettingcontainer.h b/frame/window/quicksettingcontainer.h index 0ee1a4452..e0aaf853e 100644 --- a/frame/window/quicksettingcontainer.h +++ b/frame/window/quicksettingcontainer.h @@ -44,6 +44,7 @@ class VolumeDevicesWidget; class QLabel; class PluginChildPage; class QGridLayout; +class DisplaySettingWidget; DWIDGET_USE_NAMESPACE @@ -103,6 +104,7 @@ private: BrightnessWidget *m_brihtnessWidget; VolumeDevicesWidget *m_volumeSettingWidget; + DisplaySettingWidget *m_displaySettingWidget; PluginChildPage *m_childPage; QPoint m_dragPluginPosition; };