feat: 快捷面板支持插件控制区域的显示

目前将声音的功能移到插件中实现,支持声音插件的面板的展示

Log: 简化任务栏代码,优化插件加载逻辑
Influence: 快捷面板,观察声音、亮度调整和音乐播放等功能是否显示正常
Task: https://pms.uniontech.com/task-view-208579.html
Change-Id: I8fd7917e06dd7505da65dc36767166a779ffb0e6
This commit is contained in:
donghualin 2022-11-01 10:16:07 +00:00
parent ae74a6133f
commit dd1ed1c0d4
14 changed files with 179 additions and 1141 deletions

View File

@ -74,6 +74,13 @@ void QuickSettingController::pluginItemRemoved(PluginsItemInterface * const item
Q_EMIT pluginRemoved(itemInter);
}
void QuickSettingController::requestSetPluginAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool show)
{
// 设置插件列表可见事件
if (show)
Q_EMIT requestAppletShow(itemInter, itemKey);
}
void QuickSettingController::updateDockInfo(PluginsItemInterface * const itemInter, const DockPart &part)
{
Q_EMIT pluginUpdated(itemInter, part);

View File

@ -52,6 +52,7 @@ Q_SIGNALS:
void pluginInserted(PluginsItemInterface *itemInter, const PluginAttribute &);
void pluginRemoved(PluginsItemInterface *itemInter);
void pluginUpdated(PluginsItemInterface *, const DockPart &);
void requestAppletShow(PluginsItemInterface * itemInter, const QString &itemKey);
protected:
explicit QuickSettingController(QObject *parent = Q_NULLPTR);
@ -63,7 +64,7 @@ protected:
void pluginItemRemoved(PluginsItemInterface * const itemInter, const QString &) override;
void requestPluginWindowAutoHide(PluginsItemInterface * const, const QString &, const bool) override {}
void requestRefreshPluginWindowVisible(PluginsItemInterface * const, const QString &) override {}
void requestSetPluginAppletVisible(PluginsItemInterface * const, const QString &, const bool) override {}
void requestSetPluginAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool show) override;
void updateDockInfo(PluginsItemInterface * const itemInter, const DockPart &part) override;

View File

