feat: 特效模式下右侧插件区域功能
完成插件区域的引用图标加载、快捷设置的展示、老插件的加载等功能 Log: 完成特效模式下右侧插件区域的功能 Influence: v23任务栏右侧插件区域 Task: https://pms.uniontech.com/task-view-110311.html Change-Id: I1599907d4529c57070ee2e21b70cc0c17f132e5e
@ -68,6 +68,8 @@ include_directories(
|
||||
frame/util
|
||||
frame/window
|
||||
frame/window/components
|
||||
frame/window/tray
|
||||
frame/window/tray/widgets
|
||||
frame/xcb
|
||||
../widgets
|
||||
../interfaces
|
||||
@ -84,6 +86,8 @@ aux_source_directory(frame/item/resources RESOURCES)
|
||||
aux_source_directory(frame/util UTIL)
|
||||
aux_source_directory(frame/window WINDOW)
|
||||
aux_source_directory(frame/window/components WINDOWCOMPONENTS)
|
||||
aux_source_directory(frame/window/tray WINDOWTRAY)
|
||||
aux_source_directory(frame/window/tray/widgets WINDOWTRAYWIDGET)
|
||||
aux_source_directory(frame/xcb XCB)
|
||||
|
||||
file(GLOB SRC_PATH
|
||||
@ -98,6 +102,8 @@ file(GLOB SRC_PATH
|
||||
${UTIL}
|
||||
${WINDOW}
|
||||
${WINDOWCOMPONENTS}
|
||||
${WINDOWTRAY}
|
||||
${WINDOWTRAYWIDGET}
|
||||
${XCB}
|
||||
)
|
||||
|
||||
|
@ -21,8 +21,9 @@ find_package(Qt5DBus REQUIRED)
|
||||
find_package(Qt5Svg REQUIRED)
|
||||
find_package(DtkWidget REQUIRED)
|
||||
find_package(DtkCMake REQUIRED)
|
||||
find_package(dbusmenu-qt5 REQUIRED)
|
||||
|
||||
pkg_check_modules(XCB_EWMH REQUIRED xcb-ewmh x11 xcursor)
|
||||
pkg_check_modules(XCB_EWMH REQUIRED xcb-image xcb-ewmh xcb-composite xtst x11 dbusmenu-qt5 xext xcursor)
|
||||
pkg_check_modules(DFrameworkDBus REQUIRED dframeworkdbus)
|
||||
pkg_check_modules(QGSettings REQUIRED gsettings-qt)
|
||||
pkg_check_modules(DtkGUI REQUIRED dtkgui)
|
||||
@ -43,6 +44,7 @@ target_include_directories(${BIN_NAME} PUBLIC
|
||||
${QGSettings_INCLUDE_DIRS}
|
||||
${DtkGUI_INCLUDE_DIRS}
|
||||
${Qt5Svg_INCLUDE_DIRS}
|
||||
${dbusmenu-qt5_INCLUDE_DIRS}
|
||||
../interfaces
|
||||
../widgets
|
||||
accessible
|
||||
@ -55,6 +57,8 @@ target_include_directories(${BIN_NAME} PUBLIC
|
||||
util
|
||||
window
|
||||
window/components
|
||||
window/tray
|
||||
window/tray/widgets
|
||||
xcb
|
||||
../plugins/tray
|
||||
../plugins/show-desktop
|
||||
@ -78,6 +82,7 @@ target_link_libraries(${BIN_NAME} PRIVATE
|
||||
${QGSettings_LIBRARIES}
|
||||
${DtkGUI_LIBRARIES}
|
||||
${Qt5Svg_LIBRARIES}
|
||||
-lpthread -lm
|
||||
)
|
||||
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "sw_64")
|
||||
|
@ -59,7 +59,7 @@ void DockPluginsController::itemAdded(PluginsItemInterface *const itemInter, con
|
||||
connect(static_cast<TrayPluginItem *>(item), &TrayPluginItem::trayVisableCountChanged,
|
||||
this, &DockPluginsController::trayVisableCountChanged, Qt::UniqueConnection);
|
||||
} else {
|
||||
item = new PluginsItem(itemInter, itemKey, pluginApi);
|
||||
item = createPluginsItem(itemInter, itemKey, pluginApi);
|
||||
}
|
||||
|
||||
mPluginsMap[itemInter][itemKey] = item;
|
||||
@ -67,6 +67,11 @@ void DockPluginsController::itemAdded(PluginsItemInterface *const itemInter, con
|
||||
emit pluginItemInserted(item);
|
||||
}
|
||||
|
||||
PluginsItem *DockPluginsController::createPluginsItem(PluginsItemInterface *const itemInter, const QString &itemKey, const QString &pluginApi)
|
||||
{
|
||||
return new PluginsItem(itemInter, itemKey, pluginApi);
|
||||
}
|
||||
|
||||
void DockPluginsController::itemUpdate(PluginsItemInterface *const itemInter, const QString &itemKey)
|
||||
{
|
||||
PluginsItem *item = static_cast<PluginsItem *>(pluginItemAt(itemInter, itemKey));
|
||||
|
@ -54,6 +54,9 @@ public:
|
||||
|
||||
void startLoader();
|
||||
|
||||
protected:
|
||||
virtual PluginsItem *createPluginsItem(PluginsItemInterface *const itemInter, const QString &itemKey, const QString &pluginApi);
|
||||
|
||||
signals:
|
||||
void pluginItemInserted(PluginsItem *pluginItem) const;
|
||||
void pluginItemRemoved(PluginsItem *pluginItem) const;
|
||||
|
172
frame/controller/systemplugincontroller.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 "systemplugincontroller.h"
|
||||
#include "pluginsiteminterface.h"
|
||||
#include "utils.h"
|
||||
#include "systempluginitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
SystemPluginController::SystemPluginController(QObject *parent)
|
||||
: AbstractPluginsController(parent)
|
||||
{
|
||||
setObjectName("SystemTray");
|
||||
}
|
||||
|
||||
void SystemPluginController::itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey)
|
||||
{
|
||||
QMap<PluginsItemInterface *, QMap<QString, QObject *>> &mPluginsMap = pluginsMap();
|
||||
|
||||
// check if same item added
|
||||
if (mPluginsMap.contains(itemInter))
|
||||
if (mPluginsMap[itemInter].contains(itemKey))
|
||||
return;
|
||||
|
||||
SystemPluginItem *item = new SystemPluginItem(itemInter, itemKey);
|
||||
connect(item, &SystemPluginItem::itemVisibleChanged, this, [ = ] (bool visible){
|
||||
emit visible ? pluginItemAdded(itemKey, item) : pluginItemRemoved(itemKey, item);
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
mPluginsMap[itemInter][itemKey] = item;
|
||||
|
||||
// 隐藏的插件不加入到布局中
|
||||
if (Utils::SettingValue(QString("com.deepin.dde.dock.module.") + itemInter->pluginName(), QByteArray(), "enable", true).toBool())
|
||||
emit pluginItemAdded(itemKey, item);
|
||||
}
|
||||
|
||||
void SystemPluginController::itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey)
|
||||
{
|
||||
SystemPluginItem *item = static_cast<SystemPluginItem *>(pluginItemAt(itemInter, itemKey));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->update();
|
||||
|
||||
emit pluginItemUpdated(itemKey, item);
|
||||
}
|
||||
|
||||
void SystemPluginController::itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey)
|
||||
{
|
||||
SystemPluginItem *item = static_cast<SystemPluginItem *>(pluginItemAt(itemInter, itemKey));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->detachPluginWidget();
|
||||
|
||||
emit pluginItemRemoved(itemKey, item);
|
||||
|
||||
QMap<PluginsItemInterface *, QMap<QString, QObject *>> &mPluginsMap = pluginsMap();
|
||||
mPluginsMap[itemInter].remove(itemKey);
|
||||
|
||||
// do not delete the itemWidget object(specified in the plugin interface)
|
||||
item->centralWidget()->setParent(nullptr);
|
||||
|
||||
// just delete our wrapper object(PluginsItem)
|
||||
item->deleteLater();
|
||||
}
|
||||
|
||||
void SystemPluginController::requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide)
|
||||
{
|
||||
SystemPluginItem *item = static_cast<SystemPluginItem *>(pluginItemAt(itemInter, itemKey));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
Q_EMIT item->requestWindowAutoHide(autoHide);
|
||||
}
|
||||
|
||||
void SystemPluginController::requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey)
|
||||
{
|
||||
SystemPluginItem *item = static_cast<SystemPluginItem *>(pluginItemAt(itemInter, itemKey));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
Q_EMIT item->requestRefershWindowVisible();
|
||||
}
|
||||
|
||||
void SystemPluginController::requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible)
|
||||
{
|
||||
SystemPluginItem *item = static_cast<SystemPluginItem *>(pluginItemAt(itemInter, itemKey));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (visible) {
|
||||
item->showPopupApplet(itemInter->itemPopupApplet(itemKey));
|
||||
} else {
|
||||
item->hidePopup();
|
||||
}
|
||||
}
|
||||
|
||||
int SystemPluginController::systemTrayItemSortKey(const QString &itemKey)
|
||||
{
|
||||
auto inter = pluginInterAt(itemKey);
|
||||
|
||||
if (!inter) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return inter->itemSortKey(itemKey);
|
||||
}
|
||||
|
||||
void SystemPluginController::setSystemTrayItemSortKey(const QString &itemKey, const int order)
|
||||
{
|
||||
auto inter = pluginInterAt(itemKey);
|
||||
|
||||
if (!inter) {
|
||||
return;
|
||||
}
|
||||
|
||||
inter->setSortKey(itemKey, order);
|
||||
}
|
||||
|
||||
const QVariant SystemPluginController::getValueSystemTrayItem(const QString &itemKey, const QString &key, const QVariant &fallback)
|
||||
{
|
||||
auto inter = pluginInterAt(itemKey);
|
||||
|
||||
if (!inter) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return getValue(inter, key, fallback);
|
||||
}
|
||||
|
||||
void SystemPluginController::saveValueSystemTrayItem(const QString &itemKey, const QString &key, const QVariant &value)
|
||||
{
|
||||
auto inter = pluginInterAt(itemKey);
|
||||
|
||||
if (!inter) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveValue(inter, key, value);
|
||||
}
|
||||
|
||||
void SystemPluginController::startLoader()
|
||||
{
|
||||
QString pluginsDir("../plugins/system-trays");
|
||||
if (!QDir(pluginsDir).exists()) {
|
||||
pluginsDir = "/usr/lib/dde-dock/plugins/system-trays";
|
||||
}
|
||||
qDebug() << "using system tray plugins dir:" << pluginsDir;
|
||||
|
||||
AbstractPluginsController::startLoader(new PluginLoader(pluginsDir, this));
|
||||
}
|
68
frame/controller/systemplugincontroller.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 SYSTEMTRAYSCONTROLLER_H
|
||||
#define SYSTEMTRAYSCONTROLLER_H
|
||||
|
||||
#include "pluginproxyinterface.h"
|
||||
#include "abstractpluginscontroller.h"
|
||||
#include "abstractpluginscontroller.h"
|
||||
|
||||
#include <com_deepin_dde_daemon_dock.h>
|
||||
|
||||
#include <QPluginLoader>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QDBusConnectionInterface>
|
||||
|
||||
class PluginsItemInterface;
|
||||
class SystemPluginItem;
|
||||
|
||||
class SystemPluginController : public AbstractPluginsController
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SystemPluginController(QObject *parent = nullptr);
|
||||
|
||||
// implements PluginProxyInterface
|
||||
void itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) override;
|
||||
void itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) override;
|
||||
void itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) override;
|
||||
void requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) override;
|
||||
void requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) override;
|
||||
void requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) override;
|
||||
|
||||
int systemTrayItemSortKey(const QString &itemKey);
|
||||
void setSystemTrayItemSortKey(const QString &itemKey, const int order);
|
||||
|
||||
const QVariant getValueSystemTrayItem(const QString &itemKey, const QString &key, const QVariant& fallback = QVariant());
|
||||
void saveValueSystemTrayItem(const QString &itemKey, const QString &key, const QVariant &value);
|
||||
|
||||
void startLoader();
|
||||
|
||||
signals:
|
||||
void pluginItemAdded(const QString &itemKey, SystemPluginItem *pluginItem);
|
||||
void pluginItemRemoved(const QString &itemKey, SystemPluginItem *pluginItem);
|
||||
void pluginItemUpdated(const QString &itemKey, SystemPluginItem *pluginItem);
|
||||
};
|
||||
|
||||
#endif // SYSTEMTRAYSCONTROLLER_H
|
3
frame/item/arrow-down.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill="#FFF" fill-rule="evenodd" points="6.659 10.73 5.952 11.437 .563 6.048 5.952 .659 6.659 1.366 1.977 6.048" transform="rotate(-90 9 5)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 244 B |
3
frame/item/arrow-left.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill="#FFF" fill-rule="evenodd" points="6.596 10.671 5.889 11.378 .5 5.989 5.889 .6 6.596 1.307 1.914 5.989" transform="translate(6 4)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 240 B |
3
frame/item/arrow-right.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill="#FFF" fill-rule="evenodd" points="6.5 10.693 5.793 11.4 .404 6.011 5.793 .622 6.5 1.329 1.818 6.011" transform="matrix(-1 0 0 1 14 3.978)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 249 B |
3
frame/item/arrow-up.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill="#FFF" fill-rule="evenodd" points="6.437 10.73 5.73 11.437 .341 6.048 5.73 .659 6.437 1.366 1.755 6.048" transform="matrix(0 1 1 0 4 6)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 246 B |
@ -13,6 +13,10 @@
|
||||
<file>resources/arrow-right.svg</file>
|
||||
<file>resources/arrow-left.svg</file>
|
||||
<file>resources/arrow-down.svg</file>
|
||||
<file>resources/arrow-up-dark.svg</file>
|
||||
<file>resources/arrow-right-dark.svg</file>
|
||||
<file>resources/arrow-left-dark.svg</file>
|
||||
<file>resources/arrow-down-dark.svg</file>
|
||||
<file>resources/application-x-desktop.svg</file>
|
||||
<file>resources/close_round_hover.svg</file>
|
||||
<file>resources/close_round_press.svg</file>
|
||||
|
3
frame/item/resources/arrow-down-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill-rule="evenodd" points="6.659 10.73 5.952 11.437 .563 6.048 5.952 .659 6.659 1.366 1.977 6.048" transform="rotate(-90 9 5)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 232 B |
3
frame/item/resources/arrow-left-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill-rule="evenodd" points="6.596 10.671 5.889 11.378 .5 5.989 5.889 .6 6.596 1.307 1.914 5.989" transform="translate(6 4)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 228 B |
3
frame/item/resources/arrow-right-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill-rule="evenodd" points="6.5 10.693 5.793 11.4 .404 6.011 5.793 .622 6.5 1.329 1.818 6.011" transform="matrix(-1 0 0 1 14 3.978)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 237 B |
3
frame/item/resources/arrow-up-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<polygon fill-rule="evenodd" points="6.437 10.73 5.73 11.437 .341 6.048 5.73 .659 6.437 1.366 1.755 6.048" transform="matrix(0 1 1 0 4 6)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 234 B |
153
frame/window/components/datetimedisplayer.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "datetimedisplayer.h"
|
||||
|
||||
#include <DFontSizeManager>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPainter>
|
||||
#include <QFont>
|
||||
|
||||
DWIDGET_USE_NAMESPACE
|
||||
|
||||
#define DATETIMESIZE 40
|
||||
|
||||
DateTimeDisplayer::DateTimeDisplayer(QWidget *parent)
|
||||
: QWidget (parent)
|
||||
, m_timedateInter(new Timedate("com.deepin.daemon.Timedate", "/com/deepin/daemon/Timedate", QDBusConnection::sessionBus(), this))
|
||||
, m_position(Dock::Position::Bottom)
|
||||
, m_timeFont(DFontSizeManager::instance()->t6())
|
||||
, m_dateFont(DFontSizeManager::instance()->t10())
|
||||
{
|
||||
initUi();
|
||||
setShortDateFormat(m_timedateInter->shortDateFormat());
|
||||
setShortTimeFormat(m_timedateInter->shortTimeFormat());
|
||||
connect(m_timedateInter, &Timedate::ShortDateFormatChanged, this, &DateTimeDisplayer::setShortDateFormat);
|
||||
connect(m_timedateInter, &Timedate::ShortTimeFormatChanged, this, &DateTimeDisplayer::setShortTimeFormat);
|
||||
// 连接日期时间修改信号,更新日期时间插件的布局
|
||||
connect(m_timedateInter, &Timedate::TimeUpdate, this, [ this ] {
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
DateTimeDisplayer::~DateTimeDisplayer()
|
||||
{
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::setPositon(Dock::Position position)
|
||||
{
|
||||
if (m_position == position)
|
||||
return;
|
||||
|
||||
m_position = position;
|
||||
setCurrentPolicy();
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::setCurrentPolicy()
|
||||
{
|
||||
switch (m_position) {
|
||||
case Dock::Position::Top:
|
||||
case Dock::Position::Bottom: {
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left:
|
||||
case Dock::Position::Right: {
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::initUi()
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
}
|
||||
|
||||
QSize DateTimeDisplayer::suitableSize()
|
||||
{
|
||||
DateTimeInfo info = dateTimeInfo();
|
||||
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
|
||||
return QSize(info.m_dateRect.right(), height());
|
||||
|
||||
return QSize(width(), info.m_dateRect.bottom());
|
||||
}
|
||||
|
||||
DateTimeDisplayer::DateTimeInfo DateTimeDisplayer::dateTimeInfo()
|
||||
{
|
||||
DateTimeInfo info;
|
||||
const QDateTime current = QDateTime::currentDateTime();
|
||||
|
||||
const Dock::Position position = qApp->property(PROP_POSITION).value<Dock::Position>();
|
||||
|
||||
info.m_timeRect = rect();
|
||||
info.m_dateRect = rect();
|
||||
|
||||
QString format = m_shortTimeFormat;
|
||||
if (!m_timedateInter->use24HourFormat()) {
|
||||
if (position == Dock::Top || position == Dock::Bottom)
|
||||
format = format.append(" AP");
|
||||
else
|
||||
format = format.append("\nAP");
|
||||
}
|
||||
|
||||
info.m_time = current.toString(format);
|
||||
info.m_date = current.toString(m_shortDateFormat);
|
||||
int timeWidth = QFontMetrics(m_timeFont).boundingRect(info.m_time).width() + 12 * 2;
|
||||
int dateWidth = QFontMetrics(m_dateFont).boundingRect(info.m_date).width() + 2;
|
||||
|
||||
if (position == Dock::Top || position == Dock::Bottom) {
|
||||
info.m_timeRect = QRect(10, 0, timeWidth, height());
|
||||
int right = rect().width() - QFontMetrics(m_dateFont).width(info.m_date) - 2;
|
||||
info.m_dateRect = QRect(right, 0, dateWidth, height());
|
||||
} else {
|
||||
info.m_timeRect = QRect(0, 0, timeWidth, DATETIMESIZE / 2);
|
||||
info.m_dateRect = QRect(0, DATETIMESIZE / 2 + 1, dateWidth, DATETIMESIZE / 2);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
|
||||
DateTimeInfo info = dateTimeInfo();
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setPen(QPen(palette().brightText(), 1));
|
||||
|
||||
painter.drawText(info.m_timeRect, Qt::AlignLeft | Qt::AlignVCenter, info.m_time);
|
||||
|
||||
painter.setFont(m_dateFont);
|
||||
painter.drawText(info.m_dateRect, Qt::AlignLeft | Qt::AlignVCenter, info.m_date);
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::setShortDateFormat(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 0: m_shortDateFormat = "yyyy/M/d"; break;
|
||||
case 1: m_shortDateFormat = "yyyy-M-d"; break;
|
||||
case 2: m_shortDateFormat = "yyyy.M.d"; break;
|
||||
case 3: m_shortDateFormat = "yyyy/MM/dd"; break;
|
||||
case 4: m_shortDateFormat = "yyyy-MM-dd"; break;
|
||||
case 5: m_shortDateFormat = "yyyy.MM.dd"; break;
|
||||
case 6: m_shortDateFormat = "yy/M/d"; break;
|
||||
case 7: m_shortDateFormat = "yy-M-d"; break;
|
||||
case 8: m_shortDateFormat = "yy.M.d"; break;
|
||||
default: m_shortDateFormat = "yyyy-MM-dd"; break;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void DateTimeDisplayer::setShortTimeFormat(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 0: m_shortTimeFormat = "h:mm"; break;
|
||||
case 1: m_shortTimeFormat = "hh:mm"; break;
|
||||
default: m_shortTimeFormat = "hh:mm"; break;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
51
frame/window/components/datetimedisplayer.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef DATETIMEDISPLAYER_H
|
||||
#define DATETIMEDISPLAYER_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFont>
|
||||
|
||||
#include <com_deepin_daemon_timedate.h>
|
||||
|
||||
using Timedate = com::deepin::daemon::Timedate;
|
||||
|
||||
class DateTimeDisplayer : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
struct DateTimeInfo {
|
||||
QString m_time;
|
||||
QString m_date;
|
||||
QRect m_timeRect;
|
||||
QRect m_dateRect;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit DateTimeDisplayer(QWidget *parent = nullptr);
|
||||
~DateTimeDisplayer() override;
|
||||
void setPositon(Dock::Position position);
|
||||
QSize suitableSize();
|
||||
|
||||
private:
|
||||
void initUi();
|
||||
void setCurrentPolicy();
|
||||
DateTimeInfo dateTimeInfo();
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void setShortDateFormat(int type);
|
||||
void setShortTimeFormat(int type);
|
||||
|
||||
private:
|
||||
Timedate *m_timedateInter;
|
||||
QString m_shortDateFormat;
|
||||
QString m_shortTimeFormat;
|
||||
Dock::Position m_position;
|
||||
mutable QFont m_timeFont;
|
||||
mutable QFont m_dateFont;
|
||||
};
|
||||
|
||||
#endif // DATETIMEDISPLAYER_H
|
167
frame/window/systempluginwindow.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include "systempluginwindow.h"
|
||||
#include "systemplugincontroller.h"
|
||||
#include "systempluginitem.h"
|
||||
#include "dockpluginscontroller.h"
|
||||
|
||||
#include <DListView>
|
||||
#include <QBoxLayout>
|
||||
#include <QMetaObject>
|
||||
|
||||
#define MAXICONSIZE 48
|
||||
#define MINICONSIZE 24
|
||||
#define ICONMARGIN 8
|
||||
|
||||
SystemPluginWindow::SystemPluginWindow(QWidget *parent)
|
||||
: DBlurEffectWidget(parent)
|
||||
, m_pluginController(new FixedPluginController(this))
|
||||
, m_listView(new DListView(this))
|
||||
, m_position(Dock::Position::Bottom)
|
||||
, m_mainLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight, this))
|
||||
{
|
||||
initUi();
|
||||
connect(m_pluginController, &DockPluginsController::pluginItemInserted, this, &SystemPluginWindow::onPluginItemAdded);
|
||||
connect(m_pluginController, &DockPluginsController::pluginItemRemoved, this, &SystemPluginWindow::onPluginItemRemoved);
|
||||
connect(m_pluginController, &DockPluginsController::pluginItemUpdated, this, &SystemPluginWindow::onPluginItemUpdated);
|
||||
QMetaObject::invokeMethod(m_pluginController, &DockPluginsController::startLoader, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
SystemPluginWindow::~SystemPluginWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void SystemPluginWindow::setPositon(Position position)
|
||||
{
|
||||
if (m_position == position)
|
||||
return;
|
||||
|
||||
m_position = position;
|
||||
|
||||
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
else
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
}
|
||||
|
||||
QSize SystemPluginWindow::suitableSize()
|
||||
{
|
||||
QMargins m = m_mainLayout->contentsMargins();
|
||||
if (m_mainLayout->direction() == QBoxLayout::Direction::LeftToRight) {
|
||||
int itemSize = height() - m_mainLayout->contentsMargins().top() - m_mainLayout->contentsMargins().bottom();
|
||||
int itemWidth = m.left() + m.right();
|
||||
for (int i = 0; i < m_mainLayout->count(); i++) {
|
||||
QWidget *widget = m_mainLayout->itemAt(i)->widget();
|
||||
if (!widget)
|
||||
continue;
|
||||
|
||||
PluginsItem *item = qobject_cast<PluginsItem *>(widget);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
// 如果是横向的,则高度是固定,高宽一致,因此读取高度作为它的尺寸值
|
||||
itemWidth += itemSize;
|
||||
if (i < m_mainLayout->count() - 1)
|
||||
itemWidth += m_mainLayout->spacing();
|
||||
}
|
||||
|
||||
itemWidth += m.right();
|
||||
return QSize(itemWidth, height());
|
||||
}
|
||||
|
||||
int itemSize = width() - m_mainLayout->contentsMargins().left() - m_mainLayout->contentsMargins().right();
|
||||
int itemHeight = m.top();
|
||||
for (int i = 0; i < m_mainLayout->count(); i++) {
|
||||
QWidget *widget = m_mainLayout->itemAt(i)->widget();
|
||||
if (!widget)
|
||||
continue;
|
||||
|
||||
PluginsItem *item = qobject_cast<PluginsItem *>(widget);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
itemHeight += itemSize;
|
||||
if (i < m_mainLayout->count() - 1)
|
||||
itemHeight += m_mainLayout->spacing();
|
||||
}
|
||||
|
||||
itemHeight += m.bottom();
|
||||
|
||||
return QSize(width(), itemHeight);
|
||||
}
|
||||
|
||||
void SystemPluginWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
DBlurEffectWidget::resizeEvent(event);
|
||||
Q_EMIT pluginSizeChanged();
|
||||
}
|
||||
|
||||
void SystemPluginWindow::initUi()
|
||||
{
|
||||
m_mainLayout->setContentsMargins(8, 8, 8, 8);
|
||||
m_mainLayout->setSpacing(5);
|
||||
}
|
||||
|
||||
int SystemPluginWindow::calcIconSize() const
|
||||
{
|
||||
switch (m_position) {
|
||||
case Dock::Position::Top:
|
||||
case Dock::Position::Bottom: {
|
||||
if (height() >= 56)
|
||||
return MAXICONSIZE;
|
||||
if (height() <= 40)
|
||||
return MINICONSIZE;
|
||||
return height() - ICONMARGIN * 2;
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left:
|
||||
case Dock::Position::Right: {
|
||||
if (width() >= 56)
|
||||
return MAXICONSIZE;
|
||||
if (width() <= 40)
|
||||
return MINICONSIZE;
|
||||
return width() - ICONMARGIN * 2;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SystemPluginWindow::onPluginItemAdded(PluginsItem *pluginItem)
|
||||
{
|
||||
if (m_mainLayout->children().contains(pluginItem))
|
||||
return;
|
||||
|
||||
m_mainLayout->addWidget(pluginItem);
|
||||
Q_EMIT pluginSizeChanged();
|
||||
}
|
||||
|
||||
void SystemPluginWindow::onPluginItemRemoved(PluginsItem *pluginItem)
|
||||
{
|
||||
if (!m_mainLayout->children().contains(pluginItem))
|
||||
return;
|
||||
|
||||
m_mainLayout->removeWidget(pluginItem);
|
||||
Q_EMIT pluginSizeChanged();
|
||||
}
|
||||
|
||||
void SystemPluginWindow::onPluginItemUpdated(PluginsItem *pluginItem)
|
||||
{
|
||||
pluginItem->refreshIcon();
|
||||
}
|
||||
|
||||
// can loader plugins
|
||||
FixedPluginController::FixedPluginController(QObject *parent)
|
||||
: DockPluginsController(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const QVariant FixedPluginController::getValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &fallback)
|
||||
{
|
||||
if (key == "enable")
|
||||
return (itemInter->pluginName().compare("shutdown") == 0);
|
||||
|
||||
return AbstractPluginsController::getValue(itemInter, key, fallback);
|
||||
}
|
||||
|
||||
PluginsItem *FixedPluginController::createPluginsItem(PluginsItemInterface * const itemInter, const QString &itemKey, const QString &pluginApi)
|
||||
{
|
||||
return new StretchPluginsItem(itemInter, itemKey, pluginApi);
|
||||
}
|
59
frame/window/systempluginwindow.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef SYSTEMPLUGINWINDOW_H
|
||||
#define SYSTEMPLUGINWINDOW_H
|
||||
|
||||
#include "constants.h"
|
||||
#include "dockpluginscontroller.h"
|
||||
|
||||
#include <DBlurEffectWidget>
|
||||
|
||||
class DockPluginsController;
|
||||
class PluginsItem;
|
||||
class QBoxLayout;
|
||||
|
||||
namespace Dtk { namespace Widget { class DListView; } }
|
||||
|
||||
DWIDGET_USE_NAMESPACE
|
||||
|
||||
class SystemPluginWindow : public DBlurEffectWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void pluginSizeChanged();
|
||||
|
||||
public:
|
||||
explicit SystemPluginWindow(QWidget *parent = nullptr);
|
||||
~SystemPluginWindow() override;
|
||||
void setPositon(Dock::Position position);
|
||||
QSize suitableSize();
|
||||
|
||||
private:
|
||||
void initUi();
|
||||
int calcIconSize() const;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onPluginItemAdded(PluginsItem *pluginItem);
|
||||
void onPluginItemRemoved(PluginsItem *pluginItem);
|
||||
void onPluginItemUpdated(PluginsItem *pluginItem);
|
||||
|
||||
private:
|
||||
DockPluginsController *m_pluginController;
|
||||
DListView *m_listView;
|
||||
Dock::Position m_position;
|
||||
QBoxLayout *m_mainLayout;
|
||||
};
|
||||
|
||||
class FixedPluginController : public DockPluginsController
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FixedPluginController(QObject *parent);
|
||||
|
||||
protected:
|
||||
const QVariant getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()) override;
|
||||
PluginsItem *createPluginsItem(PluginsItemInterface *const itemInter, const QString &itemKey, const QString &pluginApi) override;
|
||||
};
|
||||
|
||||
#endif // SYSTEMPLUGINWINDOW_H
|
31
frame/window/tray/dbustraymanager.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file was generated by qdbusxml2cpp version 0.8
|
||||
* Command line was: qdbusxml2cpp -c DBusTrayManager -p dbustraymanager com.deepin.dde.TrayManager.xml
|
||||
*
|
||||
* qdbusxml2cpp is Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
|
||||
*
|
||||
* This is an auto-generated file.
|
||||
* This file may have been hand-edited. Look for HAND-EDIT comments
|
||||
* before re-generating it.
|
||||
*/
|
||||
|
||||
#include "dbustraymanager.h"
|
||||
|
||||
/*
|
||||
* Implementation of interface class DBusTrayManager
|
||||
*/
|
||||
|
||||
DBusTrayManager::DBusTrayManager(QObject *parent)
|
||||
: QDBusAbstractInterface("com.deepin.dde.TrayManager", "/com/deepin/dde/TrayManager", staticInterfaceName(), QDBusConnection::sessionBus(), parent)
|
||||
{
|
||||
qRegisterMetaType<TrayList>("TrayList");
|
||||
qDBusRegisterMetaType<TrayList>();
|
||||
|
||||
QDBusConnection::sessionBus().connect(this->service(), this->path(), "org.freedesktop.DBus.Properties", "PropertiesChanged","sa{sv}as", this, SLOT(__propertyChanged__(QDBusMessage)));
|
||||
}
|
||||
|
||||
DBusTrayManager::~DBusTrayManager()
|
||||
{
|
||||
QDBusConnection::sessionBus().disconnect(service(), path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", this, SLOT(propertyChanged(QDBusMessage)));
|
||||
}
|
||||
|
113
frame/window/tray/dbustraymanager.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This file was generated by qdbusxml2cpp version 0.8
|
||||
* Command line was: qdbusxml2cpp -c DBusTrayManager -p dbustraymanager com.deepin.dde.TrayManager.xml
|
||||
*
|
||||
* qdbusxml2cpp is Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
|
||||
*
|
||||
* This is an auto-generated file.
|
||||
* Do not edit! All changes made to it will be lost.
|
||||
*/
|
||||
|
||||
#ifndef DBUSTRAYMANAGER_H_1467094672
|
||||
#define DBUSTRAYMANAGER_H_1467094672
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
typedef QList<quint32> TrayList;
|
||||
|
||||
/*
|
||||
* Proxy class for interface com.deepin.dde.TrayManager
|
||||
*/
|
||||
class DBusTrayManager: public QDBusAbstractInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SLOT void __propertyChanged__(const QDBusMessage& msg)
|
||||
{
|
||||
QList<QVariant> arguments = msg.arguments();
|
||||
if (3 != arguments.count())
|
||||
return;
|
||||
QString interfaceName = msg.arguments().at(0).toString();
|
||||
if (interfaceName !="com.deepin.dde.TrayManager")
|
||||
return;
|
||||
QVariantMap changedProps = qdbus_cast<QVariantMap>(arguments.at(1).value<QDBusArgument>());
|
||||
foreach(const QString &prop, changedProps.keys()) {
|
||||
const QMetaObject* self = metaObject();
|
||||
for (int i=self->propertyOffset(); i < self->propertyCount(); ++i) {
|
||||
QMetaProperty p = self->property(i);
|
||||
if (p.name() == prop) {
|
||||
Q_EMIT p.notifySignal().invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
static inline const char *staticInterfaceName()
|
||||
{ return "com.deepin.dde.TrayManager"; }
|
||||
|
||||
public:
|
||||
explicit DBusTrayManager(QObject *parent = 0);
|
||||
|
||||
~DBusTrayManager();
|
||||
|
||||
Q_PROPERTY(TrayList TrayIcons READ trayIcons NOTIFY TrayIconsChanged)
|
||||
inline TrayList trayIcons() const
|
||||
{ return qvariant_cast< TrayList >(property("TrayIcons")); }
|
||||
|
||||
public Q_SLOTS: // METHODS
|
||||
inline QDBusPendingReply<> EnableNotification(uint in0, bool in1)
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(in0) << QVariant::fromValue(in1);
|
||||
return asyncCallWithArgumentList(QStringLiteral("EnableNotification"), argumentList);
|
||||
}
|
||||
|
||||
inline QDBusPendingReply<QString> GetName(uint in0)
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(in0);
|
||||
return asyncCallWithArgumentList(QStringLiteral("GetName"), argumentList);
|
||||
}
|
||||
|
||||
inline QDBusPendingReply<bool> Manage()
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
return asyncCallWithArgumentList(QStringLiteral("Manage"), argumentList);
|
||||
}
|
||||
|
||||
inline QDBusPendingReply<> RetryManager()
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
return asyncCallWithArgumentList(QStringLiteral("RetryManager"), argumentList);
|
||||
}
|
||||
|
||||
inline QDBusPendingReply<bool> Unmanage()
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
return asyncCallWithArgumentList(QStringLiteral("Unmanage"), argumentList);
|
||||
}
|
||||
|
||||
Q_SIGNALS: // SIGNALS
|
||||
void Added(uint in0);
|
||||
void Changed(uint in0);
|
||||
void Inited();
|
||||
void Removed(uint in0);
|
||||
// begin property changed signals
|
||||
void TrayIconsChanged();
|
||||
};
|
||||
|
||||
namespace com {
|
||||
namespace deepin {
|
||||
namespace dde {
|
||||
typedef ::DBusTrayManager TrayManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
132
frame/window/tray/tray_delegate.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ~ 2025 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: fanpengcheng <fanpengcheng_cm@deepin.com>
|
||||
*
|
||||
* Maintainer: fanpengcheng <fanpengcheng_cm@deepin.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 "tray_delegate.h"
|
||||
#include "tray_gridview.h"
|
||||
#include "tray_model.h"
|
||||
#include "widgets/xembedtrayitemwidget.h"
|
||||
#include "widgets/indicatortrayitem.h"
|
||||
#include "widgets/indicatorplugin.h"
|
||||
#include "widgets/snitrayitemwidget.h"
|
||||
#include "widgets/expandiconwidget.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QApplication>
|
||||
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
TrayDelegate::TrayDelegate(QObject *parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
, m_position(Dock::Position::Bottom)
|
||||
{
|
||||
}
|
||||
|
||||
void TrayDelegate::setPositon(Dock::Position position)
|
||||
{
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
QWidget *TrayDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
|
||||
TrayIconType type = index.data(TrayModel::TypeRole).value<TrayIconType>();
|
||||
QString key = index.data(TrayModel::KeyRole).value<QString>();
|
||||
QString servicePath = index.data(TrayModel::ServiceRole).value<QString>();
|
||||
quint32 winId = index.data(TrayModel::WinIdRole).value<quint32>();
|
||||
|
||||
BaseTrayWidget *trayWidget = nullptr;
|
||||
if(type == TrayIconType::XEMBED) {
|
||||
if (Utils::IS_WAYLAND_DISPLAY) {
|
||||
trayWidget = new XEmbedTrayItemWidget(winId, nullptr, nullptr, parent);
|
||||
} else {
|
||||
int screenp = 0;
|
||||
static xcb_connection_t *xcb_connection = xcb_connect(qgetenv("DISPLAY"), &screenp);
|
||||
static Display *m_display = XOpenDisplay(nullptr);
|
||||
trayWidget = new XEmbedTrayItemWidget(winId, xcb_connection, m_display, parent) ;
|
||||
}
|
||||
|
||||
const TrayModel *model = qobject_cast<const TrayModel *>(index.model());
|
||||
if (model)
|
||||
connect(model, &TrayModel::requestUpdateIcon, trayWidget, &BaseTrayWidget::updateIcon);
|
||||
} else if (type == TrayIconType::SNI) {
|
||||
trayWidget = new SNITrayItemWidget(servicePath, parent);
|
||||
} else if (type == TrayIconType::EXPANDICON) {
|
||||
ExpandIconWidget *widget = new ExpandIconWidget(parent);
|
||||
widget->setPositonValue(m_position);
|
||||
connect(widget, &ExpandIconWidget::trayVisbleChanged, this, [ = ](bool visible) {
|
||||
Q_EMIT visibleChanged(index, visible);
|
||||
});
|
||||
connect(this, &TrayDelegate::requestDrag, this, [ = ](bool on) {
|
||||
if (on) {
|
||||
widget->setTrayPanelVisible(true);
|
||||
} else {
|
||||
// 如果释放鼠标,则判断当前鼠标的位置是否在托盘内部,如果在,则无需隐藏
|
||||
QPoint currentPoint = QCursor::pos();
|
||||
TrayGridView *view = widget->popupTrayView();
|
||||
if (view->geometry().contains(currentPoint))
|
||||
widget->setTrayPanelVisible(true);
|
||||
else
|
||||
widget->setTrayPanelVisible(false);
|
||||
}
|
||||
});
|
||||
trayWidget = widget;
|
||||
} else if (type == TrayIconType::INDICATOR) {
|
||||
QString indicateName = key;
|
||||
int flagIndex = indicateName.indexOf("indicator:");
|
||||
if (flagIndex >= 0)
|
||||
indicateName = indicateName.right(indicateName.length() - QString("indicator:").length());
|
||||
IndicatorTrayItem *indicatorWidget = new IndicatorTrayItem(indicateName, parent);
|
||||
connect(indicatorWidget, &IndicatorTrayItem::removed, this, [ = ]{
|
||||
Q_EMIT removeRow(index);
|
||||
});
|
||||
trayWidget = indicatorWidget;
|
||||
}
|
||||
|
||||
return trayWidget;
|
||||
}
|
||||
|
||||
void TrayDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
BaseTrayWidget *widget = static_cast<BaseTrayWidget *>(editor);
|
||||
if (widget) {
|
||||
widget->setNeedShow(!index.data(TrayModel::Blank).toBool());
|
||||
}
|
||||
}
|
||||
|
||||
QSize TrayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(index);
|
||||
|
||||
return QSize(ITEM_SIZE, ITEM_SIZE);
|
||||
}
|
||||
|
||||
void TrayDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
QRect rect = option.rect;
|
||||
editor->setGeometry(rect.x() + ITEM_SPACING, rect.y() + ITEM_SPACING, ITEM_SIZE - (2 * ITEM_SPACING), ITEM_SIZE - 2 * ITEM_SPACING);
|
||||
}
|
56
frame/window/tray/tray_delegate.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ~ 2025 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: fanpengcheng <fanpengcheng_cm@deepin.com>
|
||||
*
|
||||
* Maintainer: fanpengcheng <fanpengcheng_cm@deepin.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 TRAYDELEGATE_H
|
||||
#define TRAYDELEGATE_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#define ITEM_SIZE 30
|
||||
#define ITEM_SPACING 5
|
||||
|
||||
struct WinInfo;
|
||||
|
||||
class TrayDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void removeRow(const QModelIndex &) const;
|
||||
void visibleChanged(const QModelIndex &, bool) const;
|
||||
void requestDrag(bool) const;
|
||||
|
||||
public:
|
||||
explicit TrayDelegate(QObject *parent = nullptr);
|
||||
void setPositon(Dock::Position position);
|
||||
|
||||
protected:
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override ;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
private:
|
||||
Dock::Position m_position;
|
||||
};
|
||||
|
||||
#endif // TRAYDELEGATE_H
|
375
frame/window/tray/tray_gridview.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
#include "tray_gridview.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDragLeaveEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QLabel>
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
#include "tray_model.h"
|
||||
#include "basetraywidget.h"
|
||||
|
||||
TrayGridView::TrayGridView(QWidget *parent)
|
||||
: DListView(parent)
|
||||
, m_aniCurveType(QEasingCurve::Linear)
|
||||
, m_aniDuringTime(250)
|
||||
, m_dragDistance(15)
|
||||
, m_aniStartTime(new QTimer(this))
|
||||
, m_pressed(false)
|
||||
, m_aniRunning(false)
|
||||
{
|
||||
initUi();
|
||||
}
|
||||
|
||||
void TrayGridView::setDragDistance(int pixel)
|
||||
{
|
||||
m_dragDistance = pixel;
|
||||
}
|
||||
|
||||
void TrayGridView::setAnimationProperty(const QEasingCurve::Type easing, const int duringTime)
|
||||
{
|
||||
m_aniCurveType = easing;
|
||||
m_aniDuringTime = duringTime;
|
||||
}
|
||||
|
||||
void TrayGridView::moveAnimation()
|
||||
{
|
||||
if (m_aniRunning || m_aniStartTime->isActive())
|
||||
return;
|
||||
|
||||
const QModelIndex dropModelIndex = indexAt(m_dropPos);
|
||||
if (!dropModelIndex.isValid())
|
||||
return;
|
||||
|
||||
const QModelIndex dragModelIndex = indexAt(m_dragPos);
|
||||
if (dragModelIndex == dropModelIndex)
|
||||
return;
|
||||
|
||||
if (!dragModelIndex.isValid()) {
|
||||
m_dragPos = indexRect(dropModelIndex).center();
|
||||
return;
|
||||
}
|
||||
|
||||
TrayModel *listModel = qobject_cast<TrayModel *>(model());
|
||||
if (!listModel)
|
||||
return;
|
||||
|
||||
listModel->clearDragDropIndex();
|
||||
listModel->setDragingIndex(dragModelIndex);
|
||||
listModel->setDragDropIndex(dropModelIndex);
|
||||
|
||||
const int startPos = dragModelIndex.row();
|
||||
const int endPos = dropModelIndex.row();
|
||||
|
||||
const bool next = startPos <= endPos;
|
||||
const int start = next ? startPos : endPos;
|
||||
const int end = !next ? startPos : endPos;
|
||||
|
||||
for (int i = start + next; i <= (end - !next); i++)
|
||||
createAnimation(i, next, (i == (end - !next)));
|
||||
|
||||
m_dropPos = indexRect(dropModelIndex).center();
|
||||
m_dragPos = indexRect(dropModelIndex).center();
|
||||
}
|
||||
|
||||
const QModelIndex TrayGridView::modelIndex(const int index) const
|
||||
{
|
||||
return model()->index(index, 0, QModelIndex());
|
||||
}
|
||||
|
||||
const QRect TrayGridView::indexRect(const QModelIndex &index) const
|
||||
{
|
||||
return rectForIndex(index);
|
||||
}
|
||||
|
||||
void TrayGridView::dropSwap()
|
||||
{
|
||||
qDebug() << "drop end";
|
||||
TrayModel *listModel = qobject_cast<TrayModel *>(model());
|
||||
if (!listModel)
|
||||
return;
|
||||
|
||||
QModelIndex index = indexAt(m_dropPos);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
listModel->dropSwap(index.row());
|
||||
clearDragModelIndex();
|
||||
m_aniRunning = false;
|
||||
setState(NoState);
|
||||
}
|
||||
|
||||
void TrayGridView::clearDragModelIndex()
|
||||
{
|
||||
TrayModel *listModel = static_cast<TrayModel *>(this->model());
|
||||
if (!listModel)
|
||||
return;
|
||||
|
||||
listModel->clearDragDropIndex();
|
||||
}
|
||||
|
||||
void TrayGridView::createAnimation(const int pos, const bool moveNext, const bool isLastAni)
|
||||
{
|
||||
qDebug() << "create moveAnimation";
|
||||
const QModelIndex index(modelIndex(pos));
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QLabel *floatLabel = new QLabel(this);
|
||||
QPropertyAnimation *ani = new QPropertyAnimation(floatLabel, "pos", floatLabel);
|
||||
qreal ratio = qApp->devicePixelRatio();
|
||||
|
||||
BaseTrayWidget *widget = qobject_cast<BaseTrayWidget *>(indexWidget(index));
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
QPixmap pixmap = widget->icon();
|
||||
|
||||
QString text = index.data(Qt::DisplayRole).toString();
|
||||
|
||||
pixmap.scaled(pixmap.size() * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
pixmap.setDevicePixelRatio(ratio);
|
||||
|
||||
floatLabel->setFixedSize(indexRect(index).size());
|
||||
floatLabel->setPixmap(pixmap);
|
||||
floatLabel->show();
|
||||
|
||||
ani->setStartValue(indexRect(index).center() - QPoint(0, floatLabel->height() /2));
|
||||
ani->setEndValue(indexRect(modelIndex(moveNext ? pos - 1 : pos + 1)).center() - QPoint(0, floatLabel->height() /2));
|
||||
ani->setEasingCurve(m_aniCurveType);
|
||||
ani->setDuration(m_aniDuringTime);
|
||||
|
||||
connect(ani, &QPropertyAnimation::finished, floatLabel, &QLabel::deleteLater);
|
||||
if (isLastAni) {
|
||||
m_aniRunning = true;
|
||||
|
||||
TrayModel *model = qobject_cast<TrayModel *>(this->model());
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
connect(ani, &QPropertyAnimation::finished, this, &TrayGridView::dropSwap);
|
||||
connect(ani, &QPropertyAnimation::valueChanged, m_aniStartTime, &QTimer::stop);
|
||||
} else {
|
||||
}
|
||||
|
||||
ani->start(QPropertyAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void TrayGridView::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
if (e->buttons() == Qt::LeftButton && !m_aniRunning)
|
||||
m_dragPos = e->pos();
|
||||
|
||||
m_pressed = true;
|
||||
}
|
||||
|
||||
void TrayGridView::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
setState(QAbstractItemView::NoState);
|
||||
e->accept();
|
||||
|
||||
if (e->buttons() == Qt::RightButton)
|
||||
return DListView::mouseMoveEvent(e);
|
||||
|
||||
QModelIndex index = indexAt(e->pos());
|
||||
if (!index.isValid())
|
||||
return DListView::mouseMoveEvent(e);
|
||||
|
||||
if (m_pressed && (qAbs(e->pos().x() - m_dragPos.x()) > m_dragDistance ||
|
||||
qAbs(e->pos().y() - m_dragPos.y()) > m_dragDistance)) {
|
||||
qDebug() << "start drag";
|
||||
beginDrag(Qt::CopyAction | Qt::MoveAction);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayGridView::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
m_pressed = false;
|
||||
}
|
||||
|
||||
void TrayGridView::dragEnterEvent(QDragEnterEvent *e)
|
||||
{
|
||||
const QModelIndex index = indexAt(e->pos());
|
||||
|
||||
if (model()->canDropMimeData(e->mimeData(), e->dropAction(), index.row(),
|
||||
index.column(), QModelIndex()))
|
||||
e->accept();
|
||||
else
|
||||
e->ignore();
|
||||
|
||||
Q_EMIT dragEntered();
|
||||
}
|
||||
|
||||
void TrayGridView::dragLeaveEvent(QDragLeaveEvent *e)
|
||||
{
|
||||
m_aniStartTime->stop();
|
||||
e->accept();
|
||||
dragLeaved();
|
||||
}
|
||||
|
||||
void TrayGridView::dragMoveEvent(QDragMoveEvent *e)
|
||||
{
|
||||
m_aniStartTime->stop();
|
||||
if (m_aniRunning)
|
||||
return;
|
||||
|
||||
setState(QAbstractItemView::DraggingState);
|
||||
|
||||
QModelIndex index = indexAt(e->pos());
|
||||
if (index.isValid()) {
|
||||
if (m_dropPos != indexRect(index).center()) {
|
||||
qDebug() << "update drop position: " << index.row();
|
||||
m_dropPos = indexRect(index).center();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pressed)
|
||||
m_aniStartTime->start();
|
||||
}
|
||||
|
||||
void TrayGridView::dropEvent(QDropEvent *e)
|
||||
{
|
||||
setState(DListView::NoState);
|
||||
clearDragModelIndex();
|
||||
|
||||
if (m_aniStartTime->isActive())
|
||||
m_aniStartTime->stop();
|
||||
|
||||
if (e->mimeData()->formats().contains("type") && e->source() != this) {
|
||||
e->setDropAction(Qt::CopyAction);
|
||||
e->accept();
|
||||
|
||||
TrayModel *m = qobject_cast<TrayModel *>(model());
|
||||
if (m) {
|
||||
WinInfo info;
|
||||
info.type = static_cast<TrayIconType>(e->mimeData()->data("type").toInt());
|
||||
info.key = static_cast<QString>(e->mimeData()->data("key"));
|
||||
info.winId = static_cast<quint32>(e->mimeData()->data("winId").toInt());
|
||||
info.servicePath = static_cast<QString>(e->mimeData()->data("servicePath"));
|
||||
m->addRow(info);
|
||||
}
|
||||
} else {
|
||||
e->ignore();
|
||||
}
|
||||
|
||||
DListView::dropEvent(e);
|
||||
}
|
||||
|
||||
void TrayGridView::beginDrag(Qt::DropActions supportedActions)
|
||||
{
|
||||
QModelIndex modelIndex = indexAt(m_dragPos);
|
||||
TrayIconType trayType = modelIndex.data(TrayModel::Role::TypeRole).value<TrayIconType>();
|
||||
// 展开图标不能移动
|
||||
if (trayType == TrayIconType::EXPANDICON)
|
||||
return;
|
||||
|
||||
m_dropPos = indexRect(modelIndex).center();
|
||||
|
||||
TrayModel *listModel = qobject_cast<TrayModel *>(model());
|
||||
if (!listModel)
|
||||
return;
|
||||
|
||||
BaseTrayWidget *widget = qobject_cast<BaseTrayWidget *>(indexWidget(modelIndex));
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
auto pixmap = widget->icon();
|
||||
|
||||
QString text = modelIndex.data(Qt::DisplayRole).toString();
|
||||
QString itemKey = modelIndex.data(TrayModel::Role::KeyRole).toString();
|
||||
qreal ratio = qApp->devicePixelRatio();
|
||||
// 创建拖拽释放时的应用图标
|
||||
QLabel *pixLabel = new QLabel(this);
|
||||
pixLabel->setPixmap(pixmap);
|
||||
pixLabel->setFixedSize(indexRect(modelIndex).size() / ratio);
|
||||
|
||||
QRect rectIcon(pixLabel->rect().topLeft(), pixLabel->size());
|
||||
|
||||
listModel->setDragingIndex(modelIndex);
|
||||
|
||||
QDrag *drag = new QDrag(this);
|
||||
pixmap.scaled(pixmap.size() * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
pixmap.setDevicePixelRatio(ratio);
|
||||
drag->setPixmap(pixmap);
|
||||
drag->setHotSpot(pixmap.rect().center() / ratio);
|
||||
QMimeData *data = model()->mimeData(QModelIndexList() << modelIndex);
|
||||
data->setImageData(pixmap);
|
||||
drag->setMimeData(data);
|
||||
|
||||
setState(DraggingState);
|
||||
|
||||
listModel->setDragKey(itemKey);
|
||||
|
||||
Qt::DropAction dropAct = drag->exec(supportedActions);
|
||||
|
||||
// 拖拽完成结束动画
|
||||
m_aniStartTime->stop();
|
||||
m_pressed = false;
|
||||
|
||||
Q_EMIT dragEntered();
|
||||
if (dropAct == Qt::IgnoreAction) {
|
||||
QPropertyAnimation *posAni = new QPropertyAnimation(pixLabel, "pos", pixLabel);
|
||||
connect(posAni, &QPropertyAnimation::finished, [&, listModel, pixLabel] () {
|
||||
pixLabel->hide();
|
||||
pixLabel->deleteLater();
|
||||
listModel->setDragKey(QString());
|
||||
clearDragModelIndex();
|
||||
|
||||
m_dropPos = QPoint();
|
||||
m_dragPos = QPoint();
|
||||
});
|
||||
posAni->setEasingCurve(QEasingCurve::Linear);
|
||||
posAni->setDuration(m_aniDuringTime);
|
||||
posAni->setStartValue((QCursor::pos() - QPoint(0, pixLabel->height() / 2)));
|
||||
posAni->setEndValue(mapToGlobal(m_dropPos) - QPoint(0, pixLabel->height() / 2));
|
||||
pixLabel->show();
|
||||
posAni->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
} else {
|
||||
listModel->setDragKey(QString());
|
||||
clearDragModelIndex();
|
||||
|
||||
m_dropPos = QPoint();
|
||||
m_dragPos = QPoint();
|
||||
|
||||
Q_EMIT requestRemove(itemKey);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayGridView::initUi()
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragDrop);
|
||||
setDropIndicatorShown(false);
|
||||
|
||||
setMouseTracking(false);
|
||||
setUniformItemSizes(true);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
setMovement(DListView::Free);
|
||||
setOrientation(QListView::LeftToRight, true);
|
||||
setLayoutMode(DListView::Batched);
|
||||
setResizeMode(DListView::Adjust);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
setSpacing(0);
|
||||
setItemSpacing(0);
|
||||
setBackgroundType(DStyledItemDelegate::RoundedBackground);
|
||||
setSelectionMode(QListView::SingleSelection);
|
||||
setVerticalScrollMode(QListView::ScrollPerPixel);
|
||||
|
||||
viewport()->setAcceptDrops(true);
|
||||
viewport()->setAutoFillBackground(false);
|
||||
|
||||
m_aniStartTime->setInterval(10);
|
||||
m_aniStartTime->setSingleShot(true);
|
||||
|
||||
connect(m_aniStartTime, &QTimer::timeout, this, &TrayGridView::moveAnimation);
|
||||
}
|
63
frame/window/tray/tray_gridview.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef TRAYGRIDVIEW_H
|
||||
#define TRAYGRIDVIEW_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <DListView>
|
||||
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
DWIDGET_USE_NAMESPACE
|
||||
|
||||
class TrayGridView : public DListView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrayGridView(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
void setDragDistance(int pixel);
|
||||
void setAnimationProperty(const QEasingCurve::Type easing, const int duringTime = 250);
|
||||
void moveAnimation();
|
||||
const QModelIndex modelIndex(const int index) const;
|
||||
const QRect indexRect(const QModelIndex &index) const;
|
||||
void dropSwap();
|
||||
|
||||
Q_SIGNALS:
|
||||
void requestRemove(const QString &);
|
||||
void dragLeaved();
|
||||
void dragEntered();
|
||||
|
||||
public Q_SLOTS:
|
||||
void clearDragModelIndex();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
|
||||
void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
|
||||
void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *e) Q_DECL_OVERRIDE;
|
||||
void dragLeaveEvent(QDragLeaveEvent *e) Q_DECL_OVERRIDE;
|
||||
void dragMoveEvent(QDragMoveEvent *e) Q_DECL_OVERRIDE;
|
||||
void dropEvent(QDropEvent *e) Q_DECL_OVERRIDE;
|
||||
void beginDrag(Qt::DropActions supportedActions);
|
||||
|
||||
private:
|
||||
void initUi();
|
||||
void createAnimation(const int pos, const bool moveNext, const bool isLastAni);
|
||||
|
||||
private:
|
||||
QEasingCurve::Type m_aniCurveType;
|
||||
int m_aniDuringTime;
|
||||
|
||||
QPoint m_dragPos;
|
||||
QPoint m_dropPos;
|
||||
|
||||
int m_dragDistance;
|
||||
|
||||
QTimer *m_aniStartTime;
|
||||
bool m_pressed;
|
||||
bool m_aniRunning;
|
||||
};
|
||||
|
||||
#endif // GRIDVIEW_H
|
411
frame/window/tray/tray_model.cpp
Normal file
@ -0,0 +1,411 @@
|
||||
#include "tray_model.h"
|
||||
#include "tray_monitor.h"
|
||||
|
||||
#include "indicatortrayitem.h"
|
||||
#include "indicatorplugin.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QIcon>
|
||||
#include <QDebug>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDBusInterface>
|
||||
|
||||
#define TRAY_DRAG_FALG "tray_drag"
|
||||
|
||||
TrayModel::TrayModel(QListView *view, bool isIconTray, bool hasInputMethod, QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_dragModelIndex(QModelIndex())
|
||||
, m_dropModelIndex(QModelIndex())
|
||||
, m_view(view)
|
||||
, m_monitor(new TrayMonitor(this))
|
||||
, m_isTrayIcon(isIconTray)
|
||||
, m_hasInputMethod(hasInputMethod)
|
||||
{
|
||||
Q_ASSERT(m_view);
|
||||
|
||||
if (isIconTray) {
|
||||
connect(m_monitor, &TrayMonitor::xEmbedTrayAdded, this, &TrayModel::onXEmbedTrayAdded);
|
||||
connect(m_monitor, &TrayMonitor::indicatorFounded, this, &TrayModel::onIndicatorFounded);
|
||||
}
|
||||
connect(m_monitor, &TrayMonitor::xEmbedTrayRemoved, this, &TrayModel::onXEmbedTrayRemoved);
|
||||
connect(m_monitor, &TrayMonitor::requestUpdateIcon, this, &TrayModel::requestUpdateIcon);
|
||||
connect(m_monitor, &TrayMonitor::sniTrayAdded, this, &TrayModel::onSniTrayAdded);
|
||||
connect(m_monitor, &TrayMonitor::sniTrayRemoved, this, &TrayModel::onSniTrayRemoved);
|
||||
}
|
||||
|
||||
void TrayModel::dropSwap(int newPos)
|
||||
{
|
||||
if (!m_dragModelIndex.isValid())
|
||||
return;
|
||||
|
||||
removeRows(m_dragModelIndex.row(), 1, QModelIndex());
|
||||
dropInsert(newPos);
|
||||
|
||||
emit QAbstractItemModel::dataChanged(m_dragModelIndex, m_dropModelIndex);
|
||||
}
|
||||
|
||||
void TrayModel::dropInsert(int newPos)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), newPos, newPos);
|
||||
WinInfo name = m_dragInfo;
|
||||
m_winInfos.insert(newPos, name);
|
||||
// 更新输入法的位置
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void TrayModel::clearDragDropIndex()
|
||||
{
|
||||
const QModelIndex startIndex = m_dragModelIndex;
|
||||
const QModelIndex endIndex = m_dropModelIndex;
|
||||
|
||||
m_dragModelIndex = m_dropModelIndex = QModelIndex();
|
||||
|
||||
emit QAbstractItemModel::dataChanged(startIndex, endIndex);
|
||||
emit QAbstractItemModel::dataChanged(endIndex, startIndex);
|
||||
}
|
||||
|
||||
void TrayModel::setDragingIndex(const QModelIndex index)
|
||||
{
|
||||
m_dragModelIndex = index;
|
||||
m_dropModelIndex = index;
|
||||
|
||||
emit QAbstractListModel::dataChanged(index, index);
|
||||
}
|
||||
|
||||
void TrayModel::setDragDropIndex(const QModelIndex index)
|
||||
{
|
||||
if (m_dragModelIndex == index)
|
||||
return;
|
||||
|
||||
m_dropModelIndex = index;
|
||||
|
||||
emit QAbstractListModel::dataChanged(m_dragModelIndex, index);
|
||||
emit QAbstractListModel::dataChanged(index, m_dragModelIndex);
|
||||
}
|
||||
|
||||
void TrayModel::setDragKey(const QString &key)
|
||||
{
|
||||
m_dragKey = key;
|
||||
}
|
||||
|
||||
bool TrayModel::indexDragging(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid() && index.data(Role::KeyRole).toString() == m_dragKey)
|
||||
return true;
|
||||
|
||||
if (!m_dragModelIndex.isValid() || !m_dropModelIndex.isValid())
|
||||
return false;
|
||||
|
||||
const int start = m_dragModelIndex.row();
|
||||
const int end = m_dropModelIndex.row();
|
||||
const int current = index.row();
|
||||
|
||||
return (start <= end && current >= start && current <= end)
|
||||
|| (start >= end && current <= start && current >= end);
|
||||
}
|
||||
|
||||
QMimeData *TrayModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
Q_ASSERT(indexes.size() == 1);
|
||||
|
||||
QMimeData *mime = new QMimeData;
|
||||
mime->setData(TRAY_DRAG_FALG, QByteArray());
|
||||
for (auto index : indexes) {
|
||||
if (!index.isValid())
|
||||
continue;
|
||||
|
||||
int itemIndex = index.row();
|
||||
auto info = m_winInfos.at(itemIndex);
|
||||
mime->setData("type", QByteArray::number(static_cast<int>(info.type)));
|
||||
mime->setData("key", info.key.toLatin1());
|
||||
mime->setData("winId", QByteArray::number(info.winId));
|
||||
mime->setData("servicePath", info.servicePath.toLatin1());
|
||||
|
||||
//TODO 支持多个index的数据,待支持
|
||||
}
|
||||
return mime;
|
||||
}
|
||||
|
||||
QVariant TrayModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
int itemIndex = index.row();
|
||||
auto info = m_winInfos.at(itemIndex);
|
||||
|
||||
switch (role) {
|
||||
case Role::TypeRole:
|
||||
return info.type;
|
||||
case Role::KeyRole:
|
||||
return info.key;
|
||||
case Role::WinIdRole:
|
||||
return info.winId;
|
||||
case Role::ServiceRole:
|
||||
return info.servicePath;
|
||||
case Role::Blank:
|
||||
return indexDragging(index);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool TrayModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(count);
|
||||
Q_UNUSED(parent);
|
||||
|
||||
if (m_winInfos.size() - 1 < row)
|
||||
return false;
|
||||
|
||||
beginRemoveRows(parent, row, row);
|
||||
m_dragInfo = m_winInfos.takeAt(row);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrayModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
Q_UNUSED(row)
|
||||
Q_UNUSED(column)
|
||||
Q_UNUSED(parent)
|
||||
|
||||
return data->formats().contains(TRAY_DRAG_FALG);
|
||||
}
|
||||
|
||||
Qt::ItemFlags TrayModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
const Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||
m_view->openPersistentEditor(index);
|
||||
|
||||
return defaultFlags | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
|
||||
int TrayModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
return m_winInfos.size();
|
||||
}
|
||||
|
||||
bool TrayModel::isIconTray()
|
||||
{
|
||||
return m_isTrayIcon;
|
||||
}
|
||||
|
||||
void TrayModel::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
m_winInfos.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TrayModel::onXEmbedTrayAdded(quint32 winId)
|
||||
{
|
||||
for (auto info : m_winInfos) {
|
||||
if (info.winId == winId)
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
WinInfo info;
|
||||
info.type = XEMBED;
|
||||
info.key = "wininfo:" + QString::number(winId);
|
||||
info.winId = winId;
|
||||
m_winInfos.append(info);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void TrayModel::onXEmbedTrayRemoved(quint32 winId)
|
||||
{
|
||||
for (auto info : m_winInfos) {
|
||||
if (info.winId == winId) {
|
||||
int index = m_winInfos.indexOf(info);
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_winInfos.removeOne(info);
|
||||
endRemoveRows();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString TrayModel::fileNameByServiceName(const QString &serviceName)
|
||||
{
|
||||
QStringList serviceInfo = serviceName.split("/");
|
||||
if (serviceInfo.size() <= 0)
|
||||
return QString();
|
||||
|
||||
QDBusInterface dbsInterface("org.freedesktop.DBus", "/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus", QDBusConnection::sessionBus(), this);
|
||||
QDBusMessage msg = dbsInterface.call("GetConnectionUnixProcessID", serviceInfo[0] );
|
||||
QList<QVariant> arguments = msg.arguments();
|
||||
if (arguments.size() == 0)
|
||||
return QString();
|
||||
|
||||
QVariant v = arguments.at(0);
|
||||
uint pid = v.toUInt();
|
||||
QString path = QString("/proc/%1/cmdline").arg(pid);
|
||||
QFile file(path);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
const QString fileName = file.readAll();
|
||||
file.close();
|
||||
return fileName;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool TrayModel::isTypeWriting(const QString &servicePath)
|
||||
{
|
||||
const QString appFilePath = fileNameByServiceName(servicePath);
|
||||
return appFilePath.startsWith("/usr/bin/fcitx");
|
||||
}
|
||||
|
||||
void TrayModel::onSniTrayAdded(const QString &servicePath)
|
||||
{
|
||||
bool typeWriting = isTypeWriting(servicePath);
|
||||
if ((m_hasInputMethod && !typeWriting) || (!m_hasInputMethod && typeWriting))
|
||||
return;
|
||||
|
||||
int citxIndex = -1;
|
||||
for (int i = 0; i < m_winInfos.size(); i++) {
|
||||
WinInfo info = m_winInfos[i];
|
||||
if (info.servicePath == servicePath)
|
||||
return;
|
||||
|
||||
if (typeWriting && info.isTypeWriting)
|
||||
citxIndex = i;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
WinInfo info;
|
||||
info.type = SNI;
|
||||
info.key = "sni:" + servicePath;
|
||||
info.servicePath = servicePath;
|
||||
info.isTypeWriting = typeWriting; // 是否为输入法
|
||||
if (typeWriting) {
|
||||
if (citxIndex < 0) {
|
||||
m_winInfos.append(info);
|
||||
} else {
|
||||
// 如果输入法在指定位置,则将输入法移动到指定位置
|
||||
m_winInfos[citxIndex] = info;
|
||||
QTimer::singleShot(150, this, [ = ] {
|
||||
// 对比需要变化的图标
|
||||
emit requestUpdateWidget({ citxIndex });
|
||||
});
|
||||
}
|
||||
} else {
|
||||
m_winInfos.append(info);
|
||||
}
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void TrayModel::onSniTrayRemoved(const QString &servicePath)
|
||||
{
|
||||
for (WinInfo info : m_winInfos) {
|
||||
if (info.servicePath == servicePath) {
|
||||
int index = m_winInfos.indexOf(info);
|
||||
|
||||
// 如果为输入法,则无需立刻删除,等100毫秒后再观察是否会删除输入法(因为在100毫秒内如果是切换输入法,就会很快发送add信号)
|
||||
if (info.isTypeWriting) {
|
||||
QTimer::singleShot(100, this, [ servicePath, this ] {
|
||||
for (WinInfo info : m_winInfos) {
|
||||
if (info.servicePath == servicePath) {
|
||||
int index = m_winInfos.indexOf(info);
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_winInfos.removeOne(info);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_winInfos.removeOne(info);
|
||||
endRemoveRows();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayModel::onIndicatorFounded(const QString &indicatorName)
|
||||
{
|
||||
const QString &itemKey = IndicatorTrayItem::toIndicatorKey(indicatorName);
|
||||
if (exist(itemKey) || !IndicatorTrayItem::isIndicatorKey(itemKey))
|
||||
return;
|
||||
|
||||
IndicatorPlugin *indicatorTray = nullptr;
|
||||
if (!m_indicatorMap.keys().contains(indicatorName)) {
|
||||
indicatorTray = new IndicatorPlugin(indicatorName, this);
|
||||
m_indicatorMap[indicatorName] = indicatorTray;
|
||||
} else {
|
||||
indicatorTray = m_indicatorMap[itemKey];
|
||||
}
|
||||
|
||||
connect(indicatorTray, &IndicatorPlugin::delayLoaded, indicatorTray, [ = ] {
|
||||
onIndicatorAdded(indicatorName);
|
||||
}, Qt::UniqueConnection);
|
||||
|
||||
connect(indicatorTray, &IndicatorPlugin::removed, this, [ = ] {
|
||||
onIndicatorRemoved(indicatorName);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void TrayModel::onIndicatorAdded(const QString &indicatorName)
|
||||
{
|
||||
const QString &itemKey = IndicatorTrayItem::toIndicatorKey(indicatorName);
|
||||
for (auto info : m_winInfos) {
|
||||
if (info.key == itemKey)
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
WinInfo info;
|
||||
info.type = INDICATOR;
|
||||
info.key = itemKey;
|
||||
m_winInfos.append(info);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void TrayModel::onIndicatorRemoved(const QString &indicatorName)
|
||||
{
|
||||
const QString &itemKey = IndicatorTrayItem::toIndicatorKey(indicatorName);
|
||||
removeRow(itemKey);
|
||||
}
|
||||
|
||||
void TrayModel::removeRow(const QString &itemKey)
|
||||
{
|
||||
for (auto info : m_winInfos) {
|
||||
if (info.key == itemKey) {
|
||||
int index = m_winInfos.indexOf(info);
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_winInfos.removeOne(info);
|
||||
endRemoveRows();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayModel::addRow(WinInfo info)
|
||||
{
|
||||
for (auto i : m_winInfos) {
|
||||
if (i.key == info.key)
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_winInfos.append(info);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
bool TrayModel::exist(const QString &itemKey)
|
||||
{
|
||||
for (auto w : m_winInfos) {
|
||||
if (w.key == itemKey)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
124
frame/window/tray/tray_model.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef TRAYMODEL_H
|
||||
#define TRAYMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QListView>
|
||||
|
||||
class TrayMonitor;
|
||||
class IndicatorPlugin;
|
||||
|
||||
enum TrayIconType {
|
||||
UNKNOW,
|
||||
XEMBED,
|
||||
SNI,
|
||||
INDICATOR,
|
||||
EXPANDICON
|
||||
};
|
||||
|
||||
struct WinInfo {
|
||||
TrayIconType type;
|
||||
QString key;
|
||||
quint32 winId;
|
||||
QString servicePath;
|
||||
bool isTypeWriting;
|
||||
|
||||
WinInfo() {
|
||||
type = UNKNOW;
|
||||
key = QString();
|
||||
winId = 0;
|
||||
servicePath = QString();
|
||||
isTypeWriting = false;
|
||||
}
|
||||
bool operator==(const WinInfo &other) {
|
||||
return this->type == other.type
|
||||
&& this->key == other.key
|
||||
&& this->winId == other.winId
|
||||
&& this->servicePath == other.servicePath
|
||||
&& this->isTypeWriting == other.isTypeWriting;
|
||||
}
|
||||
};
|
||||
|
||||
class TrayModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Role {
|
||||
TypeRole = Qt::UserRole + 1,
|
||||
KeyRole,
|
||||
WinIdRole,
|
||||
ServiceRole,
|
||||
Blank
|
||||
};
|
||||
|
||||
typedef QList<WinInfo> WinInfos;
|
||||
|
||||
TrayModel(QListView *view, bool isIconTray, bool hasInputMethod, QObject *parent = Q_NULLPTR);
|
||||
|
||||
void dropSwap(int newPos);
|
||||
void dropInsert(int newPos);
|
||||
|
||||
void clearDragDropIndex();
|
||||
void setDragingIndex(const QModelIndex index);
|
||||
void setDragDropIndex(const QModelIndex index);
|
||||
|
||||
void setDragKey(const QString &key);
|
||||
|
||||
bool indexDragging(const QModelIndex &index) const;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
bool isIconTray();
|
||||
|
||||
void clear();
|
||||
|
||||
public Q_SLOTS:
|
||||
void removeRow(const QString &itemKey);
|
||||
void addRow(WinInfo info);
|
||||
|
||||
Q_SIGNALS:
|
||||
void requestUpdateIcon(quint32);
|
||||
void requestUpdateWidget(const QList<int> &);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onXEmbedTrayAdded(quint32 winId);
|
||||
void onXEmbedTrayRemoved(quint32 winId);
|
||||
void onSniTrayAdded(const QString &servicePath);
|
||||
void onSniTrayRemoved(const QString &servicePath);
|
||||
|
||||
void onIndicatorFounded(const QString &indicatorName);
|
||||
void onIndicatorAdded(const QString &indicatorName);
|
||||
void onIndicatorRemoved(const QString &indicatorName);
|
||||
|
||||
private:
|
||||
bool exist(const QString &itemKey);
|
||||
QString fileNameByServiceName(const QString &serviceName);
|
||||
bool isTypeWriting(const QString &servicePath);
|
||||
|
||||
protected:
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
|
||||
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent) Q_DECL_OVERRIDE;
|
||||
bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||
int row, int column, const QModelIndex &parent) const Q_DECL_OVERRIDE;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
WinInfos m_winInfos;
|
||||
|
||||
QModelIndex m_dragModelIndex;
|
||||
QModelIndex m_dropModelIndex;
|
||||
WinInfo m_dragInfo;
|
||||
QListView *m_view;
|
||||
TrayMonitor *m_monitor;
|
||||
|
||||
QString m_dragKey;
|
||||
|
||||
QMap<QString, IndicatorPlugin *> m_indicatorMap;
|
||||
bool m_isTrayIcon;
|
||||
bool m_hasInputMethod;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(TrayIconType);
|
||||
|
||||
#endif // TRAYMODEL_H
|
78
frame/window/tray/tray_monitor.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "tray_monitor.h"
|
||||
|
||||
TrayMonitor::TrayMonitor(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_trayInter(new DBusTrayManager(this))
|
||||
, m_sniWatcher(new StatusNotifierWatcher("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher", QDBusConnection::sessionBus(), this))
|
||||
{
|
||||
//-------------------------------Tray Embed---------------------------------------------//
|
||||
connect(m_trayInter, &DBusTrayManager::TrayIconsChanged, this, &TrayMonitor::onTrayIconsChanged, Qt::QueuedConnection);
|
||||
connect(m_trayInter, &DBusTrayManager::Changed, this, &TrayMonitor::requestUpdateIcon, Qt::QueuedConnection);
|
||||
m_trayInter->Manage();
|
||||
QMetaObject::invokeMethod(this, "onTrayIconsChanged", Qt::QueuedConnection);
|
||||
|
||||
//-------------------------------Tray SNI---------------------------------------------//
|
||||
connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemRegistered, this, &TrayMonitor::onSniItemsChanged, Qt::QueuedConnection);
|
||||
connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered, this, &TrayMonitor::onSniItemsChanged, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "onSniItemsChanged", Qt::QueuedConnection);
|
||||
|
||||
//-------------------------------Tray Indicator---------------------------------------------//
|
||||
QMetaObject::invokeMethod(this, "startLoadIndicators", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TrayMonitor::onTrayIconsChanged()
|
||||
{
|
||||
QList<quint32> wids = m_trayInter->trayIcons();
|
||||
if (m_trayWids == wids)
|
||||
return;
|
||||
|
||||
for (auto wid : wids) {
|
||||
if (!m_trayWids.contains(wid)) {
|
||||
Q_EMIT xEmbedTrayAdded(wid);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wid : m_trayWids) {
|
||||
if (!wids.contains(wid)) {
|
||||
Q_EMIT xEmbedTrayRemoved(wid);
|
||||
}
|
||||
}
|
||||
|
||||
m_trayWids = wids;
|
||||
}
|
||||
|
||||
void TrayMonitor::onSniItemsChanged()
|
||||
{
|
||||
//TODO 防止同一个进程注册多个sni服务
|
||||
const QStringList &sniServices = m_sniWatcher->registeredStatusNotifierItems();
|
||||
if (m_sniServices == sniServices)
|
||||
return;
|
||||
|
||||
for (auto s : sniServices) {
|
||||
if (!m_sniServices.contains(s)) {
|
||||
if (s.startsWith("/") || !s.contains("/")) {
|
||||
qWarning() << __FUNCTION__ << "invalid sni service" << s;
|
||||
continue;
|
||||
}
|
||||
Q_EMIT sniTrayAdded(s);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto s : m_sniServices) {
|
||||
if (!sniServices.contains(s)) {
|
||||
Q_EMIT sniTrayRemoved(s);
|
||||
}
|
||||
}
|
||||
|
||||
m_sniServices = sniServices;
|
||||
}
|
||||
|
||||
void TrayMonitor::startLoadIndicators()
|
||||
{
|
||||
QDir indicatorConfDir("/etc/dde-dock/indicator");
|
||||
|
||||
for (const QFileInfo &fileInfo : indicatorConfDir.entryInfoList({"*.json"}, QDir::Files | QDir::NoDotAndDotDot)) {
|
||||
const QString &indicatorName = fileInfo.baseName();
|
||||
Q_EMIT indicatorFounded(indicatorName);
|
||||
}
|
||||
}
|
41
frame/window/tray/tray_monitor.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef TRAYMONITOR_H
|
||||
#define TRAYMONITOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "dbustraymanager.h"
|
||||
#include "statusnotifierwatcher_interface.h"
|
||||
|
||||
using namespace org::kde;
|
||||
class TrayMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TrayMonitor(QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onTrayIconsChanged();
|
||||
void onSniItemsChanged();
|
||||
|
||||
void startLoadIndicators();
|
||||
|
||||
Q_SIGNALS:
|
||||
void requestUpdateIcon(quint32);
|
||||
void xEmbedTrayAdded(quint32);
|
||||
void xEmbedTrayRemoved(quint32);
|
||||
|
||||
void sniTrayAdded(const QString &);
|
||||
void sniTrayRemoved(const QString &);
|
||||
|
||||
void indicatorFounded(const QString &);
|
||||
|
||||
private:
|
||||
DBusTrayManager *m_trayInter;
|
||||
StatusNotifierWatcher *m_sniWatcher;
|
||||
|
||||
QList<quint32> m_trayWids;
|
||||
QStringList m_sniServices;
|
||||
};
|
||||
|
||||
#endif // TRAYMONITOR_H
|
164
frame/window/tray/widgets/basetraywidget.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 "constants.h"
|
||||
#include "basetraywidget.h"
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
#include <QMouseEvent>
|
||||
#include <QDebug>
|
||||
|
||||
BaseTrayWidget::BaseTrayWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: QWidget(parent, f)
|
||||
, m_handleMouseReleaseTimer(new QTimer(this))
|
||||
, m_ownerPID(0)
|
||||
, m_needShow(true)
|
||||
{
|
||||
m_handleMouseReleaseTimer->setSingleShot(true);
|
||||
m_handleMouseReleaseTimer->setInterval(10);
|
||||
|
||||
connect(m_handleMouseReleaseTimer, &QTimer::timeout, this, &BaseTrayWidget::handleMouseRelease);
|
||||
}
|
||||
|
||||
BaseTrayWidget::~BaseTrayWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BaseTrayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
// call QWidget::mousePressEvent means to show dock-context-menu
|
||||
// when right button of mouse is pressed immediately in fashion mode
|
||||
|
||||
// here we hide the right button press event when it is click in the special area
|
||||
if (event->button() == Qt::RightButton && perfectIconRect().contains(event->pos(), true)) {
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void BaseTrayWidget::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
//e->accept();
|
||||
|
||||
// 由于 XWindowTrayWidget 中对 发送鼠标事件到X窗口的函数, 如 sendClick/sendHoverEvent 中
|
||||
// 使用了 setX11PassMouseEvent, 而每次调用 setX11PassMouseEvent 时都会导致产生 mousePress 和 mouseRelease 事件
|
||||
// 因此如果直接在这里处理事件会导致一些问题, 所以使用 Timer 来延迟处理 100 毫秒内的最后一个事件
|
||||
m_lastMouseReleaseData.first = e->pos();
|
||||
m_lastMouseReleaseData.second = e->button();
|
||||
|
||||
m_handleMouseReleaseTimer->start();
|
||||
|
||||
QWidget::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void BaseTrayWidget::handleMouseRelease()
|
||||
{
|
||||
Q_ASSERT(sender() == m_handleMouseReleaseTimer);
|
||||
|
||||
// do not dealwith all mouse event of SystemTray, class SystemTrayItem will handle it
|
||||
if (trayType() == SystemTray)
|
||||
return;
|
||||
|
||||
const QPoint point(m_lastMouseReleaseData.first - rect().center());
|
||||
if (point.manhattanLength() > 24)
|
||||
return;
|
||||
|
||||
QPoint globalPos = QCursor::pos();
|
||||
uint8_t buttonIndex = XCB_BUTTON_INDEX_1;
|
||||
|
||||
switch (m_lastMouseReleaseData.second) {
|
||||
case Qt:: MiddleButton:
|
||||
buttonIndex = XCB_BUTTON_INDEX_2;
|
||||
break;
|
||||
case Qt::RightButton:
|
||||
buttonIndex = XCB_BUTTON_INDEX_3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sendClick(buttonIndex, globalPos.x(), globalPos.y());
|
||||
|
||||
// left mouse button clicked
|
||||
if (buttonIndex == XCB_BUTTON_INDEX_1) {
|
||||
Q_EMIT clicked();
|
||||
}
|
||||
}
|
||||
|
||||
const QRect BaseTrayWidget::perfectIconRect() const
|
||||
{
|
||||
const QRect itemRect = rect();
|
||||
const int iconSize = std::min(itemRect.width(), itemRect.height());
|
||||
|
||||
QRect iconRect;
|
||||
iconRect.setWidth(iconSize);
|
||||
iconRect.setHeight(iconSize);
|
||||
iconRect.moveTopLeft(itemRect.center() - iconRect.center());
|
||||
|
||||
return iconRect;
|
||||
}
|
||||
|
||||
void BaseTrayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
const Dock::Position position = qApp->property(PROP_POSITION).value<Dock::Position>();
|
||||
// 保持横纵比
|
||||
if (position == Dock::Bottom || position == Dock::Top) {
|
||||
setMaximumWidth(height());
|
||||
setMaximumHeight(QWIDGETSIZE_MAX);
|
||||
} else {
|
||||
setMaximumHeight(width());
|
||||
setMaximumWidth(QWIDGETSIZE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
uint BaseTrayWidget::getOwnerPID()
|
||||
{
|
||||
return this->m_ownerPID;
|
||||
}
|
||||
|
||||
bool BaseTrayWidget::needShow()
|
||||
{
|
||||
return m_needShow;
|
||||
}
|
||||
|
||||
void BaseTrayWidget::setNeedShow(bool needShow)
|
||||
{
|
||||
#ifdef QT_DEBUG
|
||||
if (m_needShow == needShow)
|
||||
return;
|
||||
|
||||
m_needShow = needShow;
|
||||
#else
|
||||
m_needShow = true;
|
||||
#endif
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void BaseTrayWidget::setOwnerPID(uint PID)
|
||||
{
|
||||
this->m_ownerPID = PID;
|
||||
}
|
76
frame/window/tray/widgets/basetraywidget.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
#include <QPair>
|
||||
|
||||
class QDBusMessage;
|
||||
class BaseTrayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum TrayType {
|
||||
ApplicationTray,
|
||||
SystemTray,
|
||||
};
|
||||
|
||||
explicit BaseTrayWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
virtual ~BaseTrayWidget() override;
|
||||
|
||||
virtual QString itemKeyForConfig() = 0;
|
||||
virtual void updateIcon() = 0;
|
||||
virtual void sendClick(uint8_t mouseButton, int x, int y) = 0;
|
||||
virtual inline TrayType trayType() const { return TrayType::ApplicationTray; } // default is ApplicationTray
|
||||
virtual bool isValid() {return true;}
|
||||
uint getOwnerPID();
|
||||
virtual bool needShow();
|
||||
virtual void setNeedShow(bool needShow);
|
||||
virtual QPixmap icon() = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void iconChanged();
|
||||
void clicked();
|
||||
void needAttention();
|
||||
void requestWindowAutoHide(const bool autoHide);
|
||||
void requestRefershWindowVisible();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
void handleMouseRelease();
|
||||
const QRect perfectIconRect() const;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void setOwnerPID(uint PID);
|
||||
|
||||
private:
|
||||
QTimer *m_handleMouseReleaseTimer;
|
||||
|
||||
QPair<QPoint, Qt::MouseButton> m_lastMouseReleaseData;
|
||||
|
||||
uint m_ownerPID;
|
||||
bool m_needShow;
|
||||
};
|
||||
|
167
frame/window/tray/widgets/expandiconwidget.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include "expandiconwidget.h"
|
||||
#include "tray_gridview.h"
|
||||
#include "tray_model.h"
|
||||
#include "tray_delegate.h"
|
||||
#include "dockpopupwindow.h"
|
||||
|
||||
#include <DGuiApplicationHelper>
|
||||
#include <DRegionMonitor>
|
||||
#include <QPainter>
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
DGUI_USE_NAMESPACE
|
||||
|
||||
ExpandIconWidget::ExpandIconWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: BaseTrayWidget(parent, f)
|
||||
, m_regionInter(new DRegionMonitor(this))
|
||||
, m_position(Dock::Position::Bottom)
|
||||
, m_trayView(nullptr)
|
||||
{
|
||||
connect(m_regionInter, &DRegionMonitor::buttonPress, this, &ExpandIconWidget::onGlobMousePress);
|
||||
}
|
||||
|
||||
ExpandIconWidget::~ExpandIconWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void ExpandIconWidget::setPositonValue(Dock::Position position)
|
||||
{
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
void ExpandIconWidget::sendClick(uint8_t mouseButton, int x, int y)
|
||||
{
|
||||
Q_UNUSED(x);
|
||||
Q_UNUSED(y);
|
||||
|
||||
if (mouseButton != XCB_BUTTON_INDEX_1)
|
||||
return;
|
||||
|
||||
TrayGridView *trayIcon = popupTrayView();
|
||||
setTrayPanelVisible(!trayIcon->isVisible());
|
||||
}
|
||||
|
||||
void ExpandIconWidget::setTrayPanelVisible(bool visible)
|
||||
{
|
||||
TrayGridView *trayIcon = popupTrayView();
|
||||
if (visible) {
|
||||
resetPosition();
|
||||
trayIcon->show();
|
||||
m_regionInter->registerRegion();
|
||||
} else {
|
||||
trayIcon->hide();
|
||||
m_regionInter->unregisterRegion();
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap ExpandIconWidget::icon()
|
||||
{
|
||||
return QPixmap(dropIconFile());
|
||||
}
|
||||
|
||||
void ExpandIconWidget::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
|
||||
QPainter painter(this);
|
||||
QPixmap pixmap(dropIconFile());
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
}
|
||||
|
||||
const QString ExpandIconWidget::dropIconFile() const
|
||||
{
|
||||
QString arrow;
|
||||
switch (m_position) {
|
||||
case Dock::Position::Bottom: {
|
||||
arrow = "up";
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Top: {
|
||||
arrow = "down";
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left: {
|
||||
arrow = "right";
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Right: {
|
||||
arrow = "left";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString iconFile = QString(":/icons/resources/arrow-%1").arg(arrow);
|
||||
if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType)
|
||||
iconFile += QString("-dark");
|
||||
|
||||
return iconFile + ".svg";
|
||||
}
|
||||
|
||||
TrayGridView *ExpandIconWidget::popupTrayView()
|
||||
{
|
||||
if (m_trayView)
|
||||
return m_trayView;
|
||||
|
||||
m_trayView = new TrayGridView(nullptr);
|
||||
TrayModel *model = new TrayModel(m_trayView, true, false);
|
||||
TrayDelegate *trayDelegate = new TrayDelegate(m_trayView);
|
||||
m_trayView->setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
|
||||
m_trayView->setModel(model);
|
||||
m_trayView->setItemDelegate(trayDelegate);
|
||||
m_trayView->setSpacing(3);
|
||||
m_trayView->setDragDistance(5);
|
||||
|
||||
connect(m_trayView, &TrayGridView::rowCountChanged, this, [ this ] {
|
||||
int count = m_trayView->model()->rowCount();
|
||||
if (count > 0) {
|
||||
int lineCount = (count % 3) != 0 ? (count / 3 + 1) : (count / 3);
|
||||
// 如果只有一行,则根据实际的数量显示宽度
|
||||
int columnCount = qMin(count, 3);
|
||||
int width = ITEM_SIZE * columnCount + m_trayView->spacing() * 2;;
|
||||
int height = lineCount * ITEM_SIZE + m_trayView->spacing() * (lineCount - 1) + ITEM_SPACING;
|
||||
m_trayView->setFixedSize(width, height);
|
||||
resetPosition();
|
||||
} else if (m_trayView->isVisible()) {
|
||||
m_trayView->hide();
|
||||
}
|
||||
Q_EMIT trayVisbleChanged(count > 0);
|
||||
});
|
||||
|
||||
connect(trayDelegate, &TrayDelegate::removeRow, this, [ = ](const QModelIndex &index) {
|
||||
QAbstractItemModel *abModel = model;
|
||||
abModel->removeRow(index.row(),index.parent());
|
||||
});
|
||||
connect(m_trayView, &TrayGridView::requestRemove, model, &TrayModel::removeRow);
|
||||
return m_trayView;
|
||||
}
|
||||
|
||||
void ExpandIconWidget::resetPosition()
|
||||
{
|
||||
if (!parentWidget())
|
||||
return;
|
||||
|
||||
TrayGridView *trayView = popupTrayView();
|
||||
QPoint ptPos = parentWidget()->mapToGlobal(this->pos());
|
||||
ptPos.setY(ptPos.y() - trayView->height());
|
||||
ptPos.setX(ptPos.x() - trayView->width());
|
||||
trayView->move(ptPos);
|
||||
}
|
||||
|
||||
void ExpandIconWidget::onGlobMousePress(const QPoint &mousePos, const int flag)
|
||||
{
|
||||
if (!isVisible() || !((flag == DRegionMonitor::WatchedFlags::Button_Left) || (flag == DRegionMonitor::WatchedFlags::Button_Right)))
|
||||
return;
|
||||
|
||||
TrayGridView *trayView = popupTrayView();
|
||||
QPoint ptPos = parentWidget()->mapToGlobal(this->pos());
|
||||
const QRect rect = QRect(ptPos, size());
|
||||
if (rect.contains(mousePos))
|
||||
return;
|
||||
|
||||
const QRect rctView(trayView->pos(), trayView->size());
|
||||
if (rctView.contains(mousePos))
|
||||
return;
|
||||
|
||||
trayView->hide();
|
||||
}
|
46
frame/window/tray/widgets/expandiconwidget.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef EXPANDICONWIDGET_H
|
||||
#define EXPANDICONWIDGET_H
|
||||
|
||||
#include "constants.h"
|
||||
#include "basetraywidget.h"
|
||||
|
||||
class TrayGridView;
|
||||
class TrayModel;
|
||||
|
||||
namespace Dtk { namespace Gui { class DRegionMonitor; } }
|
||||
|
||||
class ExpandIconWidget : public BaseTrayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void trayVisbleChanged(bool);
|
||||
|
||||
public:
|
||||
explicit ExpandIconWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
~ExpandIconWidget() override;
|
||||
void setPositonValue(Dock::Position position);
|
||||
|
||||
void sendClick(uint8_t mouseButton, int x, int y) override;
|
||||
void setTrayPanelVisible(bool visible);
|
||||
QString itemKeyForConfig() override { return "Expand"; }
|
||||
void updateIcon() override {}
|
||||
QPixmap icon() override;
|
||||
TrayGridView *popupTrayView();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onGlobMousePress(const QPoint &mousePos, const int flag);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
const QString dropIconFile() const;
|
||||
|
||||
void resetPosition();
|
||||
|
||||
private:
|
||||
Dtk::Gui::DRegionMonitor *m_regionInter;
|
||||
Dock::Position m_position;
|
||||
TrayGridView *m_trayView;
|
||||
};
|
||||
|
||||
#endif // EXPANDICONWIDGET_H
|
274
frame/window/tray/widgets/indicatorplugin.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
#include "indicatorplugin.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QDBusConnection>
|
||||
#include <QJsonObject>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QDebug>
|
||||
#include <QApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QDBusMessage>
|
||||
#include <thread>
|
||||
|
||||
class IndicatorPluginPrivate
|
||||
{
|
||||
public:
|
||||
explicit IndicatorPluginPrivate(IndicatorPlugin *parent) : q_ptr(parent) {}
|
||||
|
||||
void init();
|
||||
|
||||
void updateContent();
|
||||
|
||||
void initDBus(const QString &indicatorName);
|
||||
|
||||
template<typename Func>
|
||||
void featData(const QString &key,
|
||||
const QJsonObject &data,
|
||||
const char *propertyChangedSlot,
|
||||
Func const &callback)
|
||||
{
|
||||
Q_Q(IndicatorPlugin);
|
||||
auto dataConfig = data.value(key).toObject();
|
||||
auto dbusService = dataConfig.value("dbus_service").toString();
|
||||
auto dbusPath = dataConfig.value("dbus_path").toString();
|
||||
auto dbusInterface = dataConfig.value("dbus_interface").toString();
|
||||
auto isSystemBus = dataConfig.value("system_dbus").toBool(false);
|
||||
auto bus = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
|
||||
|
||||
QDBusInterface interface(dbusService, dbusPath, dbusInterface, bus, q);
|
||||
|
||||
if (dataConfig.contains("dbus_method")) {
|
||||
QString methodName = dataConfig.value("dbus_method").toString();
|
||||
auto ratio = qApp->devicePixelRatio();
|
||||
QDBusReply<QByteArray> reply = interface.call(methodName.toStdString().c_str(), ratio);
|
||||
callback(reply.value());
|
||||
}
|
||||
|
||||
if (dataConfig.contains("dbus_properties")) {
|
||||
auto propertyName = dataConfig.value("dbus_properties").toString();
|
||||
auto propertyNameCStr = propertyName.toStdString();
|
||||
propertyInterfaceNames.insert(key, dbusInterface);
|
||||
propertyNames.insert(key, QString::fromStdString(propertyNameCStr));
|
||||
QDBusConnection::sessionBus().connect(dbusService,
|
||||
dbusPath,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
"sa{sv}as",
|
||||
q,
|
||||
propertyChangedSlot);
|
||||
|
||||
// FIXME(sbw): hack for qt dbus property changed signal.
|
||||
// see: https://bugreports.qt.io/browse/QTBUG-48008
|
||||
QDBusConnection::sessionBus().connect(dbusService,
|
||||
dbusPath,
|
||||
dbusInterface,
|
||||
QString("%1Changed").arg(propertyName),
|
||||
"s",
|
||||
q,
|
||||
propertyChangedSlot);
|
||||
|
||||
callback(interface.property(propertyNameCStr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void propertyChanged(const QString &key, const QDBusMessage &msg, Func const &callback)
|
||||
{
|
||||
QList<QVariant> arguments = msg.arguments();
|
||||
if (1 == arguments.count())
|
||||
{
|
||||
const QString &v = msg.arguments().at(0).toString();
|
||||
callback(v);
|
||||
return;
|
||||
} else if (3 != arguments.count()) {
|
||||
qDebug() << "arguments count must be 3";
|
||||
return;
|
||||
}
|
||||
|
||||
QString interfaceName = msg.arguments().at(0).toString();
|
||||
if (interfaceName != propertyInterfaceNames.value(key)) {
|
||||
qDebug() << "interfaceName mismatch" << interfaceName << propertyInterfaceNames.value(key) << key;
|
||||
return;
|
||||
}
|
||||
QVariantMap changedProps = qdbus_cast<QVariantMap>(arguments.at(1).value<QDBusArgument>());
|
||||
if (changedProps.contains(propertyNames.value(key))) {
|
||||
callback(changedProps.value(propertyNames.value(key)));
|
||||
}
|
||||
}
|
||||
|
||||
IndicatorTrayItem* indicatorTrayWidget = Q_NULLPTR;
|
||||
QString indicatorName;
|
||||
QMap<QString, QString> propertyNames;
|
||||
QMap<QString, QString> propertyInterfaceNames;
|
||||
|
||||
IndicatorPlugin *q_ptr;
|
||||
Q_DECLARE_PUBLIC(IndicatorPlugin)
|
||||
};
|
||||
|
||||
void IndicatorPluginPrivate::init()
|
||||
{
|
||||
//Q_Q(IndicatorTray);
|
||||
|
||||
indicatorTrayWidget = new IndicatorTrayItem(indicatorName);
|
||||
|
||||
initDBus(indicatorName);
|
||||
updateContent();
|
||||
}
|
||||
|
||||
void IndicatorPluginPrivate::updateContent()
|
||||
{
|
||||
indicatorTrayWidget->update();
|
||||
|
||||
Q_EMIT indicatorTrayWidget->iconChanged();
|
||||
}
|
||||
|
||||
void IndicatorPluginPrivate::initDBus(const QString &indicatorName)
|
||||
{
|
||||
Q_Q(IndicatorPlugin);
|
||||
|
||||
QString filepath = QString("/etc/dde-dock/indicator/%1.json").arg(indicatorName);
|
||||
QFile confFile(filepath);
|
||||
if (!confFile.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "read indicator config Error";
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(confFile.readAll());
|
||||
confFile.close();
|
||||
auto config = doc.object();
|
||||
|
||||
auto delay = config.value("delay").toInt(0);
|
||||
|
||||
qDebug() << "delay load" << delay << indicatorName << q;
|
||||
|
||||
QTimer::singleShot(delay, [ = ]() {
|
||||
auto data = config.value("data").toObject();
|
||||
|
||||
if (data.contains("text")) {
|
||||
featData("text", data, SLOT(textPropertyChanged(QDBusMessage)), [ = ](QVariant v) {
|
||||
#ifdef QT_DEBUG // TODO
|
||||
if (!v.toString().isEmpty()) {
|
||||
#else
|
||||
if (v.toString().isEmpty()) {
|
||||
#endif
|
||||
Q_EMIT q->removed();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Q_EMIT q->delayLoaded();
|
||||
}
|
||||
indicatorTrayWidget->setText(v.toString());
|
||||
updateContent();
|
||||
});
|
||||
}
|
||||
|
||||
if (data.contains("icon")) {
|
||||
featData("icon", data, SLOT(iconPropertyChanged(QDBusMessage)), [ = ](QVariant v) {
|
||||
if (v.toByteArray().isEmpty()) {
|
||||
Q_EMIT q->removed();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Q_EMIT q->delayLoaded();
|
||||
}
|
||||
indicatorTrayWidget->setPixmapData(v.toByteArray());
|
||||
updateContent();
|
||||
});
|
||||
}
|
||||
|
||||
const QJsonObject action = config.value("action").toObject();
|
||||
if (!action.isEmpty() && indicatorTrayWidget)
|
||||
q->connect(indicatorTrayWidget, &IndicatorTrayItem::clicked, q, [ = ](uint8_t button_index, int x, int y) {
|
||||
/*std::thread t([=]() -> void {
|
||||
auto triggerConfig = action.value("trigger").toObject();
|
||||
auto dbusService = triggerConfig.value("dbus_service").toString();
|
||||
auto dbusPath = triggerConfig.value("dbus_path").toString();
|
||||
auto dbusInterface = triggerConfig.value("dbus_interface").toString();
|
||||
auto methodName = triggerConfig.value("dbus_method").toString();
|
||||
auto isSystemBus = triggerConfig.value("system_dbus").toBool(false);
|
||||
auto bus = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
|
||||
|
||||
QDBusInterface interface(dbusService, dbusPath, dbusInterface, bus);
|
||||
QDBusReply<void> reply = interface.call(methodName, button_index, x, y);
|
||||
if (!reply.isValid()) {
|
||||
qDebug() << interface.call(methodName);
|
||||
}
|
||||
else {
|
||||
qDebug() << reply.error();
|
||||
}
|
||||
});
|
||||
t.detach();*/
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
IndicatorPlugin::IndicatorPlugin(const QString &indicatorName, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new IndicatorPluginPrivate(this))
|
||||
{
|
||||
Q_D(IndicatorPlugin);
|
||||
|
||||
d->indicatorName = indicatorName;
|
||||
d->init();
|
||||
}
|
||||
|
||||
IndicatorPlugin::~IndicatorPlugin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IndicatorTrayItem *IndicatorPlugin::widget()
|
||||
{
|
||||
Q_D(IndicatorPlugin);
|
||||
|
||||
if (!d->indicatorTrayWidget) {
|
||||
d->init();
|
||||
}
|
||||
|
||||
return d->indicatorTrayWidget;
|
||||
}
|
||||
|
||||
void IndicatorPlugin::removeWidget()
|
||||
{
|
||||
Q_D(IndicatorPlugin);
|
||||
|
||||
d->indicatorTrayWidget = nullptr;
|
||||
}
|
||||
|
||||
void IndicatorPlugin::textPropertyChanged(const QDBusMessage &message)
|
||||
{
|
||||
Q_D(IndicatorPlugin);
|
||||
|
||||
d->propertyChanged("text", message, [=] (const QVariant &value) {
|
||||
if (value.toString().isEmpty()) {
|
||||
Q_EMIT removed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->indicatorTrayWidget) {
|
||||
d->init();
|
||||
}
|
||||
|
||||
d->indicatorTrayWidget->setText(value.toByteArray());
|
||||
});
|
||||
}
|
||||
|
||||
void IndicatorPlugin::iconPropertyChanged(const QDBusMessage &message)
|
||||
{
|
||||
Q_D(IndicatorPlugin);
|
||||
|
||||
d->propertyChanged("icon", message, [=] (const QVariant &value) {
|
||||
if (value.toByteArray().isEmpty()) {
|
||||
Q_EMIT removed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->indicatorTrayWidget) {
|
||||
d->init();
|
||||
}
|
||||
|
||||
d->indicatorTrayWidget->setPixmapData(value.toByteArray());
|
||||
});
|
||||
}
|
31
frame/window/tray/widgets/indicatorplugin.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "indicatortrayitem.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class IndicatorPluginPrivate;
|
||||
class IndicatorPlugin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IndicatorPlugin(const QString &indicatorName, QObject *parent = nullptr);
|
||||
~IndicatorPlugin();
|
||||
|
||||
IndicatorTrayItem *widget();
|
||||
|
||||
void removeWidget();
|
||||
|
||||
signals:
|
||||
void delayLoaded();
|
||||
void removed();
|
||||
|
||||
private slots:
|
||||
void textPropertyChanged(const QDBusMessage &message);
|
||||
void iconPropertyChanged(const QDBusMessage &message);
|
||||
|
||||
private:
|
||||
QScopedPointer<IndicatorPluginPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), IndicatorPlugin)
|
||||
};
|
258
frame/window/tray/widgets/indicatortrayitem.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 "indicatortrayitem.h"
|
||||
//#include "util/utils.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QBoxLayout>
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QApplication>
|
||||
#include <thread>
|
||||
|
||||
IndicatorTrayItem::IndicatorTrayItem(const QString &indicatorName, QWidget *parent, Qt::WindowFlags f)
|
||||
: BaseTrayWidget(parent, f)
|
||||
, m_indicatorName(indicatorName)
|
||||
// , m_gsettings(Utils::ModuleSettingsPtr("keyboard", QByteArray(), this))
|
||||
, m_enableClick(true)
|
||||
{
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_label = new QLabel(this);
|
||||
|
||||
QPalette p = m_label->palette();
|
||||
p.setColor(QPalette::Foreground, Qt::white);
|
||||
p.setColor(QPalette::Background, Qt::transparent);
|
||||
m_label->setPalette(p);
|
||||
|
||||
m_label->setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
layout->addWidget(m_label, 0, Qt::AlignCenter);
|
||||
setLayout(layout);
|
||||
|
||||
// register dbus
|
||||
auto path = QString("/com/deepin/dde/Dock/Indicator/") + m_indicatorName;
|
||||
auto interface = QString("com.deepin.dde.Dock.Indicator.") + m_indicatorName;
|
||||
auto sessionBus = QDBusConnection::sessionBus();
|
||||
sessionBus.registerObject(path,
|
||||
interface,
|
||||
this,
|
||||
QDBusConnection::ExportScriptableSlots);
|
||||
|
||||
initDBus(m_indicatorName);
|
||||
// if (m_gsettings) {
|
||||
// // 显示键盘布局时更新label的状态
|
||||
// if (m_gsettings->keys().contains("itemEnable"))
|
||||
// enableLabel(m_gsettings->get("itemEnable").toBool());
|
||||
|
||||
// connect(m_gsettings, &QGSettings::changed, this, &IndicatorTrayWidget::onGSettingsChanged);
|
||||
// }
|
||||
}
|
||||
|
||||
IndicatorTrayItem::~IndicatorTrayItem()
|
||||
{
|
||||
}
|
||||
|
||||
QString IndicatorTrayItem::itemKeyForConfig()
|
||||
{
|
||||
return toIndicatorKey(m_indicatorName);
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::updateIcon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::sendClick(uint8_t buttonIndex, int x, int y)
|
||||
{
|
||||
if (m_enableClick)
|
||||
Q_EMIT clicked(buttonIndex, x, y);
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::enableLabel(bool enable)
|
||||
{
|
||||
QPalette p = m_label->palette();
|
||||
if (!enable) {
|
||||
m_enableClick = false;
|
||||
p.setColor(QPalette::Disabled, QPalette::Foreground, Qt::lightGray);
|
||||
p.setColor(QPalette::Disabled, QPalette::Background, Qt::transparent);
|
||||
m_label->setEnabled(enable);
|
||||
} else {
|
||||
m_enableClick = true;
|
||||
p.setColor(QPalette::Normal, QPalette::BrightText, Qt::white);
|
||||
p.setColor(QPalette::Normal, QPalette::Background, Qt::transparent);
|
||||
m_label->setEnabled(enable);
|
||||
}
|
||||
|
||||
m_label->setPalette(p);
|
||||
m_label->update();
|
||||
}
|
||||
|
||||
QPixmap IndicatorTrayItem::icon()
|
||||
{
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::setPixmapData(const QByteArray &data)
|
||||
{
|
||||
auto rawPixmap = QPixmap::fromImage(QImage::fromData(data));
|
||||
rawPixmap.setDevicePixelRatio(devicePixelRatioF());
|
||||
m_label->setPixmap(rawPixmap);
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::setText(const QString &text)
|
||||
{
|
||||
m_label->setText(text);
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::onGSettingsChanged(const QString &key)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
|
||||
// if (m_gsettings && m_gsettings->keys().contains("itemEnable")) {
|
||||
// const bool itemEnable = m_gsettings->get("itemEnable").toBool();
|
||||
// enableLabel(itemEnable);
|
||||
// }
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void IndicatorTrayItem::featData(const QString &key,
|
||||
const QJsonObject &data,
|
||||
const char *propertyChangedSlot,
|
||||
Func const &callback)
|
||||
{
|
||||
auto dataConfig = data.value(key).toObject();
|
||||
auto dbusService = dataConfig.value("dbus_service").toString();
|
||||
auto dbusPath = dataConfig.value("dbus_path").toString();
|
||||
auto dbusInterface = dataConfig.value("dbus_interface").toString();
|
||||
auto isSystemBus = dataConfig.value("system_dbus").toBool(false);
|
||||
auto bus = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
|
||||
|
||||
QDBusInterface interface(dbusService, dbusPath, dbusInterface, bus, this);
|
||||
|
||||
if (dataConfig.contains("dbus_method")) {
|
||||
QString methodName = dataConfig.value("dbus_method").toString();
|
||||
auto ratio = qApp->devicePixelRatio();
|
||||
QDBusReply<QByteArray> reply = interface.call(methodName.toStdString().c_str(), ratio);
|
||||
callback(reply.value());
|
||||
}
|
||||
|
||||
qInfo() << dataConfig;
|
||||
if (dataConfig.contains("dbus_properties")) {
|
||||
auto propertyName = dataConfig.value("dbus_properties").toString();
|
||||
auto propertyNameCStr = propertyName.toStdString();
|
||||
//propertyInterfaceNames.insert(key, dbusInterface);
|
||||
//propertyNames.insert(key, QString::fromStdString(propertyNameCStr));
|
||||
QDBusConnection::sessionBus().connect(dbusService,
|
||||
dbusPath,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
"sa{sv}as",
|
||||
this,
|
||||
propertyChangedSlot);
|
||||
|
||||
// FIXME(sbw): hack for qt dbus property changed signal.
|
||||
// see: https://bugreports.qt.io/browse/QTBUG-48008
|
||||
QDBusConnection::sessionBus().connect(dbusService,
|
||||
dbusPath,
|
||||
dbusInterface,
|
||||
QString("%1Changed").arg(propertyName),
|
||||
"s",
|
||||
this,
|
||||
propertyChangedSlot);
|
||||
|
||||
qInfo() << dbusService << dbusPath << dbusInterface;
|
||||
qInfo() << propertyName << propertyNameCStr.c_str();
|
||||
callback(interface.property(propertyNameCStr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void IndicatorTrayItem::initDBus(const QString &indicatorName)
|
||||
{
|
||||
QString filepath = QString("/etc/dde-dock/indicator/%1.json").arg(indicatorName);
|
||||
QFile confFile(filepath);
|
||||
if (!confFile.open(QIODevice::ReadOnly)) {
|
||||
qInfo() << "read indicator config Error";
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(confFile.readAll());
|
||||
confFile.close();
|
||||
|
||||
QJsonObject config = doc.object();
|
||||
|
||||
auto delay = config.value("delay").toInt(0);
|
||||
|
||||
QTimer::singleShot(delay, [ = ]() {
|
||||
QJsonObject data = config.value("data").toObject();
|
||||
if (data.contains("text")) {
|
||||
featData("text", data, SLOT(textPropertyChanged(QDBusMessage)), [ = ](QVariant v) {
|
||||
if (v.toString().isEmpty()) {
|
||||
Q_EMIT removed();
|
||||
return;
|
||||
}
|
||||
Q_EMIT delayLoaded();
|
||||
setText(v.toString());
|
||||
//updateContent();
|
||||
});
|
||||
}
|
||||
|
||||
if (data.contains("icon")) {
|
||||
featData("icon", data, SLOT(iconPropertyChanged(QDBusMessage)), [ = ](QVariant v) {
|
||||
if (v.toByteArray().isEmpty()) {
|
||||
Q_EMIT removed();
|
||||
return;
|
||||
}
|
||||
Q_EMIT delayLoaded();
|
||||
setPixmapData(v.toByteArray());
|
||||
//updateContent();
|
||||
});
|
||||
}
|
||||
|
||||
const QJsonObject action = config.value("action").toObject();
|
||||
if (!action.isEmpty())
|
||||
connect(this, &IndicatorTrayItem::clicked, this, [ = ](uint8_t button_index, int x, int y) {
|
||||
std::thread t([=]() -> void {
|
||||
auto triggerConfig = action.value("trigger").toObject();
|
||||
auto dbusService = triggerConfig.value("dbus_service").toString();
|
||||
auto dbusPath = triggerConfig.value("dbus_path").toString();
|
||||
auto dbusInterface = triggerConfig.value("dbus_interface").toString();
|
||||
auto methodName = triggerConfig.value("dbus_method").toString();
|
||||
auto isSystemBus = triggerConfig.value("system_dbus").toBool(false);
|
||||
auto bus = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
|
||||
|
||||
QDBusInterface interface(dbusService, dbusPath, dbusInterface, bus);
|
||||
interface.call(methodName, button_index, x, y);
|
||||
});
|
||||
t.detach();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
73
frame/window/tray/widgets/indicatortrayitem.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QLabel>
|
||||
|
||||
#include "basetraywidget.h"
|
||||
|
||||
class QGSettings;
|
||||
|
||||
class IndicatorTrayItem: public BaseTrayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void removed();
|
||||
void delayLoaded();
|
||||
|
||||
public:
|
||||
explicit IndicatorTrayItem(const QString &indicatorName, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
~IndicatorTrayItem() override;
|
||||
|
||||
QString itemKeyForConfig() override;
|
||||
void updateIcon() override;
|
||||
void sendClick(uint8_t, int, int) override;
|
||||
void enableLabel(bool enable);
|
||||
static QString toIndicatorKey(const QString &indicatorName) { return QString("indicator:%1").arg(indicatorName); }
|
||||
static bool isIndicatorKey(const QString &itemKey) { return itemKey.startsWith("indicator:"); }
|
||||
QPixmap icon() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_SCRIPTABLE void setPixmapData(const QByteArray &data);
|
||||
Q_SCRIPTABLE void setText(const QString &text);
|
||||
|
||||
private slots:
|
||||
void onGSettingsChanged(const QString &key);
|
||||
|
||||
private:
|
||||
void initDBus(const QString &indicatorName);
|
||||
template<typename Func>
|
||||
void featData(const QString &key, const QJsonObject &data, const char *propertyChangedSlot, Func const &callback);
|
||||
|
||||
Q_SIGNALS:
|
||||
void clicked(uint8_t, int, int);
|
||||
|
||||
private:
|
||||
QLabel *m_label;
|
||||
|
||||
QString m_indicatorName;
|
||||
// const QGSettings *m_gsettings;
|
||||
bool m_enableClick; // 置灰时设置为false,不触发click信号
|
||||
};
|
||||
|
801
frame/window/tray/widgets/snitrayitemwidget.cpp
Normal file
@ -0,0 +1,801 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: listenerri <listenerri@gmail.com>
|
||||
*
|
||||
* Maintainer: listenerri <listenerri@gmail.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 "snitrayitemwidget.h"
|
||||
#include "themeappicon.h"
|
||||
//#include "test/tipswidget.h"
|
||||
|
||||
#include <dbusmenu-qt5/dbusmenuimporter.h>
|
||||
|
||||
#include <DGuiApplicationHelper>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
DGUI_USE_NAMESPACE
|
||||
|
||||
#define IconSize 20
|
||||
|
||||
const QStringList ItemCategoryList {"ApplicationStatus", "Communications", "SystemServices", "Hardware"};
|
||||
const QStringList ItemStatusList {"Passive", "Active", "NeedsAttention"};
|
||||
const QStringList LeftClickInvalidIdList {"sogou-qimpanel",};
|
||||
QPointer<DockPopupWindow> SNITrayItemWidget::PopupWindow = nullptr;
|
||||
Dock::Position SNITrayItemWidget::DockPosition = Dock::Position::Top;
|
||||
using namespace Dock;
|
||||
SNITrayItemWidget::SNITrayItemWidget(const QString &sniServicePath, QWidget *parent)
|
||||
: BaseTrayWidget(parent),
|
||||
m_menu(nullptr),
|
||||
m_updateIconTimer(new QTimer(this))
|
||||
, m_updateOverlayIconTimer(new QTimer(this))
|
||||
, m_updateAttentionIconTimer(new QTimer(this))
|
||||
, m_sniServicePath(sniServicePath)
|
||||
, m_popupTipsDelayTimer(new QTimer(this))
|
||||
, m_handleMouseReleaseTimer(new QTimer(this))
|
||||
{
|
||||
m_popupTipsDelayTimer->setInterval(500);
|
||||
m_popupTipsDelayTimer->setSingleShot(true);
|
||||
m_handleMouseReleaseTimer->setSingleShot(true);
|
||||
m_handleMouseReleaseTimer->setInterval(100);
|
||||
|
||||
connect(m_handleMouseReleaseTimer, &QTimer::timeout, this, &SNITrayItemWidget::handleMouseRelease);
|
||||
connect(m_popupTipsDelayTimer, &QTimer::timeout, this, &SNITrayItemWidget::showHoverTips);
|
||||
|
||||
if (PopupWindow.isNull()) {
|
||||
DockPopupWindow *arrowRectangle = new DockPopupWindow(nullptr);
|
||||
arrowRectangle->setShadowBlurRadius(20);
|
||||
arrowRectangle->setRadius(6);
|
||||
arrowRectangle->setShadowYOffset(2);
|
||||
arrowRectangle->setShadowXOffset(0);
|
||||
arrowRectangle->setArrowWidth(18);
|
||||
arrowRectangle->setArrowHeight(10);
|
||||
arrowRectangle->setObjectName("snitraypopup");
|
||||
PopupWindow = arrowRectangle;
|
||||
connect(qApp, &QApplication::aboutToQuit, PopupWindow, &DockPopupWindow::deleteLater);
|
||||
}
|
||||
|
||||
if (m_sniServicePath.startsWith("/") || !m_sniServicePath.contains("/")) {
|
||||
qDebug() << "SNI service path invalid";
|
||||
return;
|
||||
}
|
||||
|
||||
QPair<QString, QString> pair = serviceAndPath(m_sniServicePath);
|
||||
m_dbusService = pair.first;
|
||||
m_dbusPath = pair.second;
|
||||
|
||||
QDBusConnection conn = QDBusConnection::sessionBus();
|
||||
setOwnerPID(conn.interface()->servicePid(m_dbusService));
|
||||
|
||||
m_sniInter = new StatusNotifierItem(m_dbusService, m_dbusPath, QDBusConnection::sessionBus(), this);
|
||||
|
||||
if (!m_sniInter->isValid()) {
|
||||
qDebug() << "SNI dbus interface is invalid!" << m_dbusService << m_dbusPath << m_sniInter->lastError();
|
||||
return;
|
||||
}
|
||||
|
||||
m_updateIconTimer->setInterval(100);
|
||||
m_updateIconTimer->setSingleShot(true);
|
||||
m_updateOverlayIconTimer->setInterval(500);
|
||||
m_updateOverlayIconTimer->setSingleShot(true);
|
||||
m_updateAttentionIconTimer->setInterval(1000);
|
||||
m_updateAttentionIconTimer->setSingleShot(true);
|
||||
|
||||
connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &SNITrayItemWidget::refreshIcon);
|
||||
connect(m_updateIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshIcon);
|
||||
connect(m_updateOverlayIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshOverlayIcon);
|
||||
connect(m_updateAttentionIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshAttentionIcon);
|
||||
|
||||
// SNI property change
|
||||
// thses signals of properties may not be emit automatically!!
|
||||
// since the SniInter in on async mode we can not call property's getter function to obtain property directly
|
||||
// the way to refresh properties(emit the following signals) is call property's getter function and wait these signals
|
||||
connect(m_sniInter, &StatusNotifierItem::AttentionIconNameChanged, this, &SNITrayItemWidget::onSNIAttentionIconNameChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::AttentionIconPixmapChanged, this, &SNITrayItemWidget::onSNIAttentionIconPixmapChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::AttentionMovieNameChanged, this, &SNITrayItemWidget::onSNIAttentionMovieNameChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::CategoryChanged, this, &SNITrayItemWidget::onSNICategoryChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::IconNameChanged, this, &SNITrayItemWidget::onSNIIconNameChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::IconPixmapChanged, this, &SNITrayItemWidget::onSNIIconPixmapChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::IconThemePathChanged, this, &SNITrayItemWidget::onSNIIconThemePathChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::IdChanged, this, &SNITrayItemWidget::onSNIIdChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::MenuChanged, this, &SNITrayItemWidget::onSNIMenuChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::OverlayIconNameChanged, this, &SNITrayItemWidget::onSNIOverlayIconNameChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::OverlayIconPixmapChanged, this, &SNITrayItemWidget::onSNIOverlayIconPixmapChanged);
|
||||
connect(m_sniInter, &StatusNotifierItem::StatusChanged, this, &SNITrayItemWidget::onSNIStatusChanged);
|
||||
|
||||
// the following signals can be emit automatically
|
||||
// need refresh cached properties in these slots
|
||||
connect(m_sniInter, &StatusNotifierItem::NewIcon, [ = ] {
|
||||
m_sniIconName = m_sniInter->iconName();
|
||||
m_sniIconPixmap = m_sniInter->iconPixmap();
|
||||
m_sniIconThemePath = m_sniInter->iconThemePath();
|
||||
|
||||
m_updateIconTimer->start();
|
||||
});
|
||||
connect(m_sniInter, &StatusNotifierItem::NewOverlayIcon, [ = ] {
|
||||
m_sniOverlayIconName = m_sniInter->overlayIconName();
|
||||
m_sniOverlayIconPixmap = m_sniInter->overlayIconPixmap();
|
||||
m_sniIconThemePath = m_sniInter->iconThemePath();
|
||||
|
||||
m_updateOverlayIconTimer->start();
|
||||
});
|
||||
connect(m_sniInter, &StatusNotifierItem::NewAttentionIcon, [ = ] {
|
||||
m_sniAttentionIconName = m_sniInter->attentionIconName();
|
||||
m_sniAttentionIconPixmap = m_sniInter->attentionIconPixmap();
|
||||
m_sniIconThemePath = m_sniInter->iconThemePath();
|
||||
|
||||
m_updateAttentionIconTimer->start();
|
||||
});
|
||||
connect(m_sniInter, &StatusNotifierItem::NewStatus, [ = ] {
|
||||
onSNIStatusChanged(m_sniInter->status());
|
||||
});
|
||||
|
||||
initSNIPropertys();
|
||||
}
|
||||
|
||||
QString SNITrayItemWidget::itemKeyForConfig()
|
||||
{
|
||||
QString key;
|
||||
|
||||
do {
|
||||
key = m_sniId;
|
||||
if (!key.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
key = QDBusInterface(m_dbusService, m_dbusPath, StatusNotifierItem::staticInterfaceName())
|
||||
.property("Id").toString();
|
||||
if (!key.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
key = m_sniServicePath;
|
||||
} while (false);
|
||||
|
||||
return QString("sni:%1").arg(key);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::updateIcon()
|
||||
{
|
||||
m_updateIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::sendClick(uint8_t mouseButton, int x, int y)
|
||||
{
|
||||
switch (mouseButton) {
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
// left button click invalid
|
||||
if (LeftClickInvalidIdList.contains(m_sniId)) {
|
||||
showContextMenu(x, y);
|
||||
} else {
|
||||
m_sniInter->Activate(x, y);
|
||||
}
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
m_sniInter->SecondaryActivate(x, y);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
showContextMenu(x, y);
|
||||
break;
|
||||
default:
|
||||
qDebug() << "unknown mouse button key";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SNITrayItemWidget::isValid()
|
||||
{
|
||||
return m_sniInter->isValid();
|
||||
}
|
||||
|
||||
SNITrayItemWidget::ItemStatus SNITrayItemWidget::status()
|
||||
{
|
||||
if (!ItemStatusList.contains(m_sniStatus)) {
|
||||
m_sniStatus = "Active";
|
||||
return ItemStatus::Active;
|
||||
}
|
||||
|
||||
return static_cast<ItemStatus>(ItemStatusList.indexOf(m_sniStatus));
|
||||
}
|
||||
|
||||
SNITrayItemWidget::ItemCategory SNITrayItemWidget::category()
|
||||
{
|
||||
if (!ItemCategoryList.contains(m_sniCategory)) {
|
||||
return UnknownCategory;
|
||||
}
|
||||
|
||||
return static_cast<ItemCategory>(ItemCategoryList.indexOf(m_sniCategory));
|
||||
}
|
||||
|
||||
QString SNITrayItemWidget::toSNIKey(const QString &sniServicePath)
|
||||
{
|
||||
return QString("sni:%1").arg(sniServicePath);
|
||||
}
|
||||
|
||||
bool SNITrayItemWidget::isSNIKey(const QString &itemKey)
|
||||
{
|
||||
return itemKey.startsWith("sni:");
|
||||
}
|
||||
|
||||
QPair<QString, QString> SNITrayItemWidget::serviceAndPath(const QString &servicePath)
|
||||
{
|
||||
QStringList list = servicePath.split("/");
|
||||
QPair<QString, QString> pair;
|
||||
pair.first = list.takeFirst();
|
||||
|
||||
for (auto i : list) {
|
||||
pair.second.append("/");
|
||||
pair.second.append(i);
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
uint SNITrayItemWidget::servicePID(const QString &servicePath)
|
||||
{
|
||||
QString serviceName = serviceAndPath(servicePath).first;
|
||||
QDBusConnection conn = QDBusConnection::sessionBus();
|
||||
return conn.interface()->servicePid(serviceName);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::initSNIPropertys()
|
||||
{
|
||||
m_sniAttentionIconName = m_sniInter->attentionIconName();
|
||||
m_sniAttentionIconPixmap = m_sniInter->attentionIconPixmap();
|
||||
m_sniAttentionMovieName = m_sniInter->attentionMovieName();
|
||||
m_sniCategory = m_sniInter->category();
|
||||
m_sniIconName = m_sniInter->iconName();
|
||||
m_sniIconPixmap = m_sniInter->iconPixmap();
|
||||
m_sniIconThemePath = m_sniInter->iconThemePath();
|
||||
m_sniId = m_sniInter->id();
|
||||
m_sniMenuPath = m_sniInter->menu();
|
||||
m_sniOverlayIconName = m_sniInter->overlayIconName();
|
||||
m_sniOverlayIconPixmap = m_sniInter->overlayIconPixmap();
|
||||
m_sniStatus = m_sniInter->status();
|
||||
|
||||
m_updateIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::initMenu()
|
||||
{
|
||||
const QString &sniMenuPath = m_sniMenuPath.path();
|
||||
if (sniMenuPath.isEmpty()) {
|
||||
qDebug() << "Error: current sni menu path is empty of dbus service:" << m_dbusService << "id:" << m_sniId;
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "using sni service path:" << m_dbusService << "menu path:" << sniMenuPath;
|
||||
|
||||
m_dbusMenuImporter = new DBusMenuImporter(m_dbusService, sniMenuPath, ASYNCHRONOUS, this);
|
||||
|
||||
qDebug() << "generate the sni menu object";
|
||||
|
||||
m_menu = m_dbusMenuImporter->menu();
|
||||
|
||||
qDebug() << "the sni menu obect is:" << m_menu;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::refreshIcon()
|
||||
{
|
||||
QPixmap pix = newIconPixmap(Icon);
|
||||
if (pix.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pixmap = pix;
|
||||
update();
|
||||
Q_EMIT iconChanged();
|
||||
|
||||
if (!isVisible()) {
|
||||
Q_EMIT needAttention();
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::refreshOverlayIcon()
|
||||
{
|
||||
QPixmap pix = newIconPixmap(OverlayIcon);
|
||||
if (pix.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_overlayPixmap = pix;
|
||||
update();
|
||||
Q_EMIT iconChanged();
|
||||
|
||||
if (!isVisible()) {
|
||||
Q_EMIT needAttention();
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::refreshAttentionIcon()
|
||||
{
|
||||
/* TODO: A new approach may be needed to deal with attentionIcon */
|
||||
QPixmap pix = newIconPixmap(AttentionIcon);
|
||||
if (pix.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pixmap = pix;
|
||||
update();
|
||||
Q_EMIT iconChanged();
|
||||
|
||||
if (!isVisible()) {
|
||||
Q_EMIT needAttention();
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::showContextMenu(int x, int y)
|
||||
{
|
||||
// 这里的PopupWindow属性是置顶的,如果不隐藏,会导致菜单显示不出来
|
||||
hidePopup();
|
||||
|
||||
// ContextMenu does not work
|
||||
if (m_sniMenuPath.path().startsWith("/NO_DBUSMENU")) {
|
||||
m_sniInter->ContextMenu(x, y);
|
||||
} else {
|
||||
if (!m_menu) {
|
||||
qDebug() << "context menu has not be ready, init menu";
|
||||
initMenu();
|
||||
}
|
||||
|
||||
if (m_menu)
|
||||
m_menu->popup(QPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIAttentionIconNameChanged(const QString &value)
|
||||
{
|
||||
m_sniAttentionIconName = value;
|
||||
|
||||
m_updateAttentionIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIAttentionIconPixmapChanged(DBusImageList value)
|
||||
{
|
||||
m_sniAttentionIconPixmap = value;
|
||||
|
||||
m_updateAttentionIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIAttentionMovieNameChanged(const QString &value)
|
||||
{
|
||||
m_sniAttentionMovieName = value;
|
||||
|
||||
m_updateAttentionIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNICategoryChanged(const QString &value)
|
||||
{
|
||||
m_sniCategory = value;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIIconNameChanged(const QString &value)
|
||||
{
|
||||
m_sniIconName = value;
|
||||
|
||||
m_updateIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIIconPixmapChanged(DBusImageList value)
|
||||
{
|
||||
m_sniIconPixmap = value;
|
||||
|
||||
m_updateIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIIconThemePathChanged(const QString &value)
|
||||
{
|
||||
m_sniIconThemePath = value;
|
||||
|
||||
m_updateIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIIdChanged(const QString &value)
|
||||
{
|
||||
m_sniId = value;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIMenuChanged(const QDBusObjectPath &value)
|
||||
{
|
||||
m_sniMenuPath = value;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIOverlayIconNameChanged(const QString &value)
|
||||
{
|
||||
m_sniOverlayIconName = value;
|
||||
|
||||
m_updateOverlayIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIOverlayIconPixmapChanged(DBusImageList value)
|
||||
{
|
||||
m_sniOverlayIconPixmap = value;
|
||||
|
||||
m_updateOverlayIconTimer->start();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::onSNIStatusChanged(const QString &status)
|
||||
{
|
||||
if (!ItemStatusList.contains(status) || m_sniStatus == status) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sniStatus = status;
|
||||
|
||||
Q_EMIT statusChanged(static_cast<SNITrayItemWidget::ItemStatus>(ItemStatusList.indexOf(status)));
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
if (!needShow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pixmap.isNull())
|
||||
return;
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
//#ifdef QT_DEBUG
|
||||
// painter.fillRect(rect(), Qt::green);
|
||||
//#endif
|
||||
|
||||
const QRectF &rf = QRect(rect());
|
||||
const QRectF &rfp = QRect(m_pixmap.rect());
|
||||
const QPointF &p = rf.center() - rfp.center() / m_pixmap.devicePixelRatioF();
|
||||
painter.drawPixmap(p, m_pixmap);
|
||||
|
||||
if (!m_overlayPixmap.isNull()) {
|
||||
painter.drawPixmap(p, m_overlayPixmap);
|
||||
}
|
||||
|
||||
painter.end();
|
||||
}
|
||||
|
||||
QPixmap SNITrayItemWidget::newIconPixmap(IconType iconType)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
if (iconType == UnknownIconType) {
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
QString iconName;
|
||||
DBusImageList dbusImageList;
|
||||
|
||||
QString iconThemePath = m_sniIconThemePath;
|
||||
|
||||
switch (iconType) {
|
||||
case Icon:
|
||||
iconName = m_sniIconName;
|
||||
dbusImageList = m_sniIconPixmap;
|
||||
break;
|
||||
case OverlayIcon:
|
||||
iconName = m_sniOverlayIconName;
|
||||
dbusImageList = m_sniOverlayIconPixmap;
|
||||
break;
|
||||
case AttentionIcon:
|
||||
iconName = m_sniAttentionIconName;
|
||||
dbusImageList = m_sniAttentionIconPixmap;
|
||||
break;
|
||||
case AttentionMovieIcon:
|
||||
iconName = m_sniAttentionMovieName;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const auto ratio = devicePixelRatioF();
|
||||
const int iconSizeScaled = IconSize * ratio;
|
||||
do {
|
||||
// load icon from sni dbus
|
||||
if (!dbusImageList.isEmpty() && !dbusImageList.first().pixels.isEmpty()) {
|
||||
for (DBusImage dbusImage : dbusImageList) {
|
||||
char *image_data = dbusImage.pixels.data();
|
||||
|
||||
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
|
||||
for (int i = 0; i < dbusImage.pixels.size(); i += 4) {
|
||||
*(qint32 *)(image_data + i) = qFromBigEndian(*(qint32 *)(image_data + i));
|
||||
}
|
||||
}
|
||||
|
||||
QImage image((const uchar *)dbusImage.pixels.constData(), dbusImage.width, dbusImage.height, QImage::Format_ARGB32);
|
||||
pixmap = QPixmap::fromImage(image.scaled(iconSizeScaled, iconSizeScaled, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
pixmap.setDevicePixelRatio(ratio);
|
||||
if (!pixmap.isNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load icon from specified file
|
||||
if (!iconThemePath.isEmpty() && !iconName.isEmpty()) {
|
||||
QDirIterator it(iconThemePath, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (it.fileName().startsWith(iconName, Qt::CaseInsensitive)) {
|
||||
QImage image(it.filePath());
|
||||
pixmap = QPixmap::fromImage(image.scaled(iconSizeScaled, iconSizeScaled, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
pixmap.setDevicePixelRatio(ratio);
|
||||
if (!pixmap.isNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!pixmap.isNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// load icon from theme
|
||||
// Note: this will ensure return a None-Null pixmap
|
||||
// so, it should be the last fallback
|
||||
if (!iconName.isEmpty()) {
|
||||
// ThemeAppIcon::getIcon 会处理高分屏缩放问题
|
||||
ThemeAppIcon::getIcon(pixmap, iconName, IconSize);
|
||||
if (!pixmap.isNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pixmap.isNull()) {
|
||||
qDebug() << "get icon faild!" << iconType;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
// QLabel *l = new QLabel;
|
||||
// l->setPixmap(pixmap);
|
||||
// l->setFixedSize(100, 100);
|
||||
// l->show();
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::enterEvent(QEvent *event)
|
||||
{
|
||||
// 触屏不显示hover效果
|
||||
if (!qApp->property(IS_TOUCH_STATE).toBool()) {
|
||||
m_popupTipsDelayTimer->start();
|
||||
}
|
||||
|
||||
BaseTrayWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::leaveEvent(QEvent *event)
|
||||
{
|
||||
m_popupTipsDelayTimer->stop();
|
||||
if (m_popupShown && !PopupWindow->model())
|
||||
hidePopup();
|
||||
|
||||
update();
|
||||
|
||||
BaseTrayWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
// call QWidget::mousePressEvent means to show dock-context-menu
|
||||
// when right button of mouse is pressed immediately in fashion mode
|
||||
|
||||
// here we hide the right button press event when it is click in the special area
|
||||
m_popupTipsDelayTimer->stop();
|
||||
if (event->button() == Qt::RightButton && perfectIconRect().contains(event->pos(), true)) {
|
||||
event->accept();
|
||||
setMouseData(event);
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
//e->accept();
|
||||
|
||||
// 由于 XWindowTrayWidget 中对 发送鼠标事件到X窗口的函数, 如 sendClick/sendHoverEvent 中
|
||||
// 使用了 setX11PassMouseEvent, 而每次调用 setX11PassMouseEvent 时都会导致产生 mousePress 和 mouseRelease 事件
|
||||
// 因此如果直接在这里处理事件会导致一些问题, 所以使用 Timer 来延迟处理 100 毫秒内的最后一个事件
|
||||
setMouseData(e);
|
||||
|
||||
QWidget::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::handleMouseRelease()
|
||||
{
|
||||
Q_ASSERT(sender() == m_handleMouseReleaseTimer);
|
||||
|
||||
// do not dealwith all mouse event of SystemTray, class SystemTrayItem will handle it
|
||||
if (trayType() == SystemTray)
|
||||
return;
|
||||
|
||||
const QPoint point(m_lastMouseReleaseData.first - rect().center());
|
||||
if (point.manhattanLength() > 24)
|
||||
return;
|
||||
|
||||
QPoint globalPos = QCursor::pos();
|
||||
uint8_t buttonIndex = XCB_BUTTON_INDEX_1;
|
||||
|
||||
switch (m_lastMouseReleaseData.second) {
|
||||
case Qt:: MiddleButton:
|
||||
buttonIndex = XCB_BUTTON_INDEX_2;
|
||||
break;
|
||||
case Qt::RightButton:
|
||||
buttonIndex = XCB_BUTTON_INDEX_3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sendClick(buttonIndex, globalPos.x(), globalPos.y());
|
||||
|
||||
// left mouse button clicked
|
||||
if (buttonIndex == XCB_BUTTON_INDEX_1) {
|
||||
Q_EMIT clicked();
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::showHoverTips()
|
||||
{
|
||||
if (PopupWindow->model())
|
||||
return;
|
||||
|
||||
// if not in geometry area
|
||||
const QRect r(topleftPoint(), size());
|
||||
if (!r.contains(QCursor::pos()))
|
||||
return;
|
||||
|
||||
QProcess p;
|
||||
p.start("qdbus", {m_dbusService});
|
||||
if (!p.waitForFinished(1000)) {
|
||||
qDebug() << "sni dbus service error : " << m_dbusService;
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusInterface infc(m_dbusService, m_dbusPath);
|
||||
QDBusMessage msg = infc.call("Get", "org.kde.StatusNotifierItem", "ToolTip");
|
||||
if (msg.type() == QDBusMessage::ReplyMessage) {
|
||||
QDBusArgument arg = msg.arguments().at(0).value<QDBusVariant>().variant().value<QDBusArgument>();
|
||||
DBusToolTip tooltip = qdbus_cast<DBusToolTip>(arg);
|
||||
|
||||
if (tooltip.title.isEmpty())
|
||||
return;
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
setToolTip(tooltip.title);
|
||||
#else
|
||||
// // 当提示信息中有换行符时,需要使用setTextList
|
||||
// if (tooltip.title.contains('\n'))
|
||||
// m_tipsLabel->setTextList(tooltip.title.split('\n'));
|
||||
// else
|
||||
// m_tipsLabel->setText(tooltip.title);
|
||||
|
||||
// m_tipsLabel->setAccessibleName(itemKeyForConfig().replace("sni:",""));
|
||||
|
||||
|
||||
// showPopupWindow(m_tipsLabel);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::hideNonModel()
|
||||
{
|
||||
// auto hide if popup is not model window
|
||||
if (m_popupShown && !PopupWindow->model())
|
||||
hidePopup();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::popupWindowAccept()
|
||||
{
|
||||
if (!PopupWindow->isVisible())
|
||||
return;
|
||||
|
||||
hidePopup();
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::hidePopup()
|
||||
{
|
||||
m_popupTipsDelayTimer->stop();
|
||||
m_popupShown = false;
|
||||
PopupWindow->hide();
|
||||
|
||||
emit PopupWindow->accept();
|
||||
emit requestWindowAutoHide(true);
|
||||
}
|
||||
// 获取在最外层的窗口(MainWindow)中的位置
|
||||
const QPoint SNITrayItemWidget::topleftPoint() const
|
||||
{
|
||||
QPoint p;
|
||||
const QWidget *w = this;
|
||||
do {
|
||||
p += w->pos();
|
||||
w = qobject_cast<QWidget *>(w->parent());
|
||||
} while (w);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
const QPoint SNITrayItemWidget::popupMarkPoint() const
|
||||
{
|
||||
QPoint p(topleftPoint());
|
||||
|
||||
const QRect r = rect();
|
||||
const QRect wr = window()->rect();
|
||||
|
||||
switch (DockPosition) {
|
||||
case Dock::Position::Top:
|
||||
p += QPoint(r.width() / 2, r.height() + (wr.height() - r.height()) / 2);
|
||||
break;
|
||||
case Dock::Position::Bottom:
|
||||
p += QPoint(r.width() / 2, 0 - (wr.height() - r.height()) / 2);
|
||||
break;
|
||||
case Dock::Position::Left:
|
||||
p += QPoint(r.width() + (wr.width() - r.width()) / 2, r.height() / 2);
|
||||
break;
|
||||
case Dock::Position::Right:
|
||||
p += QPoint(0 - (wr.width() - r.width()) / 2, r.height() / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
QPixmap SNITrayItemWidget::icon()
|
||||
{
|
||||
return m_pixmap;
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::showPopupWindow(QWidget *const content, const bool model)
|
||||
{
|
||||
m_popupShown = true;
|
||||
|
||||
if (model)
|
||||
emit requestWindowAutoHide(false);
|
||||
|
||||
DockPopupWindow *popup = PopupWindow.data();
|
||||
QWidget *lastContent = popup->getContent();
|
||||
if (lastContent)
|
||||
lastContent->setVisible(false);
|
||||
|
||||
switch (DockPosition) {
|
||||
case Dock::Position::Top: popup->setArrowDirection(DockPopupWindow::ArrowTop); break;
|
||||
case Dock::Position::Bottom: popup->setArrowDirection(DockPopupWindow::ArrowBottom); break;
|
||||
case Dock::Position::Left: popup->setArrowDirection(DockPopupWindow::ArrowLeft); break;
|
||||
case Dock::Position::Right: popup->setArrowDirection(DockPopupWindow::ArrowRight); break;
|
||||
}
|
||||
popup->resize(content->sizeHint());
|
||||
popup->setContent(content);
|
||||
|
||||
QPoint p = popupMarkPoint();
|
||||
if (!popup->isVisible())
|
||||
QMetaObject::invokeMethod(popup, "show", Qt::QueuedConnection, Q_ARG(QPoint, p), Q_ARG(bool, model));
|
||||
else
|
||||
popup->show(p, model);
|
||||
}
|
||||
|
||||
void SNITrayItemWidget::setMouseData(QMouseEvent *e)
|
||||
{
|
||||
m_lastMouseReleaseData.first = e->pos();
|
||||
m_lastMouseReleaseData.second = e->button();
|
||||
|
||||
m_handleMouseReleaseTimer->start();
|
||||
}
|
156
frame/window/tray/widgets/snitrayitemwidget.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: listenerri <listenerri@gmail.com>
|
||||
*
|
||||
* Maintainer: listenerri <listenerri@gmail.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 SNITRAYWIDGET_H
|
||||
#define SNITRAYWIDGET_H
|
||||
|
||||
#include "constants.h"
|
||||
#include "basetraywidget.h"
|
||||
#include "dockpopupwindow.h"
|
||||
|
||||
#include <org_kde_statusnotifieritem.h>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QDBusObjectPath>
|
||||
//DWIDGET_USE_NAMESPACE
|
||||
//DGUI_USE_NAMESPACE
|
||||
class DBusMenuImporter;
|
||||
//namespace Dock {
|
||||
//class TipsWidget;
|
||||
//}
|
||||
//using namespace com::deepin::dde;
|
||||
using namespace org::kde;
|
||||
|
||||
/**
|
||||
* @brief The SNITrayWidget class
|
||||
* @note 系统托盘第三方程序窗口
|
||||
*/
|
||||
class SNITrayItemWidget : public BaseTrayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ItemCategory {UnknownCategory = -1, ApplicationStatus, Communications, SystemServices, Hardware};
|
||||
enum ItemStatus {Passive, Active, NeedsAttention};
|
||||
enum IconType {UnknownIconType = -1, Icon, OverlayIcon, AttentionIcon, AttentionMovieIcon};
|
||||
|
||||
public:
|
||||
SNITrayItemWidget(const QString &sniServicePath, QWidget *parent = Q_NULLPTR);
|
||||
|
||||
QString itemKeyForConfig() override;
|
||||
void updateIcon() override;
|
||||
void sendClick(uint8_t mouseButton, int x, int y) override;
|
||||
|
||||
bool isValid() override;
|
||||
SNITrayItemWidget::ItemStatus status();
|
||||
SNITrayItemWidget::ItemCategory category();
|
||||
|
||||
static QString toSNIKey(const QString &sniServicePath);
|
||||
static bool isSNIKey(const QString &itemKey);
|
||||
static QPair<QString, QString> serviceAndPath(const QString &servicePath);
|
||||
static uint servicePID(const QString &servicePath);
|
||||
|
||||
void showHoverTips();
|
||||
const QPoint topleftPoint() const;
|
||||
void showPopupWindow(QWidget *const content, const bool model = false);
|
||||
const QPoint popupMarkPoint() const;
|
||||
|
||||
static void setDockPostion(const Dock::Position pos) { DockPosition = pos; }
|
||||
|
||||
QPixmap icon() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void statusChanged(SNITrayItemWidget::ItemStatus status);
|
||||
|
||||
private Q_SLOTS:
|
||||
void initSNIPropertys();
|
||||
void initMenu();
|
||||
void refreshIcon();
|
||||
void refreshOverlayIcon();
|
||||
void refreshAttentionIcon();
|
||||
void showContextMenu(int x, int y);
|
||||
// SNI property change slot
|
||||
void onSNIAttentionIconNameChanged(const QString &value);
|
||||
void onSNIAttentionIconPixmapChanged(DBusImageList value);
|
||||
void onSNIAttentionMovieNameChanged(const QString &value);
|
||||
void onSNICategoryChanged(const QString &value);
|
||||
void onSNIIconNameChanged(const QString &value);
|
||||
void onSNIIconPixmapChanged(DBusImageList value);
|
||||
void onSNIIconThemePathChanged(const QString &value);
|
||||
void onSNIIdChanged(const QString &value);
|
||||
void onSNIMenuChanged(const QDBusObjectPath &value);
|
||||
void onSNIOverlayIconNameChanged(const QString &value);
|
||||
void onSNIOverlayIconPixmapChanged(DBusImageList value);
|
||||
void onSNIStatusChanged(const QString &status);
|
||||
void hidePopup();
|
||||
void hideNonModel();
|
||||
void popupWindowAccept();
|
||||
void enterEvent(QEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
QPixmap newIconPixmap(IconType iconType);
|
||||
void setMouseData(QMouseEvent *e);
|
||||
void handleMouseRelease();
|
||||
|
||||
private:
|
||||
StatusNotifierItem *m_sniInter;
|
||||
|
||||
DBusMenuImporter *m_dbusMenuImporter;
|
||||
|
||||
QMenu *m_menu;
|
||||
QTimer *m_updateIconTimer;
|
||||
QTimer *m_updateOverlayIconTimer;
|
||||
QTimer *m_updateAttentionIconTimer;
|
||||
|
||||
QString m_sniServicePath;
|
||||
QString m_dbusService;
|
||||
QString m_dbusPath;
|
||||
|
||||
QPixmap m_pixmap;
|
||||
QPixmap m_overlayPixmap;
|
||||
|
||||
// SNI propertys
|
||||
QString m_sniAttentionIconName;
|
||||
DBusImageList m_sniAttentionIconPixmap;
|
||||
QString m_sniAttentionMovieName;
|
||||
QString m_sniCategory;
|
||||
QString m_sniIconName;
|
||||
DBusImageList m_sniIconPixmap;
|
||||
QString m_sniIconThemePath;
|
||||
QString m_sniId;
|
||||
QDBusObjectPath m_sniMenuPath;
|
||||
QString m_sniOverlayIconName;
|
||||
DBusImageList m_sniOverlayIconPixmap;
|
||||
QString m_sniStatus;
|
||||
QTimer *m_popupTipsDelayTimer;
|
||||
QTimer *m_handleMouseReleaseTimer;
|
||||
QPair<QPoint, Qt::MouseButton> m_lastMouseReleaseData;
|
||||
static Dock::Position DockPosition;
|
||||
static QPointer<DockPopupWindow> PopupWindow;
|
||||
// Dock::TipsWidget *m_tipsLabel;
|
||||
bool m_popupShown;
|
||||
};
|
||||
|
||||
#endif /* SNIWIDGET_H */
|
508
frame/window/tray/widgets/systempluginitem.cpp
Normal file
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: listenerri <listenerri@gmail.com>
|
||||
*
|
||||
* Maintainer: listenerri <listenerri@gmail.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 "systempluginitem.h"
|
||||
#include "utils.h"
|
||||
#include "dockpopupwindow.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDebug>
|
||||
#include <QPointer>
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
Dock::Position SystemPluginItem::DockPosition = Dock::Position::Top;
|
||||
QPointer<DockPopupWindow> SystemPluginItem::PopupWindow = nullptr;
|
||||
|
||||
SystemPluginItem::SystemPluginItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent)
|
||||
: BaseTrayWidget(parent)
|
||||
, m_popupShown(false)
|
||||
, m_tapAndHold(false)
|
||||
, m_pluginInter(pluginInter)
|
||||
, m_centralWidget(m_pluginInter->itemWidget(itemKey))
|
||||
, m_popupTipsDelayTimer(new QTimer(this))
|
||||
, m_popupAdjustDelayTimer(new QTimer(this))
|
||||
, m_itemKey(itemKey)
|
||||
, m_gsettings(Utils::ModuleSettingsPtr(pluginInter->pluginName(), QByteArray(), this))
|
||||
{
|
||||
qDebug() << "load tray plugins item: " << m_pluginInter->pluginName() << itemKey << m_centralWidget;
|
||||
|
||||
m_centralWidget->setParent(this);
|
||||
m_centralWidget->setVisible(true);
|
||||
m_centralWidget->installEventFilter(this);
|
||||
|
||||
QBoxLayout *hLayout = new QHBoxLayout(this);
|
||||
hLayout->addWidget(m_centralWidget);
|
||||
hLayout->setSpacing(0);
|
||||
hLayout->setMargin(0);
|
||||
|
||||
setLayout(hLayout);
|
||||
setAccessibleName(m_itemKey);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
if (PopupWindow.isNull()) {
|
||||
DockPopupWindow *arrowRectangle = new DockPopupWindow(nullptr);
|
||||
arrowRectangle->setShadowBlurRadius(20);
|
||||
arrowRectangle->setRadius(6);
|
||||
arrowRectangle->setShadowYOffset(2);
|
||||
arrowRectangle->setShadowXOffset(0);
|
||||
arrowRectangle->setArrowWidth(18);
|
||||
arrowRectangle->setArrowHeight(10);
|
||||
arrowRectangle->setObjectName("systemtraypopup");
|
||||
PopupWindow = arrowRectangle;
|
||||
connect(qApp, &QApplication::aboutToQuit, PopupWindow, &DockPopupWindow::deleteLater);
|
||||
}
|
||||
|
||||
// 必须初始化父窗口,否则当主题切换之后再设置父窗口的时候palette会更改为主题切换前的palette
|
||||
if (QWidget *w = m_pluginInter->itemPopupApplet(m_itemKey)) {
|
||||
w->setParent(PopupWindow.data());
|
||||
w->setVisible(false);
|
||||
}
|
||||
|
||||
m_popupTipsDelayTimer->setInterval(500);
|
||||
m_popupTipsDelayTimer->setSingleShot(true);
|
||||
|
||||
m_popupAdjustDelayTimer->setInterval(10);
|
||||
m_popupAdjustDelayTimer->setSingleShot(true);
|
||||
|
||||
connect(m_popupTipsDelayTimer, &QTimer::timeout, this, &SystemPluginItem::showHoverTips);
|
||||
connect(m_popupAdjustDelayTimer, &QTimer::timeout, this, &SystemPluginItem::updatePopupPosition, Qt::QueuedConnection);
|
||||
connect(&m_contextMenu, &QMenu::triggered, this, &SystemPluginItem::menuActionClicked);
|
||||
|
||||
if (m_gsettings)
|
||||
connect(m_gsettings, &QGSettings::changed, this, &SystemPluginItem::onGSettingsChanged);
|
||||
|
||||
grabGesture(Qt::TapAndHoldGesture);
|
||||
}
|
||||
|
||||
SystemPluginItem::~SystemPluginItem()
|
||||
{
|
||||
if (m_popupShown)
|
||||
popupWindowAccept();
|
||||
}
|
||||
|
||||
QString SystemPluginItem::itemKeyForConfig()
|
||||
{
|
||||
return m_itemKey;
|
||||
}
|
||||
|
||||
void SystemPluginItem::updateIcon()
|
||||
{
|
||||
m_pluginInter->refreshIcon(m_itemKey);
|
||||
}
|
||||
|
||||
void SystemPluginItem::sendClick(uint8_t mouseButton, int x, int y)
|
||||
{
|
||||
Q_UNUSED(mouseButton);
|
||||
Q_UNUSED(x);
|
||||
Q_UNUSED(y);
|
||||
|
||||
// do not process this callback
|
||||
// handle all mouse event in override mouse function
|
||||
}
|
||||
|
||||
QWidget *SystemPluginItem::trayTipsWidget()
|
||||
{
|
||||
if (m_pluginInter->itemTipsWidget(m_itemKey)) {
|
||||
m_pluginInter->itemTipsWidget(m_itemKey)->setAccessibleName(m_pluginInter->pluginName());
|
||||
}
|
||||
|
||||
return m_pluginInter->itemTipsWidget(m_itemKey);
|
||||
}
|
||||
|
||||
QWidget *SystemPluginItem::trayPopupApplet()
|
||||
{
|
||||
if (m_pluginInter->itemPopupApplet(m_itemKey)) {
|
||||
m_pluginInter->itemPopupApplet(m_itemKey)->setAccessibleName(m_pluginInter->pluginName());
|
||||
}
|
||||
|
||||
return m_pluginInter->itemPopupApplet(m_itemKey);
|
||||
}
|
||||
|
||||
const QString SystemPluginItem::trayClickCommand()
|
||||
{
|
||||
return m_pluginInter->itemCommand(m_itemKey);
|
||||
}
|
||||
|
||||
const QString SystemPluginItem::contextMenu() const
|
||||
{
|
||||
return m_pluginInter->itemContextMenu(m_itemKey);
|
||||
}
|
||||
|
||||
void SystemPluginItem::invokedMenuItem(const QString &menuId, const bool checked)
|
||||
{
|
||||
m_pluginInter->invokedMenuItem(m_itemKey, menuId, checked);
|
||||
}
|
||||
|
||||
QWidget *SystemPluginItem::centralWidget() const
|
||||
{
|
||||
return m_centralWidget;
|
||||
}
|
||||
|
||||
void SystemPluginItem::detachPluginWidget()
|
||||
{
|
||||
QWidget *widget = m_pluginInter->itemWidget(m_itemKey);
|
||||
if (widget)
|
||||
widget->setParent(nullptr);
|
||||
}
|
||||
|
||||
bool SystemPluginItem::event(QEvent *event)
|
||||
{
|
||||
if (m_popupShown) {
|
||||
switch (event->type()) {
|
||||
case QEvent::Paint:
|
||||
if (!m_popupAdjustDelayTimer->isActive())
|
||||
m_popupAdjustDelayTimer->start();
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::Gesture)
|
||||
gestureEvent(static_cast<QGestureEvent *>(event));
|
||||
|
||||
return BaseTrayWidget::event(event);
|
||||
}
|
||||
|
||||
void SystemPluginItem::enterEvent(QEvent *event)
|
||||
{
|
||||
if (checkGSettingsControl()) {
|
||||
//网络需要显示Tips,需要特殊处理。
|
||||
if (m_pluginInter->pluginName() != "network")
|
||||
return;
|
||||
}
|
||||
|
||||
// 触屏不显示hover效果
|
||||
if (!qApp->property(IS_TOUCH_STATE).toBool()) {
|
||||
m_popupTipsDelayTimer->start();
|
||||
}
|
||||
update();
|
||||
|
||||
BaseTrayWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
void SystemPluginItem::leaveEvent(QEvent *event)
|
||||
{
|
||||
m_popupTipsDelayTimer->stop();
|
||||
|
||||
// auto hide if popup is not model window
|
||||
if (m_popupShown && !PopupWindow->model())
|
||||
hidePopup();
|
||||
|
||||
update();
|
||||
|
||||
BaseTrayWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
void SystemPluginItem::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (checkGSettingsControl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_popupTipsDelayTimer->stop();
|
||||
hideNonModel();
|
||||
|
||||
if (event->button() == Qt::RightButton
|
||||
&& perfectIconRect().contains(event->pos(), true)) {
|
||||
return (m_gsettings && (!m_gsettings->keys().contains("menuEnable") || m_gsettings->get("menuEnable").toBool())) ? showContextMenu() : void();
|
||||
}
|
||||
|
||||
BaseTrayWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void SystemPluginItem::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (checkGSettingsControl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkAndResetTapHoldGestureState() && event->source() == Qt::MouseEventSynthesizedByQt) {
|
||||
qDebug() << "SystemTray: tap and hold gesture detected, ignore the synthesized mouse release event";
|
||||
return;
|
||||
}
|
||||
|
||||
event->accept();
|
||||
|
||||
showPopupApplet(trayPopupApplet());
|
||||
|
||||
if (!trayClickCommand().isEmpty()) {
|
||||
QProcess::startDetached(trayClickCommand());
|
||||
}
|
||||
|
||||
BaseTrayWidget::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void SystemPluginItem::showEvent(QShowEvent *event)
|
||||
{
|
||||
QTimer::singleShot(0, this, [ = ] {
|
||||
onGSettingsChanged("enable");
|
||||
});
|
||||
|
||||
return BaseTrayWidget::showEvent(event);
|
||||
}
|
||||
|
||||
const QPoint SystemPluginItem::popupMarkPoint() const
|
||||
{
|
||||
QPoint p(topleftPoint());
|
||||
|
||||
const QRect r = rect();
|
||||
const QRect wr = window()->rect();
|
||||
|
||||
switch (DockPosition) {
|
||||
case Dock::Position::Top:
|
||||
p += QPoint(r.width() / 2, r.height() + (wr.height() - r.height()) / 2);
|
||||
break;
|
||||
case Dock::Position::Bottom:
|
||||
p += QPoint(r.width() / 2, 0 - (wr.height() - r.height()) / 2);
|
||||
break;
|
||||
case Dock::Position::Left:
|
||||
p += QPoint(r.width() + (wr.width() - r.width()) / 2, r.height() / 2);
|
||||
break;
|
||||
case Dock::Position::Right:
|
||||
p += QPoint(0 - (wr.width() - r.width()) / 2, r.height() / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// 获取在最外层的窗口(MainWindow)中的位置
|
||||
const QPoint SystemPluginItem::topleftPoint() const
|
||||
{
|
||||
QPoint p;
|
||||
const QWidget *w = this;
|
||||
do {
|
||||
p += w->pos();
|
||||
w = qobject_cast<QWidget *>(w->parent());
|
||||
} while (w);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void SystemPluginItem::hidePopup()
|
||||
{
|
||||
m_popupTipsDelayTimer->stop();
|
||||
m_popupAdjustDelayTimer->stop();
|
||||
m_popupShown = false;
|
||||
PopupWindow->hide();
|
||||
|
||||
DockPopupWindow *popup = PopupWindow.data();
|
||||
QWidget *content = popup->getContent();
|
||||
if (content) {
|
||||
content->setVisible(false);
|
||||
}
|
||||
|
||||
emit PopupWindow->accept();
|
||||
emit requestWindowAutoHide(true);
|
||||
}
|
||||
|
||||
void SystemPluginItem::hideNonModel()
|
||||
{
|
||||
// auto hide if popup is not model window
|
||||
if (m_popupShown && !PopupWindow->model())
|
||||
hidePopup();
|
||||
}
|
||||
|
||||
void SystemPluginItem::popupWindowAccept()
|
||||
{
|
||||
if (!PopupWindow->isVisible())
|
||||
return;
|
||||
|
||||
disconnect(PopupWindow.data(), &DockPopupWindow::accept, this, &SystemPluginItem::popupWindowAccept);
|
||||
|
||||
hidePopup();
|
||||
}
|
||||
|
||||
void SystemPluginItem::showPopupApplet(QWidget *const applet)
|
||||
{
|
||||
if (!applet)
|
||||
return;
|
||||
|
||||
// another model popup window already exists
|
||||
if (PopupWindow->model()) {
|
||||
applet->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
showPopupWindow(applet, true);
|
||||
}
|
||||
|
||||
void SystemPluginItem::showPopupWindow(QWidget *const content, const bool model)
|
||||
{
|
||||
m_popupShown = true;
|
||||
m_lastPopupWidget = content;
|
||||
|
||||
if (model)
|
||||
emit requestWindowAutoHide(false);
|
||||
|
||||
DockPopupWindow *popup = PopupWindow.data();
|
||||
QWidget *lastContent = popup->getContent();
|
||||
if (lastContent)
|
||||
lastContent->setVisible(false);
|
||||
|
||||
switch (DockPosition) {
|
||||
case Dock::Position::Top: popup->setArrowDirection(DockPopupWindow::ArrowTop); break;
|
||||
case Dock::Position::Bottom: popup->setArrowDirection(DockPopupWindow::ArrowBottom); break;
|
||||
case Dock::Position::Left: popup->setArrowDirection(DockPopupWindow::ArrowLeft); break;
|
||||
case Dock::Position::Right: popup->setArrowDirection(DockPopupWindow::ArrowRight); break;
|
||||
}
|
||||
popup->resize(content->sizeHint());
|
||||
popup->setContent(content);
|
||||
|
||||
QPoint p = popupMarkPoint();
|
||||
if (!popup->isVisible())
|
||||
QMetaObject::invokeMethod(popup, "show", Qt::QueuedConnection, Q_ARG(QPoint, p), Q_ARG(bool, model));
|
||||
else
|
||||
popup->show(p, model);
|
||||
|
||||
connect(popup, &DockPopupWindow::accept, this, &SystemPluginItem::popupWindowAccept, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void SystemPluginItem::showHoverTips()
|
||||
{
|
||||
// another model popup window already exists
|
||||
if (PopupWindow->model())
|
||||
return;
|
||||
|
||||
// if not in geometry area
|
||||
const QRect r(topleftPoint(), size());
|
||||
if (!r.contains(QCursor::pos()))
|
||||
return;
|
||||
|
||||
QWidget *const content = trayTipsWidget();
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
showPopupWindow(content);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \sa DockItem::checkAndResetTapHoldGestureState
|
||||
*/
|
||||
bool SystemPluginItem::checkAndResetTapHoldGestureState()
|
||||
{
|
||||
bool ret = m_tapAndHold;
|
||||
m_tapAndHold = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SystemPluginItem::gestureEvent(QGestureEvent *event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
QGesture *gesture = event->gesture(Qt::TapAndHoldGesture);
|
||||
|
||||
if (!gesture)
|
||||
return;
|
||||
|
||||
qDebug() << "SystemTray: got TapAndHoldGesture";
|
||||
|
||||
m_tapAndHold = true;
|
||||
}
|
||||
|
||||
void SystemPluginItem::showContextMenu()
|
||||
{
|
||||
const QString menuJson = contextMenu();
|
||||
if (menuJson.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(menuJson.toLocal8Bit().data());
|
||||
if (jsonDocument.isNull())
|
||||
return;
|
||||
|
||||
QJsonObject jsonMenu = jsonDocument.object();
|
||||
|
||||
qDeleteAll(m_contextMenu.actions());
|
||||
|
||||
QJsonArray jsonMenuItems = jsonMenu.value("items").toArray();
|
||||
for (auto item : jsonMenuItems) {
|
||||
QJsonObject itemObj = item.toObject();
|
||||
QAction *action = new QAction(itemObj.value("itemText").toString());
|
||||
action->setCheckable(itemObj.value("isCheckable").toBool());
|
||||
action->setChecked(itemObj.value("checked").toBool());
|
||||
action->setData(itemObj.value("itemId").toString());
|
||||
action->setEnabled(itemObj.value("isActive").toBool());
|
||||
m_contextMenu.addAction(action);
|
||||
}
|
||||
|
||||
hidePopup();
|
||||
emit requestWindowAutoHide(false);
|
||||
|
||||
m_contextMenu.exec(QCursor::pos());
|
||||
|
||||
onContextMenuAccepted();
|
||||
}
|
||||
|
||||
void SystemPluginItem::menuActionClicked(QAction *action)
|
||||
{
|
||||
invokedMenuItem(action->data().toString(), true);
|
||||
}
|
||||
|
||||
void SystemPluginItem::onContextMenuAccepted()
|
||||
{
|
||||
emit requestRefershWindowVisible();
|
||||
emit requestWindowAutoHide(true);
|
||||
}
|
||||
|
||||
void SystemPluginItem::updatePopupPosition()
|
||||
{
|
||||
Q_ASSERT(sender() == m_popupAdjustDelayTimer);
|
||||
|
||||
if (!m_popupShown || !PopupWindow->model())
|
||||
return;
|
||||
|
||||
if (PopupWindow->getContent() != m_lastPopupWidget.data())
|
||||
return popupWindowAccept();
|
||||
|
||||
const QPoint p = popupMarkPoint();
|
||||
PopupWindow->show(p, PopupWindow->model());
|
||||
}
|
||||
|
||||
void SystemPluginItem::onGSettingsChanged(const QString &key) {
|
||||
if (key != "enable") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_gsettings && m_gsettings->keys().contains("enable")) {
|
||||
const bool visible = m_gsettings->get("enable").toBool();
|
||||
setVisible(visible);
|
||||
emit itemVisibleChanged(visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemPluginItem::checkGSettingsControl() const
|
||||
{
|
||||
// 优先判断com.deepin.dde.dock.module.systemtray的control值是否为true(优先级更高),如果不为true,再判断每一个托盘对应的gsetting配置的control值
|
||||
bool isEnable = Utils::SettingValue("com.deepin.dde.dock.module.systemtray", QByteArray(), "control", false).toBool();
|
||||
return (isEnable || (m_gsettings && m_gsettings->get("control").toBool()));
|
||||
}
|
||||
|
||||
QPixmap SystemPluginItem::icon()
|
||||
{
|
||||
return QPixmap();
|
||||
}
|
118
frame/window/tray/widgets/systempluginitem.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: listenerri <listenerri@gmail.com>
|
||||
*
|
||||
* Maintainer: listenerri <listenerri@gmail.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 SYSTEMTRAYITEM_H
|
||||
#define SYSTEMTRAYITEM_H
|
||||
|
||||
#include "constants.h"
|
||||
#include "basetraywidget.h"
|
||||
#include "pluginsiteminterface.h"
|
||||
|
||||
#include <QGestureEvent>
|
||||
#include <QMenu>
|
||||
|
||||
class QGSettings;
|
||||
class Menu;
|
||||
class DockPopupWindow;
|
||||
|
||||
class SystemPluginItem : public BaseTrayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SystemPluginItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent = nullptr);
|
||||
~SystemPluginItem() override;
|
||||
|
||||
public:
|
||||
QString itemKeyForConfig() override;
|
||||
void updateIcon() override;
|
||||
void sendClick(uint8_t mouseButton, int x, int y) override;
|
||||
inline TrayType trayType() const override { return TrayType::SystemTray; }
|
||||
QPixmap icon() override;
|
||||
|
||||
QWidget *trayTipsWidget();
|
||||
QWidget *trayPopupApplet();
|
||||
const QString trayClickCommand();
|
||||
const QString contextMenu() const;
|
||||
void invokedMenuItem(const QString &menuId, const bool checked);
|
||||
|
||||
static void setDockPostion(const Dock::Position pos) { DockPosition = pos; }
|
||||
|
||||
QWidget *centralWidget() const;
|
||||
void detachPluginWidget();
|
||||
void showContextMenu();
|
||||
|
||||
void showPopupApplet(QWidget * const applet);
|
||||
void hidePopup();
|
||||
|
||||
signals:
|
||||
void itemVisibleChanged(bool visible);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
void enterEvent(QEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
protected:
|
||||
const QPoint popupMarkPoint() const;
|
||||
const QPoint topleftPoint() const;
|
||||
|
||||
void hideNonModel();
|
||||
void popupWindowAccept();
|
||||
|
||||
virtual void showPopupWindow(QWidget * const content, const bool model = false);
|
||||
virtual void showHoverTips();
|
||||
|
||||
bool checkAndResetTapHoldGestureState();
|
||||
virtual void gestureEvent(QGestureEvent *event);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onContextMenuAccepted();
|
||||
|
||||
private:
|
||||
void updatePopupPosition();
|
||||
void onGSettingsChanged(const QString &key);
|
||||
bool checkGSettingsControl() const;
|
||||
void menuActionClicked(QAction *action);
|
||||
|
||||
private:
|
||||
bool m_popupShown;
|
||||
bool m_tapAndHold;
|
||||
QMenu m_contextMenu;
|
||||
|
||||
PluginsItemInterface* m_pluginInter;
|
||||
QWidget *m_centralWidget;
|
||||
|
||||
QTimer *m_popupTipsDelayTimer;
|
||||
QTimer *m_popupAdjustDelayTimer;
|
||||
|
||||
QPointer<QWidget> m_lastPopupWidget;
|
||||
QString m_itemKey;
|
||||
|
||||
static Dock::Position DockPosition;
|
||||
static QPointer<DockPopupWindow> PopupWindow;
|
||||
const QGSettings* m_gsettings;
|
||||
};
|
||||
|
||||
#endif // SYSTEMTRAYITEM_H
|
621
frame/window/tray/widgets/xembedtrayitemwidget.cpp
Normal file
@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 "constants.h"
|
||||
#include "xembedtrayitemwidget.h"
|
||||
//#include "utils.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QPainter>
|
||||
#include <QX11Info>
|
||||
#include <QDebug>
|
||||
#include <QMouseEvent>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QMap>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <X11/Xregion.h>
|
||||
|
||||
#include <xcb/composite.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#define NORMAL_WINDOW_PROP_NAME "WM_CLASS"
|
||||
#define WINE_WINDOW_PROP_NAME "__wine_prefix"
|
||||
#define IS_WINE_WINDOW_BY_WM_CLASS "explorer.exe"
|
||||
|
||||
static const qreal iconSize = PLUGIN_ICON_MAX_SIZE;
|
||||
|
||||
const bool IS_WAYLAND_DISPLAY = !qgetenv("WAYLAND_DISPLAY").isEmpty();
|
||||
|
||||
// this static var hold all suffix of tray widget keys.
|
||||
// that is in order to fix can not show multiple trays provide by one application,
|
||||
// so only one property: AppName is not enough to identify all trays.
|
||||
// here we add a suffix for every tray to fix this problem.
|
||||
// the first suffix is 1, second is 2, etc.
|
||||
// NOTE: the first suffix will be omit when construct the key of tray widget.
|
||||
static QMap<QString, QMap<quint32, int>> AppWinidSuffixMap;
|
||||
|
||||
//using namespace Utils;
|
||||
|
||||
const QPoint rawXPosition(const QPoint &scaledPos)
|
||||
{
|
||||
QRect g = qApp->primaryScreen() ? qApp->primaryScreen()->geometry() : QRect();
|
||||
for (auto *screen : qApp->screens())
|
||||
{
|
||||
const QRect &sg = screen->geometry();
|
||||
if (sg.contains(scaledPos))
|
||||
{
|
||||
g = sg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return g.topLeft() + (scaledPos - g.topLeft()) * qApp->devicePixelRatio();
|
||||
}
|
||||
|
||||
void sni_cleanup_xcb_image(void *data)
|
||||
{
|
||||
xcb_image_destroy(static_cast<xcb_image_t*>(data));
|
||||
}
|
||||
|
||||
XEmbedTrayItemWidget::XEmbedTrayItemWidget(quint32 winId, xcb_connection_t *cnn, Display *disp, QWidget *parent)
|
||||
: BaseTrayWidget(parent)
|
||||
, m_windowId(winId)
|
||||
, m_appName(getAppNameForWindow(winId))
|
||||
, m_valid(true)
|
||||
, m_xcbCnn(cnn)
|
||||
, m_display(disp)
|
||||
{
|
||||
wrapWindow();
|
||||
setOwnerPID(getWindowPID(winId));
|
||||
|
||||
m_updateTimer = new QTimer(this);
|
||||
m_updateTimer->setInterval(100);
|
||||
m_updateTimer->setSingleShot(true);
|
||||
|
||||
m_sendHoverEvent = new QTimer(this);
|
||||
m_sendHoverEvent->setInterval(100);
|
||||
m_sendHoverEvent->setSingleShot(true);
|
||||
|
||||
connect(m_updateTimer, &QTimer::timeout, this, &XEmbedTrayItemWidget::refershIconImage);
|
||||
|
||||
setMouseTracking(true);
|
||||
connect(m_sendHoverEvent, &QTimer::timeout, this, &XEmbedTrayItemWidget::sendHoverEvent);
|
||||
|
||||
m_updateTimer->start();
|
||||
}
|
||||
|
||||
XEmbedTrayItemWidget::~XEmbedTrayItemWidget()
|
||||
{
|
||||
AppWinidSuffixMap[m_appName].remove(m_windowId);
|
||||
}
|
||||
|
||||
QString XEmbedTrayItemWidget::itemKeyForConfig()
|
||||
{
|
||||
return QString("window:%1").arg(getAppNameForWindow(m_windowId));
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::showEvent(QShowEvent *e)
|
||||
{
|
||||
QWidget::showEvent(e);
|
||||
|
||||
m_updateTimer->start();
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
if (!needShow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_image.isNull())
|
||||
return m_updateTimer->start();
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
//#ifdef QT_DEBUG
|
||||
// painter.fillRect(rect(), Qt::red);
|
||||
//#endif
|
||||
|
||||
const QRectF &rf = QRectF(rect());
|
||||
const QRectF &rfp = QRectF(m_image.rect());
|
||||
const QPointF &p = rf.center() - rfp.center() / m_image.devicePixelRatioF();
|
||||
painter.drawImage(p, m_image);
|
||||
|
||||
painter.end();
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
BaseTrayWidget::mouseMoveEvent(e);
|
||||
|
||||
// ignore the touchEvent
|
||||
if (e->source() == Qt::MouseEventSynthesizedByQt) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sendHoverEvent->start();
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::configContainerPosition()
|
||||
{
|
||||
auto c = IS_WAYLAND_DISPLAY ? m_xcbCnn : QX11Info::connection();
|
||||
if (!c) {
|
||||
qWarning() << "QX11Info::connection() is " << c;
|
||||
return;
|
||||
}
|
||||
|
||||
const QPoint p(rawXPosition(QCursor::pos()));
|
||||
|
||||
const uint32_t containerVals[4] = {uint32_t(p.x()), uint32_t(p.y()), 1, 1};
|
||||
xcb_configure_window(c, m_containerWid,
|
||||
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
||||
containerVals);
|
||||
|
||||
|
||||
// move the actual tray window to {0,0}, because tray icons from some wine
|
||||
// applications (QQ, TIM, etc...) may somehow moved to very long distance positions.
|
||||
const uint32_t trayVals[2] = { 0, 0 };
|
||||
xcb_configure_window(c, m_windowId, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, trayVals);
|
||||
|
||||
xcb_flush(c);
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::wrapWindow()
|
||||
{
|
||||
auto c = IS_WAYLAND_DISPLAY ? m_xcbCnn : QX11Info::connection();
|
||||
if (!c) {
|
||||
qWarning() << "QX11Info::connection() is " << c;
|
||||
return;
|
||||
}
|
||||
|
||||
auto cookie = xcb_get_geometry(c, m_windowId);
|
||||
xcb_get_geometry_reply_t *clientGeom(xcb_get_geometry_reply(c, cookie, Q_NULLPTR));
|
||||
if (!clientGeom) {
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
free(clientGeom);
|
||||
|
||||
//create a container window
|
||||
const auto ratio = devicePixelRatioF();
|
||||
auto screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
|
||||
m_containerWid = xcb_generate_id(c);
|
||||
uint32_t values[2];
|
||||
auto mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT;
|
||||
values[0] = ParentRelative; //draw a solid background so the embedded icon doesn't get garbage in it
|
||||
values[1] = true; //bypass wM
|
||||
xcb_create_window (c, /* connection */
|
||||
XCB_COPY_FROM_PARENT, /* depth */
|
||||
m_containerWid, /* window Id */
|
||||
screen->root, /* parent window */
|
||||
0, 0, /* x, y */
|
||||
iconSize * ratio, iconSize * ratio, /* width, height */
|
||||
0, /* border_width */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */
|
||||
screen->root_visual, /* visual */
|
||||
mask, values); /* masks */
|
||||
|
||||
/*
|
||||
We need the window to exist and be mapped otherwise the child won't render it's contents
|
||||
|
||||
We also need it to exist in the right place to get the clicks working as GTK will check sendEvent locations to see if our window is in the right place. So even though our contents are drawn via compositing we still put this window in the right place
|
||||
|
||||
We can't composite it away anything parented owned by the root window (apparently)
|
||||
Stack Under works in the non composited case, but it doesn't seem to work in kwin's composited case (probably need set relevant NETWM hint)
|
||||
|
||||
As a last resort set opacity to 0 just to make sure this container never appears
|
||||
*/
|
||||
// const uint32_t stackBelowData[] = {XCB_STACK_MODE_BELOW};
|
||||
// xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackBelowData);
|
||||
|
||||
if (!IS_WAYLAND_DISPLAY) {
|
||||
QWindow * win = QWindow::fromWinId(m_containerWid);
|
||||
win->setOpacity(0);
|
||||
} else {
|
||||
const char* opacityName = "_NET_WM_WINDOW_OPACITY\0";
|
||||
xcb_intern_atom_cookie_t opacityCookie = xcb_intern_atom(c, false, strlen(opacityName), opacityName);
|
||||
xcb_intern_atom_reply_t *opacityReply = xcb_intern_atom_reply(c, opacityCookie, 0);
|
||||
xcb_atom_t opacityAtom = opacityReply->atom;
|
||||
quint32 opacity = 10;
|
||||
xcb_change_property(c,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
m_containerWid,
|
||||
opacityAtom,
|
||||
XCB_ATOM_CARDINAL,
|
||||
32,
|
||||
1,
|
||||
(uchar *)&opacity);
|
||||
}
|
||||
|
||||
// setX11PassMouseEvent(true);
|
||||
|
||||
xcb_flush(c);
|
||||
|
||||
xcb_map_window(c, m_containerWid);
|
||||
|
||||
xcb_reparent_window(c, m_windowId,
|
||||
m_containerWid,
|
||||
0, 0);
|
||||
|
||||
/*
|
||||
* Render the embedded window offscreen
|
||||
*/
|
||||
xcb_composite_redirect_window(c, m_windowId, XCB_COMPOSITE_REDIRECT_MANUAL);
|
||||
|
||||
|
||||
/* we grab the window, but also make sure it's automatically reparented back
|
||||
* to the root window if we should die.
|
||||
*/
|
||||
xcb_change_save_set(c, XCB_SET_MODE_INSERT, m_windowId);
|
||||
|
||||
//tell client we're embedding it
|
||||
// xembed_message_send(m_windowId, XEMBED_EMBEDDED_NOTIFY, m_containerWid, 0, 0);
|
||||
|
||||
//move window we're embedding
|
||||
/*
|
||||
const uint32_t windowMoveConfigVals[2] = { 0, 0 };
|
||||
xcb_configure_window(c, m_windowId,
|
||||
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
|
||||
windowMoveCentially quitting the application. Returns onfigVals);
|
||||
*/
|
||||
|
||||
//if the window is a clearly stupid size resize to be something sensible
|
||||
//this is needed as chormium and such when resized just fill the icon with transparent space and only draw in the middle
|
||||
//however spotify does need this as by default the window size is 900px wide.
|
||||
//use an artbitrary heuristic to make sure icons are always sensible
|
||||
// if (clientGeom->width > iconSize || clientGeom->height > iconSize )
|
||||
{
|
||||
const uint32_t windowMoveConfigVals[2] = { uint32_t(iconSize * ratio), uint32_t(iconSize * ratio) };
|
||||
xcb_configure_window(c, m_windowId,
|
||||
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
||||
windowMoveConfigVals);
|
||||
}
|
||||
|
||||
//show the embedded window otherwise nothing happens
|
||||
xcb_map_window(c, m_windowId);
|
||||
|
||||
// xcb_clear_area(c, 0, m_windowId, 0, 0, qMin(clientGeom->width, iconSize), qMin(clientGeom->height, iconSize));
|
||||
|
||||
xcb_flush(c);
|
||||
// setWindowOnTop(false);
|
||||
setWindowOnTop(true);
|
||||
setX11PassMouseEvent(true);
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::sendHoverEvent()
|
||||
{
|
||||
if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fake enter event
|
||||
const QPoint p(rawXPosition(QCursor::pos()));
|
||||
configContainerPosition();
|
||||
setX11PassMouseEvent(false);
|
||||
setWindowOnTop(true);
|
||||
Display *display = IS_WAYLAND_DISPLAY ? m_display : QX11Info::display();
|
||||
if (display) {
|
||||
XTestFakeMotionEvent(display, 0, p.x(), p.y(), CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
|
||||
QTimer::singleShot(100, this, [=] { setX11PassMouseEvent(true); });
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::updateIcon()
|
||||
{
|
||||
// if (!isVisible() && !m_active)
|
||||
// return;
|
||||
|
||||
m_updateTimer->start();
|
||||
}
|
||||
|
||||
//void TrayWidget::hideIcon()
|
||||
//{
|
||||
// auto c = QX11Info::connection();
|
||||
|
||||
// const uint32_t stackAboveData[] = {XCB_STACK_MODE_BELOW};
|
||||
// xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData);
|
||||
|
||||
// const uint32_t windowMoveConfigVals[2] = {0, 0};
|
||||
// xcb_configure_window(c, m_containerWid,
|
||||
// XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
|
||||
// windowMoveConfigVals);
|
||||
|
||||
// hide();
|
||||
//}
|
||||
|
||||
void XEmbedTrayItemWidget::sendClick(uint8_t mouseButton, int x, int y)
|
||||
{
|
||||
if (isBadWindow())
|
||||
return;
|
||||
|
||||
m_sendHoverEvent->stop();
|
||||
|
||||
const QPoint p(rawXPosition(QPoint(x, y)));
|
||||
configContainerPosition();
|
||||
setX11PassMouseEvent(false);
|
||||
setWindowOnTop(true);
|
||||
|
||||
Display *display = IS_WAYLAND_DISPLAY ? m_display : QX11Info::display();
|
||||
XTestFakeMotionEvent(display, 0, p.x(), p.y(), CurrentTime);
|
||||
XFlush(display);
|
||||
XTestFakeButtonEvent(display, mouseButton, true, CurrentTime);
|
||||
XFlush(display);
|
||||
XTestFakeButtonEvent(display, mouseButton, false, CurrentTime);
|
||||
XFlush(display);
|
||||
QTimer::singleShot(100, this, [=] { setX11PassMouseEvent(true); });
|
||||
}
|
||||
|
||||
// NOTE: WM_NAME may can not obtain successfully
|
||||
QString XEmbedTrayItemWidget::getWindowProperty(quint32 winId, QString propName)
|
||||
{
|
||||
const auto display = IS_WAYLAND_DISPLAY ? XOpenDisplay(nullptr) : QX11Info::display();
|
||||
if (!display) {
|
||||
qWarning() << "QX11Info::display() is " << display;
|
||||
return QString();
|
||||
}
|
||||
|
||||
Atom atom_prop = XInternAtom(display, propName.toLocal8Bit(), true);
|
||||
if (!atom_prop) {
|
||||
qDebug() << "Error: get window property failed, invalid property atom";
|
||||
return QString();
|
||||
}
|
||||
|
||||
Atom actual_type_return;
|
||||
int actual_format_return;
|
||||
unsigned long nitems_return;
|
||||
unsigned long bytes_after_return;
|
||||
unsigned char *prop_return;
|
||||
|
||||
int r = XGetWindowProperty(display, winId, atom_prop, 0, 100, false, AnyPropertyType,
|
||||
&actual_type_return, &actual_format_return, &nitems_return,
|
||||
&bytes_after_return, &prop_return);
|
||||
|
||||
Q_UNUSED(r);
|
||||
|
||||
// qDebug() << (r == Success)
|
||||
// << actual_type_return
|
||||
// << actual_format_return
|
||||
// << nitems_return
|
||||
// << bytes_after_return
|
||||
// << QString::fromLocal8Bit((char*)prop_return);
|
||||
if (IS_WAYLAND_DISPLAY)
|
||||
XCloseDisplay(display);
|
||||
|
||||
return QString::fromLocal8Bit((char*)prop_return);
|
||||
}
|
||||
|
||||
QString XEmbedTrayItemWidget::toXEmbedKey(quint32 winId)
|
||||
{
|
||||
return QString("window:%1").arg(winId);
|
||||
}
|
||||
|
||||
bool XEmbedTrayItemWidget::isXEmbedKey(const QString &itemKey)
|
||||
{
|
||||
return itemKey.startsWith("window:");
|
||||
}
|
||||
|
||||
QPixmap XEmbedTrayItemWidget::icon()
|
||||
{
|
||||
return QPixmap::fromImage(m_image);
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::refershIconImage()
|
||||
{
|
||||
const auto ratio = devicePixelRatioF();
|
||||
auto c = IS_WAYLAND_DISPLAY ? m_xcbCnn : QX11Info::connection();
|
||||
if (!c) {
|
||||
qWarning() << "QX11Info::connection() is " << c;
|
||||
return;
|
||||
}
|
||||
|
||||
auto cookie = xcb_get_geometry(c, m_windowId);
|
||||
xcb_get_geometry_reply_t *geom(xcb_get_geometry_reply(c, cookie, Q_NULLPTR));
|
||||
if (!geom) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_expose_event_t expose;
|
||||
expose.response_type = XCB_EXPOSE;
|
||||
expose.window = m_containerWid;
|
||||
expose.x = 0;
|
||||
expose.y = 0;
|
||||
expose.width = iconSize * ratio;
|
||||
expose.height = iconSize * ratio;
|
||||
xcb_send_event_checked(c, false, m_containerWid, XCB_EVENT_MASK_VISIBILITY_CHANGE, reinterpret_cast<char *>(&expose));
|
||||
xcb_flush(c);
|
||||
|
||||
xcb_image_t *image = xcb_image_get(c, m_windowId, 0, 0, geom->width, geom->height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
|
||||
if (!image) {
|
||||
free(geom);
|
||||
return;
|
||||
}
|
||||
|
||||
QImage qimage(image->data, image->width, image->height, image->stride, QImage::Format_ARGB32, sni_cleanup_xcb_image, image);
|
||||
if (qimage.isNull()) {
|
||||
free(geom);
|
||||
return;
|
||||
}
|
||||
|
||||
m_image = qimage.scaled(iconSize * ratio, iconSize * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
m_image.setDevicePixelRatio(ratio);
|
||||
|
||||
update();
|
||||
Q_EMIT iconChanged();
|
||||
|
||||
if (!isVisible()) {
|
||||
Q_EMIT needAttention();
|
||||
}
|
||||
}
|
||||
|
||||
QString XEmbedTrayItemWidget::getAppNameForWindow(quint32 winId)
|
||||
{
|
||||
QString appName;
|
||||
do {
|
||||
// is normal application
|
||||
appName = getWindowProperty(winId, NORMAL_WINDOW_PROP_NAME);
|
||||
if (!appName.isEmpty() && appName != IS_WINE_WINDOW_BY_WM_CLASS) {
|
||||
break;
|
||||
}
|
||||
|
||||
// is wine application
|
||||
appName = getWindowProperty(winId, WINE_WINDOW_PROP_NAME).split("/").last();
|
||||
if (!appName.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// fallback to window id
|
||||
appName = QString::number(winId);
|
||||
} while (false);
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
//int XEmbedTrayWidget::getTrayWidgetKeySuffix(const QString &appName, quint32 winId)
|
||||
//{
|
||||
// int suffix = AppWinidSuffixMap.value(appName).value(winId, 0);
|
||||
|
||||
// // return the exist suffix
|
||||
// if (suffix != 0) {
|
||||
// return suffix;
|
||||
// }
|
||||
|
||||
// // it is the first window for this application
|
||||
// if (!AppWinidSuffixMap.contains(appName)) {
|
||||
// QMap<quint32, int> winIdSuffixMap;
|
||||
// winIdSuffixMap.insert(winId, 1);
|
||||
// AppWinidSuffixMap.insert(appName, winIdSuffixMap);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// QMap<quint32, int> subMap = AppWinidSuffixMap.value(appName);
|
||||
// QList<int> suffixList = subMap.values();
|
||||
|
||||
// // suffix will never be 0
|
||||
// suffixList.removeAll(0);
|
||||
// std::sort(suffixList.begin(), suffixList.end());
|
||||
|
||||
// // get the minimum of useable suffix
|
||||
// int index = 0;
|
||||
// for (; index < suffixList.size(); ++index) {
|
||||
// if (suffixList.at(index) != index + 1) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// suffix = index + 1;
|
||||
|
||||
// subMap.insert(winId, suffix);
|
||||
// AppWinidSuffixMap.insert(appName, subMap);
|
||||
|
||||
// return suffix;
|
||||
//}
|
||||
|
||||
void XEmbedTrayItemWidget::setX11PassMouseEvent(const bool pass)
|
||||
{
|
||||
if (IS_WAYLAND_DISPLAY) {
|
||||
//会导致wayland下鼠标穿透到桌面,所以直接return掉
|
||||
return;
|
||||
}
|
||||
|
||||
if (pass)
|
||||
{
|
||||
XShapeCombineRectangles(QX11Info::display(), m_containerWid, ShapeBounding, 0, 0, nullptr, 0, ShapeSet, YXBanded);
|
||||
XShapeCombineRectangles(QX11Info::display(), m_containerWid, ShapeInput, 0, 0, nullptr, 0, ShapeSet, YXBanded);
|
||||
}
|
||||
else
|
||||
{
|
||||
XRectangle rectangle;
|
||||
rectangle.x = 0;
|
||||
rectangle.y = 0;
|
||||
rectangle.width = 1;
|
||||
rectangle.height = 1;
|
||||
|
||||
XShapeCombineRectangles(QX11Info::display(), m_containerWid, ShapeBounding, 0, 0, &rectangle, 1, ShapeSet, YXBanded);
|
||||
XShapeCombineRectangles(QX11Info::display(), m_containerWid, ShapeInput, 0, 0, &rectangle, 1, ShapeSet, YXBanded);
|
||||
}
|
||||
|
||||
XFlush(QX11Info::display());
|
||||
}
|
||||
|
||||
void XEmbedTrayItemWidget::setWindowOnTop(const bool top)
|
||||
{
|
||||
auto c = IS_WAYLAND_DISPLAY ? m_xcbCnn : QX11Info::connection();
|
||||
if (!c) {
|
||||
qWarning() << "QX11Info::connection() is " << c;
|
||||
return;
|
||||
}
|
||||
const uint32_t stackAboveData[] = {top ? XCB_STACK_MODE_ABOVE : XCB_STACK_MODE_BELOW};
|
||||
xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData);
|
||||
xcb_flush(c);
|
||||
}
|
||||
|
||||
bool XEmbedTrayItemWidget::isBadWindow()
|
||||
{
|
||||
auto c = IS_WAYLAND_DISPLAY ? m_xcbCnn : QX11Info::connection();
|
||||
|
||||
auto cookie = xcb_get_geometry(c, m_windowId);
|
||||
xcb_get_geometry_reply_t *clientGeom = xcb_get_geometry_reply(c, cookie, Q_NULLPTR);
|
||||
bool result = clientGeom ? false : true;
|
||||
|
||||
free(clientGeom);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint XEmbedTrayItemWidget::getWindowPID(uint winId)
|
||||
{
|
||||
const auto display = IS_WAYLAND_DISPLAY ? XOpenDisplay(nullptr) : QX11Info::display();
|
||||
if (!display) {
|
||||
qWarning() << "QX11Info::connection() is " << display;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Atom nameAtom = XInternAtom(display, "_NET_WM_PID", 1);
|
||||
Atom type;
|
||||
int format, status;
|
||||
|
||||
unsigned long nitems, after;
|
||||
unsigned char *data;
|
||||
unsigned int pid = 0;
|
||||
|
||||
status = XGetWindowProperty(display, winId, nameAtom, 0, 1024, 0,
|
||||
XInternAtom(display, "CARDINAL", 0), &type, &format, &nitems, &after, &data);
|
||||
if (status == Success && data) {
|
||||
pid = *((uint*)data);
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
if (IS_WAYLAND_DISPLAY)
|
||||
XCloseDisplay(display);
|
||||
|
||||
return pid;
|
||||
}
|
84
frame/window/tray/widgets/xembedtrayitemwidget.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||||
*
|
||||
* Author: sbw <sbw@sbw.so>
|
||||
*
|
||||
* Maintainer: sbw <sbw@sbw.so>
|
||||
*
|
||||
* 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 XEMBEDTRAYWIDGET_H
|
||||
#define XEMBEDTRAYWIDGET_H
|
||||
|
||||
#include "basetraywidget.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
typedef struct _XDisplay Display;
|
||||
|
||||
class XEmbedTrayItemWidget : public BaseTrayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit XEmbedTrayItemWidget(quint32 winId, xcb_connection_t *cnn = nullptr, Display *disp = nullptr, QWidget *parent = nullptr);
|
||||
~XEmbedTrayItemWidget() override;
|
||||
|
||||
QString itemKeyForConfig() override;
|
||||
void updateIcon() override;
|
||||
void sendClick(uint8_t mouseButton, int x, int y) override;
|
||||
|
||||
static QString getWindowProperty(quint32 winId, QString propName);
|
||||
static QString toXEmbedKey(quint32 winId);
|
||||
static uint getWindowPID(quint32 winId);
|
||||
static bool isXEmbedKey(const QString &itemKey);
|
||||
virtual bool isValid() override {return m_valid;}
|
||||
QPixmap icon() override;
|
||||
|
||||
private:
|
||||
void showEvent(QShowEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void configContainerPosition();
|
||||
|
||||
void wrapWindow();
|
||||
void sendHoverEvent();
|
||||
void refershIconImage();
|
||||
|
||||
static QString getAppNameForWindow(quint32 winId);
|
||||
|
||||
private slots:
|
||||
void setX11PassMouseEvent(const bool pass);
|
||||
void setWindowOnTop(const bool top);
|
||||
bool isBadWindow();
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
WId m_windowId;
|
||||
WId m_containerWid;
|
||||
QImage m_image;
|
||||
QString m_appName;
|
||||
|
||||
QTimer *m_updateTimer;
|
||||
QTimer *m_sendHoverEvent;
|
||||
bool m_valid;
|
||||
xcb_connection_t *m_xcbCnn;
|
||||
Display* m_display;
|
||||
};
|
||||
|
||||
#endif // XEMBEDTRAYWIDGET_H
|
381
frame/window/traymanagerwindow.cpp
Normal file
@ -0,0 +1,381 @@
|
||||
#include "traymanagerwindow.h"
|
||||
#include "quickpluginwindow.h"
|
||||
#include "tray_gridview.h"
|
||||
#include "tray_delegate.h"
|
||||
#include "tray_model.h"
|
||||
#include "constants.h"
|
||||
#include "quicksettingcontainer.h"
|
||||
#include "systempluginwindow.h"
|
||||
#include "datetimedisplayer.h"
|
||||
|
||||
#include <DGuiApplicationHelper>
|
||||
#include <DRegionMonitor>
|
||||
|
||||
#include <QDropEvent>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMimeData>
|
||||
#include <QDBusConnection>
|
||||
#include <QPainter>
|
||||
|
||||
#define MAXFIXEDSIZE 999999
|
||||
#define CRITLCALHEIGHT 56
|
||||
|
||||
TrayManagerWindow::TrayManagerWindow(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_appPluginDatetimeWidget(new DBlurEffectWidget(this))
|
||||
, m_systemPluginWidget(new SystemPluginWindow(this))
|
||||
, m_appPluginWidget(new QWidget(m_appPluginDatetimeWidget))
|
||||
, m_quickIconWidget(new QuickPluginWindow(m_appPluginWidget))
|
||||
, m_dateTimeWidget(new DateTimeDisplayer(m_appPluginDatetimeWidget))
|
||||
, m_appPluginLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight, this))
|
||||
, m_appDatetimeLayout(new QBoxLayout(QBoxLayout::Direction::TopToBottom, this))
|
||||
, m_mainLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight, this))
|
||||
, m_trayView(new TrayGridView(this))
|
||||
, m_model(new TrayModel(m_trayView, false, true))
|
||||
, m_postion(Dock::Position::Bottom)
|
||||
{
|
||||
initUi();
|
||||
initConnection();
|
||||
|
||||
setAcceptDrops(true);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
TrayManagerWindow::~TrayManagerWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void TrayManagerWindow::setPositon(Dock::Position position)
|
||||
{
|
||||
if (m_postion == position)
|
||||
return;
|
||||
|
||||
m_postion = position;
|
||||
QModelIndex index = m_model->index(0, 0);
|
||||
m_trayView->closePersistentEditor(index);
|
||||
TrayDelegate *delegate = static_cast<TrayDelegate *>(m_trayView->itemDelegate());
|
||||
delegate->setPositon(position);
|
||||
m_trayView->openPersistentEditor(index);
|
||||
|
||||
m_quickIconWidget->setPositon(position);
|
||||
m_dateTimeWidget->setPositon(position);
|
||||
m_systemPluginWidget->setPositon(position);
|
||||
|
||||
QTimer::singleShot(0, this, [ this ]{
|
||||
if (showSingleRow())
|
||||
resetSingleDirection();
|
||||
else
|
||||
resetMultiDirection();
|
||||
|
||||
resetChildWidgetSize();
|
||||
});
|
||||
}
|
||||
|
||||
int TrayManagerWindow::appDatetimeSize()
|
||||
{
|
||||
int count = m_trayView->model()->rowCount();
|
||||
if (m_postion == Dock::Position::Top || m_postion == Dock::Position::Bottom) {
|
||||
QMargins m = m_appDatetimeLayout->contentsMargins();
|
||||
int trayWidth = count * ITEM_SIZE + m_trayView->spacing() * (count - 1) + 5;
|
||||
int topWidth = trayWidth + m_quickIconWidget->suitableSize().width() + m.left() + m.right() + m_appPluginLayout->spacing();
|
||||
int spacing = m.left() + m.right() + m_appPluginLayout->spacing();
|
||||
if (m_appDatetimeLayout->direction() == QBoxLayout::Direction::LeftToRight)
|
||||
return topWidth + m_appDatetimeLayout->spacing() + m_dateTimeWidget->suitableSize().width() + m_appDatetimeLayout->spacing() + 10;
|
||||
|
||||
int bottomWidth = m_dateTimeWidget->suitableSize().width();
|
||||
return (topWidth > bottomWidth ? topWidth : bottomWidth) + m_appDatetimeLayout->spacing() + spacing + 10;
|
||||
}
|
||||
|
||||
int trayHeight = count * ITEM_SIZE + m_trayView->spacing() * (count - 1) + 5;
|
||||
int datetimeHeight = m_dateTimeWidget->suitableSize().height();
|
||||
QMargins m = m_appDatetimeLayout->contentsMargins();
|
||||
int traypluginHeight = trayHeight + m_quickIconWidget->suitableSize().height() + m.top() + m.bottom() + m_appPluginLayout->spacing();
|
||||
if (m_appDatetimeLayout->direction() == QBoxLayout::Direction::TopToBottom)
|
||||
return traypluginHeight + m_appDatetimeLayout->spacing() + m_dateTimeWidget->suitableSize().height() + 10;
|
||||
return (traypluginHeight > datetimeHeight ? traypluginHeight : datetimeHeight) + 10;
|
||||
}
|
||||
|
||||
QSize TrayManagerWindow::suitableSize()
|
||||
{
|
||||
QMargins m = m_mainLayout->contentsMargins();
|
||||
if (m_postion == Dock::Position::Top || m_postion == Dock::Position::Bottom) {
|
||||
return QSize(appDatetimeSize() + m_appDatetimeLayout->spacing() +
|
||||
m_systemPluginWidget->suitableSize().width() + m_mainLayout->spacing() +
|
||||
m.left() + m.right(), height());
|
||||
}
|
||||
|
||||
return QSize(width(), appDatetimeSize() + m_appDatetimeLayout->spacing() +
|
||||
m_systemPluginWidget->suitableSize().height() + m_mainLayout->spacing() +
|
||||
m.top() + m.bottom());
|
||||
}
|
||||
|
||||
void TrayManagerWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
if (showSingleRow())
|
||||
resetSingleDirection();
|
||||
else
|
||||
resetMultiDirection();
|
||||
|
||||
resetChildWidgetSize();
|
||||
}
|
||||
|
||||
void TrayManagerWindow::initUi()
|
||||
{
|
||||
TrayDelegate *delegate = new TrayDelegate(m_trayView);
|
||||
m_trayView->setModel(m_model);
|
||||
m_trayView->setItemDelegate(delegate);
|
||||
|
||||
WinInfo info;
|
||||
info.type = TrayIconType::EXPANDICON;
|
||||
m_model->addRow(info);
|
||||
m_trayView->openPersistentEditor(m_model->index(0, 0));
|
||||
|
||||
// 左侧的区域,包括应用托盘插件和下方的日期时间区域
|
||||
m_appPluginDatetimeWidget->setBlurRectXRadius(10);
|
||||
m_appPluginDatetimeWidget->setBlurRectYRadius(10);
|
||||
m_appPluginDatetimeWidget->setMaskAlpha(uint8_t(0.1 * 255));
|
||||
m_appPluginDatetimeWidget->installEventFilter(this);
|
||||
|
||||
m_appPluginLayout->setSpacing(0);
|
||||
m_appPluginWidget->setLayout(m_appPluginLayout);
|
||||
m_appPluginLayout->addWidget(m_trayView);
|
||||
m_appPluginLayout->addWidget(m_quickIconWidget);
|
||||
|
||||
m_appPluginDatetimeWidget->setLayout(m_appDatetimeLayout);
|
||||
m_appDatetimeLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_appDatetimeLayout->setSpacing(3);
|
||||
m_appDatetimeLayout->addWidget(m_appPluginWidget);
|
||||
m_appDatetimeLayout->addWidget(m_dateTimeWidget);
|
||||
|
||||
m_systemPluginWidget->setBlurRectXRadius(10);
|
||||
m_systemPluginWidget->setBlurRectYRadius(10);
|
||||
m_systemPluginWidget->installEventFilter(this);
|
||||
m_systemPluginWidget->setMaskAlpha(uint8_t(0.1 * 255));
|
||||
|
||||
setLayout(m_mainLayout);
|
||||
m_mainLayout->setContentsMargins(8, 8, 8, 8);
|
||||
m_mainLayout->setSpacing(10);
|
||||
m_mainLayout->addWidget(m_appPluginDatetimeWidget);
|
||||
m_mainLayout->addWidget(m_systemPluginWidget);
|
||||
}
|
||||
|
||||
void TrayManagerWindow::initConnection()
|
||||
{
|
||||
connect(m_trayView, &TrayGridView::requestRemove, m_model, &TrayModel::removeRow);
|
||||
connect(m_trayView, &TrayGridView::rowCountChanged, this, &TrayManagerWindow::sizeChanged);
|
||||
connect(m_quickIconWidget, &QuickPluginWindow::itemCountChanged, this, [ this ] {
|
||||
m_quickIconWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
if (m_postion == Dock::Position::Top || m_postion == Dock::Position::Bottom)
|
||||
m_quickIconWidget->setFixedWidth(m_quickIconWidget->suitableSize().width());
|
||||
else
|
||||
m_quickIconWidget->setFixedHeight(m_quickIconWidget->suitableSize().height());
|
||||
|
||||
Q_EMIT sizeChanged();
|
||||
});
|
||||
|
||||
connect(m_systemPluginWidget, &SystemPluginWindow::pluginSizeChanged, this, [ this ] {
|
||||
m_systemPluginWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
if (m_postion == Dock::Position::Top || m_postion == Dock::Position::Bottom)
|
||||
m_systemPluginWidget->setFixedWidth(m_systemPluginWidget->suitableSize().width());
|
||||
else
|
||||
m_systemPluginWidget->setFixedHeight(m_systemPluginWidget->suitableSize().height());
|
||||
|
||||
Q_EMIT sizeChanged();
|
||||
});
|
||||
|
||||
TrayDelegate *trayDelegate = static_cast<TrayDelegate *>(m_trayView->itemDelegate());
|
||||
connect(trayDelegate, &TrayDelegate::visibleChanged, this, [ this ](const QModelIndex &index, bool visible) {
|
||||
m_trayView->setRowHidden(index.row(), !visible);
|
||||
resetChildWidgetSize();
|
||||
Q_EMIT sizeChanged();
|
||||
});
|
||||
|
||||
connect(m_trayView, &TrayGridView::dragLeaved, trayDelegate, [ trayDelegate ]{
|
||||
Q_EMIT trayDelegate->requestDrag(true);
|
||||
});
|
||||
connect(m_trayView, &TrayGridView::dragEntered, trayDelegate, [ trayDelegate ]{
|
||||
Q_EMIT trayDelegate->requestDrag(false);
|
||||
});
|
||||
connect(m_model, &TrayModel::requestUpdateWidget, this, [ this ](const QList<int> &idxs) {
|
||||
for (int i = 0; i < idxs.size(); i++) {
|
||||
int idx = idxs[i];
|
||||
if (idx < m_model->rowCount()) {
|
||||
QModelIndex index = m_model->index(idx);
|
||||
m_trayView->closePersistentEditor(index);
|
||||
m_trayView->openPersistentEditor(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_trayView->installEventFilter(this);
|
||||
m_quickIconWidget->installEventFilter(this);
|
||||
installEventFilter(this);
|
||||
QMetaObject::invokeMethod(this, &TrayManagerWindow::resetChildWidgetSize, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool TrayManagerWindow::showSingleRow()
|
||||
{
|
||||
if (m_postion == Dock::Position::Top || m_postion == Dock::Position::Bottom)
|
||||
return height() < CRITLCALHEIGHT;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrayManagerWindow::resetChildWidgetSize()
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < m_model->rowCount(); i++) {
|
||||
if (!m_trayView->isRowHidden(i))
|
||||
count++;
|
||||
}
|
||||
|
||||
switch (m_postion) {
|
||||
case Dock::Position::Top:
|
||||
case Dock::Position::Bottom: {
|
||||
int trayWidth = count * ITEM_SIZE + m_trayView->spacing() * (count - 1) + 5;
|
||||
QMargins m = m_appPluginLayout->contentsMargins();
|
||||
m_appPluginDatetimeWidget->setFixedHeight(QWIDGETSIZE_MAX);// 取消固定高度显示
|
||||
if (m_appDatetimeLayout->direction() == QBoxLayout::Direction::LeftToRight) {
|
||||
// 单行显示
|
||||
int trayHeight = m_appPluginDatetimeWidget->height() - m.top() - m.bottom();
|
||||
m_trayView->setFixedSize(trayWidth, trayHeight);
|
||||
m_quickIconWidget->setFixedSize(m_quickIconWidget->suitableSize().width(), trayHeight);
|
||||
m_dateTimeWidget->setFixedSize(m_dateTimeWidget->suitableSize().width(), trayHeight);
|
||||
} else {
|
||||
// 多行显示
|
||||
int trayHeight = m_appPluginDatetimeWidget->height() / 2 - m.top() - m.bottom();
|
||||
m_trayView->setFixedSize(trayWidth, trayHeight);
|
||||
m_quickIconWidget->setFixedSize(m_quickIconWidget->suitableSize().width(), trayHeight);
|
||||
m_dateTimeWidget->setFixedSize(m_dateTimeWidget->suitableSize().width(), m_appPluginDatetimeWidget->height() / 2);
|
||||
}
|
||||
m_appPluginDatetimeWidget->setFixedWidth(appDatetimeSize());
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left:
|
||||
case Dock::Position::Right: {
|
||||
int trayHeight = count * ITEM_SIZE + m_trayView->spacing() * (count - 1) + 5;
|
||||
int quickAreaHeight = m_quickIconWidget->suitableSize().height();
|
||||
QMargins m = m_appPluginLayout->contentsMargins();
|
||||
m_appPluginDatetimeWidget->setFixedWidth(QWIDGETSIZE_MAX);// 取消固定宽度显示
|
||||
if (m_appDatetimeLayout->direction() == QBoxLayout::Direction::TopToBottom) {
|
||||
// 宽度较小的情况下,显示一列
|
||||
int datetimeHeight = m_dateTimeWidget->suitableSize().height();
|
||||
int sizeWidth = m_appPluginDatetimeWidget->width() - m.left() - m.right();
|
||||
m_trayView->setFixedSize(sizeWidth, trayHeight);
|
||||
m_quickIconWidget->setFixedSize(sizeWidth, quickAreaHeight);
|
||||
m_dateTimeWidget->setFixedSize(sizeWidth, datetimeHeight);
|
||||
} else {
|
||||
// 显示两列
|
||||
int trayWidth = m_appPluginDatetimeWidget->width() / 2 - m.left() - m.right();
|
||||
m_trayView->setFixedSize(trayWidth, trayHeight);
|
||||
m_quickIconWidget->setFixedSize(trayWidth, quickAreaHeight);
|
||||
m_dateTimeWidget->setFixedSize(m_appPluginDatetimeWidget->width() / 2, m_dateTimeWidget->suitableSize().height());
|
||||
}
|
||||
m_appPluginDatetimeWidget->setFixedHeight(appDatetimeSize());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayManagerWindow::resetSingleDirection()
|
||||
{
|
||||
switch (m_postion) {
|
||||
case Dock::Position::Top: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
// 应用和时间在一行显示
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appPluginLayout->setContentsMargins(2, 2, 2, 4);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Bottom: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appPluginLayout->setContentsMargins(2, 4, 2, 2);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appPluginLayout->setContentsMargins(2, 2, 4, 2);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Right: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appPluginLayout->setContentsMargins(4, 2, 2, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayManagerWindow::resetMultiDirection()
|
||||
{
|
||||
switch (m_postion) {
|
||||
case Dock::Position::Top: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::BottomToTop);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appPluginLayout->setContentsMargins(2, 2, 2, 4);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Bottom: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
|
||||
m_appPluginLayout->setContentsMargins(2, 4, 2, 2);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Left: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appPluginLayout->setContentsMargins(2, 2, 4, 2);
|
||||
break;
|
||||
}
|
||||
case Dock::Position::Right: {
|
||||
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appDatetimeLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
|
||||
m_appPluginLayout->setContentsMargins(4, 2, 2, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayManagerWindow::dragEnterEvent(QDragEnterEvent *e)
|
||||
{
|
||||
e->setDropAction(Qt::CopyAction);
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void TrayManagerWindow::dragMoveEvent(QDragMoveEvent *e)
|
||||
{
|
||||
e->setDropAction(Qt::CopyAction);
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void TrayManagerWindow::dropEvent(QDropEvent *e)
|
||||
{
|
||||
CustomMimeData *mimeData = const_cast<CustomMimeData *>(qobject_cast<const CustomMimeData *>(e->mimeData()));
|
||||
if (!mimeData)
|
||||
return;
|
||||
|
||||
if (e->source() == this)
|
||||
return;
|
||||
|
||||
QuickSettingItem *pluginItem = static_cast<QuickSettingItem *>(mimeData->data());
|
||||
if (pluginItem)
|
||||
m_quickIconWidget->addPlugin(pluginItem);
|
||||
}
|
||||
|
||||
void TrayManagerWindow::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
}
|
72
frame/window/traymanagerwindow.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef TRAYMANAGERWINDOW_H
|
||||
#define TRAYMANAGERWINDOW_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <com_deepin_daemon_timedate.h>
|
||||
|
||||
namespace Dtk { namespace Gui { class DRegionMonitor; };
|
||||
namespace Widget { class DBlurEffectWidget; } }
|
||||
|
||||
using namespace Dtk::Widget;
|
||||
|
||||
using Timedate = com::deepin::daemon::Timedate;
|
||||
|
||||
class QuickPluginWindow;
|
||||
class QBoxLayout;
|
||||
class TrayGridView;
|
||||
class TrayModel;
|
||||
class SystemPluginWindow;
|
||||
class QLabel;
|
||||
class QDropEvent;
|
||||
class DateTimeDisplayer;
|
||||
|
||||
class TrayManagerWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void sizeChanged();
|
||||
|
||||
public:
|
||||
explicit TrayManagerWindow(QWidget *parent = nullptr);
|
||||
~TrayManagerWindow() override;
|
||||
void setPositon(Dock::Position position);
|
||||
QSize suitableSize();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
void initUi();
|
||||
void initConnection();
|
||||
|
||||
void resetChildWidgetSize();
|
||||
void resetMultiDirection();
|
||||
void resetSingleDirection();
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *e) override;
|
||||
void dragMoveEvent(QDragMoveEvent *e) override;
|
||||
void dropEvent(QDropEvent *e) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
|
||||
bool showSingleRow();
|
||||
int appDatetimeSize();
|
||||
|
||||
private:
|
||||
DBlurEffectWidget *m_appPluginDatetimeWidget;
|
||||
SystemPluginWindow *m_systemPluginWidget;
|
||||
QWidget *m_appPluginWidget;
|
||||
QuickPluginWindow *m_quickIconWidget;
|
||||
DateTimeDisplayer *m_dateTimeWidget;
|
||||
QBoxLayout *m_appPluginLayout;
|
||||
QBoxLayout *m_appDatetimeLayout;
|
||||
QBoxLayout *m_mainLayout;
|
||||
TrayGridView *m_trayView;
|
||||
TrayModel *m_model;
|
||||
Dock::Position m_postion;
|
||||
};
|
||||
|
||||
#endif // PLUGINWINDOW_H
|
@ -54,10 +54,11 @@ find_package(Qt5 COMPONENTS Test REQUIRED)
|
||||
#find_package(DdeControlCenter REQUIRED)
|
||||
find_package(GTest REQUIRED)
|
||||
find_package(GMock REQUIRED)
|
||||
find_package(dbusmenu-qt5 REQUIRED)
|
||||
|
||||
pkg_check_modules(QGSettings REQUIRED gsettings-qt)
|
||||
pkg_check_modules(DFrameworkDBus REQUIRED dframeworkdbus)
|
||||
pkg_check_modules(XCB_EWMH REQUIRED xcb-ewmh x11 xcursor)
|
||||
pkg_check_modules(XCB_EWMH REQUIRED xcb-image xcb-composite xtst xcb-ewmh xext dbusmenu-qt5 x11 xcursor)
|
||||
|
||||
# 添加执行文件信息
|
||||
add_executable(${BIN_NAME}
|
||||
|