You've already forked ukui-panel
mirror of
https://github.com/ukui/ukui-panel.git
synced 2026-03-09 09:24:21 -07:00
525 lines
16 KiB
C++
525 lines
16 KiB
C++
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||
* (c)LGPL2+
|
||
*
|
||
* LXQt - a lightweight, Qt based, desktop toolset
|
||
* https://lxqt.org
|
||
*
|
||
* Copyright: 2015 LXQt team
|
||
* Authors:
|
||
* Balázs Béla <balazsbela[at]gmail.com>
|
||
* Paulo Lieuthier <paulolieuthier@gmail.com>
|
||
*
|
||
* This program or library is free software; you can redistribute it
|
||
* and/or modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* This library 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General
|
||
* Public License along with this library; if not, write to the
|
||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
* Boston, MA 02110-1301 USA
|
||
*
|
||
* END_COMMON_COPYRIGHT_HEADER */
|
||
|
||
#include "statusnotifierbutton.h"
|
||
|
||
#include <QDir>
|
||
#include <QFile>
|
||
#include <QApplication>
|
||
#include <QDrag>
|
||
#include "../panel/iukuipanelplugin.h"
|
||
#include "sniasync.h"
|
||
#include "../panel/customstyle.h"
|
||
#include "../panel/highlight-effect.h"
|
||
#include <QDebug>
|
||
#include <KWindowEffects>
|
||
//#include <XdgIcon>
|
||
|
||
#define MIMETYPE "ukui/UkuiTaskBar"
|
||
|
||
StatusNotifierButton::StatusNotifierButton(QString service, QString objectPath, IUKUIPanelPlugin* plugin, QWidget *parent)
|
||
: QToolButton(parent),
|
||
mMenu(nullptr),
|
||
mMenuImporter(nullptr),
|
||
mStatus(Passive),
|
||
mFallbackIcon(QIcon::fromTheme("application-x-executable")),
|
||
mPlugin(plugin)
|
||
{
|
||
// setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
// setAutoRaise(true);
|
||
this->setAcceptDrops(true);
|
||
interface = new SniAsync(service, objectPath, QDBusConnection::sessionBus(), this);
|
||
|
||
connect(interface, &SniAsync::NewIcon, this, &StatusNotifierButton::newIcon);
|
||
connect(interface, &SniAsync::NewOverlayIcon, this, &StatusNotifierButton::newOverlayIcon);
|
||
connect(interface, &SniAsync::NewAttentionIcon, this, &StatusNotifierButton::newAttentionIcon);
|
||
connect(interface, &SniAsync::NewToolTip, this, &StatusNotifierButton::newToolTip);
|
||
connect(interface, &SniAsync::NewStatus, this, &StatusNotifierButton::newStatus);
|
||
|
||
hideAbleStatusNotifierButton();
|
||
connect(this,&StatusNotifierButton::paramReady,this,[=](){
|
||
if(!this->mId.isEmpty() && this->mIconStatus && !mParamInit){
|
||
emit layoutReady();
|
||
mParamInit = true;
|
||
}
|
||
else{
|
||
if(this->mId.isEmpty()){
|
||
if(mCount < 5) //超过5次将不再获取
|
||
hideAbleStatusNotifierButton();
|
||
mCount++;
|
||
}
|
||
}
|
||
});
|
||
|
||
|
||
/*Menu返回值:
|
||
无菜单项返回 - "/NO_DBUSMENU";
|
||
有菜单项返回 - "/MenuBar",其他;
|
||
x-sni注册的图标返回 - ""
|
||
*/
|
||
interface->propertyGetAsync(QLatin1String("Menu"), [this] (QDBusObjectPath path) {
|
||
if(path.path() != "/NO_DBUSMENU" && !path.path().isEmpty())
|
||
{
|
||
mMenuImporter = new MenuImporter(interface->service(), path.path(), this);
|
||
if(mMenuImporter){
|
||
connect(mMenuImporter, &MenuImporter::menuUpdated, this, &StatusNotifierButton::updataItemMenu);
|
||
mMenu = mMenuImporter->menu();
|
||
if(mMenu){
|
||
mMenu->setObjectName(QLatin1String("StatusNotifierMenu"));
|
||
KWindowEffects::enableBlurBehind(mMenu->winId(), true);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
interface->propertyGetAsync(QLatin1String("Status"), [this] (QString status) {
|
||
newStatus(status);
|
||
});
|
||
|
||
interface->propertyGetAsync(QLatin1String("IconThemePath"), [this] (QString value) {
|
||
mThemePath = value;
|
||
//do the logic of icons after we've got the theme path
|
||
refetchIcon(Active);
|
||
refetchIcon(Passive);
|
||
refetchIcon(NeedsAttention);
|
||
});
|
||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
this->setProperty("useButtonPalette", true);
|
||
this->setProperty("useIconHighlightEffect", 0x02);
|
||
newToolTip();
|
||
systemThemeChanges();
|
||
}
|
||
|
||
StatusNotifierButton::~StatusNotifierButton()
|
||
{
|
||
delete interface;
|
||
}
|
||
|
||
void StatusNotifierButton::newIcon()
|
||
{
|
||
refetchIcon(Passive);
|
||
}
|
||
|
||
void StatusNotifierButton::newOverlayIcon()
|
||
{
|
||
refetchIcon(Active);
|
||
}
|
||
|
||
void StatusNotifierButton::newAttentionIcon()
|
||
{
|
||
refetchIcon(NeedsAttention);
|
||
}
|
||
|
||
void StatusNotifierButton::refetchIcon(Status status)
|
||
{
|
||
QString nameProperty, pixmapProperty;
|
||
if (status == Active)
|
||
{
|
||
nameProperty = QLatin1String("OverlayIconName");
|
||
pixmapProperty = QLatin1String("OverlayIconPixmap");
|
||
}
|
||
else if (status == NeedsAttention)
|
||
{
|
||
nameProperty = QLatin1String("AttentionIconName");
|
||
pixmapProperty = QLatin1String("AttentionIconPixmap");
|
||
}
|
||
else // status == Passive
|
||
{
|
||
nameProperty = QLatin1String("IconName");
|
||
pixmapProperty = QLatin1String("IconPixmap");
|
||
}
|
||
|
||
interface->propertyGetAsync(nameProperty, [this, status, pixmapProperty] (QString iconName) {
|
||
QIcon nextIcon;
|
||
if (!iconName.isEmpty())
|
||
{
|
||
if (QIcon::hasThemeIcon(iconName)){
|
||
nextIcon = QIcon::fromTheme(iconName);
|
||
}
|
||
else
|
||
{
|
||
QDir themeDir(mThemePath);
|
||
if (themeDir.exists())
|
||
{
|
||
if (themeDir.exists(iconName + ".png"))
|
||
nextIcon.addFile(themeDir.filePath(iconName + ".png"));
|
||
|
||
if (themeDir.cd("hicolor") || (themeDir.cd("icons") && themeDir.cd("hicolor")))
|
||
{
|
||
const QStringList sizes = themeDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
||
for (const QString &dir : sizes)
|
||
{
|
||
const QStringList dirs = QDir(themeDir.filePath(dir)).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
||
for (const QString &innerDir : dirs)
|
||
{
|
||
QString file = themeDir.absolutePath() + "/" + dir + "/" + innerDir + "/" + iconName + ".png";
|
||
if (QFile::exists(file))
|
||
nextIcon.addFile(file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
switch (status)
|
||
{
|
||
case Active:
|
||
mOverlayIcon = nextIcon;
|
||
break;
|
||
case NeedsAttention:
|
||
mAttentionIcon = nextIcon;
|
||
break;
|
||
case Passive:
|
||
mIcon = nextIcon;
|
||
break;
|
||
}
|
||
|
||
resetIcon();
|
||
}
|
||
else
|
||
{
|
||
interface->propertyGetAsync(pixmapProperty, [this, status, pixmapProperty] (IconPixmapList iconPixmaps) {
|
||
if (iconPixmaps.empty())
|
||
return;
|
||
|
||
QIcon nextIcon;
|
||
|
||
for (IconPixmap iconPixmap: iconPixmaps)
|
||
{
|
||
if (!iconPixmap.bytes.isNull())
|
||
{
|
||
QImage image((uchar*) iconPixmap.bytes.data(), iconPixmap.width,
|
||
iconPixmap.height, QImage::Format_ARGB32);
|
||
|
||
const uchar *end = image.constBits() + image.byteCount();
|
||
uchar *dest = reinterpret_cast<uchar*>(iconPixmap.bytes.data());
|
||
for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
|
||
qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest);
|
||
|
||
//图标反白
|
||
QImage currentImage= getBlackThemeIcon(image);
|
||
nextIcon.addPixmap(QPixmap::fromImage(currentImage));
|
||
}
|
||
}
|
||
|
||
switch (status)
|
||
{
|
||
case Active:
|
||
mOverlayIcon = nextIcon;
|
||
break;
|
||
case NeedsAttention:
|
||
mAttentionIcon = nextIcon;
|
||
break;
|
||
case Passive:
|
||
mIcon = nextIcon;
|
||
break;
|
||
}
|
||
resetIcon();
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
void StatusNotifierButton::newToolTip()
|
||
{
|
||
interface->propertyGetAsync(QLatin1String("ToolTip"), [this] (ToolTip tooltip) {
|
||
|
||
QString toolTipTitle = tooltip.title;
|
||
if (!toolTipTitle.isEmpty())
|
||
setToolTip(toolTipTitle);
|
||
else
|
||
interface->propertyGetAsync(QLatin1String("Title"), [this] (QString title) {
|
||
// we should get here only in case the ToolTip.title was empty
|
||
if (!title.isEmpty())
|
||
setToolTip(title);
|
||
});
|
||
});
|
||
}
|
||
|
||
void StatusNotifierButton::newStatus(QString status)
|
||
{
|
||
Status newStatus;
|
||
if (status == QLatin1String("Passive"))
|
||
newStatus = Passive;
|
||
else if (status == QLatin1String("Active"))
|
||
newStatus = Active;
|
||
else
|
||
newStatus = NeedsAttention;
|
||
|
||
if (mStatus == newStatus)
|
||
return;
|
||
|
||
mStatus = newStatus;
|
||
resetIcon();
|
||
}
|
||
|
||
void StatusNotifierButton::contextMenuEvent(QContextMenuEvent* event)
|
||
{
|
||
//XXX: avoid showing of parent's context menu, we are (optionaly) providing context menu on mouseReleaseEvent
|
||
//QWidget::contextMenuEvent(event);
|
||
}
|
||
|
||
void StatusNotifierButton::mouseMoveEvent(QMouseEvent *e)
|
||
{
|
||
if (e->button() == Qt::RightButton)
|
||
return;
|
||
if (!(e->buttons() & Qt::LeftButton))
|
||
return;
|
||
if ((e->pos() - mDragStart).manhattanLength() < QApplication::startDragDistance())
|
||
return;
|
||
|
||
if (e->modifiers() == Qt::ControlModifier)
|
||
{
|
||
return;
|
||
}
|
||
QDrag *drag = new QDrag(this);
|
||
QIcon ico = icon();
|
||
int size = mPlugin->panel()->iconSize();
|
||
QPixmap img = ico.pixmap(ico.actualSize({size, size}));
|
||
|
||
drag->setMimeData(mimeData());
|
||
drag->setPixmap(img);
|
||
|
||
switch (mPlugin->panel()->position())
|
||
{
|
||
case IUKUIPanel::PositionLeft:
|
||
case IUKUIPanel::PositionTop:
|
||
drag->setHotSpot({0, 0});
|
||
break;
|
||
case IUKUIPanel::PositionRight:
|
||
case IUKUIPanel::PositionBottom:
|
||
drag->setHotSpot(img.rect().bottomRight());
|
||
break;
|
||
}
|
||
|
||
drag->exec();
|
||
drag->deleteLater();
|
||
|
||
//QAbstractButton::mouseMoveEvent(e);
|
||
}
|
||
|
||
void StatusNotifierButton::mouseReleaseEvent(QMouseEvent *event)
|
||
{
|
||
if (event->button() == Qt::LeftButton)
|
||
interface->Activate(QCursor::pos().x(), QCursor::pos().y());
|
||
else if (event->button() == Qt::MidButton)
|
||
interface->SecondaryActivate(QCursor::pos().x(), QCursor::pos().y());
|
||
else if (Qt::RightButton == event->button())
|
||
{
|
||
mCursorLeftPos = QCursor::pos();
|
||
if(mMenuImporter) {
|
||
mMenuImporter->updateMenu();
|
||
}
|
||
else {
|
||
interface->ContextMenu(mCursorLeftPos.x(), mCursorLeftPos.y());
|
||
}
|
||
|
||
}
|
||
update();
|
||
QToolButton::mouseReleaseEvent(event);
|
||
}
|
||
|
||
void StatusNotifierButton::wheelEvent(QWheelEvent *event)
|
||
{
|
||
interface->Scroll(event->delta(), "vertical");
|
||
}
|
||
|
||
void StatusNotifierButton::resetIcon()
|
||
{
|
||
QIcon appIcon;
|
||
if (mStatus == Active && !mOverlayIcon.isNull())
|
||
appIcon = mOverlayIcon;
|
||
else if (mStatus == NeedsAttention && !mAttentionIcon.isNull())
|
||
appIcon = mAttentionIcon;
|
||
else if (!mIcon.isNull()) // mStatus == Passive
|
||
appIcon = mIcon;
|
||
else if (!mOverlayIcon.isNull())
|
||
appIcon = mOverlayIcon;
|
||
else if (!mAttentionIcon.isNull())
|
||
appIcon = mAttentionIcon;
|
||
else
|
||
appIcon = mFallbackIcon;
|
||
|
||
QStringList stylelist;
|
||
stylelist<<STYLE_NAME_KEY_DARK<<STYLE_NAME_KEY_BLACK;
|
||
if (stylelist.contains(mThemeSettings->get(STYLE_NAME).toString())){
|
||
appIcon=QIcon(HighLightEffect::drawSymbolicColoredIcon(appIcon));
|
||
}
|
||
|
||
setIcon(appIcon);
|
||
|
||
mIconStatus=true;
|
||
emit paramReady();
|
||
}
|
||
|
||
void StatusNotifierButton::systemThemeChanges()
|
||
{
|
||
//主题变化
|
||
const QByteArray styleId(ORG_UKUI_STYLE);
|
||
if(QGSettings::isSchemaInstalled(styleId)){
|
||
mThemeSettings = new QGSettings(styleId);
|
||
connect(mThemeSettings, &QGSettings::changed, this, [=] (const QString &key){
|
||
if(key == STYLE_NAME){
|
||
//主题变化任务栏主动更新图标
|
||
resetIcon();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
void StatusNotifierButton::updataItemMenu()
|
||
{
|
||
mMenu = mMenuImporter->menu();
|
||
if (mMenu && !mMenu->isEmpty()){
|
||
mPlugin->willShowWindow(mMenu);
|
||
mMenu->exec(mPlugin->panel()->calculatePopupWindowPos(mCursorLeftPos, mMenu->sizeHint()).topLeft()); //任务栏显示右键菜单
|
||
}
|
||
else{
|
||
interface->ContextMenu(mCursorLeftPos.x(), mCursorLeftPos.y()); //应用显示右键菜单
|
||
}
|
||
}
|
||
|
||
void StatusNotifierButton::dragMoveEvent(QDragMoveEvent * e)
|
||
{
|
||
update();
|
||
// if (e->mimeData()->hasFormat(MIMETYPE))
|
||
// e->acceptProposedAction();
|
||
// else
|
||
// e->ignore();
|
||
|
||
}
|
||
|
||
void StatusNotifierButton::dragEnterEvent(QDragEnterEvent *e)
|
||
{
|
||
e->acceptProposedAction();
|
||
const StatusNotifierButtonMimeData *mimeData = qobject_cast<const StatusNotifierButtonMimeData*>(e->mimeData());
|
||
if (mimeData && mimeData->button()){
|
||
emit switchButtons(mimeData->button(), this);
|
||
emit sendTitle(mimeData->button()->hideAbleStatusNotifierButton());
|
||
}
|
||
QToolButton::dragEnterEvent(e);
|
||
}
|
||
|
||
void StatusNotifierButton::dragLeaveEvent(QDragLeaveEvent *e)
|
||
{
|
||
update(); //拖拽离开wigget时,需要updata
|
||
e->accept();
|
||
}
|
||
|
||
QMimeData * StatusNotifierButton::mimeData()
|
||
{
|
||
StatusNotifierButtonMimeData *mimeData = new StatusNotifierButtonMimeData();
|
||
// QByteArray ba;
|
||
// mimeData->setData(mimeDataFormat(), ba);
|
||
mimeData->setButton(this);
|
||
return mimeData;
|
||
}
|
||
|
||
void StatusNotifierButton::mousePressEvent(QMouseEvent *e)
|
||
{
|
||
if (e->button() == Qt::LeftButton ) {
|
||
mDragStart = e->pos();
|
||
return;
|
||
}
|
||
QToolButton::mousePressEvent(e);
|
||
}
|
||
|
||
bool StatusNotifierButton::event(QEvent *e)
|
||
{
|
||
// if(e->type() != QEvent::ToolTipChange && e->type()!=QEvent::HoverMove && e->type()!=QEvent::Paint &&
|
||
// e->type() != QEvent::HoverLeave && e->type()!=QEvent::Paint &&e->type() != QEvent::DragMove &&
|
||
// e->type() != QEvent::Leave && e->type()!=QEvent::Enter &&e->type() != QEvent::DragMove &&
|
||
// e->type() != QEvent::Gesture && e->type() != QEvent::MouseButtonPress && e->type() != QEvent::MouseButtonRelease &&
|
||
// e->type() != QEvent::GestureOverride && e->type() !=QEvent::HoverEnter && e->type() != QEvent::MouseMove &&
|
||
// e->type() !=QEvent::ChildAdded && e->type() != QEvent::DragEnter )
|
||
// qDebug()<<e->type();
|
||
|
||
if(e->type() == QEvent::ChildRemoved) {
|
||
emit cleansignal();
|
||
}
|
||
return QToolButton::event(e);
|
||
}
|
||
|
||
void StatusNotifierButton::resizeEvent(QResizeEvent *event){
|
||
IUKUIPanel *panel = mPlugin->panel();
|
||
|
||
if (panel->isHorizontal())
|
||
{
|
||
this->setIconSize(QSize(this->width()*0.5,this->width()*0.5));
|
||
}
|
||
else
|
||
{
|
||
this->setIconSize(QSize(this->height()*0.5,this->height()*0.5));
|
||
}
|
||
|
||
QToolButton::resizeEvent(event);
|
||
}
|
||
|
||
void StatusNotifierButton::enterEvent(QEvent *event)
|
||
{
|
||
update();
|
||
}
|
||
|
||
void StatusNotifierButton::leaveEvent(QEvent *event)
|
||
{
|
||
update();
|
||
}
|
||
|
||
QString StatusNotifierButton::hideAbleStatusNotifierButton()
|
||
{
|
||
interface->propertyGetAsync(QLatin1String("Id"), [this] (QString title) {
|
||
mId = "";
|
||
mId = title;
|
||
emit paramReady();
|
||
|
||
});
|
||
return mId;
|
||
}
|
||
|
||
QImage StatusNotifierButton::getBlackThemeIcon(QImage image)
|
||
{
|
||
QColor standard (31,32,34);
|
||
for (int x = 0; x < image.width(); x++) {
|
||
for (int y = 0; y < image.height(); y++) {
|
||
auto color = image.pixelColor(x, y);
|
||
if (color.alpha() > 0) {
|
||
if(qAbs(color.red()-standard.red())<20 && qAbs(color.green()-standard.green())<20 && qAbs(color.blue()-standard.blue())<20){
|
||
color.setRed(255);
|
||
color.setGreen(255);
|
||
color.setBlue(255);
|
||
image.setPixelColor(x, y, color);
|
||
}
|
||
else{
|
||
image.setPixelColor(x, y, color);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return image;
|
||
}
|