@ -1,394 +0,0 @@
#include "volumemodel.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariantMap>
#include <QDebug>
/**
* @brief
* @param parent
*/
static const QString serviceName = QString("org.deepin.daemon.Audio1");
static const QString servicePath = QString("/org/deepin/daemon/Audio1");
VolumeModel::VolumeModel(QObject *parent)
: QObject(parent)
, m_audio(new DBusAudio(serviceName, servicePath, QDBusConnection::sessionBus(), this))
{
reloadSinks();
reloadPorts();
connect(m_audio, &DBusAudio::DefaultSinkChanged, this, &VolumeModel::onDefaultSinkChanged);
}
VolumeModel::~VolumeModel()
{
clearPorts();
clearSinks();
}
void VolumeModel::setActivePort(AudioPorts *port)
{
m_audio->SetPort(port->cardId(), port->name(), port->direction());
}
QList<AudioSink *> VolumeModel::sinks() const
{
return m_sinks;
}
QList<AudioPorts *> VolumeModel::ports() const
{
return m_ports;
}
AudioSink *VolumeModel::defaultSink() const
{
for (AudioSink *sink : m_sinks) {
if (sink->isDefault())
return sink;
}
return nullptr;
}
void VolumeModel::setVolume(int volumn)
{
for (AudioSink *audiosink : m_sinks) {
if (audiosink->isDefault()) {
audiosink->setVolume(volumn, true);
break;
}
}
}
void VolumeModel::setMute(bool value)
{
for (AudioSink *audiosink : m_sinks) {
if (audiosink->isDefault()) {
audiosink->setMute(value);
break;
}
}
}
int VolumeModel::volume()
{
for (AudioSink *audiosink : m_sinks) {
if (audiosink->isDefault())
return audiosink->volume();
}
return 0;
}
bool VolumeModel::isMute()
{
for (AudioSink *audiosink : m_sinks) {
if (audiosink->isDefault())
return audiosink->isMute();
}
return false;
}
bool VolumeModel::existActiveOutputDevice()
{
for (AudioPorts *port : m_ports) {
if (port->direction() == 1)
return true;
}
return false;
}
void VolumeModel::reloadSinks()
{
clearSinks();
const QString defaultSinkPath = m_audio->defaultSink().path();
QList<QDBusObjectPath> sinkPaths = m_audio->sinks();
for (const QDBusObjectPath &sinkPath : sinkPaths) {
const QString path = sinkPath.path();
AudioSink *sink = new AudioSink(path, (path == defaultSinkPath), this);
connect(sink, &AudioSink::volumeChanged, this, [ = ](int volume) {
if (sink->isDefault())
Q_EMIT volumeChanged(volume);
});
connect(sink, &AudioSink::muteChanged, this, [ = ](bool isMute) {
if (sink->isDefault())
Q_EMIT muteChanged(isMute);
});
m_sinks << sink;
}
}
void VolumeModel::reloadPorts()
{
clearPorts();
QString cards = m_audio->cardsWithoutUnavailable();
QJsonParseError error;
QJsonDocument json = QJsonDocument::fromJson(cards.toLocal8Bit(), &error);
if (error.error != QJsonParseError::NoError)
return;
uint sinkCardId = 0;
QString sinkCardName;
AudioSink *sink = defaultSink();
if (sink) {
sinkCardId = sink->cardId();
sinkCardName = sink->name();
}
QJsonArray array = json.array();
for (const QJsonValue value : array) {
QJsonObject cardObject = value.toObject();
uint cardId = static_cast<uint>(cardObject.value("Id").toInt());
QString cardName = cardObject.value("Name").toString();
QJsonArray jPorts = cardObject.value("Ports").toArray();
for (const QJsonValue jPortValue : jPorts) {
QJsonObject jPort = jPortValue.toObject();
if (!jPort.value("Enabled").toBool())
continue;
int direction = jPort.value("Direction").toInt();
if (direction != 1)
continue;
AudioPorts *port = new AudioPorts(cardId, cardName);
port->setName(jPort.value("Name").toString());
port->setDescription(jPort.value("Description").toString());
port->setDirection(direction);
if (port->cardId() == sinkCardId && port->name() == sinkCardName)
port->setIsChecked(true);
m_ports << port;
}
}
}
void VolumeModel::onDefaultSinkChanged(const QDBusObjectPath &value)
{
AudioSink *audioSink = nullptr;
const QString defaultPath = value.path();
for (AudioSink *sink : m_sinks) {
sink->setDefault(defaultPath == sink->m_devicePath);
if (sink->isDefault())
audioSink = sink;
}
if (!audioSink)
return;
bool checkChanged = false;
for (AudioPorts *port : m_ports) {
bool oldChecked = port->isChecked();
port->setIsChecked(port->cardId() == audioSink->cardId()
&& port->name() == audioSink->name());
if (oldChecked != port->isChecked() && port->isChecked())
checkChanged = true;
}
if (checkChanged)
Q_EMIT checkPortChanged();
Q_EMIT defaultSinkChanged(audioSink);
}
void VolumeModel::clearSinks()
{
for (AudioSink *sink : m_sinks)
delete sink;
m_sinks.clear();
}
void VolumeModel::clearPorts()
{
for (AudioPorts *port : m_ports)
delete port;
m_ports.clear();
}
/**
* @brief
* @param parent
*/
AudioSink::AudioSink(QString path, bool isDefault, QObject *parent)
: QObject(parent)
, m_devicePath(path)
, m_sink(new DBusSink(serviceName, path, QDBusConnection::sessionBus(), this))
, m_isDefault(isDefault)
{
connect(m_sink, &DBusSink::MuteChanged, this, &AudioSink::muteChanged);
connect(m_sink, &DBusSink::VolumeChanged, this, [ this ](double value) {
Q_EMIT this->volumeChanged(static_cast<int>(value * 100));
});
}
AudioSink::~AudioSink()
{
}
void AudioSink::setDefault(bool isDefaultSink)
{
m_isDefault = isDefaultSink;
}
bool AudioSink::isDefault()
{
return m_isDefault;
}
bool AudioSink::isHeadPhone()
{
return false;
}
void AudioSink::setBalance(double value, bool isPlay)
{
m_sink->SetBalance(value, isPlay);
}
void AudioSink::setFade(double value)
{
m_sink->SetFade(value);
}
void AudioSink::setMute(bool mute)
{
m_sink->SetMute(mute);
}
void AudioSink::setPort(QString name)
{
m_sink->SetPort(name);
}
void AudioSink::setVolume(int value, bool isPlay)
{
m_sink->SetVolume((value * 0.01), isPlay);
}
bool AudioSink::isMute()
{
return m_sink->mute();
}
bool AudioSink::supportBalance()
{
return m_sink->supportBalance();
}
bool AudioSink::suoportFade()
{
return m_sink->supportFade();
}
double AudioSink::balance()
{
return m_sink->balance();
}
double AudioSink::baseVolume()
{
return m_sink->baseVolume();
}
double AudioSink::fade()
{
return m_sink->fade();
}
int AudioSink::volume()
{
return static_cast<int>(m_sink->volume() * 100);
}
QString AudioSink::description()
{
return m_sink->activePort().description;
}
QString AudioSink::name()
{
return m_sink->activePort().name;
}
uint AudioSink::cardId()
{
return m_sink->card();
}
AudioPorts::AudioPorts(uint cardId, QString cardName)
: m_cardId(cardId)
, m_cardName(cardName)
, m_direction(0)
, m_isCheck(false)
, m_isHeadPhone(false)
{
}
AudioPorts::~AudioPorts()
{
}
uint AudioPorts::cardId() const
{
return m_cardId;
}
QString AudioPorts::cardName() const
{
return m_cardName;
}
void AudioPorts::setName(const QString &name)
{
m_portName = name;
}
QString AudioPorts::name() const
{
return m_portName;
}
void AudioPorts::setDescription(const QString &desc)
{
m_description = desc;
}
QString AudioPorts::description() const
{
return m_description;
}
void AudioPorts::setDirection(int dir)
{
m_direction = dir;
}
void AudioPorts::setIsChecked(bool isChecked)
{
m_isCheck = isChecked;
}
int AudioPorts::direction() const
{
return m_direction;
}
bool AudioPorts::isChecked() const
{
return m_isCheck;
}
bool AudioPorts::isHeadPhone() const
{
return m_isHeadPhone;
}

View File

