dde-dock/plugins/sound/sounddeviceswidget.cpp
donghualin ec092bb980 fix: 修复任务栏插件音量设备显示错误的问题
音量设备解析错误,没有和控制中心保持一致,参照之前的音量的解析逻辑重新来显示音量的功能即可

Log:
Influence: 从任务栏打开音量,进入音量的详情页面,观察是否和控制中心一致
Bug: https://pms.uniontech.com/bug-view-165853.html
Change-Id: I39a6b0664ac2adc40f2ea523d8e0693426640ae5
2022-11-24 09:53:05 +00:00

474 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 "sounddeviceswidget.h"
#include "brightnessmodel.h"
#include "settingdelegate.h"
#include "imageutil.h"
#include "slidercontainer.h"
#include "sounddeviceport.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
#define AUDIOPORT 0
#define AUDIOSETTING 1
const int sortRole = 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_soundInter(new DBusAudio("org.deepin.daemon.Audio1", "/org/deepin/daemon/Audio1", QDBusConnection::sessionBus(), this))
, m_sinkInter(new DBusSink("org.deepin.daemon.Audio1", m_soundInter->defaultSink().path(), QDBusConnection::sessionBus(), this))
, m_model(new QStandardItemModel(this))
, m_delegate(new SettingDelegate(m_deviceList))
, m_lastPort(nullptr)
{
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);
m_model->setSortRole(sortRole);
// 增加音量设置
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::onAudioDevicesChanged()
{
QMap<uint, QStringList> tmpCardIds;
const QString cards = m_soundInter->cardsWithoutUnavailable();
QJsonDocument doc = QJsonDocument::fromJson(cards.toUtf8());
QJsonArray jCards = doc.array();
for (QJsonValue cV : jCards) {
QJsonObject jCard = cV.toObject();
const uint cardId = jCard["Id"].toInt();
const QString cardName = jCard["Name"].toString();
QJsonArray jPorts = jCard["Ports"].toArray();
QStringList tmpPorts;
for (QJsonValue pV : jPorts) {
QJsonObject jPort = pV.toObject();
const double portAvai = jPort["Available"].toDouble();
if (portAvai == 2 || portAvai == 0 ) {
const QString portId = jPort["Name"].toString();
const QString portName = jPort["Description"].toString();
SoundDevicePort *port = findPort(portId, cardId);
bool includePort = (port != nullptr);
if (!port)
port = new SoundDevicePort(m_model);
port->setId(portId);
port->setName(portName);
port->setDirection(SoundDevicePort::Direction(jPort["Direction"].toDouble()));
port->setCardId(cardId);
port->setCardName(cardName);
if (!includePort)
startAddPort(port);
tmpPorts << portId;
}
}
tmpCardIds.insert(cardId, tmpPorts);
}
onDefaultSinkChanged(m_soundInter->defaultSink());//重新获取切换的设备信息
for (SoundDevicePort *port : m_ports) {
//只要有一个设备在控制中心被禁用后,在任务栏声音设备列表中该设备会被移除,
if (!m_soundInter->IsPortEnabled(port->cardId(), port->id())) {
removeDisabledDevice(port->id(), port->cardId());
}
//判断端口是否在最新的设备列表中
if (tmpCardIds.contains(port->cardId())) {
if (!tmpCardIds[port->cardId()].contains(port->id())) {
startRemovePort(port->id(), port->cardId());
}
}
else {
startRemovePort(port->id(), port->cardId());
}
}
//当只有一个设备剩余时,该设备也需要移除
removeLastDevice();
}
void SoundDevicesWidget::initConnection()
{
connect(m_sinkInter, &DBusSink::VolumeChanged, this, [ = ](double value) { m_sliderContainer->updateSliderValue(value * 100); });
connect(m_sinkInter, &DBusSink::MuteChanged, this, [ = ] { m_sliderContainer->updateSliderValue(m_sinkInter->volume() * 100); });
connect(m_soundInter, &DBusAudio::DefaultSinkChanged, this, &SoundDevicesWidget::onDefaultSinkChanged);
connect(m_delegate, &SettingDelegate::selectIndexChanged, this, &SoundDevicesWidget::onSelectIndexChanged);
connect(m_soundInter, &DBusAudio::PortEnabledChanged, this, &SoundDevicesWidget::onAudioDevicesChanged);
connect(m_soundInter, &DBusAudio::CardsWithoutUnavailableChanged, this, &SoundDevicesWidget::onAudioDevicesChanged);
connect(m_sliderContainer, &SliderContainer::sliderValueChanged, this, [ this ](int value) {
m_sinkInter->SetVolume(value * 0.01, true);
});
}
/**
* @brief SoundApplet::startAddPort 添加端口前判断
* @param port 端口
*/
void SoundDevicesWidget::startAddPort(SoundDevicePort *port)
{
if (findPort(port->id(), port->cardId()))
return;
if (port->direction() == SoundDevicePort::Out) {
m_ports.append(port);
addPort(port);
}
}
void SoundDevicesWidget::startRemovePort(const QString &portId, const uint &cardId)
{
SoundDevicePort *port = findPort(portId, cardId);
if (port) {
m_ports.removeOne(port);
port->deleteLater();
removePort(portId, cardId);
}
}
void SoundDevicesWidget::addPort(const SoundDevicePort *port)
{
DStandardItem *portItem = new DStandardItem;
QString deviceName = port->name() + "(" + port->cardName() + ")";
portItem->setIcon(QIcon(soundIconFile()));
portItem->setText(deviceName);
portItem->setTextColorRole(QPalette::BrightText);
portItem->setData(QVariant::fromValue<const SoundDevicePort *>(port), itemDataRole);
portItem->setData(port->isActive(), itemCheckRole);
portItem->setData(AUDIOPORT, itemFlagRole);
connect(port, &SoundDevicePort::nameChanged, this, [ = ](const QString &str) {
QString devName = str + "(" + port->cardName() + ")";
portItem->setText(devName);
});
connect(port, &SoundDevicePort::cardNameChanged, this, [ = ](const QString &str) {
QString devName = port->name() + "(" + str + ")";
portItem->setText(devName);
});
connect(port, &SoundDevicePort::isActiveChanged, this, [ = ](bool isActive) {
portItem->setCheckState(isActive ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
});
if (port->isActive()) {
portItem->setCheckState(Qt::CheckState::Checked);
}
m_model->appendRow(portItem);
// 遍历列表,依次对不同的设备排序
int row = 0;
int rowCount = m_model->rowCount();
for (int i = 0; i < rowCount; i++) {
QStandardItem *item = m_model->item(i);
if (item->data(itemFlagRole).toInt() == AUDIOSETTING) {
item->setData(rowCount - 1, sortRole);
} else {
item->setData(row, sortRole);
row++;
}
}
m_model->sort(0);
}
void SoundDevicesWidget::removePort(const QString &portId, const uint &cardId)
{
int removeRow = -1;
for (int i = 0; i < m_model->rowCount(); i++) {
QStandardItem *item = m_model->item(i);
if (item->data(itemFlagRole).toInt() != AUDIOPORT)
continue;
const SoundDevicePort *port = item->data(itemDataRole).value<const SoundDevicePort *>();
if (port && port->id() == portId && cardId == port->cardId()) {
removeRow = i;
break;
}
}
if (removeRow >= 0)
m_model->removeRow(removeRow);
}
void SoundDevicesWidget::activePort(const QString &portId, const uint &cardId)
{
for (SoundDevicePort *it : m_ports)
it->setIsActive(it->id() == portId && it->cardId() == cardId);
}
void SoundDevicesWidget::removeLastDevice()
{
if (m_ports.count() == 1 && m_ports.at(0)) {
m_lastPort = new SoundDevicePort(m_model);
m_lastPort->setId(m_ports.at(0)->id());
m_lastPort->setName(m_ports.at(0)->name());
m_lastPort->setDirection(m_ports.at(0)->direction());
m_lastPort->setCardId(m_ports.at(0)->cardId());
m_lastPort->setCardName(m_ports.at(0)->cardName());
startRemovePort(m_ports.at(0)->id(), m_ports.at(0)->cardId());
qDebug() << "remove last output device";
}
}
void SoundDevicesWidget::removeDisabledDevice(QString portId, unsigned int cardId)
{
startRemovePort(portId, cardId);
if (m_sinkInter->activePort().name == portId && m_sinkInter->card() == cardId) {
for (SoundDevicePort *port : m_ports)
port->setIsActive(false);
}
}
QString SoundDevicesWidget::leftIcon()
{
QString iconLeft = QString(":/icons/resources/audio-volume-%1").arg(m_sinkInter->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
{
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()
{
//无声卡状态下会有伪sink设备显示音量为0
m_sliderContainer->updateSliderValue(findPort(m_sinkInter->activePort().name, m_sinkInter->card()) != nullptr ? m_sinkInter->volume() * 100 : 0);
}
uint SoundDevicesWidget::audioPortCardId(const AudioPort &audioport) const
{
QString cards = m_soundInter->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<uint>(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;
}
SoundDevicePort *SoundDevicesWidget::findPort(const QString &portId, const uint &cardId) const
{
auto it = std::find_if(m_ports.begin(), m_ports.end(), [ = ] (SoundDevicePort *p) {
return (p->id() == portId && p->cardId() == cardId);
});
if (it != m_ports.end()) {
return *it;
}
return nullptr;
}
void SoundDevicesWidget::onSelectIndexChanged(const QModelIndex &index)
{
int flag = index.data(itemFlagRole).toInt();
if (flag == AUDIOPORT) {
const SoundDevicePort *port = m_model->data(index, itemDataRole).value<const SoundDevicePort *>();
if (port) {
m_soundInter->SetPort(port->cardId(), port->id(), int(port->direction()));
//手动勾选时启用设备
m_soundInter->SetPortEnabled(port->cardId(), port->id(), true);
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_sinkInter;
m_sinkInter = new DBusSink("org.deepin.daemon.Audio1", m_soundInter->defaultSink().path(), QDBusConnection::sessionBus(), this);
connect(m_sinkInter, &DBusSink::VolumeChanged, this, [ = ](double value) { m_sliderContainer->updateSliderValue(value * 100); });
connect(m_sinkInter, &DBusSink::MuteChanged, this, [ = ] { m_sliderContainer->updateSliderValue(m_sinkInter->volume() * 100); });
QString portId = m_sinkInter->activePort().name;
uint cardId = m_sinkInter->card();
//最后一个设备会被移除,但是当在控制中心选中此设备后需要添加,并勾选
if (m_lastPort && m_lastPort->cardId() == cardId && m_lastPort->id() == portId)
startAddPort(m_lastPort);
activePort(portId, cardId);
for (int i = 0; i < m_model->rowCount() ; i++) {
QStandardItem *item = m_model->item(i);
if (item->data(itemFlagRole).toInt() != AUDIOPORT)
continue;
const SoundDevicePort *soundPort = item->data(itemDataRole).value<const SoundDevicePort *>();
item->setData((soundPort && soundPort->id() == portId && soundPort->cardId() == cardId), itemCheckRole);
}
resetVolumeInfo();
m_deviceList->update();
}