diff options
-rw-r--r-- | src-qt5/core/libLumina/LIconCache.cpp | 23 | ||||
-rw-r--r-- | src-qt5/core/libLumina/LIconCache.h | 4 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeEmbedWidget.cpp | 25 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeWindowSystem.cpp | 35 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow-animations.cpp | 2 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow.h | 6 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootWindow.cpp | 3 | ||||
-rw-r--r-- | src-qt5/core/lumina-desktop-unified/JsonMenu.h | 71 | ||||
-rw-r--r-- | src-qt5/core/lumina-desktop-unified/LSession.cpp | 10 | ||||
-rw-r--r-- | src-qt5/core/lumina-desktop-unified/global-includes.h | 5 | ||||
-rw-r--r-- | src-qt5/core/lumina-desktop-unified/lumina-desktop.pro | 4 | ||||
-rw-r--r-- | src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp | 32 |
12 files changed, 194 insertions, 26 deletions
diff --git a/src-qt5/core/libLumina/LIconCache.cpp b/src-qt5/core/libLumina/LIconCache.cpp index 70c360fb..84428546 100644 --- a/src-qt5/core/libLumina/LIconCache.cpp +++ b/src-qt5/core/libLumina/LIconCache.cpp @@ -184,6 +184,27 @@ void LIconCache::loadIcon(QLabel *label, QString icon, bool noThumb){ if(needload){ startReadFile(icon, idata.fullpath); } } +void LIconCache::loadIcon(QMenu *action, QString icon, bool noThumb){ + if(icon.isEmpty()){ return; } + if(isThemeIcon(icon)){ + action->setIcon( iconFromTheme(icon)); + return ; + } + //See if the icon has already been loaded into the HASH + bool needload = !HASH.contains(icon); + if(!needload){ + if(!noThumb && !HASH[icon].thumbnail.isNull()){ action->setIcon( HASH[icon].thumbnail ); return; } + else if(!HASH[icon].icon.isNull()){ action->setIcon( HASH[icon].icon ); return; } + } + //Need to load the icon + icon_data idata; + if(HASH.contains(icon)){ idata = HASH.value(icon); } + else { idata = createData(icon); } + idata.pendingMenus << QPointer<QMenu>(action); //save this button for later + HASH.insert(icon, idata); + if(needload){ startReadFile(icon, idata.fullpath); } +} + void LIconCache::clearIconTheme(){ //use when the icon theme changes to refresh all requested icons QStringList keys = HASH.keys(); @@ -282,6 +303,8 @@ void LIconCache::startReadFile(QString id, QString path){ idat.pendingLabels.clear(); for(int i=0; i<idat.pendingActions.length(); i++){ if(!idat.pendingActions[i].isNull()){ idat.pendingActions[i]->setIcon(idat.icon); } } idat.pendingActions.clear(); + for(int i=0; i<idat.pendingMenus.length(); i++){ if(!idat.pendingMenus[i].isNull()){ idat.pendingMenus[i]->setIcon(idat.icon); } } + idat.pendingMenus.clear(); //Now update the hash and let the world know it is available now HASH.insert(id, idat); this->emit IconAvailable(id); diff --git a/src-qt5/core/libLumina/LIconCache.h b/src-qt5/core/libLumina/LIconCache.h index 428ffcab..691d328c 100644 --- a/src-qt5/core/libLumina/LIconCache.h +++ b/src-qt5/core/libLumina/LIconCache.h @@ -4,7 +4,7 @@ // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== -// This is a simple class for loading/serving icon files +// This is a simple class for loading/serving icon files // from the icon theme or local filesystem //=========================================== #include <QHash> @@ -26,6 +26,7 @@ struct icon_data{ QList<QPointer<QLabel> > pendingLabels; QList<QPointer<QAbstractButton> > pendingButtons; QList<QPointer<QAction> > pendingActions; + QList<QPointer<QMenu> > pendingMenus; QIcon icon; QIcon thumbnail; }; @@ -50,6 +51,7 @@ public: void loadIcon(QAbstractButton *button, QString icon, bool noThumb = false); void loadIcon(QLabel *label, QString icon, bool noThumb = false); void loadIcon(QAction *action, QString icon, bool noThumb = false); + void loadIcon(QMenu *action, QString icon, bool noThumb = false); QIcon loadIcon(QString icon, bool noThumb = false); //generic loading routine - does not background the loading of icons when not in the cache diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.cpp b/src-qt5/core/libLumina/NativeEmbedWidget.cpp index 96393e98..c01c16b3 100644 --- a/src-qt5/core/libLumina/NativeEmbedWidget.cpp +++ b/src-qt5/core/libLumina/NativeEmbedWidget.cpp @@ -46,9 +46,24 @@ inline void renderWindowToWidget(WId id, QWidget *widget, bool hastransparency = 0, 0, 0, 0, 0, 0, (uint16_t) widget->width(), (uint16_t) widget->height() ); }*/ - -inline void registerClientEvents(WId id){ - uint32_t value_list[1] = { (XCB_EVENT_MASK_PROPERTY_CHANGE +#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_FOCUS_CHANGE | \ + XCB_EVENT_MASK_POINTER_MOTION) + +#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_POINTER_MOTION | \ + XCB_EVENT_MASK_EXPOSURE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_ENTER_WINDOW) + +inline void registerClientEvents(WId id, bool client = true){ + uint32_t values[] = {XCB_NONE}; + values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ; + /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION @@ -58,8 +73,8 @@ inline void registerClientEvents(WId id){ | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_ENTER_WINDOW) - }; - xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); + };*/ + xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values); } // ============ diff --git a/src-qt5/core/libLumina/NativeWindowSystem.cpp b/src-qt5/core/libLumina/NativeWindowSystem.cpp index 9d04228d..f9e787ad 100644 --- a/src-qt5/core/libLumina/NativeWindowSystem.cpp +++ b/src-qt5/core/libLumina/NativeWindowSystem.cpp @@ -63,10 +63,41 @@ XCB_EVENT_MASK_PROPERTY_CHANGE | \ XCB_EVENT_MASK_FOCUS_CHANGE) -inline void registerClientEvents(WId id){ +#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_FOCUS_CHANGE | \ + XCB_EVENT_MASK_POINTER_MOTION) + +#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_POINTER_MOTION | \ + XCB_EVENT_MASK_EXPOSURE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_ENTER_WINDOW) + +inline void registerClientEvents(WId id, bool client = true){ + uint32_t values[] = {XCB_NONE}; + values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ; + /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE + | XCB_EVENT_MASK_BUTTON_PRESS + | XCB_EVENT_MASK_BUTTON_RELEASE + | XCB_EVENT_MASK_POINTER_MOTION + | XCB_EVENT_MASK_BUTTON_MOTION + | XCB_EVENT_MASK_EXPOSURE + | XCB_EVENT_MASK_STRUCTURE_NOTIFY + | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT + | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY + | XCB_EVENT_MASK_ENTER_WINDOW) + };*/ + xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values); +} + +/*inline void registerClientEvents(WId id){ uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK}; xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); -} +}*/ //Internal XCB private objects class class NativeWindowSystem::p_objects{ diff --git a/src-qt5/core/libLumina/RootSubWindow-animations.cpp b/src-qt5/core/libLumina/RootSubWindow-animations.cpp index 54524838..efab20fe 100644 --- a/src-qt5/core/libLumina/RootSubWindow-animations.cpp +++ b/src-qt5/core/libLumina/RootSubWindow-animations.cpp @@ -61,6 +61,7 @@ void RootSubWindow::loadAnimation(QString name, NativeWindow::Property prop, QVa } if(nval.toBool()){ this->setGeometry( anim->startValue().toRect() ); //ensure the window is the initial geom before it becomes visible + //QTimer::singleShot( anim->duration()+5, this, SLOT(activate()) ); }else{ QVariant tmp = anim->startValue(); anim->setStartValue(anim->endValue()); @@ -111,4 +112,5 @@ void RootSubWindow::animFinished(){ animResetProp = QVariant(); //clear the variable //QTimer::singleShot(10, WinWidget, SLOT(resume()) ); WinWidget->resume(); + emit windowAnimFinished(); } diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h index c4dd2e0b..c1964724 100644 --- a/src-qt5/core/libLumina/RootSubWindow.h +++ b/src-qt5/core/libLumina/RootSubWindow.h @@ -67,7 +67,7 @@ private: public slots: void giveMouseFocus(){ WinWidget->raiseWindow(); } void removeMouseFocus(){ WinWidget->lowerWindow(); } - void giveKeyboardFocus(){ WIN->requestProperty(NativeWindow::Active, true); } + void giveKeyboardFocus(){ WIN->requestProperty(NativeWindow::Active, true, true); } void clientClosed(); void LoadAllProperties(); @@ -99,7 +99,9 @@ protected: //void enterEvent(QEvent *ev); void moveEvent(QMoveEvent *ev); - +signals: + void windowMoved(RootSubWindow*); + void windowAnimFinished(); }; #endif diff --git a/src-qt5/core/libLumina/RootWindow.cpp b/src-qt5/core/libLumina/RootWindow.cpp index 2ec33097..fdbc1eb8 100644 --- a/src-qt5/core/libLumina/RootWindow.cpp +++ b/src-qt5/core/libLumina/RootWindow.cpp @@ -236,11 +236,12 @@ void RootWindow::NewWindow(NativeWindow *win){ subwin = new RootSubWindow(this, win); subwin->setWhatsThis("RootSubWindow"); connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(CloseWindow(WId)) ); + connect(subwin, SIGNAL(windowAnimFinished()), this, SLOT(checkMouseFocus()) ); WINDOWS << subwin; } CheckWindowPosition(win->id(), true); //first-time run win->setProperty(NativeWindow::Visible, true); - win->requestProperty( NativeWindow::Active, true); + //win->requestProperty( NativeWindow::Active, true); //win->requestProperties(QList<NativeWindow::Property>() << NativeWindow::Visible << NativeWindow::Active, QList<QVariant>() << true << true, true); if(!mouseFocusTimer->isActive()){ mouseFocusTimer->start(); } } diff --git a/src-qt5/core/lumina-desktop-unified/JsonMenu.h b/src-qt5/core/lumina-desktop-unified/JsonMenu.h new file mode 100644 index 00000000..2624153d --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/JsonMenu.h @@ -0,0 +1,71 @@ +//=========================================== +// Lumina Desktop source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This menu is used to automatically generate menu contents +// based on the JSON output of an external script/utility +//=========================================== +#ifndef _LUMINA_DESKTOP_JSON_MENU_H +#define _LUMINA_DESKTOP_JSON_MENU_H + +#include <global-objects.h> + +class JsonMenu : public QMenu{ + Q_OBJECT +private: + QString exec; + +public: + JsonMenu(QString execpath, QWidget *parent = 0) : QMenu(parent){ + exec = execpath; + connect(this, SIGNAL(aboutToShow()), this, SLOT(updateMenu()) ); + connect(this, SIGNAL(triggered(QAction*)), this, SLOT(itemTriggered(QAction*)) ); + } + +private slots: + void parseObject(QString label, QJsonObject obj){ + if( label.isEmpty() || !obj.contains("type") ){ return; } + QString type = obj.value("type").toString(); + if(type.toLower()=="item"){ + QAction *act = this->addAction(label); + if(obj.contains("icon")){ LIconCache::instance()->loadIcon(act, obj.value("icon").toString()); } + if(obj.contains("action")){ act->setWhatsThis( obj.value("action").toString() ); } + else{ act->setEnabled(false); } //not interactive + }else if(type.toLower()=="menu"){ + + }else if(type.toLower()=="jsonmenu"){ + //This is a recursive JSON menu object + if(!obj.contains("exec")){ return; } + JsonMenu *menu = new JsonMenu(obj.value("exec").toString(), this); + menu->setTitle(label); + if(obj.contains("icon")){ LIconCache::instance()->loadIcon(menu, obj.value("icon").toString()); } + this->addMenu(menu); + } + } + + void updateMenu(){ + this->clear(); + QJsonDocument doc = QJsonDocument::fromJson( LUtils::getCmdOutput(exec).join(" ").toLocal8Bit() ); + if(doc.isNull() || !doc.isObject()){ + this->addAction( QString(tr("Error parsing script output: %1")).arg("\n"+exec) )->setEnabled(false); + }else{ + QStringList keys = doc.object().keys(); + for(int i=0; i<keys.length(); i++){ + if(doc.object().value(keys[i]).isObject()){ + parseObject(keys[i], doc.object().value(keys[i]).toObject()); + } + } + } + } + + void itemTriggered(QAction *act){ + if(act->parent()!=this || act->whatsThis().isEmpty() ){ return; } //only handle direct child actions - needed for recursive nature of menu + QString cmd = act->whatsThis(); + QString bin = cmd.section(" ",0,0); + if( !LUtils::isValidBinary(bin) ){ cmd.prepend("lumina-open "); } + LSession::instance()->LaunchApplication(cmd); + } +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/LSession.cpp b/src-qt5/core/lumina-desktop-unified/LSession.cpp index 05575b27..d70ff973 100644 --- a/src-qt5/core/lumina-desktop-unified/LSession.cpp +++ b/src-qt5/core/lumina-desktop-unified/LSession.cpp @@ -39,9 +39,9 @@ LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lu this->setOrganizationName("LuminaDesktopEnvironment"); this->setQuitOnLastWindowClosed(false); //since the LDesktop's are not necessarily "window"s //Enable a few of the simple effects by default - //this->setEffectEnabled( Qt::UI_AnimateMenu, true); - //this->setEffectEnabled( Qt::UI_AnimateCombo, true); - //this->setEffectEnabled( Qt::UI_AnimateTooltip, true); + this->setEffectEnabled( Qt::UI_AnimateMenu, true); + this->setEffectEnabled( Qt::UI_AnimateCombo, true); + this->setEffectEnabled( Qt::UI_AnimateTooltip, true); this->setAttribute(Qt::AA_UseDesktopOpenGL); this->setAttribute(Qt::AA_UseHighDpiPixmaps); //allow pixmaps to be scaled up as well as down @@ -51,8 +51,8 @@ LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lu Lumina::SS = new LScreenSaver(); //Now put the Native Window System into it's own thread to keep things snappy Lumina::EVThread = new QThread(); - Lumina::NWS->moveToThread(Lumina::EVThread); - Lumina::EVThread->start(); + //Lumina::NWS->moveToThread(Lumina::EVThread); + //Lumina::EVThread->start(); Lumina::APPLIST = XDGDesktopList::instance(); Lumina::ROOTWIN = new RootWindow(); Lumina::SHORTCUTS = new LShortcutEvents(); //this can be moved to it's own thread eventually as well diff --git a/src-qt5/core/lumina-desktop-unified/global-includes.h b/src-qt5/core/lumina-desktop-unified/global-includes.h index 8b8cd16c..184f5b8d 100644 --- a/src-qt5/core/lumina-desktop-unified/global-includes.h +++ b/src-qt5/core/lumina-desktop-unified/global-includes.h @@ -50,6 +50,10 @@ #include <QMediaPlayer> #include <QVideoWidget> #include <QMediaPlaylist> +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonDocument> + // libLumina includes #include <LuminaX11.h> @@ -66,6 +70,7 @@ #include <NativeWindowSystem.h> #include <NativeEventFilter.h> #include <XDGMime.h> +#include <LIconCache.h> // Standard C includes #include <unistd.h> diff --git a/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro b/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro index 04d5b602..e8cf2f28 100644 --- a/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro +++ b/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro @@ -20,6 +20,7 @@ include(../libLumina/RootWindow.pri) include(../libLumina/ExternalProcess.pri) include(../libLumina/NativeWindow.pri) include(../libLumina/XDGMime.pri) +include(../libLumina/LIconCache.pri) #include all the main individual source groups include(src-screensaver/screensaver.pri) @@ -35,7 +36,8 @@ SOURCES += main.cpp \ HEADERS += global-includes.h \ global-objects.h \ LSession.h \ - BootSplash.h + BootSplash.h \ + JsonMenu.h FORMS += BootSplash.ui diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp index 143a3667..47f0e3d7 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp @@ -6,6 +6,7 @@ //=========================================== #include "ContextMenu.h" #include <global-objects.h> +#include <JsonMenu.h> void DesktopContextMenu::SettingsChanged(DesktopSettings::File file){ if(file == DesktopSettings::ContextMenu){ UpdateMenu(false); } @@ -24,13 +25,25 @@ void DesktopContextMenu::UpdateMenu(bool fast){ QStringList items = DesktopSettings::instance()->value(DesktopSettings::ContextMenu, "itemlist", QStringList()<< "terminal" << "filemanager" << "line" << "applications" << "windowlist" << "settings" << "lockdesktop").toStringList(); usewinmenu=false; for(int i=0; i<items.length(); i++){ - if(items[i]=="terminal"){ this->addAction(LXDG::findIcon("utilities-terminal",""), tr("Terminal"))->setWhatsThis("--terminal"); } - else if(items[i]=="lockdesktop"){ this->addAction(LXDG::findIcon("system-lock-screen",""), tr("Lock Session"), this, SIGNAL(LockSession()) ); } - else if(items[i]=="filemanager"){ this->addAction( LXDG::findIcon("user-home",""), tr("Browse Files"))->setWhatsThis(QDir::homePath()); } + if(items[i]=="terminal"){ + QAction *act = this->addAction( tr("Terminal")); + LIconCache::instance()->loadIcon(act, "utilities-terminal"); + act->setWhatsThis("--terminal"); + } + else if(items[i]=="lockdesktop"){ + QAction *act = this->addAction( tr("Lock Session"), this, SIGNAL(LockSession()) ); + LIconCache::instance()->loadIcon(act, "system-lock-screen"); + } + else if(items[i]=="filemanager"){ + QAction *act = this->addAction( tr("Browse Files")); + LIconCache::instance()->loadIcon(act, "user-home"); + act->setWhatsThis(QDir::homePath()); + } else if(items[i]=="applications"){ if(appMenu==0){ updateAppMenu(); } this->addMenu( appMenu ); - }else if(items[i]=="line"){ this->addSeparator(); } + } + else if(items[i]=="line"){ this->addSeparator(); } //else if(items[i]=="settings"){ this->addMenu( LSession::handle()->settingsMenu() ); } else if(items[i]=="windowlist"){ if(winMenu==0){ updateWinMenu(); } @@ -46,18 +59,18 @@ void DesktopContextMenu::UpdateMenu(bool fast){ XDGDesktop xdgf(file);// = LXDG::loadDesktopFile(file, ok); if(xdgf.type!=XDGDesktop::BAD){ xdgf.addToMenu(this); } } - }/*else if(items[i].startsWith("jsonmenu::::")){ + }else if(items[i].startsWith("jsonmenu::::")){ //Custom JSON menu system (populated on demand via external scripts/tools QStringList info = items[i].split("::::"); //FORMAT:[ "jsonmenu",exec,name, icon(optional)] if(info.length()>=3){ - qDebug() << "Custom JSON Menu Loaded:" << info; - JsonMenu *tmp = new JsonMenu(info[1], deskMenu); + //qDebug() << "Custom JSON Menu Loaded:" << info; + JsonMenu *tmp = new JsonMenu(info[1], this); tmp->setTitle(info[2]); connect(tmp, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) ); if(info.length()>=4){ tmp->setIcon( LXDG::findIcon(info[3],"") ); } this->addMenu(tmp); } - }*/ + } } //Now add the system quit options this->addSeparator(); @@ -148,7 +161,7 @@ void DesktopContextMenu::updateAppMenu(){ if(appMenu==0){ appMenu = new QMenu(this); appMenu->setTitle( tr("Applications")); - appMenu->setIcon( LXDG::findIcon("system-run","") ); + LIconCache::instance()->loadIcon( appMenu, "system-run"); connect(appMenu, SIGNAL(triggered(QAction*)), this, SLOT(LaunchApp(QAction*)) ); } //qDebug() << "Populate App Menu"; @@ -160,6 +173,7 @@ void DesktopContextMenu::updateWinMenu(){ if(winMenu==0){ winMenu = new QMenu(this); winMenu->setTitle( tr("Task Manager") ); + LIconCache::instance()->loadIcon( winMenu, "preferences-system-windows"); } winMenu->clear(); QList<NativeWindow*> wins = Lumina::NWS->currentWindows(); |