@ -1,135 +0,0 @@
#ifndef VOLUMNMODEL_H
#define VOLUMNMODEL_H
#include "org_deepin_daemon_audio.h"
#include "org_deepin_daemon_audio_sink.h"
#include <QObject>
using DBusAudio = org::deepin::daemon::Audio1;
using DBusSink = org::deepin::daemon::audio1::Sink;
class QDBusMessage;
class AudioSink;
class AudioPorts;
class VolumeModel : public QObject
{
Q_OBJECT
public:
explicit VolumeModel(QObject *parent);
~VolumeModel();
void setActivePort(AudioPorts *port);
QList<AudioSink *> sinks() const;
QList<AudioPorts *> ports() const;
AudioSink *defaultSink() const;
void setVolume(int volume);
void setMute(bool value);
int volume();
bool isMute();
bool existActiveOutputDevice();
Q_SIGNALS:
void defaultSinkChanged(AudioSink *);
void volumeChanged(int);
void muteChanged(bool);
void checkPortChanged();
private Q_SLOTS:
void onDefaultSinkChanged(const QDBusObjectPath & value);
private:
void reloadSinks();
void reloadPorts();
void clearSinks();
void clearPorts();
private:
QList<AudioSink *> m_sinks;
QList<AudioPorts *> m_ports;
DBusAudio *m_audio;
};
class AudioSink : public QObject
{
Q_OBJECT
friend class VolumeModel;
Q_SIGNALS:
void volumeChanged(int);
void muteChanged(bool);
public:
bool isDefault();
bool isHeadPhone();
void setBalance(double value, bool isPlay);
void setFade(double value);
void setMute(bool mute);
void setPort(QString name);
void setVolume(int value, bool isPlay);
bool isMute();
bool supportBalance();
bool suoportFade();
double balance();
double baseVolume();
double fade();
int volume();
QString description();
QString name();
uint cardId();
protected:
explicit AudioSink(QString path, bool isDefault, QObject *parent = nullptr);
~AudioSink();
void setDefault(bool isDefaultSink);
private:
QString m_devicePath;
DBusSink *m_sink;
bool m_isDefault;
};
class AudioPorts : public QObject
{
Q_OBJECT
friend class VolumeModel;
public:
uint cardId() const;
QString cardName() const;
QString name() const;
QString description() const;
int direction() const;
bool isChecked() const;
bool isHeadPhone() const;
protected:
AudioPorts(uint cardId, QString cardName);
~AudioPorts();
void setName(const QString &name);
void setDescription(const QString &desc);
void setDirection(int dir);
void setIsChecked(bool isChecked);
private:
uint m_cardId;
QString m_cardName;
QString m_portName;
QString m_description;
int m_direction;
bool m_isCheck;
bool m_isHeadPhone;
};
#endif // VOLUMNMODEL_H

View File

