From 9985c9b2392edc318765032d0d9b7f7e3a2a5a4b Mon Sep 17 00:00:00 2001 From: donghualin Date: Tue, 1 Nov 2022 08:13:27 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A3=B0=E9=9F=B3=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E9=80=82=E9=85=8Dv23?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将声音的相关功能移到sound插件中,方便扩展,并适配v23的接口 Log: 声音插件适配v23 Influence: 观察任务栏是否存在声音插件 Task: https://pms.uniontech.com/task-view-210309.html Change-Id: I7f782af6955a017af940e639a02eedab8f459905 --- plugins/CMakeLists.txt | 2 +- plugins/sound/CMakeLists.txt | 5 +- plugins/sound/sound.json | 3 +- plugins/sound/soundapplet.cpp | 4 +- plugins/sound/sounddeviceswidget.cpp | 282 +++++++++++++++++++++++++++ plugins/sound/sounddeviceswidget.h | 84 ++++++++ plugins/sound/sounditem.cpp | 5 + plugins/sound/sounditem.h | 1 + plugins/sound/soundplugin.cpp | 38 +++- plugins/sound/soundplugin.h | 7 + plugins/sound/soundwidget.cpp | 154 +++++++++++++++ plugins/sound/soundwidget.h | 66 +++++++ 12 files changed, 638 insertions(+), 13 deletions(-) create mode 100644 plugins/sound/sounddeviceswidget.cpp create mode 100644 plugins/sound/sounddeviceswidget.h create mode 100644 plugins/sound/soundwidget.cpp create mode 100644 plugins/sound/soundwidget.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index de79d7fd0..298eeda5d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,7 +2,7 @@ #add_subdirectory("disk-mount") add_subdirectory("shutdown") add_subdirectory("power") -#add_subdirectory("sound") +add_subdirectory("sound") #add_subdirectory("tray") add_subdirectory("trash") add_subdirectory("keyboard-layout") diff --git a/plugins/sound/CMakeLists.txt b/plugins/sound/CMakeLists.txt index 1e677d8db..0624825f0 100644 --- a/plugins/sound/CMakeLists.txt +++ b/plugins/sound/CMakeLists.txt @@ -28,12 +28,13 @@ pkg_check_modules(QGSettings REQUIRED gsettings-qt) add_definitions("${QT_DEFINITIONS} -DQT_PLUGIN") add_library(${PLUGIN_NAME} SHARED ${SRCS} resources/sound.qrc) -set_target_properties(${PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../system-trays) +set_target_properties(${PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../quick-trays) target_include_directories(${PLUGIN_NAME} PUBLIC ${DtkWidget_INCLUDE_DIRS} ${QGSettings_INCLUDE_DIRS} ./dbusinterface ./dbusinterface/generation_dbus_interface ../../interfaces + ../../widgets ../../frame ../../frame/accessible ../../frame/qtdbusextended @@ -48,4 +49,4 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE ${Qt5Svg_LIBRARIES} ) -install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION lib/dde-dock/plugins/system-trays) +install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION lib/dde-dock/plugins/quick-trays) diff --git a/plugins/sound/sound.json b/plugins/sound/sound.json index 181aef926..e5b6168c7 100644 --- a/plugins/sound/sound.json +++ b/plugins/sound/sound.json @@ -1,4 +1,5 @@ { "api": "2.0.0", - "depends-daemon-dbus-service": "org.deepin.daemon.Audio1" + "depends-daemon-dbus-service": "org.deepin.daemon.Audio1", + "applet": true } diff --git a/plugins/sound/soundapplet.cpp b/plugins/sound/soundapplet.cpp index c9e78b94e..067b53c3e 100644 --- a/plugins/sound/soundapplet.cpp +++ b/plugins/sound/soundapplet.cpp @@ -285,7 +285,7 @@ void SoundApplet::onDefaultSinkChanged() connect(m_defSinkInter, &DBusSink::VolumeChanged, this, &SoundApplet::onVolumeChanged); connect(m_defSinkInter, &DBusSink::MuteChanged, this, [ = ] { - onVolumeChanged(m_defSinkInter->volume()); + onVolumeChanged(m_defSinkInter->volume() * 100); }); QString portId = m_defSinkInter->activePort().name; @@ -297,7 +297,7 @@ void SoundApplet::onDefaultSinkChanged() activePort(portId,cardId); //无声卡状态下,会有伪sink设备,显示音量为0 - onVolumeChanged(findPort(portId, cardId) != nullptr ? m_defSinkInter->volume() : 0); + onVolumeChanged(findPort(portId, cardId) != nullptr ? m_defSinkInter->volume() * 100 : 0); emit defaultSinkChanged(m_defSinkInter); } diff --git a/plugins/sound/sounddeviceswidget.cpp b/plugins/sound/sounddeviceswidget.cpp new file mode 100644 index 000000000..7557b6511 --- /dev/null +++ b/plugins/sound/sounddeviceswidget.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 "sounddeviceswidget.h" +#include "brightnessmodel.h" +#include "settingdelegate.h" +#include "imageutil.h" +#include "slidercontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +#define HEADERHEIGHT 30 +#define ITEMSPACE 16 + +#define AUDIOPORT 0 +#define AUDIOSETTING 1 + +const int cardIdRole = itemFlagRole + 1; + +SoundDevicesWidget::SoundDevicesWidget(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(new DBusAudio("org.deepin.daemon.Audio1", "/org/deepin/daemon/Audio1", QDBusConnection::sessionBus(), this)) + , m_audioSink(new DBusSink("org.deepin.daemon.Audio1", m_volumeModel->defaultSink().path(), QDBusConnection::sessionBus(), this)) + , m_model(new QStandardItemModel(this)) + , m_delegate(new SettingDelegate(m_deviceList)) +{ + initUi(); + initConnection(); + onAudioDevicesChanged(); + m_sliderParent->installEventFilter(this); + + QMetaObject::invokeMethod(this, [ this ] { + resetVolumeInfo(); + resizeHeight(); + }, Qt::QueuedConnection); +} + +SoundDevicesWidget::~SoundDevicesWidget() +{ +} + +bool SoundDevicesWidget::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 SoundDevicesWidget::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 SoundDevicesWidget::onAudioDevicesChanged() +{ + QList ports = m_audioSink->ports(); + for (AudioPort port : ports) { + if (port.availability != 0 && port.availability != 2) + continue; + + uint cardId = audioPortCardId(port); + if (!m_volumeModel->IsPortEnabled(cardId, port.name)) + continue; + + DStandardItem *item = new DStandardItem; + item->setText(QString("%1(%2)").arg(port.description).arg(port.name)); + item->setIcon(QIcon(soundIconFile(port))); + item->setFlags(Qt::NoItemFlags); + item->setData(port.availability == 2, itemCheckRole); + item->setData(QVariant::fromValue(port), itemDataRole); + item->setData(AUDIOPORT, itemFlagRole); + item->setData(cardId, cardIdRole); + m_model->appendRow(item); + if (port.availability == 2) + m_deviceList->setCurrentIndex(m_model->indexFromItem(item)); + } + + DStandardItem *settingItem = new DStandardItem; + settingItem->setText(tr("Sound settings")); + settingItem->setFlags(Qt::NoItemFlags); + settingItem->setData(false, itemCheckRole); + settingItem->setData(AUDIOSETTING, itemFlagRole); + m_model->appendRow(settingItem); +} + +void SoundDevicesWidget::initConnection() +{ + connect(m_audioSink, &DBusSink::VolumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue); + connect(m_volumeModel, &DBusAudio::DefaultSinkChanged, this, &SoundDevicesWidget::onDefaultSinkChanged); + connect(m_delegate, &SettingDelegate::selectIndexChanged, this, &SoundDevicesWidget::onSelectIndexChanged); + connect(m_volumeModel, &DBusAudio::PortEnabledChanged, this, &SoundDevicesWidget::onAudioDevicesChanged); + connect(m_volumeModel, &DBusAudio::CardsWithoutUnavailableChanged, this, &SoundDevicesWidget::onAudioDevicesChanged); + + connect(m_sliderContainer, &SliderContainer::sliderValueChanged, this, [ this ](int value) { + m_audioSink->SetVolume(value, true); + }); +} + +QString SoundDevicesWidget::leftIcon() +{ + QString iconLeft = QString(":/icons/resources/audio-volume-%1").arg(m_audioSink->mute() ? "muted" : "low"); + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + iconLeft.append("-dark"); + + return iconLeft; +} + +QString SoundDevicesWidget::rightIcon() +{ + QString iconRight = QString(":/icons/resources/audio-volume-high"); + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + iconRight.append("-dark"); + + return iconRight; +} + +const QString SoundDevicesWidget::soundIconFile(const AudioPort port) const +{ + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + return QString(":/icons/resources/ICON_Device_Laptop_dark.svg"); + + return QString(":/icons/resources/ICON_Device_Laptop.svg"); +} + +void SoundDevicesWidget::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 SoundDevicesWidget::resetVolumeInfo() +{ + m_sliderContainer->updateSliderValue(m_audioSink->volume() * 100); +} + +uint SoundDevicesWidget::audioPortCardId(const AudioPort &audioport) const +{ + QString cards = m_volumeModel->cardsWithoutUnavailable(); + QJsonParseError error; + QJsonDocument json = QJsonDocument::fromJson(cards.toLocal8Bit(), &error); + if (error.error != QJsonParseError::NoError) + return -1; + + QJsonArray array = json.array(); + for (const QJsonValue value : array) { + QJsonObject cardObject = value.toObject(); + uint cardId = static_cast(cardObject.value("Id").toInt()); + 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; + + if (jPort.value("Name").toString() == audioport.name) + return cardId; + } + } + + return -1; +} + +void SoundDevicesWidget::onSelectIndexChanged(const QModelIndex &index) +{ + int flag = index.data(itemFlagRole).toInt(); + if (flag == AUDIOPORT) { + // 如果是点击具体的声音设备 + AudioPort port = index.data(itemDataRole).value(); + uint cardId = index.data(cardIdRole).toUInt(); + if (cardId >= 0) { + m_volumeModel->SetPort(cardId, port.name, 1); + m_deviceList->update(); + } + } else { + // 如果是点击声音设置,则打开控制中心的声音模块 + DDBusSender().service("org.deepin.dde.ControlCenter1") + .path("/org/deepin/dde/ControlCenter1") + .interface("org.deepin.dde.ControlCenter1") + .method("ShowPage").arg(QString("sound")).call(); + hide(); + } +} + +void SoundDevicesWidget::onDefaultSinkChanged(const QDBusObjectPath &value) +{ + delete m_audioSink; + m_audioSink = new DBusSink("org.deepin.daemon.Audio1", m_volumeModel->defaultSink().path(), QDBusConnection::sessionBus(), this); + connect(m_audioSink, &DBusSink::VolumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue); + + resetVolumeInfo(); + m_deviceList->update(); +} diff --git a/plugins/sound/sounddeviceswidget.h b/plugins/sound/sounddeviceswidget.h new file mode 100644 index 000000000..776c84487 --- /dev/null +++ b/plugins/sound/sounddeviceswidget.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 SOUNDDEVICESWIDGET_H +#define SOUNDDEVICESWIDGET_H + +#include "org_deepin_daemon_audio.h" +#include "org_deepin_daemon_audio_sink.h" + +#include + +#include + +namespace Dtk { namespace Widget { class DListView; } } + +using namespace Dtk::Widget; + +class SliderContainer; +class QStandardItemModel; +class QLabel; +class VolumeModel; +class AudioSink; +class SettingDelegate; + +using DBusAudio = org::deepin::daemon::Audio1; +using DBusSink = org::deepin::daemon::audio1::Sink; + +class SoundDevicesWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SoundDevicesWidget(QWidget *parent = nullptr); + ~SoundDevicesWidget() override; + +protected: + bool eventFilter(QObject *watcher, QEvent *event) override; + +private: + void initUi(); + void initConnection(); + QString leftIcon(); + QString rightIcon(); + const QString soundIconFile(const AudioPort port) const; + + void resizeHeight(); + + void resetVolumeInfo(); + uint audioPortCardId(const AudioPort &audioport) const; + +private Q_SLOTS: + void onSelectIndexChanged(const QModelIndex &index); + void onDefaultSinkChanged(const QDBusObjectPath & value); + void onAudioDevicesChanged(); + +private: + QWidget *m_sliderParent; + SliderContainer *m_sliderContainer; + QLabel *m_descriptionLabel; + DListView *m_deviceList; + DBusAudio *m_volumeModel; + DBusSink *m_audioSink; + QStandardItemModel *m_model; + SettingDelegate *m_delegate; +}; + +#endif // VOLUMEDEVICESWIDGET_H diff --git a/plugins/sound/sounditem.cpp b/plugins/sound/sounditem.cpp index 1ac357979..1872af7d9 100644 --- a/plugins/sound/sounditem.cpp +++ b/plugins/sound/sounditem.cpp @@ -249,6 +249,11 @@ void SoundItem::refreshTips(const int volume, const bool force) } } +QPixmap SoundItem::pixmap() const +{ + return m_iconPixmap; +} + void SoundItem::sinkChanged(DBusSink *sink) { m_sinkInter = sink; diff --git a/plugins/sound/sounditem.h b/plugins/sound/sounditem.h index 2dd144367..d06b2d2e9 100644 --- a/plugins/sound/sounditem.h +++ b/plugins/sound/sounditem.h @@ -50,6 +50,7 @@ public: void refreshIcon(); void refreshTips(const int volume, const bool force = false); + QPixmap pixmap() const; signals: void requestContextMenu() const; diff --git a/plugins/sound/soundplugin.cpp b/plugins/sound/soundplugin.cpp index 2dc55b321..81d915ffd 100644 --- a/plugins/sound/soundplugin.cpp +++ b/plugins/sound/soundplugin.cpp @@ -21,6 +21,8 @@ #include "soundplugin.h" #include "soundaccessible.h" +#include "soundwidget.h" +#include "sounddeviceswidget.h" #include #include @@ -28,8 +30,9 @@ #define STATE_KEY "enable" SoundPlugin::SoundPlugin(QObject *parent) - : QObject(parent), - m_soundItem(nullptr) + : QObject(parent) + , m_soundItem(nullptr) + , m_soundWidget(nullptr) { QAccessible::installFactory(soundAccessibleFactory); } @@ -52,9 +55,17 @@ void SoundPlugin::init(PluginProxyInterface *proxyInter) return; m_soundItem.reset(new SoundItem); + m_soundWidget.reset(new SoundWidget); + m_soundWidget->setFixedHeight(60); - if (!pluginIsDisable()) + m_soundDeviceWidget.reset(new SoundDevicesWidget); + + if (!pluginIsDisable()) { m_proxyInter->itemAdded(this, SOUND_KEY); + connect(m_soundWidget.data(), &SoundWidget::rightIconClick, this, [ this, proxyInter ] { + proxyInter->requestSetAppletVisible(this, QUICK_ITEM_DETAIL_KEY, true); + }); + } } void SoundPlugin::pluginStateSwitched() @@ -71,18 +82,19 @@ bool SoundPlugin::pluginIsDisable() QWidget *SoundPlugin::itemWidget(const QString &itemKey) { - if (itemKey == SOUND_KEY) { + if (itemKey == SOUND_KEY) return m_soundItem.data(); - } + + if (itemKey == QUICK_ITEM_DETAIL_KEY) + return m_soundWidget.data(); return nullptr; } QWidget *SoundPlugin::itemTipsWidget(const QString &itemKey) { - if (itemKey == SOUND_KEY) { + if (itemKey == SOUND_KEY) return m_soundItem->tipsWidget(); - } return nullptr; } @@ -92,6 +104,8 @@ QWidget *SoundPlugin::itemPopupApplet(const QString &itemKey) if (itemKey == SOUND_KEY) { return m_soundItem->popupApplet(); } + if (itemKey == QUICK_ITEM_DETAIL_KEY) + return m_soundDeviceWidget.data(); return nullptr; } @@ -138,6 +152,16 @@ void SoundPlugin::pluginSettingsChanged() refreshPluginItemsVisible(); } +QIcon SoundPlugin::icon(const DockPart &) +{ + return m_soundItem->pixmap(); +} + +PluginsItemInterface::PluginStatus SoundPlugin::status() const +{ + return SoundPlugin::Active; +} + void SoundPlugin::refreshPluginItemsVisible() { if (pluginIsDisable()) diff --git a/plugins/sound/soundplugin.h b/plugins/sound/soundplugin.h index a76d99ddf..f14f5020a 100644 --- a/plugins/sound/soundplugin.h +++ b/plugins/sound/soundplugin.h @@ -25,6 +25,9 @@ #include "pluginsiteminterface.h" #include "sounditem.h" +class SoundWidget; +class SoundDevicesWidget; + class SoundPlugin : public QObject, PluginsItemInterface { Q_OBJECT @@ -49,12 +52,16 @@ public: void setSortKey(const QString &itemKey, const int order) override; void refreshIcon(const QString &itemKey) override; void pluginSettingsChanged() override; + QIcon icon(const DockPart &) override; + PluginStatus status() const override; private: void refreshPluginItemsVisible(); private: QScopedPointer m_soundItem; + QScopedPointer m_soundWidget; + QScopedPointer m_soundDeviceWidget; }; #endif // SOUNDPLUGIN_H diff --git a/plugins/sound/soundwidget.cpp b/plugins/sound/soundwidget.cpp new file mode 100644 index 000000000..3f6fbfbac --- /dev/null +++ b/plugins/sound/soundwidget.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 "soundwidget.h" +#include "brightnessmodel.h" +#include "imageutil.h" +#include "imageutil.h" +#include "slidercontainer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DGUI_USE_NAMESPACE + +#define ICON_SIZE 24 +#define BACKSIZE 36 + +SoundWidget::SoundWidget(QWidget *parent) + : QWidget(parent) + , m_dbusAudio(new DBusAudio("org.deepin.daemon.Audio1", "/org/deepin/daemon/Audio1", QDBusConnection::sessionBus(), this)) + , m_sliderContainer(new SliderContainer(this)) + , m_defaultSink(new DBusSink("org.deepin.daemon.Audio1", m_dbusAudio->defaultSink().path(), QDBusConnection::sessionBus(), this)) +{ + initUi(); + initConnection(); +} + +SoundWidget::~SoundWidget() +{ +} + +void SoundWidget::initUi() +{ + if (m_defaultSink) + m_sliderContainer->updateSliderValue(m_defaultSink->volume() * 100); + + 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); + + setEnabled(existActiveOutputDevice()); +} + +void SoundWidget::initConnection() +{ + connect(m_defaultSink, &DBusSink::VolumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue); + + connect(m_dbusAudio, &DBusAudio::DefaultSinkChanged, this, [ this ](const QDBusObjectPath &value) { + if (m_defaultSink) + delete m_defaultSink; + + m_defaultSink = new DBusSink("org.deepin.daemon.Audio1", value.path(), QDBusConnection::sessionBus(), this); + m_sliderContainer->updateSliderValue(m_defaultSink->volume() * 100); + connect(m_defaultSink, &DBusSink::VolumeChanged, m_sliderContainer, &SliderContainer::updateSliderValue); + }); + + connect(m_sliderContainer, &SliderContainer::sliderValueChanged, this, [ this ](int value) { + m_defaultSink->SetVolume(value, true); + }); + + connect(m_defaultSink, &DBusSink::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 (existActiveOutputDevice()) + m_defaultSink->SetMute(!m_defaultSink->mute()); + break; + } + case SliderContainer::IconPosition::RightIcon: { + // 弹出音量选择对话框 + Q_EMIT rightIconClick(); + break; + } + } + }); +} + +const QString SoundWidget::leftIcon() +{ + const bool mute = existActiveOutputDevice() ? m_defaultSink->mute() : true; + if (mute) + return QString(":/icons/resources/audio-volume-muted-dark"); + + return QString(":/icons/resources/volume"); +} + +const QString SoundWidget::rightIcon() +{ + return QString(":/icons/resources/broadcast"); +} + +/** 判断是否存在未禁用的声音输出设备 + * @brief SoundApplet::existActiveOutputDevice + * @return 存在返回true,否则返回false + */ +bool SoundWidget::existActiveOutputDevice() const +{ + QString info = m_dbusAudio->property("CardsWithoutUnavailable").toString(); + + QJsonDocument doc = QJsonDocument::fromJson(info.toUtf8()); + QJsonArray jCards = doc.array(); + for (QJsonValue cV : jCards) { + QJsonObject jCard = cV.toObject(); + QJsonArray jPorts = jCard["Ports"].toArray(); + + for (QJsonValue pV : jPorts) { + QJsonObject jPort = pV.toObject(); + if (jPort["Direction"].toInt() == 1 && jPort["Enabled"].toBool()) + return true; + } + } + + return false; +} diff --git a/plugins/sound/soundwidget.h b/plugins/sound/soundwidget.h new file mode 100644 index 000000000..ea3cdc727 --- /dev/null +++ b/plugins/sound/soundwidget.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 SOUNDWIDGET_H +#define SOUNDWIDGET_H + +#include "org_deepin_daemon_audio.h" +#include "org_deepin_daemon_audio_sink.h" + +#include +#include + +class QDBusMessage; +class SliderContainer; +class QLabel; +class AudioSink; + +DWIDGET_USE_NAMESPACE + +using DBusAudio = org::deepin::daemon::Audio1; +using DBusSink = org::deepin::daemon::audio1::Sink; + +class SoundWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SoundWidget(QWidget *parent = nullptr); + ~SoundWidget() override; + +Q_SIGNALS: + void rightIconClick(); + +protected: + void initUi(); + void initConnection(); + +private: + const QString leftIcon(); + const QString rightIcon(); + bool existActiveOutputDevice() const; + +private: + DBusAudio *m_dbusAudio; + SliderContainer *m_sliderContainer; + DBusSink *m_defaultSink; +}; + +#endif // VOLUMEWIDGET_H