@ -1,260 +0,0 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "volumedeviceswidget.h"
#include "brightnessmodel.h"
#include "volumemodel.h"
#include "settingdelegate.h"
#include "imageutil.h"
#include "slidercontainer.h"
#include "dbusutil.h"
#include <DListView>
#include <DPushButton>
#include <DLabel>
#include <DGuiApplicationHelper>
#include <DDBusSender>
#include <DBlurEffectWidget>
#include <DPaletteHelper>
#include <QVBoxLayout>
#include <QScrollBar>
#include <QEvent>
#include <QProcess>
#include <QDBusInterface>
#include <QDBusConnection>
DWIDGET_USE_NAMESPACE
#define HEADERHEIGHT 30
#define ITEMSPACE 16
VolumeDevicesWidget::VolumeDevicesWidget(VolumeModel *model, QWidget *parent)
: QWidget(parent)
, m_sliderParent(new QWidget(this))
, m_sliderContainer(new SliderContainer(m_sliderParent))
, m_descriptionLabel(new QLabel(tr("Output Device"), this))
, m_deviceList(new DListView(this))
, m_volumeModel(model)
, m_audioSink(nullptr)
, m_model(new QStandardItemModel(this))
, m_delegate(new SettingDelegate(m_deviceList))
{
initUi();
initConnection();
reloadAudioDevices();
m_sliderParent->installEventFilter(this);
QMetaObject::invokeMethod(this, [ this ] {
resetVolumeInfo();
resizeHeight();
}, Qt::QueuedConnection);
}
VolumeDevicesWidget::~VolumeDevicesWidget()
{
}
bool VolumeDevicesWidget::eventFilter(QObject *watcher, QEvent *event)
{
if ((watcher == m_sliderParent) && (event->type() == QEvent::Paint)) {
QPainter painter(m_sliderParent);
painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿
painter.setPen(Qt::NoPen);
DPalette dpa = DPaletteHelper::instance()->palette(m_sliderParent);
painter.setBrush(dpa.brush(DPalette::ColorRole::Midlight));
painter.drawRoundedRect(m_sliderParent->rect(), 10, 10);
}
return QWidget::eventFilter(watcher, event);
}
void VolumeDevicesWidget::initUi()
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(6);
m_sliderParent->setFixedHeight(36);
QHBoxLayout *sliderLayout = new QHBoxLayout(m_sliderParent);
sliderLayout->setContentsMargins(11, 0, 11, 0);
sliderLayout->setSpacing(0);
QPixmap leftPixmap = ImageUtil::loadSvg(leftIcon(), QSize(24, 24));
m_sliderContainer->setIcon(SliderContainer::IconPosition::LeftIcon, leftPixmap, QSize(), 5);
QPixmap rightPixmap = ImageUtil::loadSvg(rightIcon(), QSize(24, 24));
m_sliderContainer->setIcon(SliderContainer::IconPosition::RightIcon, rightPixmap, QSize(), 7);
SliderProxyStyle *proxy = new SliderProxyStyle(SliderProxyStyle::Normal);
m_sliderContainer->setSliderProxyStyle(proxy);
sliderLayout->addWidget(m_sliderContainer);
QHBoxLayout *topLayout = new QHBoxLayout(this);
topLayout->setContentsMargins(10, 0, 10, 0);
topLayout->setSpacing(0);
topLayout->addWidget(m_sliderParent);
layout->addLayout(topLayout);
layout->addSpacing(4);
layout->addWidget(m_descriptionLabel);
m_deviceList->setModel(m_model);
m_deviceList->setViewMode(QListView::ListMode);
m_deviceList->setMovement(QListView::Free);
m_deviceList->setItemRadius(12);
m_deviceList->setWordWrap(false);
m_deviceList->verticalScrollBar()->setVisible(false);
m_deviceList->horizontalScrollBar()->setVisible(false);
m_deviceList->setOrientation(QListView::Flow::TopToBottom, false);
layout->addWidget(m_deviceList);
m_deviceList->setSpacing(10);
m_deviceList->setItemDelegate(m_delegate);
}
void VolumeDevicesWidget::reloadAudioDevices()
{
QList<AudioPorts *> ports = m_volumeModel->ports();
for (AudioPorts *port : ports) {
DStandardItem *item = new DStandardItem;
item->setText(QString("%1(%2)").arg(port->description()).arg(port->cardName()));
item->setIcon(QIcon(soundIconFile(port)));
item->setFlags(Qt::NoItemFlags);
item->setData(port->isChecked(), itemCheckRole);
item->setData(QVariant::fromValue(port), itemDataRole);
m_model->appendRow(item);
if (port->isChecked())
m_deviceList->setCurrentIndex(m_model->indexFromItem(item));
}
DStandardItem *settingItem = new DStandardItem;
settingItem->setText(tr("Sound settings"));
settingItem->setFlags(Qt::NoItemFlags);
settingItem->setData(false, itemCheckRole);
m_model->appendRow(settingItem);
}
void VolumeDevicesWidget::initConnection()
{
m_audioSink = m_volumeModel->defaultSink();
if (m_audioSink)
connect(m_audioSink, &AudioSink::volumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue);
connect(m_volumeModel, &VolumeModel::defaultSinkChanged, this, [ this ](AudioSink *sink) {
if (m_audioSink)
disconnect(m_audioSink);
m_audioSink = sink;
if (sink)
connect(sink, &AudioSink::volumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue);
resetVolumeInfo();
m_deviceList->update();
});
connect(m_sliderContainer, &SliderContainer::sliderValueChanged, this, [ this ](int value) {
AudioSink *defSink = m_volumeModel->defaultSink();
if (!defSink)
return;
defSink->setVolume(value, true);
});
connect(m_volumeModel, &VolumeModel::checkPortChanged, this, [ this ] {
for (int i = 0; i < m_model->rowCount(); i++) {
QModelIndex index = m_model->index(i, 0);
AudioPorts *port = index.data(itemDataRole).value<AudioPorts *>();
if (port)
m_model->setData(index, port->isChecked(), itemCheckRole);
}
m_deviceList->update();
});
connect(m_delegate, &SettingDelegate::selectIndexChanged, this, [ this ](const QModelIndex &index) {
AudioPorts *port = index.data(itemDataRole).value<AudioPorts *>();
if (port) {
m_volumeModel->setActivePort(port);
m_deviceList->update();
} else {
// 打开控制中心的声音模块
DDBusSender().service(controllCenterService)
.path(controllCenterPath)
.interface(controllCenterInterface)
.method("ShowPage").arg(QString("sound")).call();
hide();
}
});
}
QString VolumeDevicesWidget::leftIcon()
{
QString iconLeft = QString(":/icons/resources/audio-volume-%1").arg(m_volumeModel->isMute() ? "muted" : "low");
if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType)
iconLeft.append("-dark");
return iconLeft;
}
QString VolumeDevicesWidget::rightIcon()
{
QString iconRight = QString(":/icons/resources/audio-volume-high");
if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType)
iconRight.append("-dark");
return iconRight;
}
const QString VolumeDevicesWidget::soundIconFile(AudioPorts *port) const
{
if (!port)
return QString();
if (port->isHeadPhone()) {
if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType)
return QString(":/icons/resources/ICON_Device_Headphone_dark.svg");
return QString(":/icons/resources/ICON_Device_Headphone.svg");
}
if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType)
return QString(":/icons/resources/ICON_Device_Laptop_dark.svg");
return QString(":/icons/resources/ICON_Device_Laptop.svg");
}
void VolumeDevicesWidget::resizeHeight()
{
m_deviceList->adjustSize();
QMargins m = layout()->contentsMargins();
int height = m.top() + m.bottom() + HEADERHEIGHT + m_sliderContainer->height() + ITEMSPACE
+ m_descriptionLabel->height() + m_deviceList->height();
setFixedHeight(height);
}
void VolumeDevicesWidget::resetVolumeInfo()
{
AudioSink *defaultSink = m_volumeModel->defaultSink();
if (!defaultSink)
return;
m_sliderContainer->updateSliderValue(defaultSink->volume());
}

View File

@ -1,74 +0,0 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef VOLUMEDEVICESWIDGET_H
#define VOLUMEDEVICESWIDGET_H
#include <DStyledItemDelegate>
#include <QWidget>
namespace Dtk { namespace Widget { class DListView; } }
using namespace Dtk::Widget;
class SliderContainer;
class QStandardItemModel;
class QLabel;
class VolumeModel;
class AudioPorts;
class AudioSink;
class SettingDelegate;
class VolumeDevicesWidget : public QWidget
{
Q_OBJECT
public:
explicit VolumeDevicesWidget(VolumeModel *model, QWidget *parent = nullptr);
~VolumeDevicesWidget() override;
protected:
bool eventFilter(QObject *watcher, QEvent *event) override;
private:
void initUi();
void reloadAudioDevices();
void initConnection();
QString leftIcon();
QString rightIcon();
const QString soundIconFile(AudioPorts *port) const;
void resizeHeight();
void resetVolumeInfo();
private:
QWidget *m_sliderParent;
SliderContainer *m_sliderContainer;
QLabel *m_descriptionLabel;
DListView *m_deviceList;
VolumeModel *m_volumeModel;
AudioSink *m_audioSink;
QStandardItemModel *m_model;
SettingDelegate *m_delegate;
};
#endif // VOLUMEDEVICESWIDGET_H

View File

@ -1,150 +0,0 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "volumewidget.h"
#include "brightnessmodel.h"
#include "imageutil.h"
#include "volumemodel.h"
#include "imageutil.h"
#include "slidercontainer.h"
#include <DGuiApplicationHelper>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusPendingCall>
#include <QDBusPendingReply>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QEvent>
#include <QHBoxLayout>
#include <QMetaMethod>
DGUI_USE_NAMESPACE
#define ICON_SIZE 24
#define BACKSIZE 36
VolumeWidget::VolumeWidget(VolumeModel *model, QWidget *parent)
: DBlurEffectWidget(parent)
, m_model(model)
, m_sliderContainer(new SliderContainer(this))
, m_defaultSink(m_model->defaultSink())
{
initUi();
initConnection();
}
VolumeWidget::~VolumeWidget()
{
}
void VolumeWidget::initUi()
{
if (m_defaultSink)
m_sliderContainer->updateSliderValue(m_defaultSink->volume());
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(17, 0, 12, 0);
mainLayout->addWidget(m_sliderContainer);
QPixmap leftPixmap = ImageUtil::loadSvg(leftIcon(), QSize(ICON_SIZE, ICON_SIZE));
QPixmap rightPixmap = ImageUtil::loadSvg(rightIcon(), QSize(ICON_SIZE, ICON_SIZE));
m_sliderContainer->setIcon(SliderContainer::IconPosition::LeftIcon, leftPixmap, QSize(), 12);
m_sliderContainer->setIcon(SliderContainer::IconPosition::RightIcon, rightPixmap, QSize(BACKSIZE, BACKSIZE), 12);
SliderProxyStyle *proxy = new SliderProxyStyle;
m_sliderContainer->setSliderProxyStyle(proxy);
bool existActiveOutputDevice = m_model->existActiveOutputDevice();
setEnabled(existActiveOutputDevice);
}
void VolumeWidget::initConnection()
{
if (m_defaultSink)
connect(m_defaultSink, &AudioSink::volumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue);
connect(m_model, &VolumeModel::defaultSinkChanged, this, [ this ](AudioSink *sink) {
if (m_defaultSink)
disconnect(m_defaultSink);
m_defaultSink = sink;
if (sink) {
m_sliderContainer->updateSliderValue(sink->volume());
connect(sink, &AudioSink::volumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue);
}
});
connect(m_sliderContainer, &SliderContainer::sliderValueChanged, this, [ this ](int value) {
AudioSink *sink = m_model->defaultSink();
if (sink)
sink->setVolume(value, true);
});
connect(m_model, &VolumeModel::muteChanged, this, [ this ] {
m_sliderContainer->setIcon(SliderContainer::IconPosition::LeftIcon, QIcon(leftIcon()));
});
connect(m_sliderContainer, &SliderContainer::iconClicked, this, [ this ](const SliderContainer::IconPosition icon) {
switch (icon) {
case SliderContainer::IconPosition::LeftIcon: {
if (m_model->existActiveOutputDevice())
m_model->setMute(!m_model->isMute());
break;
}
case SliderContainer::IconPosition::RightIcon: {
// 弹出音量选择对话框
Q_EMIT rightIconClick();
break;
}
}
});
}
void VolumeWidget::showEvent(QShowEvent *event)
{
DBlurEffectWidget::showEvent(event);
Q_EMIT visibleChanged(true);
}
void VolumeWidget::hideEvent(QHideEvent *event)
{
DBlurEffectWidget::hideEvent(event);
Q_EMIT visibleChanged(false);
}
const QString VolumeWidget::leftIcon()
{
bool existActiveOutputDevice = m_model->existActiveOutputDevice();
const bool mute = existActiveOutputDevice ? m_model->isMute() : true;
if (mute)
return QString(":/icons/resources/audio-volume-muted-dark");
return QString(":/icons/resources/volume");
}
const QString VolumeWidget::rightIcon()
{
return QString(":/icons/resources/broadcast");
}

View File

@ -1,64 +0,0 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef VOLUMEWIDGET_H
#define VOLUMEWIDGET_H
#include <DBlurEffectWidget>
#include <QWidget>
class VolumeModel;
class QDBusMessage;
class SliderContainer;
class QLabel;
class AudioSink;
DWIDGET_USE_NAMESPACE
class VolumeWidget : public DBlurEffectWidget
{
Q_OBJECT
public:
explicit VolumeWidget(VolumeModel *model, QWidget *parent = nullptr);
~VolumeWidget() override;
Q_SIGNALS:
void visibleChanged(bool);
void rightIconClick();
protected:
void initUi();
void initConnection();
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
private:
const QString leftIcon();
const QString rightIcon();
private:
VolumeModel *m_model;
SliderContainer *m_sliderContainer;
AudioSink *m_defaultSink;
};
#endif // VOLUMEWIDGET_H

View File

@ -27,10 +27,7 @@
#include "dockpopupwindow.h"
#include "brightnesswidget.h"
#include "slidercontainer.h"
#include "volumewidget.h"
#include "volumedeviceswidget.h"
#include "pluginchildpage.h"
#include "volumemodel.h"
#include "utils.h"
#include "displaysettingwidget.h"
@ -47,11 +44,13 @@ DWIDGET_USE_NAMESPACE
struct QuickDragInfo {
QPoint dragPosition;
QuickSettingItem *dragItem = nullptr;
QWidget *dragItem = nullptr;
PluginsItemInterface *pluginInter = nullptr;
void reset() {
dragPosition.setX(0);
dragPosition.setY(0);
dragItem = nullptr;
pluginInter = nullptr;
}
bool isNull() {
return !dragItem;
@ -76,11 +75,8 @@ QuickSettingContainer::QuickSettingContainer(QWidget *parent)
, m_mainlayout(new QVBoxLayout(m_mainWidget))
, m_pluginLoader(QuickSettingController::instance())
, m_playerWidget(new MediaWidget(m_componentWidget))
, m_volumeModel(new VolumeModel(this))
, m_brightnessModel(new BrightnessModel(this))
, 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_dragInfo(new struct QuickDragInfo)
@ -190,13 +186,27 @@ bool QuickSettingContainer::eventFilter(QObject *watched, QEvent *event)
break;
}
case QEvent::MouseButtonPress: {
QuickSettingItem *item = qobject_cast<QuickSettingItem *>(watched);
if (!item)
break;
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
m_dragInfo->dragPosition = mouseEvent->pos();
m_dragInfo->dragItem = item;
QuickSettingItem *item = qobject_cast<QuickSettingItem *>(watched);
if (item) {
m_dragInfo->dragPosition = mouseEvent->pos();
m_dragInfo->dragItem = item;
m_dragInfo->pluginInter = item->pluginItem();
} else {
QList<PluginsItemInterface *> plugins = QuickSettingController::instance()->pluginItems(QuickSettingController::PluginAttribute::Quick);
for (PluginsItemInterface *plugin : plugins) {
if (!isApplet(plugin))
continue;
if (plugin->itemWidget(QUICK_ITEM_DETAIL_KEY) != watched)
continue;
m_dragInfo->dragPosition = mouseEvent->pos();
m_dragInfo->dragItem = plugin->itemWidget(QUICK_ITEM_DETAIL_KEY);
m_dragInfo->pluginInter = plugin;
break;
}
}
break;
}
case QEvent::MouseButtonRelease: {
@ -228,36 +238,103 @@ QPoint QuickSettingContainer::hotSpot(const QPixmap &pixmap)
return QPoint(pixmap.width(), pixmap.height());
}
bool QuickSettingContainer::isApplet(PluginsItemInterface *itemInter) const
{
if (!itemInter->itemWidget(QUICK_ITEM_DETAIL_KEY))
return false;
QJsonObject json = QuickSettingController::instance()->metaData(itemInter);
if (!json.contains("applet"))
return false;
return json.value("applet").toBool();
}
void QuickSettingContainer::onPluginInsert(PluginsItemInterface * itemInter)
{
initQuickItem(itemInter);
updateItemLayout();
QWidget *itemWidget = itemInter->itemWidget(QUICK_ITEM_DETAIL_KEY);
if (isApplet(itemInter)) {
// 如果存在这个窗体,就让其显示在下方
DBlurEffectWidget *effectWidget = new DBlurEffectWidget(m_componentWidget);
QVBoxLayout *layout = new QVBoxLayout(effectWidget);
layout->setContentsMargins(0, 0, 0, 0);
itemWidget->setParent(effectWidget);
itemWidget->setVisible(true);
layout->addWidget(itemWidget);
effectWidget->setFixedHeight(itemWidget->height());
effectWidget->setMaskColor(QColor(239, 240, 245));
effectWidget->setBlurRectXRadius(8);
effectWidget->setBlurRectYRadius(8);
m_componentWidget->layout()->addWidget(effectWidget);
itemWidget->installEventFilter(this);
} else {
// 如果不存在获取到的子窗体,就让其显示在上方插件显示的位置
initQuickItem(itemInter);
updateItemLayout();
}
onResizeView();
}
void QuickSettingContainer::onPluginRemove(PluginsItemInterface * itemInter)
{
QuickSettingItem *quickItem = nullptr;
for (QuickSettingItem *settingItem : m_quickSettings) {
if (settingItem->pluginItem() != itemInter)
continue;
QWidget *itemWidget = itemInter->itemWidget(QUICK_ITEM_DETAIL_KEY);
if (itemWidget) {
for (int i = 0; i < m_componentWidget->layout()->count(); i++) {
QLayoutItem *layoutItem = m_componentWidget->layout()->itemAt(i);
if (!layoutItem)
continue;
quickItem = settingItem;
break;
DBlurEffectWidget *effectWidget = qobject_cast<DBlurEffectWidget *>(layoutItem->widget());
if (!effectWidget || !effectWidget->layout())
continue;
bool found = false;
for (int j = 0; j < effectWidget->layout()->count(); j++) {
QLayoutItem *layoutItem = effectWidget->layout()->itemAt(i);
if (!layoutItem)
continue;
if (layoutItem->widget() == itemWidget) {
effectWidget->layout()->removeWidget(itemWidget);
itemWidget->setParent(nullptr);
found = true;
break;
}
}
if (!found)
continue;
m_componentWidget->layout()->removeWidget(effectWidget);
effectWidget->setParent(nullptr);
effectWidget->deleteLater();
break;
}
} else {
QuickSettingItem *quickItem = nullptr;
for (QuickSettingItem *settingItem : m_quickSettings) {
if (settingItem->pluginItem() != itemInter)
continue;
quickItem = settingItem;
break;
}
if (!quickItem)
return;
m_pluginLayout->removeWidget(quickItem);
m_quickSettings.removeOne(quickItem);
disconnect(quickItem, &QuickSettingItem::detailClicked, this, &QuickSettingContainer::onItemDetailClick);
quickItem->setParent(nullptr);
quickItem->removeEventFilter(this);
quickItem->setMouseTracking(false);
quickItem->deleteLater();
//调整子控件的位置
updateItemLayout();
}
if (!quickItem)
return;
m_pluginLayout->removeWidget(quickItem);
m_quickSettings.removeOne(quickItem);
disconnect(quickItem, &QuickSettingItem::detailClicked, this, &QuickSettingContainer::onItemDetailClick);
quickItem->setParent(nullptr);
quickItem->removeEventFilter(this);
quickItem->setMouseTracking(false);
quickItem->deleteLater();
//调整子控件的位置
updateItemLayout();
onResizeView();
}
@ -269,16 +346,23 @@ void QuickSettingContainer::mouseMoveEvent(QMouseEvent *event)
QPoint pointCurrent = event->pos();
if (qAbs(m_dragInfo->dragPosition.x() - pointCurrent.x()) > 5
|| qAbs(m_dragInfo->dragPosition.y() - pointCurrent.y()) > 5) {
QuickSettingItem *moveItem = m_dragInfo->dragItem;
m_dragInfo->reset();
QDrag *drag = new QDrag(this);
QuickPluginMimeData *mimedata = new QuickPluginMimeData(moveItem->pluginItem());
QuickSettingItem *moveItem = qobject_cast<QuickSettingItem *>(m_dragInfo->dragItem);
QuickPluginMimeData *mimedata = new QuickPluginMimeData(m_dragInfo->pluginInter);
drag->setMimeData(mimedata);
QPixmap dragPixmap = moveItem->dragPixmap();
drag->setPixmap(dragPixmap);
drag->setHotSpot(hotSpot(dragPixmap));
if (moveItem) {
QPixmap dragPixmap = moveItem->dragPixmap();
drag->setPixmap(dragPixmap);
drag->setHotSpot(hotSpot(dragPixmap));
} else {
// 如果拖动的是声音等插件
QPixmap dragPixmap = m_dragInfo->dragItem->grab();
drag->setPixmap(dragPixmap);
drag->setHotSpot(hotSpot(dragPixmap));
}
m_dragInfo->reset();
drag->exec(Qt::MoveAction | Qt::CopyAction);
}
}
@ -312,11 +396,9 @@ void QuickSettingContainer::initUi()
// 添加音乐播放插件
m_playerWidget->setFixedHeight(ITEMHEIGHT);
m_volumnWidget->setFixedHeight(ITEMHEIGHT);
m_brihtnessWidget->setFixedHeight(ITEMHEIGHT);
setWidgetStyle(m_playerWidget);
setWidgetStyle(m_volumnWidget);
setWidgetStyle(m_brihtnessWidget);
m_mainlayout->setSpacing(ITEMSPACE);
@ -336,20 +418,17 @@ void QuickSettingContainer::initUi()
ctrlLayout->setSpacing(ITEMSPACE);
ctrlLayout->addWidget(m_playerWidget);
ctrlLayout->addWidget(m_volumnWidget);
ctrlLayout->addWidget(m_brihtnessWidget);
m_mainlayout->addWidget(m_componentWidget);
// 加载所有的插件
QList<PluginsItemInterface *> plugins = m_pluginLoader->pluginItems(QuickSettingController::PluginAttribute::Quick);
for (PluginsItemInterface *plugin : plugins)
initQuickItem(plugin);
onPluginInsert(plugin);
m_switchLayout->addWidget(m_mainWidget);
m_switchLayout->addWidget(m_childPage);
m_volumeSettingWidget->hide();
setMouseTracking(true);
setAcceptDrops(true);
@ -373,12 +452,8 @@ void QuickSettingContainer::initConnection()
onPluginInsert(itemInter);
});
connect(m_pluginLoader, &QuickSettingController::pluginRemoved, this, &QuickSettingContainer::onPluginRemove);
connect(m_pluginLoader, &QuickSettingController::requestAppletShow, this, &QuickSettingContainer::onRequestAppletShow);
connect(m_playerWidget, &MediaWidget::visibleChanged, this, &QuickSettingContainer::onResizeView);
connect(m_volumnWidget, &VolumeWidget::visibleChanged, this, &QuickSettingContainer::onResizeView);
connect(m_volumnWidget, &VolumeWidget::rightIconClick, this, [ this ] {
showWidget(m_volumeSettingWidget, tr("voice"));
onResizeView();
});
connect(m_brihtnessWidget, &BrightnessWidget::visibleChanged, this, &QuickSettingContainer::onResizeView);
connect(m_brihtnessWidget->sliderContainer(), &SliderContainer::iconClicked, this, [ this ](const SliderContainer::IconPosition &iconPosition) {
if (iconPosition == SliderContainer::RightIcon) {
@ -412,17 +487,50 @@ void QuickSettingContainer::onResizeView()
m_pluginWidget->setFixedHeight(ITEMHEIGHT * rowCount + ITEMSPACE * (rowCount - 1));
int panelCount = 0;
if (m_playerWidget->isVisible())
panelCount++;
if (m_volumnWidget->isVisible())
panelCount++;
if (m_brihtnessWidget->isVisible())
panelCount++;
int height = 0;
int widgetCount = 0;
for (int i = 0; i < m_componentWidget->layout()->count(); i++) {
QLayoutItem *layoutItem = m_componentWidget->layout()->itemAt(i);
if (!layoutItem)
continue;
DBlurEffectWidget *widget = qobject_cast<DBlurEffectWidget *>(layoutItem->widget());
if (!widget)
continue;
if (widget == m_playerWidget || widget == m_brihtnessWidget)
continue;
height += widget->height();
widgetCount++;
}
if (m_playerWidget->isVisible()) {
height += m_playerWidget->height();
widgetCount++;
}
if (m_brihtnessWidget->isVisible()) {
height += m_brihtnessWidget->height();
widgetCount++;
}
m_componentWidget->setFixedHeight(height + (widgetCount - 1) * ITEMSPACE);
m_componentWidget->setFixedHeight(ITEMHEIGHT * panelCount + ITEMSPACE * (panelCount - 1));
setFixedHeight(ITEMSPACE * 3 + m_pluginWidget->height() + m_componentWidget->height());
} else if (m_switchLayout->currentWidget() == m_childPage) {
setFixedHeight(m_childPage->height());
}
}
void QuickSettingContainer::onRequestAppletShow(PluginsItemInterface *itemInter, const QString &itemKey)
{
if (itemKey == QUICK_ITEM_DETAIL_KEY) {
// 显示弹出的内容
QWidget *itemApplet = itemInter->itemPopupApplet(itemKey);
if (!itemApplet)
return;
showWidget(itemApplet, itemInter->pluginDisplayName());
onResizeView();
}
}

View File

@ -33,9 +33,7 @@ class DockItem;
class QVBoxLayout;
class QuickSettingController;
class MediaWidget;
class VolumeModel;
class BrightnessModel;
class VolumeWidget;
class BrightnessWidget;
class QuickSettingItem;
class DockPopupWindow;
@ -70,6 +68,7 @@ private Q_SLOTS:
void onPluginRemove(PluginsItemInterface * itemInter);
void onItemDetailClick(PluginsItemInterface *pluginInter);
void onResizeView();
void onRequestAppletShow(PluginsItemInterface * itemInter, const QString &itemKey);
private:
// 加载UI
@ -84,6 +83,8 @@ private:
void showWidget(QWidget *widget, const QString &title);
// 获取拖动图标的热点
QPoint hotSpot(const QPixmap &pixmap);
// 判断是否支持显示在面板上
bool isApplet(PluginsItemInterface * itemInter) const;
private:
static DockPopupWindow *m_popWindow;
@ -96,12 +97,9 @@ private:
QVBoxLayout *m_mainlayout;
QuickSettingController *m_pluginLoader;
MediaWidget *m_playerWidget;
VolumeModel *m_volumeModel;
BrightnessModel *m_brightnessModel;
VolumeWidget *m_volumnWidget;
BrightnessWidget *m_brihtnessWidget;
VolumeDevicesWidget *m_volumeSettingWidget;
DisplaySettingWidget *m_displaySettingWidget;
PluginChildPage *m_childPage;
QuickDragInfo *m_dragInfo;

View File

@ -27,6 +27,7 @@ DWIDGET_USE_NAMESPACE
static const int itemCheckRole = Dtk::UserRole + 1;
static const int itemDataRole = Dtk::UserRole + 2;
static const int itemFlagRole = Dtk::UserRole + 3;
class SettingDelegate : public DStyledItemDelegate
{