aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/lumina-desktop-unified/src-desktop
diff options
context:
space:
mode:
Diffstat (limited to 'src-qt5/core/lumina-desktop-unified/src-desktop')
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp213
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h50
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp65
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h26
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-QML.cpp (renamed from src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp)19
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-Widgets.cpp53
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.h6
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri33
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp330
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h178
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp76
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h8
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp174
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h52
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp34
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h9
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/src-cpp.pri6
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/ContextMenu.qml24
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml223
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml35
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml29
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml14
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Clock_Digital.qml23
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Spacer.qml15
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/StatusTray.qml94
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/plugins.pri11
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/BatteryButton.qml20
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/NetworkButton.qml23
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/TrayIcon.qml34
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/UpdateButton.qml24
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/VolumeButton.qml25
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.pri4
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.qrc9
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.cpp (renamed from src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp)19
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.h (renamed from src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h)2
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp48
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h34
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.cpp120
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.h44
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/src-widgets.pri8
40 files changed, 2156 insertions, 58 deletions
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp
new file mode 100644
index 00000000..b94a241d
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp
@@ -0,0 +1,213 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2017-2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "DesktopManager.h"
+
+#include "global-objects.h"
+
+// === PUBLIC ===
+DesktopManager::DesktopManager(){
+
+}
+
+DesktopManager::~DesktopManager(){
+
+}
+
+void DesktopManager::start(){
+ connect(DesktopSettings::instance(), SIGNAL(FileModified(DesktopSettings::File)), this, SLOT(settingsChanged(DesktopSettings::File)) );
+ //Perform the initial load of the settings files
+ QTimer::singleShot(0, this, SLOT(updateSessionSettings()) );
+ QTimer::singleShot(0, this, SLOT(updateDesktopSettings()) );
+ QTimer::singleShot(0, this, SLOT(updatePanelSettings()) );
+ QTimer::singleShot(0, this, SLOT(updatePluginSettings()) );
+ QTimer::singleShot(0, this, SLOT(updateMenuSettings()) );
+ QTimer::singleShot(0, this, SLOT(updateAnimationSettings()) );
+}
+
+void DesktopManager::stop(){
+ disconnect(DesktopSettings::instance(), SIGNAL(FileModified(DesktopSettings::File)), this, SLOT(settingsChanged(DesktopSettings::File)) );
+}
+
+// === PRIVATE ===
+void DesktopManager::updateWallpaper(QString screen_id, int wkspace){
+ QString current = RootDesktopObject::instance()->CurrentWallpaper(screen_id);
+ if(!current.isEmpty()){ current = QUrl(current).toLocalFile(); } //convert it back to the normal file syntax
+ //First find the list of options from the settings
+ //First look for a list that matches this exact screen/workspace combo
+ QStringList wpaperList = DesktopSettings::instance()->value(DesktopSettings::Desktop, "wallpapers/"+screen_id+"_wk_"+QString::number(wkspace), QStringList()).toStringList();
+ //Next look for a list that matches this exact workspace
+ if(wpaperList.isEmpty()){ wpaperList= DesktopSettings::instance()->value(DesktopSettings::Desktop, "wallpapers/default_wk_"+QString::number(wkspace), QStringList()).toStringList(); }
+ wpaperList.removeAll("");
+ //Next look for a list that matches this exact screen
+ if(wpaperList.isEmpty()){ wpaperList= DesktopSettings::instance()->value(DesktopSettings::Desktop, "wallpapers/"+screen_id, QStringList()).toStringList(); }
+ wpaperList.removeAll("");
+ //Now look for a list that matches any screen/workspace
+ if(wpaperList.isEmpty()){ wpaperList= DesktopSettings::instance()->value(DesktopSettings::Desktop, "wallpapers/default", QStringList()).toStringList(); }
+ wpaperList.removeAll("");
+ //Now use the failover wallpaper directory
+ if(wpaperList.isEmpty()){
+ QString def = LOS::LuminaShare().section("/",0,-3)+"/wallpapers/lumina-nature"; //Note: LuminaShare() ends with an extra "/"
+ //qDebug() << "Default Wallpaper:" << def;
+ if(QFile::exists(def)){ wpaperList << def; }
+ }
+ //qDebug() << "Got wallpaper list:" << screen_id << wkspace << wpaperList;
+ //Wallpaper selection/randomization
+ if(wpaperList.count()==1 && wpaperList.first()==current){ return; } //nothing to do - just the same image
+ QString wpaper;
+ QStringList bgL = wpaperList; //need a copy at the moment - could change the entire list in a second (opening a dir for instance)
+ while(wpaper.isEmpty() || QFileInfo(wpaper).isDir()){
+ QString prefix;
+ if(!wpaper.isEmpty()){
+ //Got a directory - update the list of files and re-randomize the selection
+ QStringList imgs = LUtils::imageExtensions(true);
+ //qDebug() << " - Got Dir: " << imgs;
+ QDir tdir(wpaper);
+ prefix=wpaper+"/";
+ bgL = tdir.entryList(imgs, QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
+ //If directory no longer has any valid images - remove it from list and try again
+ if(bgL.isEmpty()){
+ wpaperList.removeAll(wpaper); //invalid directory - remove it from the list for the moment
+ bgL = wpaperList; //reset the list back to the original list (not within a directory)
+ }
+ }
+ //Verify that there are files in the list - otherwise use the default
+ if(bgL.isEmpty()){ wpaper.clear(); break; }
+ int index = ( qrand() % bgL.length() );
+ if(index== bgL.indexOf(current)){ //if the current wallpaper was selected by the randomization again
+ //Go to the next in the list
+ if(index < 0 || index >= bgL.length()-1){ index = 0; } //if invalid or last item in the list - go to first
+ else{ index++; } //go to next
+ }
+ wpaper = prefix+bgL[index];
+ }
+ //Now go ahead and set the wallpaper in the screen object
+ if(wpaper.isEmpty() || wpaper=="default"){ wpaper = LOS::LuminaShare()+"/desktop-background.jpg"; } //failover image
+ //qDebug() << "Updating Wallpaper:" << screen_id << wpaper;
+ RootDesktopObject::instance()->ChangeWallpaper(screen_id,QUrl::fromLocalFile(wpaper).toString() );
+}
+
+void DesktopManager::updatePlugins(QString plugin_id){
+
+}
+
+// === PUBLIC SLOTS ===
+void DesktopManager::workspaceChanged(int wknum){
+ //qDebug() << "Got Workspace Changed:" << wknum;
+ syncWindowList();
+}
+
+void DesktopManager::settingsChanged(DesktopSettings::File type){
+ switch(type){
+ case DesktopSettings::Session:
+ QTimer::singleShot(0, this, SLOT(updateSessionSettings()) );
+ case DesktopSettings::Desktop:
+ QTimer::singleShot(1, this, SLOT(updateDesktopSettings()) );
+ case DesktopSettings::Panels:
+ QTimer::singleShot(2, this, SLOT(updatePanelSettings()) );
+ case DesktopSettings::Plugins:
+ QTimer::singleShot(3, this, SLOT(updatePluginSettings()) );
+ case DesktopSettings::ContextMenu:
+ QTimer::singleShot(4, this, SLOT(updateMenuSettings()) );
+ case DesktopSettings::Animation:
+ QTimer::singleShot(5, this, SLOT(updateAnimationSettings()) );
+ default:
+ break;
+ //Do nothing - not a settings change we care about here
+ }
+}
+
+void DesktopManager::NewWindowAvailable(NativeWindowObject* win){
+ //connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(syncWindowList()) );
+#ifdef USE_WIDGETS
+ qDebug() << "Got New Widget Window:" << win->name();
+#endif
+ syncWindowList();
+}
+
+void DesktopManager::NewTrayWindowAvailable(NativeWindowObject* win){
+ //connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(syncTrayWindowList()) );
+ syncTrayWindowList();
+}
+
+void DesktopManager::syncWindowList(){
+ QList<NativeWindowObject*> allWins = Lumina::NWS->currentWindows();
+ //Filter out all the windows not in the current workspace
+ QList<NativeWindowObject*> current;
+ QList<NativeWindowObject*> currentStacked;
+ int wkspace = Lumina::NWS->currentWorkspace();
+ for(int i=0; i<allWins.length(); i++){
+ if(allWins[i]->isSticky() || (allWins[i]->workspace() == wkspace)){
+ current << allWins[i];
+ }
+ }
+ //qDebug() << "Synced Window List:" << current.length();
+ RootDesktopObject::instance()->setWindows(current);
+}
+
+void DesktopManager::syncTrayWindowList(){
+ QList<NativeWindowObject*> allWins = Lumina::NWS->currentTrayWindows();
+ //qDebug() << "Synced Tray Window List:" << allWins.length();
+ RootDesktopObject::instance()->setTrayWindows(allWins);
+}
+
+// === PRIVATE SLOTS ===
+void DesktopManager::updateSessionSettings(){
+ //qDebug() << "Update Session Settings...";
+
+ RootDesktopObject::instance()->updateCurrentTimeFormat(DesktopSettings::instance()->value(DesktopSettings::Session, "datetime_format", "").toString());
+}
+
+void DesktopManager::updateDesktopSettings(){
+ //qDebug() << "Update Desktop Settings...";
+ QList<QScreen*> scrns = QGuiApplication::screens();
+ int wkspace = Lumina::NWS->currentWorkspace();
+ for(int i=0; i<scrns.length(); i++){ updateWallpaper(scrns[i]->name(), wkspace); }
+
+}
+
+void DesktopManager::updatePanelSettings(){
+ QList<QScreen*> scrns = QGuiApplication::screens();
+ int primary = QApplication::desktop()->primaryScreen();
+ //qDebug() << "Panel Settings Changed:" << primary << scrns.length();
+ QStringList total_ids;
+ for(int i=0; i<scrns.length(); i++){
+ //qDebug() << " - Check Screen Name:" << scrns[i]->name();
+ ScreenObject *sObj = RootDesktopObject::instance()->screen(scrns[i]->name());
+ if(sObj == 0){ continue; } //screen is not managed directly - skip it
+ QStringList ids = DesktopSettings::instance()->value(DesktopSettings::Panels, scrns[i]->name().replace("-","_")+"/active_ids", QStringList()).toStringList();
+ if(ids.isEmpty() && (scrns.length()==1 || i==primary)){
+ //qDebug() << " -- PRIMARY";
+ //Also look for the "default" panel id's for the primary/default screen
+ ids = DesktopSettings::instance()->value(DesktopSettings::Panels, "default/active_ids", QStringList()).toStringList();
+ }
+ ids.removeAll("");
+ //qDebug() << " -- settings:" << ids;
+ for(int j=0; j<ids.length(); j++){
+ total_ids << scrns[i]->name()+"/"+ids[j];
+ }
+ }
+ //Now do the global-session panels
+ QStringList ids = DesktopSettings::instance()->value(DesktopSettings::Panels, "session/active_ids", QStringList()).toStringList();
+ ids.removeAll("");
+ for(int i=0; i<ids.length(); i++){
+ total_ids << "session/"+ids[i];
+ }
+ //qDebug() << "Panel Settings Changed:" << total_ids;
+ RootDesktopObject::instance()->setPanels(total_ids); //put the new ones in place
+}
+
+void DesktopManager::updatePluginSettings(){
+
+}
+
+void DesktopManager::updateMenuSettings(){
+
+}
+
+void DesktopManager::updateAnimationSettings(){
+
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h
new file mode 100644
index 00000000..b3511318
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h
@@ -0,0 +1,50 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2017-2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the main class that updates the interface objects
+// on-demand as settings files and other stuff changes
+//===========================================
+#ifndef _LUMINA_DESKTOP_OBJECT_MANAGER_H
+#define _LUMINA_DESKTOP_OBJECT_MANAGER_H
+
+#include <global-includes.h>
+
+class DesktopManager : public QObject {
+ Q_OBJECT
+public:
+ DesktopManager();
+ ~DesktopManager();
+
+ void start();
+ void stop();
+
+private:
+ void updateWallpaper(QString screen_id, int wkspace);
+ void updatePlugins(QString plugin_id);
+
+public slots:
+ void workspaceChanged(int);
+ void settingsChanged(DesktopSettings::File);
+
+ void NewWindowAvailable(NativeWindowObject*);
+ void NewTrayWindowAvailable(NativeWindowObject*);
+
+ void syncWindowList();
+ void syncTrayWindowList();
+
+private slots:
+ void updateSessionSettings();
+ void updateDesktopSettings();
+ void updatePanelSettings();
+ void updatePluginSettings();
+ void updateMenuSettings();
+ void updateAnimationSettings();
+
+signals:
+ void PanelLocationsChanged(); //reserved screen space changed
+};
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp
new file mode 100644
index 00000000..a74d2585
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp
@@ -0,0 +1,65 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include <global-objects.h>
+#include "QMLImageProvider.h"
+
+QMLImageProvider::QMLImageProvider(QQmlImageProviderBase::ImageType type) : QQuickImageProvider(type, 0){
+
+}
+
+QMLImageProvider::~QMLImageProvider(){
+
+}
+
+/*QMLImageProvider* QMLImageProvider::instance(){
+ static QMLImageProvider *_prov = 0;
+ if(_prov==0){ _prov = new QMLImageProvider(); }
+ return _prov;
+}*/
+
+QImage QMLImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize){
+ NativeWindowObject *win = Lumina::NWS->findWindow( id.section(":",1,1).toInt(), false);
+ if(win==0){ win = Lumina::NWS->findTrayWindow(id.section(":",1,1).toInt()); }
+
+ if(!id.startsWith("image:")){ qDebug() << "Request Image:" << id << win << requestedSize; }
+
+ QImage img(requestedSize,QImage::Format_RGB32);
+ if(win==0){ img.fill("black"); } //invalid window ID (should never happen)
+ else if(id.startsWith("image:")){ img = Lumina::NWS->GetWindowImage(win); }
+ else if(id.startsWith("icon:")){
+ QIcon ico = win->property(NativeWindowObject::Icon).value<QIcon>();
+ QList<QSize> sizes = ico.availableSizes();
+ QSize sz(0,0);
+ //Just grab the largest size currently available
+ for(int i=0; i<sizes.length(); i++){
+ if(sz.width()<sizes[i].width() && sz.height()<sizes[i].height()){ sz = sizes[i]; }
+ }
+ qDebug() << "Icon Sizes:" <<sizes;
+ img = ico.pixmap(sz).toImage();
+ }
+ //qDebug() << "Got Window Image:" << img.size();
+ if(img.size().isNull()){
+ if(requestedSize.isValid()){ img = QImage(requestedSize,QImage::Format_RGB32); }
+ else{ img = QImage(QSize(64,64), QImage::Format_RGB32); }
+ img.fill("black");
+ }
+ //qDebug() << "Final Window Image:" << img.size();
+ if(size!=0){
+ size->setHeight(img.height());
+ size->setWidth( img.width() );
+ }
+ if(requestedSize.isValid() && !requestedSize.isNull() && img.size()!=requestedSize){
+ img = img.scaled(requestedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ return img;
+}
+
+QPixmap QMLImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize){
+ qDebug() << "Pixmap Requested:" << id;
+ QImage img = requestImage(id, size, requestedSize);
+ return QPixmap::fromImage(img);
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h
new file mode 100644
index 00000000..8719176e
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h
@@ -0,0 +1,26 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifndef _LUMINA_DESKTOP_QML_IMAGE_PROVIDER_H
+#define _LUMINA_DESKTOP_QML_IMAGE_PROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QImage>
+#include <QPixmap>
+#include <QSize>
+
+class QMLImageProvider : public QQuickImageProvider{
+public:
+ QMLImageProvider(QQmlImageProviderBase::ImageType);
+ ~QMLImageProvider();
+
+ //static QMLImageProvider* instance();
+
+ virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+ virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+};
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-QML.cpp
index 0cfa4e6b..8619544b 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-QML.cpp
@@ -1,14 +1,18 @@
//===========================================
// Lumina-desktop source code
-// Copyright (c) 2017, Ken Moore
+// Copyright (c) 2017-2018, Ken Moore
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
#include "RootWindow.h"
+#include "QMLImageProvider.h"
+#include <QQmlImageProviderBase>
+
+QQuickView *root_view;
RootWindow::RootWindow() : QObject(){
- root_win = QWindow::fromWinId( QX11Info::appRootWindow() ); //
- root_view = new QQuickView(root_win); //make it a child of the root window
+ root_win = QWindow::fromWinId( QX11Info::appRootWindow() );
+ root_view = new QQuickView(); //make it a child of the root window
root_obj = RootDesktopObject::instance();
syncRootSize();
connect(root_win, SIGNAL(widthChanged(int)), this, SLOT(syncRootSize()) );
@@ -16,6 +20,8 @@ RootWindow::RootWindow() : QObject(){
//Now setup the QQuickView
root_view->setResizeMode(QQuickView::SizeRootObjectToView);
root_view->engine()->rootContext()->setContextProperty("RootObject", root_obj);
+ root_view->engine()->addImageProvider("native_window", new QMLImageProvider(QQmlImageProviderBase::Image) );
+ //root_view->engine()->addImageProvider("native_window_icon", new QMLImageProvider(QQmlImageProviderBase::Pixmap) );
RootDesktopObject::RegisterType(); //make sure object classes are registered with the QML subsystems
}
@@ -27,15 +33,22 @@ RootWindow::~RootWindow(){
void RootWindow::start(){
root_view->setSource(QUrl("qrc:///qml/RootDesktop.qml"));
root_win->show();
+ if(root_view->parent()!=0){ root_view->parent()->show(); }
root_view->show();
}
+WId RootWindow::viewID(){
+ if(root_view->parent()!=0){ return root_view->parent()->winId(); }
+ return root_view->winId();
+}
+
void RootWindow::syncRootSize(){
//qDebug() << "Sync Root Size:" << root_win->width() << root_win->height() << root_view->geometry();
QList<QScreen*> screens = QApplication::screens();
QRect unif;
for(int i=0; i<screens.length(); i++){ unif = unif.united(screens[i]->geometry()); }
if(unif.width() != root_view->width() || unif.height() != root_view->height()){
+ if(root_view->parent()!=0){ root_view->parent()->setGeometry(0,0,unif.width(), unif.height()); }
root_view->setGeometry(0, 0, unif.width(), unif.height() );
emit RootResized(root_view->geometry());
}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-Widgets.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-Widgets.cpp
new file mode 100644
index 00000000..6a4c4cb0
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow-Widgets.cpp
@@ -0,0 +1,53 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2017-2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "RootWindow.h"
+
+//include the Widgets-based classes we need
+#include "RootDesktop.h"
+
+RootDesktop *root_view;
+
+RootWindow::RootWindow() : QObject(){
+ root_win = QWindow::fromWinId( QX11Info::appRootWindow() );
+ root_view = new RootDesktop(root_win); //make it a child of the root window
+ root_obj = RootDesktopObject::instance();
+ syncRootSize();
+ connect(root_win, SIGNAL(widthChanged(int)), this, SLOT(syncRootSize()) );
+ connect(root_win, SIGNAL(heightChanged(int)),this, SLOT(syncRootSize()) );
+}
+
+RootWindow::~RootWindow(){
+ root_view->deleteLater();
+ root_obj->deleteLater();
+}
+
+void RootWindow::start(){
+ root_win->show();
+ //if(root_view->parent()!=0){ root_view->parent()->show(); }
+ root_view->show();
+ root_view->start();
+ QTimer::singleShot(1000, this, SLOT(syncRootSize()) ); //just in case something changed during init routines
+}
+
+WId RootWindow::viewID(){
+ //if(root_view->parent()!=0){ return root_view->parent()->winId(); }
+ return root_view->winId();
+}
+
+void RootWindow::syncRootSize(){
+ //qDebug() << "Sync Root Size:" << root_win->width() << root_win->height() << root_view->geometry();
+ QList<QScreen*> screens = QApplication::screens();
+ QRect unif;
+ for(int i=0; i<screens.length(); i++){ unif = unif.united(screens[i]->geometry()); }
+ if(unif.width() != root_view->width() || unif.height() != root_view->height()){
+ //if(root_view->parent()!=0){ root_view->parent()->setGeometry(0,0,unif.width(), unif.height()); }
+ root_view->setGeometry(0, 0, unif.width(), unif.height() );
+ emit RootResized(root_view->geometry());
+ }
+ root_obj->updateScreens();
+ //qDebug() << " - after:" << root_view->geometry();
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.h b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.h
index ba489465..3c7414f2 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.h
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.h
@@ -1,18 +1,18 @@
//===========================================
// Lumina-desktop source code
-// Copyright (c) 2017, Ken Moore
+// Copyright (c) 2017-2018, Ken Moore
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
#ifndef _LUMINA_DESKTOP_ROOT_WINDOW_H
#define _LUMINA_DESKTOP_ROOT_WINDOW_H
#include <global-includes.h>
+#include "src-cpp/RootDesktopObject.h"
class RootWindow : public QObject{
Q_OBJECT
private:
QWindow *root_win;
- QQuickView *root_view;
RootDesktopObject *root_obj;
public:
@@ -21,6 +21,8 @@ public:
void start();
+ WId viewID();
+
public slots:
void syncRootSize();
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri
index e4c4faeb..89542e23 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri
@@ -1,11 +1,32 @@
-QT *= gui widgets qml quick
-
-SOURCES *= $${PWD}/RootWindow.cpp
-
-HEADERS *= $${PWD}/RootWindow.h
+QT *= gui widgets
#update the includepath so we can just #include as needed without paths
INCLUDEPATH *= $${PWD}
+SOURCES *= $${PWD}/Desktopmanager.cpp \
+ $${PWD}/QMLImageProvider.cpp
+
+HEADERS *= $${PWD}/RootWindow.h \
+ $${PWD}/DesktopManager.h
+
+#include the base objects
include($${PWD}/src-cpp/src-cpp.pri)
-include($${PWD}/src-qml/src-qml.pri)
+
+#Now do the QML/Widgets interface switch
+isEmpty(USE_QML){
+ #Widgets-based interface
+ DEFINES += USE_WIDGETS="true"
+
+ SOURCES *= $${PWD}/RootWindow-Widgets.cpp
+
+ include($${PWD}/src-widgets/src-widgets.pri)
+
+}else{
+ #QML-based interface
+ QT *= qml quick
+ DEFINES += USE_QML="true"
+ SOURCES *= $${PWD}/RootWindow-QML.cpp
+ HEADERS *= $${PWD}/QMLImageProvider.h
+
+ include($${PWD}/src-qml/src-qml.pri)
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp
new file mode 100644
index 00000000..e2cac852
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp
@@ -0,0 +1,330 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017-2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "NativeWindowObject.h"
+#include <QQmlEngine>
+#include <QDebug>
+#include <QBuffer>
+
+// == QML Type Registration ==
+void NativeWindowObject::RegisterType(){
+ static bool done = false;
+ if(done){ return; }
+ done=true;
+ qmlRegisterType<NativeWindowObject>("Lumina.Backend.NativeWindowObject",2,0, "NativeWindowObject");
+}
+
+// === PUBLIC ===
+NativeWindowObject::NativeWindowObject(WId id) : QObject(){
+ winid = id;
+ frameid = 0;
+ dmgID = dmg = 0;
+ geomTimer = new QTimer(this);
+ geomTimer->setSingleShot(true);
+ geomTimer->setInterval(50); //1/20 second
+ connect(geomTimer, SIGNAL(timeout()), this, SLOT(sendNewGeom()) );
+}
+
+NativeWindowObject::~NativeWindowObject(){
+ hash.clear();
+}
+
+void NativeWindowObject::addFrameWinID(WId fid){
+ frameid = fid;
+}
+
+void NativeWindowObject::addDamageID(unsigned int dmg){
+ dmgID = dmg;
+}
+
+bool NativeWindowObject::isRelatedTo(WId tmp){
+ return (relatedTo.contains(tmp) || winid == tmp || frameid == tmp);
+}
+
+WId NativeWindowObject::id(){
+ return winid;
+}
+
+WId NativeWindowObject::frameId(){
+ return frameid;
+}
+
+unsigned int NativeWindowObject::damageId(){
+ return dmgID;
+}
+
+QVariant NativeWindowObject::property(NativeWindowObject::Property prop){
+ if(hash.contains(prop)){ return hash.value(prop); }
+ else if(prop == NativeWindowObject::RelatedWindows){ return QVariant::fromValue(relatedTo); }
+ return QVariant(); //null variant
+}
+
+void NativeWindowObject::setProperty(NativeWindowObject::Property prop, QVariant val, bool force){
+ if(prop == NativeWindowObject::RelatedWindows){ relatedTo = val.value< QList<WId> >(); }
+ else if(prop == NativeWindowObject::None || (!force && hash.value(prop)==val)){ return; }
+ else if(prop == NativeWindowObject::WinImage){
+ //special case - This should never be actually set in the property hash
+ // it is loaded dynamically by the QMLImageProvider instead (prevent flickering/caching image)
+ } else{ hash.insert(prop, val); }
+ emitSinglePropChanged(prop);
+ emit PropertiesChanged(QList<NativeWindowObject::Property>() << prop, QList<QVariant>() << val);
+}
+
+void NativeWindowObject::setProperties(QList<NativeWindowObject::Property> props, QList<QVariant> vals, bool force){
+ for(int i=0; i<props.length(); i++){
+ if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property
+ if(props[i] == NativeWindowObject::None || (!force && (hash.value(props[i]) == vals[i])) ){
+ props.removeAt(i); vals.removeAt(i); i--; continue; //Invalid property or identical value
+ }else if(props[i] == NativeWindowObject::WinImage){
+ //special case - This should never be actually set in the property hash
+ // it is loaded dynamically by the QMLImageProvider instead (prevent flickering/caching image)
+ }else{
+ hash.insert(props[i], vals[i]);
+ }
+ emitSinglePropChanged(props[i]);
+ }
+ emit PropertiesChanged(props, vals);
+}
+
+void NativeWindowObject::requestProperty(NativeWindowObject::Property prop, QVariant val, bool force){
+ if(prop == NativeWindowObject::None || prop == NativeWindowObject::RelatedWindows || (!force && hash.value(prop)==val) ){ return; }
+ emit RequestPropertiesChange(winid, QList<NativeWindowObject::Property>() << prop, QList<QVariant>() << val);
+}
+
+void NativeWindowObject::requestProperties(QList<NativeWindowObject::Property> props, QList<QVariant> vals, bool force){
+ //Verify/adjust inputs as needed
+ for(int i=0; i<props.length(); i++){
+ if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property
+ if(props[i] == NativeWindowObject::None || props[i] == NativeWindowObject::RelatedWindows || (!force && hash.value(props[i])==vals[i]) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value
+ /*if( (props[i] == NativeWindowObject::Visible || props[i] == NativeWindowObject::Active) && frameid !=0){
+ //These particular properties needs to change the frame - not the window itself
+ emit RequestPropertiesChange(frameid, QList<NativeWindowObject::Property>() << props[i], QList<QVariant>() << vals[i]);
+ props.removeAt(i); vals.removeAt(i); i--;
+ }*/
+ }
+ emit RequestPropertiesChange(winid, props, vals);
+}
+
+QRect NativeWindowObject::geometry(){
+ //Calculate the "full" geometry of the window + frame (if any)
+ //Check that the size is between the min/max limitations
+ QSize size = hash.value(NativeWindowObject::Size).toSize();
+ QSize min = hash.value(NativeWindowObject::MinSize).toSize();
+ QSize max = hash.value(NativeWindowObject::MaxSize).toSize();
+ if(min.isValid() && min.width() > size.width() ){ size.setWidth(min.width()); }
+ if(min.isValid() && min.height() > size.height()){ size.setHeight(min.height()); }
+ if(max.isValid() && max.width() < size.width() && max.width()>min.width()){ size.setWidth(max.width()); }
+ if(max.isValid() && max.height() < size.height() && max.height()>min.height()){ size.setHeight(max.height()); }
+ //Assemble the full geometry
+ QRect geom( hash.value(NativeWindowObject::GlobalPos).toPoint(), size );
+ //Now adjust the window geom by the frame margins
+ QList<int> frame = hash.value(NativeWindowObject::FrameExtents).value< QList<int> >(); //Left,Right,Top,Bottom
+ //qDebug() << "Calculate Geometry:" << geom << frame;
+ if(frame.length()==4){
+ geom = geom.adjusted( -frame[0], -frame[2], frame[1], frame[3] );
+ }
+ //qDebug() << " - Total:" << geom;
+ return geom;
+}
+
+void NativeWindowObject::setGeometryNow(QRect geom){
+ updateGeometry(geom.x(), geom.y(), geom.width(), geom.height(), true);
+}
+
+// QML ACCESS FUNCTIONS (shortcuts for particular properties in a format QML can use)
+QString NativeWindowObject::winImage(){
+ //Need to alternate something on the end to ensure that QML knows to fetch the new image (non-cached only)
+ if(dmg==0){ dmg = 1; }
+ else{ dmg = 0; }
+ return "image://native_window/image:"+QString::number(winid)+":"+QString::number(dmg);
+}
+
+QString NativeWindowObject::name(){
+ return this->property(NativeWindowObject::Name).toString();
+}
+
+QString NativeWindowObject::title(){
+ return this->property(NativeWindowObject::Title).toString();
+}
+
+QString NativeWindowObject::shortTitle(){
+ QString tmp = this->property(NativeWindowObject::ShortTitle).toString();
+ if(tmp.isEmpty()){ tmp = title(); }
+ if(tmp.isEmpty()){ tmp = name(); }
+ return tmp;
+}
+
+QString NativeWindowObject::icon(){
+ if(icodmg==0){ icodmg=1; }
+ else{ icodmg = 0; }
+ qDebug() << "Window Icon:" << icodmg << this->property(NativeWindowObject::Icon).value<QIcon>().availableSizes();
+ return "image://native_window/icon:"+QString::number(winid)+":"+QString::number(icodmg);
+}
+
+//QML Button states
+bool NativeWindowObject::showCloseButton(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+bool NativeWindowObject::showMaxButton(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+bool NativeWindowObject::showMinButton(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND << NativeWindowObject::T_DIALOG;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+bool NativeWindowObject::showTitlebar(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+bool NativeWindowObject::showGenericButton(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+bool NativeWindowObject::showWindowFrame(){
+ QList<NativeWindowObject::Type> types = this->property(NativeWindowObject::WinTypes).value<QList < NativeWindowObject::Type> >();
+ QList<NativeWindowObject::Type> badtypes;
+ badtypes << NativeWindowObject::T_DESKTOP << NativeWindowObject::T_TOOLBAR << NativeWindowObject::T_MENU \
+ << NativeWindowObject::T_SPLASH << NativeWindowObject::T_DROPDOWN_MENU << NativeWindowObject::T_POPUP_MENU \
+ << NativeWindowObject::T_NOTIFICATION << NativeWindowObject::T_COMBO << NativeWindowObject::T_DND;
+ for(int i=0; i<types.length(); i++){
+ if(badtypes.contains(types[i])){ return false; }
+ }
+ return true;
+}
+
+//QML Window States
+bool NativeWindowObject::isSticky(){
+ return (this->property(NativeWindowObject::Workspace).toInt()<0 || this->property(NativeWindowObject::States).value<QList<NativeWindowObject::State> >().contains(NativeWindowObject::S_STICKY) );
+}
+
+bool NativeWindowObject::isVisible(){
+ return (this->property(NativeWindowObject::Visible).toBool() );
+}
+
+int NativeWindowObject::workspace(){
+ return this->property(NativeWindowObject::Workspace).toInt();
+}
+
+//QML Geometry reporting
+QRect NativeWindowObject::frameGeometry(){
+ return geometry();
+}
+
+QRect NativeWindowObject::imageGeometry(){
+ QRect geom( this->property(NativeWindowObject::GlobalPos).toPoint(), this->property(NativeWindowObject::Size).toSize() );
+ return geom;
+}
+
+void NativeWindowObject::updateGeometry(int x, int y, int width, int height, bool now){
+ // Full frame+window geometry - go ahead and pull it apart and only update the interior window geom
+ QList<int> fgeom = this->property(NativeWindowObject::FrameExtents).value<QList<int> >();
+ if(fgeom.isEmpty()){ fgeom << 0<<0<<0<<0; } //just in case (left/right/top/bottom)
+ QPoint pos(x+fgeom[0], y+fgeom[2]);
+ QSize sz(width-fgeom[0]-fgeom[1], height-fgeom[2]-fgeom[3]);
+ newgeom = QRect(pos, sz);
+ if(!now){
+ //qDebug() << "Update Geometry:" << fgeom << QRect(x,y,width,height) << pos << sz;
+ //requestProperties(QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size, QList<QVariant>() << pos << sz);
+ if(!geomTimer->isActive()){ geomTimer->start(); }
+ }else{
+ sendNewGeom();
+ }
+}
+
+// ==== PUBLIC SLOTS ===
+void NativeWindowObject::toggleVisibility(){
+ setProperty(NativeWindowObject::Visible, !property(NativeWindowObject::Visible).toBool() );
+}
+
+void NativeWindowObject::requestClose(){
+ emit RequestClose(winid);
+}
+
+void NativeWindowObject::requestKill(){
+ emit RequestKill(winid);
+}
+
+void NativeWindowObject::requestPing(){
+ emit RequestPing(winid);
+}
+
+// ==== PRIVATE ====
+void NativeWindowObject::emitSinglePropChanged(NativeWindowObject::Property prop){
+ //Simple switch to emit the QML-usable signals as properties are changed
+ switch(prop){
+ case NativeWindowObject::Name:
+ emit nameChanged(); break;
+ case NativeWindowObject::Title:
+ emit titleChanged();
+ if(this->property(NativeWindowObject::ShortTitle).toString().isEmpty()){ emit shortTitleChanged(); }
+ break;
+ case NativeWindowObject::ShortTitle:
+ emit shortTitleChanged(); break;
+ case NativeWindowObject::Icon:
+ emit iconChanged(); break;
+ case NativeWindowObject::Workspace:
+ case NativeWindowObject::States:
+ emit stickyChanged(); break;
+ case NativeWindowObject::WinImage:
+ emit winImageChanged(); break;
+ case NativeWindowObject::WinTypes:
+ emit winTypeChanged(); break;
+ case NativeWindowObject::Visible:
+ emit visibilityChanged(); break;
+ default:
+ break; //do nothing otherwise
+ }
+}
+
+void NativeWindowObject::sendNewGeom(){
+ QList<NativeWindowObject::Property> props; props << NativeWindowObject::GlobalPos << NativeWindowObject::Size;
+ QList<QVariant> vals; vals << newgeom.topLeft() << newgeom.size();
+ requestProperties(props, vals);
+ setProperties(props,vals);
+ emit VerifyNewGeometry(winid);
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h
new file mode 100644
index 00000000..87df1f10
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h
@@ -0,0 +1,178 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017-2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a container object for setting/announcing changes
+// in a native window's properties.
+// The WM will usually run the "setProperty" function on this object,
+// and any other classes/widgets which watch this window can act appropriatly after-the-fact
+// Non-WM classes should use the "Request" signals to ask the WM to do something, and listen for changes later
+//===========================================
+#ifndef _LUMINA_SOURCES_NATIVE_WINDOW_OBJECT_H
+#define _LUMINA_SOURCES_NATIVE_WINDOW_OBJECT_H
+#include "global-includes.h"
+
+class NativeWindowObject : public QObject{
+ Q_OBJECT
+ // QML-ACCESSIBLE PROPERTIES
+ Q_PROPERTY( QString winImage READ winImage NOTIFY winImageChanged)
+ Q_PROPERTY( QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY( QString title READ title NOTIFY titleChanged)
+ Q_PROPERTY( QString shortTitle READ shortTitle NOTIFY shortTitleChanged)
+ Q_PROPERTY( QString icon READ icon NOTIFY iconChanged)
+ Q_PROPERTY( bool sticky READ isSticky NOTIFY stickyChanged)
+ Q_PROPERTY(bool isVisible READ isVisible NOTIFY visibilityChanged)
+ //Button/Titlebar visibility
+ Q_PROPERTY( bool showCloseButton READ showCloseButton NOTIFY winTypeChanged)
+ Q_PROPERTY( bool showMinButton READ showMinButton NOTIFY winTypeChanged)
+ Q_PROPERTY( bool showMaxButton READ showMaxButton NOTIFY winTypeChanged)
+ Q_PROPERTY( bool showTitlebar READ showTitlebar NOTIFY winTypeChanged)
+ Q_PROPERTY( bool showGenericButton READ showGenericButton NOTIFY winTypeChanged)
+ Q_PROPERTY( bool showWindowFrame READ showWindowFrame NOTIFY winTypeChanged)
+ //Geometry information
+ Q_PROPERTY( QRect frameGeometry READ frameGeometry NOTIFY geomChanged)
+ Q_PROPERTY( QRect imageGeometry READ imageGeometry NOTIFY geomChanged)
+
+public:
+ enum State{ S_MODAL, S_STICKY, S_MAX_VERT, S_MAX_HORZ, S_SHADED, S_SKIP_TASKBAR, S_SKIP_PAGER, S_HIDDEN, S_FULLSCREEN, S_ABOVE, S_BELOW, S_ATTENTION };
+ enum Type{T_DESKTOP, T_DOCK, T_TOOLBAR, T_MENU, T_UTILITY, T_SPLASH, T_DIALOG, T_DROPDOWN_MENU, T_POPUP_MENU, T_TOOLTIP, T_NOTIFICATION, T_COMBO, T_DND, T_NORMAL };
+ enum Action {A_MOVE, A_RESIZE, A_MINIMIZE, A_SHADE, A_STICK, A_MAX_VERT, A_MAX_HORZ, A_FULLSCREEN, A_CHANGE_DESKTOP, A_CLOSE, A_ABOVE, A_BELOW};
+ enum Location { TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT };
+ Q_ENUM(Location)
+
+ enum Property{ /*QVariant Type*/
+ None=0, /*null*/
+ MinSize=1, /*QSize*/
+ MaxSize=2, /*QSize*/
+ Size=3, /*QSize*/
+ GlobalPos=4, /*QPoint*/
+ Title=5, /*QString*/
+ ShortTitle=6, /*QString*/
+ Icon=7, /*QIcon*/
+ Name=8, /*QString*/
+ Workspace=9, /*int*/
+ States=10, /*QList<NativeWindowObject::State> : Current state of the window */
+ WinTypes=11, /*QList<NativeWindowObject::Type> : Current type of window (typically does not change)*/
+ WinActions=12, /*QList<NativeWindowObject::Action> : Current actions that the window allows (Managed/set by the WM)*/
+ FrameExtents=13, /*QList<int> : [Left, Right, Top, Bottom] in pixels */
+ RelatedWindows=14, /* QList<WId> - better to use the "isRelatedTo(WId)" function instead of reading this directly*/
+ Active=15, /*bool*/
+ Visible=16, /*bool*/
+ WinImage=17 /*QImage*/
+ };
+
+ static QList<NativeWindowObject::Property> allProperties(){
+ //Return all the available properties (excluding "None" and "FrameExtents" (WM control only) )
+ QList<NativeWindowObject::Property> props;
+ props << MinSize << MaxSize << Size << GlobalPos << Title << ShortTitle << Icon << Name << Workspace \
+ << States << WinTypes << WinActions << RelatedWindows << Active << Visible;
+ return props;
+ };
+
+ static void RegisterType();
+
+ NativeWindowObject(WId id = 0);
+ ~NativeWindowObject();
+
+ void addFrameWinID(WId);
+ void addDamageID(unsigned int);
+ bool isRelatedTo(WId);
+
+ WId id();
+ WId frameId();
+ unsigned int damageId();
+
+ //QWindow* window();
+
+ QVariant property(NativeWindowObject::Property);
+ void setProperty(NativeWindowObject::Property, QVariant, bool force = false);
+ void setProperties(QList<NativeWindowObject::Property>, QList<QVariant>, bool force = false);
+ void requestProperty(NativeWindowObject::Property, QVariant, bool force = false);
+ void requestProperties(QList<NativeWindowObject::Property>, QList<QVariant>, bool force = false);
+
+ Q_INVOKABLE QRect geometry(); //this returns the "full" geometry of the window (window + frame)
+ void setGeometryNow(QRect geom);
+
+ // QML ACCESS FUNCTIONS (shortcuts for particular properties in a format QML can use)
+ Q_INVOKABLE QString winImage();
+ Q_INVOKABLE QString name();
+ Q_INVOKABLE QString title();
+ Q_INVOKABLE QString shortTitle();
+ Q_INVOKABLE QString icon();
+ //QML Button states
+ Q_INVOKABLE bool showCloseButton();
+ Q_INVOKABLE bool showMaxButton();
+ Q_INVOKABLE bool showMinButton();
+ Q_INVOKABLE bool showTitlebar();
+ Q_INVOKABLE bool showGenericButton();
+ Q_INVOKABLE bool showWindowFrame();
+ //QML Window States
+ Q_INVOKABLE bool isSticky();
+ Q_INVOKABLE bool isVisible();
+ Q_INVOKABLE int workspace();
+
+ //QML Geometry reporting
+ Q_INVOKABLE QRect frameGeometry();
+ Q_INVOKABLE QRect imageGeometry();
+ Q_INVOKABLE void updateGeometry(int x, int y, int width, int height, bool now = false); //For QML to change the current window position
+
+public slots:
+ Q_INVOKABLE void toggleVisibility();
+ Q_INVOKABLE void requestClose(); //ask the app to close the window (may/not depending on activity)
+ Q_INVOKABLE void requestKill(); //ask the WM to kill the app associated with this window (harsh - only use if not responding)
+ Q_INVOKABLE void requestPing(); //ask the app if it is still active (a WindowNotResponding signal will get sent out if there is no reply);
+
+private:
+ QHash <NativeWindowObject::Property, QVariant> hash;
+ //QWindow *WIN;
+ WId winid, frameid;
+ QList<WId> relatedTo;
+ unsigned int dmgID, dmg, icodmg;
+ //Collation/Delay for window resize events
+ QTimer *geomTimer;
+ QRect newgeom;
+
+ void emitSinglePropChanged(NativeWindowObject::Property);
+
+private slots:
+ void sendNewGeom();
+
+signals:
+ //General Notifications
+ void PropertiesChanged(QList<NativeWindowObject::Property>, QList<QVariant>);
+ void RequestPropertiesChange(WId, QList<NativeWindowObject::Property>, QList<QVariant>);
+ void WindowClosed(WId);
+ void WindowNotResponding(WId); //will be sent out if a window does not respond to a ping request
+
+ //Action Requests (not automatically emitted - typically used to ask the WM to do something)
+ //Note: "WId" should be the NativeWindowObject id()
+ void RequestClose(WId); //Close the window
+ void RequestKill(WId); //Kill the window/app (usually from being unresponsive)
+ void RequestPing(WId); //Verify that the window is still active (such as not closing after a request
+ void RequestReparent(WId, WId, QPoint); //client window, frame window, relative origin point in frame
+ void VerifyNewGeometry(WId);
+ // System Tray Icon Embed/Unembed Requests
+ //void RequestEmbed(WId, QWidget*);
+ //void RequestUnEmbed(WId, QWidget*);
+
+ // QML update signals
+ void winImageChanged();
+ void nameChanged();
+ void titleChanged();
+ void shortTitleChanged();
+ void iconChanged();
+ void stickyChanged();
+ void winTypeChanged();
+ void geomChanged();
+ void visibilityChanged();
+};
+
+// Declare the enumerations as Qt MetaTypes
+Q_DECLARE_METATYPE(NativeWindowObject::Type);
+Q_DECLARE_METATYPE(NativeWindowObject::Action);
+Q_DECLARE_METATYPE(NativeWindowObject::State);
+Q_DECLARE_METATYPE(NativeWindowObject::Property);
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp
index 471da58f..e8830bde 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp
@@ -5,6 +5,8 @@
// See the LICENSE file for full details
//===========================================
#include "PanelObject.h"
+#include <global-objects.h>
+
#include <QQmlEngine>
#include <QDebug>
@@ -28,6 +30,13 @@ int PanelObject::x(){ return geom.x(); }
int PanelObject::y(){ return geom.y(); }
int PanelObject::width(){ return geom.width(); }
int PanelObject::height(){ return geom.height(); }
+bool PanelObject::isVertical(){
+ return ( geom.width() < geom.height() );
+}
+
+QStringList PanelObject::plugins(){
+ return panel_plugins;
+}
void PanelObject::setBackground(QString fileOrColor){
if(bg!=fileOrColor){
@@ -42,3 +51,70 @@ void PanelObject::setGeometry( QRect newgeom ){
emit geomChanged();
}
}
+
+void PanelObject::setPlugins( QStringList plist){
+ //Iterate through the list and find the URL's for the files
+ QStringList dirs; dirs << ":/qml/plugins/"; //add local directories here
+ for(int i=0; i<plist.length(); i++){
+ bool found = false;
+ for(int j=0; j<dirs.length() && !found; j++){
+ if(QFile::exists(dirs[j]+plist[i]+".qml")){
+ plist[i] = QUrl::fromLocalFile(dirs[j]+plist[i]+".qml").url();
+ found = true;
+ }
+ }
+ if(!found){
+ qWarning() << "Could not find panel plugin on system:" << plist[i];
+ plist.removeAt(i);
+ i--;
+ }
+ }
+ //Now update the internal list if it has changed
+ if(panel_plugins != plist){
+ panel_plugins = plist;
+ this->emit pluginsChanged();
+ }
+}
+
+void PanelObject::syncWithSettings(QRect parent_geom){
+ //Read off all the settings
+ QString id = panel_id.section("/",-1); //last section (allow for prefixes to distinguish multiple monitors with the same profile but different screens)
+ //qDebug() << "Sync Panel Settings:" << panel_id << id << parent_geom;
+ QString anchor = DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/anchor", "bottom").toString().toLower();
+ QString align = DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/align", "center").toString().toLower();
+ double length = DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/length_percent", 100).toDouble()/100.0;
+ double width = DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/width_font_percent", 2.1).toDouble();
+ width = qRound(width * QApplication::fontMetrics().height() );
+ this->setBackground( DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/background", "rgba(0,0,0,120)").toString() );
+ // qDebug() << "Update Panel:" << panel_id << id << anchor+"/"+align << length << width;
+ //Now calculate the geometry of the panel
+ QRect newgeom;
+ //Figure out the size of the panel
+ if(anchor=="top" || anchor=="bottom"){ newgeom.setWidth( parent_geom.width()*length ); newgeom.setHeight(width); }
+ else{ newgeom.setWidth(width); newgeom.setHeight(parent_geom.height()*length); }
+ //qDebug() << " - Size:" << newgeom;
+ //Now figure out the location of the panel
+ if(align=="left" || align=="top"){
+ if(anchor=="top" || anchor=="left"){ newgeom.moveTopLeft(QPoint(0,0)); }
+ else if(anchor=="right"){ newgeom.moveTopRight(QPoint(parent_geom.width(), 0)); }
+ else{ newgeom.moveBottomLeft(QPoint(0, parent_geom.height()) ); } //bottom by default
+
+ }else if(align=="right" || align=="bottom"){
+ if(anchor=="top"){ newgeom.moveTopRight(QPoint(parent_geom.width(),0)); }
+ else if(anchor=="left"){ newgeom.moveBottomLeft(QPoint(0, parent_geom.height())); }
+ else if(anchor=="right"){ newgeom.moveBottomRight(QPoint(parent_geom.width(), parent_geom.height())); }
+ else{ newgeom.moveBottomRight(QPoint(parent_geom.width(), parent_geom.height()) ); }
+
+ }else{ //center
+ if(anchor=="top"){ newgeom.moveTopLeft(QPoint( (parent_geom.width()-newgeom.width())/2,0)); }
+ else if(anchor=="left"){ newgeom.moveTopLeft(QPoint(0, (parent_geom.height()-newgeom.height())/2 )); }
+ else if(anchor=="right"){ newgeom.moveTopRight(QPoint(parent_geom.width(), (parent_geom.height()-newgeom.height())/2 )); }
+ else{ newgeom.moveBottomLeft(QPoint( (parent_geom.width()-newgeom.width())/2, parent_geom.height()) ); }
+ }
+ //qDebug() << " - Calculated Geometry (relative to parent):" << newgeom;
+ //Note: newgeom is currently in parent-relative coordinates (not global)
+ newgeom.translate(parent_geom.x(), parent_geom.y());
+ //qDebug() << " - Calculated Geometry (global):" << newgeom;
+ this->setGeometry(newgeom); //shift to global coordinates
+ this->setPlugins( DesktopSettings::instance()->value(DesktopSettings::Panels, id+"/plugins", QStringList()).toStringList() );
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h
index a788fa07..945deec0 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h
@@ -20,10 +20,13 @@ class PanelObject : public QObject {
Q_PROPERTY( int y READ y NOTIFY geomChanged)
Q_PROPERTY( int width READ width NOTIFY geomChanged)
Q_PROPERTY( int height READ height NOTIFY geomChanged)
+ Q_PROPERTY( bool isVertical READ isVertical NOTIFY geomChanged)
+ Q_PROPERTY( QStringList plugins READ plugins NOTIFY pluginsChanged)
private:
QString panel_id, bg;
QRect geom;
+ QStringList panel_plugins;
public:
PanelObject(QString id = "", QObject *parent = 0);
@@ -36,14 +39,19 @@ public:
Q_INVOKABLE int y();
Q_INVOKABLE int width();
Q_INVOKABLE int height();
+ Q_INVOKABLE bool isVertical();
+ Q_INVOKABLE QStringList plugins();
public slots:
void setBackground(QString fileOrColor);
void setGeometry(QRect newgeom);
+ void syncWithSettings(QRect parent_geom);
+ void setPlugins(QStringList plist);
signals:
void backgroundChanged();
void geomChanged();
+ void pluginsChanged();
};
#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp
index 5750ac2d..d9a81f54 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp
@@ -8,12 +8,16 @@
#include <QQmlEngine>
#include <QApplication>
#include <QScreen>
-
+#include <global-objects.h>
#include <QDebug>
// === PUBLIC ===
RootDesktopObject::RootDesktopObject(QObject *parent) : QObject(parent){
+ last_window_up = 0;
updateScreens(); //make sure the internal list is updated right away
+ connect(this, SIGNAL(changePanels(QStringList)), this, SLOT(setPanels(QStringList)) );
+ currentTimeTimer = new QTimer(this);
+ connect(currentTimeTimer, SIGNAL(timeout()), this, SLOT(updateCurrentTime()) );
}
RootDesktopObject::~RootDesktopObject(){
@@ -27,6 +31,8 @@ void RootDesktopObject::RegisterType(){
qmlRegisterType<RootDesktopObject>("Lumina.Backend.RootDesktopObject", 2, 0, "RootDesktopObject");
//Also register any types that are needed by this class
ScreenObject::RegisterType();
+ NativeWindowObject::RegisterType();
+ OSInterface::RegisterType();
}
RootDesktopObject* RootDesktopObject::instance(){
@@ -36,14 +42,14 @@ RootDesktopObject* RootDesktopObject::instance(){
//QML Read Functions
QStringList RootDesktopObject::screens(){
- qDebug() << "Request Screens:" << s_objects.length();
+ //qDebug() << "Request Screens:" << s_objects.length();
QStringList names;
for(int i=0; i<s_objects.length(); i++){ names << s_objects[i]->name(); }
return names;
}
ScreenObject* RootDesktopObject::screen(QString id){
- qDebug() << "Got Screen Request:" << id;
+ //qDebug() << "Got Screen Request:" << id;
for(int i=0; i<s_objects.length(); i++){
if(s_objects[i]->name()==id){ return s_objects[i]; }
}
@@ -72,7 +78,7 @@ QStringList RootDesktopObject::windows(){
return names;
}
-NativeWindow* RootDesktopObject::window(QString id){
+NativeWindowObject* RootDesktopObject::window(QString id){
//qDebug() << "Got Panel Request:" << id;
WId chk = id.toInt(); //numerical ID's in this case
for(int i=0; i<window_objects.length(); i++){
@@ -81,26 +87,159 @@ NativeWindow* RootDesktopObject::window(QString id){
return 0;
}
+QStringList RootDesktopObject::trayWindows(){
+ //qDebug() << "Request Panels:" << panel_objects.length();
+ QStringList names;
+ for(int i=0; i<tray_window_objects.length(); i++){ names << QString::number(tray_window_objects[i]->id()); }
+ return names;
+}
+
+NativeWindowObject* RootDesktopObject::trayWindow(QString id){
+ //qDebug() << "Got Panel Request:" << id;
+ WId chk = id.toInt(); //numerical ID's in this case
+ for(int i=0; i<tray_window_objects.length(); i++){
+ if(tray_window_objects[i]->id()==chk){ return tray_window_objects[i]; }
+ }
+ return 0;
+}
+
+bool RootDesktopObject::hasTrayWindows(){
+ return !tray_window_objects.isEmpty();
+}
+
+QString RootDesktopObject::currentTime(){
+ return currentTimeString;
+}
+
+QDateTime RootDesktopObject::currentDateTime(){
+ return currentDateTimeStruct;
+}
+
+OSInterface* RootDesktopObject::os_interface(){
+ return OSInterface::instance();
+}
+
void RootDesktopObject::setPanels(QList<PanelObject*> list){
panel_objects = list;
emit panelsChanged();
}
-void RootDesktopObject::setWindows(QList<NativeWindow*> list){
+void RootDesktopObject::setPanels(QStringList ids){
+ //Make this thread-safe for object creation
+ if(this->thread() != QThread::currentThread()){
+ //use internal signal/slot combo to change threads
+ this->emit changePanels(ids);
+ return;
+ }
+ //qDebug() << "GOT PANEL CHANGE:" << ids;
+ //Get the current bounding rectangle for the session
+ QRect total;
+ bool change = false;
+ for(int i=0; i<=s_objects.length(); i++){
+ QRect geom;
+ QString prefix;
+ if(i==s_objects.length()){
+ geom = total; //session geometry
+ prefix="session/";
+ }else{
+ geom = s_objects[i]->geometry();
+ total = total.united(geom);
+ prefix=s_objects[i]->name()+"/";
+ }
+ QStringList newids = ids.filter(prefix);
+ //qDebug() << " Check Panel IDs:" << prefix << newids << ids;
+ //First update/remove any current panel objects
+ for(int i=0; i<panel_objects.length(); i++){
+ if(newids.contains(panel_objects[i]->name()) ){
+ //qDebug() << " - Update Existing Panel:" << panel_objects[i]->name();
+ newids.removeAll(panel_objects[i]->name()); //already handled
+ panel_objects[i]->syncWithSettings(geom);
+ }else if(panel_objects[i]->name().startsWith(prefix) ){
+ //qDebug() << " - Remove Existing Panel:" << panel_objects[i]->name();
+ panel_objects.takeAt(i)->deleteLater();
+ i--;
+ change = true; //list changed
+ }
+ }
+ //Now create any new panel objects as needed
+ for(int i=0; i<newids.length(); i++){
+ //qDebug() << " - Create Panel:" << newids[i];
+ PanelObject *tmp = new PanelObject(newids[i], this);
+ tmp->syncWithSettings(geom);
+ panel_objects << tmp;
+ change = true; //list changed
+ }
+ } //end loop over screens+session
+ if(change){ emit panelsChanged(); }
+}
+
+void RootDesktopObject::setWindows(QList<NativeWindowObject*> list){
window_objects = list;
emit windowsChanged();
+ mousePositionChanged(true);
+}
+
+void RootDesktopObject::setTrayWindows(QList<NativeWindowObject*> list){
+ tray_window_objects = list;
+ emit trayWindowsChanged();
+ mousePositionChanged(true);
+}
+
+void RootDesktopObject::updateCurrentTimeFormat(QString fmt){
+ //sanitize the time format as needed
+ if(fmt.contains("z")){ fmt.replace("z",""); } //do not allow millisecond updates - too fast for the desktop
+ fmt = fmt.simplified();
+ //Verify that anything has changed first
+ if(currentTimeFormat == fmt && currentTimeTimer->isActive()){ return; } //nothing changed
+ if(currentTimeTimer->isActive()){ currentTimeTimer->stop(); } //make sure this does not trigger during the changeover
+ currentTimeFormat = fmt;
+ int interval = 1000; //default to 1 second intervals
+ //Adjust the refresh time for the clock based on the smallest unit requested
+ if(fmt.contains("s")){ interval=500; } //1/2 second pings for 1-second displays
+ else if(fmt.contains("m") || currentTimeFormat.isEmpty()){ interval = 5000; } //5 second pings for 1-minute displays
+ else if(fmt.contains("h")){ interval = 30000; } //30 second pings for 1-hour displays
+ currentTimeTimer->setInterval(interval);
+ updateCurrentTime(); //refresh the currently-available time
+ QTimer::singleShot(0,currentTimeTimer, SLOT(start()) );//start the update timer
}
void RootDesktopObject::logout(){
- emit startLogout();
+ //Emit the logout signal in a few ms (let the display close/sync first)
+ QTimer::singleShot(50, this, SIGNAL(startLogout()));
}
void RootDesktopObject::lockscreen(){
emit lockScreen();
}
-void RootDesktopObject::mousePositionChanged(){
+void RootDesktopObject::mousePositionChanged(bool lowerall){
emit mouseMoved();
+ // Go through the transparent windows (in order of high->low in stack)
+ // and raise/lower the transparent overlays as needed
+ QPoint pos = QCursor::pos();
+ for(int i=window_objects.length()-1; i>=0; i--){
+ if(window_objects[i]->geometry().contains(pos) ){
+ if(last_window_up!= window_objects[i]){
+ if(last_window_up!=0){ Lumina::NWS->lowerWindow(last_window_up); }
+ Lumina::NWS->raiseWindow(window_objects[i]);
+ last_window_up = window_objects[i];
+ }
+ if(!lowerall){ return; } //found the currently-hovered window
+ }else if(lowerall){
+ Lumina::NWS->lowerWindow(window_objects[i]);
+ }
+ }
+ //failover for when no window has the mouse over it (lower all of them)
+ if(last_window_up!=0 && !lowerall){ Lumina::NWS->lowerWindow(last_window_up); }
+}
+
+void RootDesktopObject::launchApp(QString appOrPath){
+ emit launchApplication(appOrPath);
+}
+
+//C++ Access Functions (simplifications for the QML ones)
+QList<NativeWindowObject*> RootDesktopObject::windowObjects(){
+ return window_objects;
}
// === PUBLIC SLOTS ===
@@ -129,5 +268,26 @@ void RootDesktopObject::ChangeWallpaper(QString screen, QString value){
}
}
+QString RootDesktopObject::CurrentWallpaper(QString screen){
+ for(int i=0; i<s_objects.length(); i++){
+ if(s_objects[i]->name()==screen){ return s_objects[i]->background();}
+ }
+ return ""; //unknown
+}
+
+
// === PRIVATE ===
+
+// === PRIVATE SLOTS ===
+void RootDesktopObject::updateCurrentTime(){
+ QDateTime DT = QDateTime::currentDateTime();
+ QString tmp;
+ if(currentTimeFormat.isEmpty()){ tmp = DT.toString(Qt::DefaultLocaleShortDate); }
+ else{ tmp = DT.toString(currentTimeFormat); }
+ if(tmp!=currentTimeString){ //prevent sending signals to update the interface if nothing changed
+ currentDateTimeStruct = DT;
+ currentTimeString = tmp;
+ emit currentTimeChanged();
+ }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h
index 838b5f7d..3c525848 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h
@@ -8,18 +8,20 @@
//===========================================
#ifndef _LUMINA_DESKTOP_QML_BACKEND_ROOT_DESKTOP_OBJECT_H
#define _LUMINA_DESKTOP_QML_BACKEND_ROOT_DESKTOP_OBJECT_H
-#include <QObject>
-#include <QList>
#include <global-includes.h>
-
-#include "ScreenObject.h"
+#include <ScreenObject.h>
+#include <QThread>
class RootDesktopObject : public QObject{
Q_OBJECT
//Define all the QML Properties here (interface between QML and the C++ methods below)
Q_PROPERTY( QStringList screens READ screens NOTIFY screensChanged)
Q_PROPERTY( QStringList panels READ panels NOTIFY panelsChanged)
- Q_PROPERTY( QStringList windows READ windows NOTIFY windowsChanged);
+ Q_PROPERTY( QStringList windows READ windows NOTIFY windowsChanged)
+ Q_PROPERTY( QStringList trayWindows READ trayWindows NOTIFY trayWindowsChanged)
+ Q_PROPERTY( bool hasTrayWindows READ hasTrayWindows NOTIFY trayWindowsChanged)
+ Q_PROPERTY( QString currentTime READ currentTime NOTIFY currentTimeChanged);
+ Q_PROPERTY( QDateTime currentDateTime READ currentDateTime NOTIFY currentTimeChanged);
public:
//main contructor/destructor
@@ -37,35 +39,65 @@ public:
Q_INVOKABLE QStringList panels();
Q_INVOKABLE PanelObject* panel(QString id);
Q_INVOKABLE QStringList windows();
- Q_INVOKABLE NativeWindow* window(QString id);
+ Q_INVOKABLE NativeWindowObject* window(QString id);
+ Q_INVOKABLE QStringList trayWindows();
+ Q_INVOKABLE NativeWindowObject* trayWindow(QString id);
+ Q_INVOKABLE bool hasTrayWindows();
+ Q_INVOKABLE QString currentTime();
+ Q_INVOKABLE QDateTime currentDateTime();
- void setPanels(QList<PanelObject*> list);
- void setWindows(QList<NativeWindow*> list);
+ //QML Globals Access
+ Q_INVOKABLE OSInterface* os_interface();
//QML Access Functions
Q_INVOKABLE void logout();
Q_INVOKABLE void lockscreen();
- Q_INVOKABLE void mousePositionChanged();
+ Q_INVOKABLE void mousePositionChanged(bool lowerall = false);
+ Q_INVOKABLE void launchApp(QString appOrPath);
+
+ //C++ Access Functions (simplifications for the QML ones)
+ QList<NativeWindowObject*> windowObjects();
private:
QList<ScreenObject*> s_objects;
QList<PanelObject*> panel_objects;
- QList<NativeWindow*> window_objects;
+ QList<NativeWindowObject*> window_objects;
+ QList<NativeWindowObject*> tray_window_objects;
+ QPointer<NativeWindowObject> last_window_up;
+ QTimer *currentTimeTimer;
+ QString currentTimeFormat, currentTimeString;
+ QDateTime currentDateTimeStruct;
public slots:
void updateScreens(); //rescan/update screen objects
void ChangeWallpaper(QString screen, QString);
+ QString CurrentWallpaper(QString screen);
+
+ void setPanels(QList<PanelObject*> list);
+ void setPanels(QStringList ids);
+ QList<PanelObject*> panelObjectList(){ return panel_objects; }
+
+ void setWindows(QList<NativeWindowObject*> list);
+ void setTrayWindows(QList<NativeWindowObject*> list);
+
+ void updateCurrentTimeFormat(QString);
private slots:
+ void updateCurrentTime();
signals:
void screensChanged();
void panelsChanged();
void windowsChanged();
+ void trayWindowsChanged();
+ void currentTimeChanged();
void startLogout();
void mouseMoved();
void lockScreen();
+ void launchApplication(QString);
+ //Internal signals for thread-safety
+ void changePanels(QStringList);
};
#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp
index 82622403..c754906d 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp
@@ -10,6 +10,7 @@
ScreenObject::ScreenObject(QScreen *scrn, QObject *parent) : QObject(parent){
bg_screen = scrn;
+ connect(this, SIGNAL(changePanels(QStringList)), this, SLOT(setPanels(QStringList)) );
}
void ScreenObject::RegisterType(){
@@ -22,7 +23,7 @@ void ScreenObject::RegisterType(){
}
QString ScreenObject::name(){ return bg_screen->name(); }
-QString ScreenObject::background(){ qDebug() << "Got Background:" << bg_screen->name() << bg << bg_screen->geometry(); return bg; }
+QString ScreenObject::background(){ return bg; }
int ScreenObject::x(){ return bg_screen->geometry().x(); }
int ScreenObject::y(){ return bg_screen->geometry().y(); }
int ScreenObject::width(){ return bg_screen->geometry().width(); }
@@ -40,6 +41,37 @@ void ScreenObject::setPanels(QList<PanelObject*> list){
emit panelsChanged();
}
+void ScreenObject::setPanels(QStringList ids){
+ //Make this thread-safe for object creation
+ if(this->thread() != QThread::currentThread()){
+ //use internal signal/slot combo to change threads
+ this->emit changePanels(ids);
+ return;
+ }
+
+ //First update/remove any current panel objects
+ bool change = false;
+ for(int i=0; i<panel_objects.length(); i++){
+ if(ids.contains(panel_objects[i]->name()) ){
+ ids.removeAll(panel_objects[i]->name()); //already handled
+ panel_objects[i]->syncWithSettings(bg_screen->geometry());
+ }else{
+ panel_objects.takeAt(i)->deleteLater();
+ i--;
+ change = true; //list changed
+ }
+ }
+ //Now create any new panel objects as needed
+ for(int i=0; i<ids.length(); i++){
+ PanelObject *tmp = new PanelObject(ids[i], this);
+ tmp->syncWithSettings(bg_screen->geometry());
+ panel_objects << tmp;
+ change = true; //list changed
+ }
+ if(change){ emit panelsChanged(); }
+}
+
+
//QML Read Functions
QStringList ScreenObject::panels(){
//qDebug() << "Request Panels:" << panel_objects.length();
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h
index 1afff6d2..250c9403 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h
@@ -11,6 +11,7 @@
#include <QObject>
#include <QString>
#include <QScreen>
+#include <QThread>
#include "PanelObject.h"
@@ -42,16 +43,24 @@ public:
Q_INVOKABLE int height();
Q_INVOKABLE QStringList panels();
Q_INVOKABLE PanelObject* panel(QString id);
+ Q_INVOKABLE QRect geometry(){ return bg_screen->geometry(); }
void setPanels(QList<PanelObject*> list);
+ QList<PanelObject*> panelObjectList(){ return panel_objects; }
+
public slots:
+ void setPanels(QStringList ids);
void setBackground(QString fileOrColor);
signals:
void backgroundChanged();
void geomChanged();
void panelsChanged();
+
+ //Internal signals for thread-safety
+ void changePanels(QStringList);
+
};
#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/src-cpp.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/src-cpp.pri
index 899f4968..25bdc019 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/src-cpp.pri
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/src-cpp.pri
@@ -1,9 +1,11 @@
SOURCES *= $${PWD}/RootDesktopObject.cpp \
$${PWD}/ScreenObject.cpp \
- $${PWD}/PanelObject.cpp
+ $${PWD}/PanelObject.cpp \
+ $${PWD}/NativeWindowObject.cpp
HEADERS *= $${PWD}/RootDesktopObject.h \
$${PWD}/ScreenObject.h \
- $${PWD}/PanelObject.h
+ $${PWD}/PanelObject.h \
+ $${PWD}/NativeWindowObject.h
INCLUDEPATH *= $${PWD}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/ContextMenu.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/ContextMenu.qml
index 4ab8e156..ee98a8db 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/ContextMenu.qml
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/ContextMenu.qml
@@ -6,15 +6,33 @@
//===========================================
import QtQuick 2.2
import QtQuick.Window 2.2
-import QtQuick.Controls 1
+import QtQuick.Controls 2
import Lumina.Backend.RootDesktopObject 2.0
+
+
Menu {
id: contextMenu
+
+ MenuItem {
+ text: "Launch Terminal"
+ //iconName: "utilities-terminal"
+ onTriggered: {
+ RootObject.launchApp("application/terminal")
+ }
+ }
+ MenuItem {
+ text: "Launch File Browser"
+ //iconName: "user-home"
+ onTriggered: {
+ RootObject.launchApp("inode/directory")
+ }
+ }
+
MenuItem {
text: "Lock Screen"
- iconName: "system-lock-screen"
+ //iconName: "system-lock-screen"
onTriggered: {
RootObject.lockscreen()
}
@@ -22,7 +40,7 @@ Menu {
MenuItem {
text: "Logout"
- iconName: "system-log-out"
+ //iconName: "system-log-out"
onTriggered: {
RootObject.logout()
}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml
new file mode 100644
index 00000000..d75f2898
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml
@@ -0,0 +1,223 @@
+// vi: ft=qml
+import QtQuick 2.0
+import QtQuick.Window 2.2
+import QtQuick.Controls 1.4
+import QtQuick.Layouts 1.3
+
+import Lumina.Backend.NativeWindowObject 2.0
+import Lumina.Backend.RootDesktopObject 2.0
+
+Rectangle {
+ property NativeWindowObject object
+ property string window_id
+
+ SystemPalette { id:palette }
+
+ id: windowFrame
+ visible: object.isVisible
+ border.width: 5
+ border.color: palette.highlight
+ radius: 5
+ color: palette.window //"transparent"
+ x: object.frameGeometry.x
+ y: object.frameGeometry.y
+ width: object.frameGeometry.width
+ height: object.frameGeometry.height
+
+ onXChanged: {
+ windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height)
+ }
+ onYChanged: {
+ windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height)
+ }
+
+ MouseArea {
+ id: resizeArea
+ anchors.fill: parent
+ drag.target: undefined
+ property int resizeDirection: NativeWindowObject.TOP_LEFT
+ property int positionX: -1
+ property int positionY: -1
+
+ onPressed: {
+ //NOTE: This is only triggered for resize events
+ var globalP = windowFrame.mapToItem(rootCanvas, mouse.x, mouse.y)
+ positionX = globalP.x
+ positionY = globalP.y
+ if(positionY <= windowFrame.y + 10 ) {
+ if(positionX <= windowFrame.x + 10)
+ resizeDirection = NativeWindowObject.TOP_LEFT
+ else if(positionX >= windowFrame.x + windowFrame.width - 10)
+ resizeDirection = NativeWindowObject.TOP_RIGHT
+ else
+ resizeDirection = NativeWindowObject.TOP
+ }else if(positionY >= windowFrame.y + windowFrame.height - 10) {
+ if(positionX <= windowFrame.x + 10)
+ resizeDirection = NativeWindowObject.BOTTOM_LEFT
+ else if(positionX >= windowFrame.x + windowFrame.width - 10)
+ resizeDirection = NativeWindowObject.BOTTOM_RIGHT
+ else
+ resizeDirection = NativeWindowObject.BOTTOM
+ }else if(positionX <= windowFrame.x + 10) {
+ resizeDirection = NativeWindowObject.LEFT
+ }else if(positionX >= windowFrame.x + windowFrame.width - 10) {
+ resizeDirection = NativeWindowObject.RIGHT
+ }
+ //console.log("Initial X: ", positionX, "Initial Y: ", positionY);
+ //console.log("Initial X Frame: ", windowFrame.x, "Initial Y Frame: ", windowFrame.y);
+ }
+
+ onReleased: {
+ positionX = -1
+ positionY = -1
+ //windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height)
+ }
+
+ onPositionChanged: {
+ //NOTE: This is only triggered for resize events
+ if(positionX != -1 && positionY != -1) {
+ var globalP = windowFrame.mapToItem(rootCanvas, mouse.x, mouse.y)
+ /*console.log("Global P: ", globalP);
+ console.log("Position X: ", positionX, "Position Y: ", positionY)
+ console.log("Old Position : ", windowFrame.x, " , ", windowFrame.y)
+ console.log(resizeDirection);*/
+ if(resizeDirection == NativeWindowObject.TOP_LEFT) {
+ windowFrame.height -= globalP.y - positionY
+ windowFrame.width -= globalP.x - positionX
+ windowFrame.y = globalP.y
+ windowFrame.x = globalP.x
+ }else if(resizeDirection == NativeWindowObject.TOP_RIGHT) {
+ //console.log("TOP RIGHT Old Height: ", windowFrame.height, "Old Width: ", windowFrame.width)
+ windowFrame.height -= globalP.y - positionY
+ windowFrame.width += globalP.x - positionX
+ //console.log("New Height: ", windowFrame.height, "New Width: ", windowFrame.width)
+ windowFrame.y = globalP.y
+ //console.log("New Position : ", windowFrame.x, " , ", windowFrame.y)
+ }else if(resizeDirection == NativeWindowObject.TOP) {
+ windowFrame.height -= globalP.y - positionY
+ windowFrame.y = globalP.y
+ } else if(resizeDirection == NativeWindowObject.RIGHT) {
+ windowFrame.width += globalP.x - positionX
+ } else if(resizeDirection == NativeWindowObject.BOTTOM_RIGHT) {
+ windowFrame.height += globalP.y - positionY
+ windowFrame.width += globalP.x - positionX
+ } else if(resizeDirection == NativeWindowObject.BOTTOM) {
+ windowFrame.height += globalP.y - positionY
+ } else if(resizeDirection == NativeWindowObject.BOTTOM_LEFT) {
+ windowFrame.width -= globalP.x - positionX
+ windowFrame.height += globalP.y - positionY
+ windowFrame.x = globalP.x
+ } else if(resizeDirection == NativeWindowObject.LEFT) {
+ windowFrame.width -= globalP.x - positionX
+ windowFrame.x = globalP.x
+ }
+ //Set a miniumum width and height as 80x50
+ if(windowFrame.width < 80) {
+ windowFrame.width = 80
+ }
+ if(windowFrame.height < 50) {
+ windowFrame.height = 50
+ }
+ positionY = globalP.y
+ positionX = globalP.x
+ }
+ windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height)
+ }
+ }
+
+ Rectangle {
+ id: titleBar
+ border.width: 0
+ color: palette.window
+ height: 25
+ anchors.top: windowFrame.top
+ anchors.right: windowFrame.right
+ anchors.left: windowFrame.left
+ anchors.margins: windowFrame.border.width
+ width: parent.width
+
+ MouseArea {
+ id: dragArea
+ anchors.fill: parent
+ drag.target: windowFrame
+ drag.axis: Drag.XAndYAxis
+ //acceptedButtons: Qt.RightButton
+ //onClicked: contextMenu.open()
+ //released: { function(); }
+ }
+
+ ToolButton {
+ id: otherButton
+ anchors.left: parent.left
+ height: parent.height
+ iconSource: windowFrame.object.icon
+ }
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ color: palette.windowText
+ text: windowFrame.object.shortTitle
+ fontSizeMode: Text.Fit
+ }
+
+ RowLayout {
+ spacing: 0
+ anchors.right: parent.right
+ height: parent.height
+
+ ToolButton {
+ id: minButton
+ Layout.fillHeight: true
+ iconName: "window-minimize"
+ onClicked: { windowFrame.object.toggleVisibility() }
+ }
+
+ ToolButton {
+ id: maxButton
+ Layout.fillHeight: true
+ iconName: "window-maximize"
+ //onClicked: { windowFrame.object.toggleMaximize() }
+ }
+
+ ToolButton {
+ id: closeButton
+ Layout.fillHeight: true
+ iconName: "document-close"
+ onClicked: { windowFrame.object.requestClose() }
+ }
+ }
+ }
+
+ Image {
+ id: frameContents
+ cache: false
+ source: windowFrame.object.winImage
+ anchors.top: titleBar.bottom
+ anchors.bottom: parent.bottom
+ anchors.left: windowFrame.left
+ anchors.right: windowFrame.right
+ anchors.leftMargin: windowFrame.border.width
+ anchors.rightMargin: windowFrame.border.width
+ anchors.bottomMargin: windowFrame.border.width
+ width: parent.width
+ height: parent.height
+ //color: palette.window
+
+ //Image {
+ //anchors.fill: frameContents
+ //cache: false
+ //source: windowFrame.object.winImage
+ //}
+
+ MouseArea {
+ width: parent.width
+ height: parent.height
+ anchors.fill: frameContents
+ onClicked: { console.log(parent.mapToGlobal(mouse.x, mouse.y)); }
+ onPositionChanged: {
+ RootObject.mousePositionChanged()
+ }
+ }
+ }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml
index 846b5b55..65c8a0eb 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml
@@ -7,20 +7,43 @@
import QtQuick 2.2
import QtQuick.Window 2.2
import QtQuick.Controls 1
+import QtQuick.Layouts 1.3
import Lumina.Backend.PanelObject 2.0
-AnimatedImage {
+Rectangle {
//C++ backend object
- property string screen_id
+ property string panel_id
property PanelObject object
+ id: panel
//Normal geometries/placements
- asynchronous: true
- clip: true
- source: object.background
+ color: object.background
x: object.x
y: object.y
width: object.width
height: object.height
- }
+
+ GridLayout{
+ id: layout
+ anchors.fill: parent
+ //columns: parent.object.isVertical ? 1 : -1
+ //rows: parent.object.isVertical ? -1 : 1
+ flow: parent.isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight
+ //horizontalItemAlignment: parent.object.isVertical ? Grid.AlignHCenter : Qt.AlignLeft
+ //verticalItemAlignment: parent.object.isVertical ? Grid.AlignTop : Qt.AlignVCenter
+ Repeater {
+ model: panel.object.plugins
+ Loader{
+ asynchronous: true
+ property bool vertical : layout.parent.object.isVertical
+ property bool isspacer : modelData.endsWith("/Spacer.qml");
+ source: modelData
+ Layout.fillWidth : (vertical || isspacer) ? true : false
+ Layout.fillHeight : (vertical && ! isspacer) ? false : true
+ Layout.alignment : Qt.AlignVCenter | Qt.AlignHCenter
+ }
+
+ }
+ } //end of grid layout
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml
index c564ee42..7310fd50 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml
@@ -17,12 +17,13 @@
//===========================================
import QtQuick 2.2
import QtQuick.Window 2.2
-import QtQuick.Controls 1
+import QtQuick.Controls 2
import "." as QML
import Lumina.Backend.RootDesktopObject 2.0
import Lumina.Backend.ScreenObject 2.0
+import Lumina.Backend.NativeWindowObject 2.0
Rectangle {
id: rootCanvas
@@ -33,10 +34,10 @@ Rectangle {
anchors.fill: rootCanvas
acceptedButtons: Qt.RightButton
onClicked: {
- /*contextMenu.x = mouseX
+ contextMenu.x = mouseX
contextMenu.y = mouseY
- contextMenu.open() */
- contextMenu.popup()
+ contextMenu.open()
+ //contextMenu.popup()
}
onPositionChanged: {
RootObject.mousePositionChanged()
@@ -55,4 +56,24 @@ Rectangle {
z: 0+index
}
}
+
+ //Setup the windows
+ Repeater{
+ model: RootObject.windows
+ QML.NativeWindow{
+ window_id: modelData
+ object: RootObject.window(modelData)
+ z: 100+index
+ }
+ }
+
+ //Setup the Panels
+ Repeater{
+ model: RootObject.panels
+ QML.Panel{
+ panel_id: modelData
+ object: RootObject.panel(panel_id)
+ z: 10100+index
+ }
+ }
}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml
index 3b83653a..82e7c89d 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml
@@ -9,6 +9,9 @@ import QtQuick.Window 2.2
import QtQuick.Controls 1
import Lumina.Backend.ScreenObject 2.0
+import Lumina.Backend.PanelObject 2.0
+
+import "." as QML
AnimatedImage {
//C++ backend object
@@ -23,4 +26,15 @@ AnimatedImage {
y: object.y
width: object.width
height: object.height
+
+ //Setup the Panels
+ Repeater{
+ model: object.panels
+ QML.Panel{
+ panel_id: modelData
+ object: parent.object.panel(panel_id)
+ z: 10000+index
+ }
+ }
+
}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Clock_Digital.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Clock_Digital.qml
new file mode 100644
index 00000000..e68788f6
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Clock_Digital.qml
@@ -0,0 +1,23 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the QML plugin that displays the OS status/system tray
+//===========================================
+import QtQuick 2.2
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+
+Label{
+ anchors.fill: parent
+ text: RootObject.currentTime
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ rotation: parent.vertical ? 90 : 0
+ transformOrigin: Item.Center
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Spacer.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Spacer.qml
new file mode 100644
index 00000000..93556790
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/Spacer.qml
@@ -0,0 +1,15 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a tiny QML plugin for a basic Item which can be set to grow
+//===========================================
+import QtQuick 2.2
+import QtQuick.Layouts 1.3
+
+
+Item{
+ anchors.fill: parent
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/StatusTray.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/StatusTray.qml
new file mode 100644
index 00000000..f4063ed9
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/StatusTray.qml
@@ -0,0 +1,94 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the QML plugin that displays the OS status/system tray
+//===========================================
+import QtQuick 2.2
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+import Lumina.Backend.NativeWindowObject 2.0
+
+import "./status_tray" as QML
+
+
+Item {
+ property int prefsize: parent.vertical ? parent.width : parent.height
+ id: "status_tray"
+ anchors.fill: parent
+
+ GridLayout{
+ anchors.fill: parent
+ flow: status_tray.parent.vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight
+ //columns: vertical ? 1 : -1
+ //rows: vertical ? -1 : 1
+ columnSpacing: 2
+ rowSpacing: 2
+
+ //Volume Status
+ QML.VolumeButton{
+ //Layout.preferredHeight: status_tray.prefsize
+ //Layout.preferredWidth: status_tray.prefsize
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ }
+ //Network Status
+ QML.NetworkButton{
+ //Layout.preferredHeight: status_tray.prefsize
+ //Layout.preferredWidth: status_tray.prefsize
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ }
+ //Battery Status
+ QML.BatteryButton{
+ //Layout.preferredHeight: status_tray.prefsize
+ //Layout.preferredWidth: status_tray.prefsize
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ }
+ //Update Status
+ QML.UpdateButton{
+ //Layout.preferredHeight: status_tray.prefsize
+ //Layout.preferredWidth: status_tray.prefsize
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ }
+ //System Tray Menu Button
+ ToolButton{
+ id: "trayButton"
+ text: "Tray"
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ //iconName: "view-more-vertical"
+ visible: RootObject.hasTrayWindows
+ onClicked: trayMenu.open()
+ menu: Menu{
+ id: "trayMenu"
+ //MenuItem{ text: "sample" }
+ //MenuItem{ text: "sample2" }
+ Rectangle{ color: "blue"; width: 48; height: 48 }
+ GridLayout{
+ columns: 4
+ Repeater{
+ model: RootObject.trayWindows
+ QML.TrayIcon{
+
+ winObj: RootObject.trayWindow(modelData)
+ parent: trayMenu
+ Layout.preferredWidth: 48
+ Layout.preferredHeight: 48
+ }
+ }
+ }
+ }
+ } //end of system tray menu button
+ /*Repeater{
+ model: RootObject.trayWindows
+ QML.TrayIcon{
+ winObj: RootObject.trayWindow(modelData)
+ Layout.preferredHeight: status_tray.prefsize
+ Layout.preferredWidth: status_tray.prefsize
+ }
+ }*/
+ } //end of layout
+
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/plugins.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/plugins.pri
new file mode 100644
index 00000000..d1939616
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/plugins.pri
@@ -0,0 +1,11 @@
+#Show the QML files to lupdate for translation purposes - not for the actual build
+lupdate_only{
+ SOURCES *= $${PWD}/StatusTray.qml \
+ $${PWD}/status_tray/TrayIcon.qml \
+ $${PWD}/status_tray/VolumeButton.qml \
+ $${PWD}/status_tray/NetworkButton.qml \
+ $${PWD}/status_tray/BatteryButton.qml \
+ $${PWD}/status_tray/UpdateButton.qml \
+ $${PWD}/Clock_Digital.qml \
+ $${PWD}/Spacer.qml
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/BatteryButton.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/BatteryButton.qml
new file mode 100644
index 00000000..bc8ba68a
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/BatteryButton.qml
@@ -0,0 +1,20 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+import QtQuick 2.2
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+import Lumina.Backend.OSInterface 2.0
+
+ToolButton{
+ property OSInterface os: RootObject.os_interface()
+ id: "batButton"
+ iconName: os.batteryIcon
+ tooltip: os.batteryStatus
+ visible: os.batteryAvailable()
+ //enabled: false
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/NetworkButton.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/NetworkButton.qml
new file mode 100644
index 00000000..387c130b
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/NetworkButton.qml
@@ -0,0 +1,23 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+import QtQuick 2.2
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+import Lumina.Backend.OSInterface 2.0
+
+ToolButton{
+ id: "netButton"
+ property OSInterface os: RootObject.os_interface()
+ iconName: os.networkIcon
+ tooltip: os.networkStatus
+ visible: os.networkAvailable
+ enabled: os.hasNetworkManager()
+ onClicked: {
+ RootObject.launchApplication(os.networkManagerUtility())
+ }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/TrayIcon.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/TrayIcon.qml
new file mode 100644
index 00000000..6012ba5d
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/TrayIcon.qml
@@ -0,0 +1,34 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+import QtQuick 2.2
+import QtQuick.Controls 2
+
+import Lumina.Backend.NativeWindowObject 2.0
+
+Image{
+ property NativeWindowObject winObj
+ source: winObj.icon
+ id: "trayIcon"
+ fillMode: Qt.PreserveAspectFit
+ smooth: true
+ cache: false
+ asynchronous: false
+ //text: winObj.name
+
+ onXChanged: { }
+
+ MouseArea{
+ //property point globalPos
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ onEntered: {
+ //Need to ensure the invisible native window is over this place right now
+ console.log("Enter Tray Icon:", parent.mapToGlobal(0,0));
+ winObj.updateGeometry( parent.mapToGlobal(0,0).x, parent.mapToGlobal(0,0).y, parent.width(), parent.height(), true)
+ }
+ }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/UpdateButton.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/UpdateButton.qml
new file mode 100644
index 00000000..9ba824ae
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/UpdateButton.qml
@@ -0,0 +1,24 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+import QtQuick 2.2
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+import Lumina.Backend.OSInterface 2.0
+
+ToolButton{
+ id: "updateButton"
+ property OSInterface os: RootObject.os_interface()
+ iconName: os.updateIcon
+ tooltip: os.updateStatus
+ visible: os.updateInfoAvailable
+ /*enabled: os.hasUpdateManager()
+ onClicked: {
+ RootObject.launchApplication(os.updateManagerUtility())
+ }*/
+ //TODO - add a menu with update manager and start/view updates options
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/VolumeButton.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/VolumeButton.qml
new file mode 100644
index 00000000..e98a2603
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/plugins/status_tray/VolumeButton.qml
@@ -0,0 +1,25 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+import QtQuick 2.2
+import QtQuick.Controls 1
+
+import Lumina.Backend.RootDesktopObject 2.0
+import Lumina.Backend.OSInterface 2.0
+
+ToolButton{
+ property OSInterface os: RootObject.os_interface()
+ id: "volButton"
+ iconName: os.volumeIcon
+ tooltip: os.volume+"%"
+ visible: os.volumeSupported()
+ enabled: os.hasAudioMixer()
+ //Simple launch of mixer at the moment - make this popup a menu later
+ onClicked: {
+ RootObject.launchApplication( os.audioMixerShortcut() )
+ }
+
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.pri
index ad07902a..fa29aa96 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.pri
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.pri
@@ -3,7 +3,9 @@ lupdate_only{
SOURCES *= $${PWD}/RootDesktop.qml \
$${PWD}/ContextMenu.qml \
$${PWD}/Screen.qml \
- $${PWD}/Panel.qml
+ $${PWD}/Panel.qml \
+ $${PWD}/NativeWindow.qml
}
+include(plugins/plugins.pri)
RESOURCES *= $${PWD}/src-qml.qrc
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.qrc b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.qrc
index b0c66e20..0513e467 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.qrc
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/src-qml.qrc
@@ -4,5 +4,14 @@
<file>ContextMenu.qml</file>
<file>Screen.qml</file>
<file>Panel.qml</file>
+ <file>NativeWindow.qml</file>
+ <file>plugins/StatusTray.qml</file>
+ <file>plugins/status_tray/TrayIcon.qml</file>
+ <file>plugins/status_tray/VolumeButton.qml</file>
+ <file>plugins/status_tray/NetworkButton.qml</file>
+ <file>plugins/status_tray/BatteryButton.qml</file>
+ <file>plugins/status_tray/UpdateButton.qml</file>
+ <file>plugins/Clock_Digital.qml</file>
+ <file>plugins/Spacer.qml</file>
</qresource>
</RCC>
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.cpp
index 47f0e3d7..139eee89 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.cpp
@@ -7,6 +7,7 @@
#include "ContextMenu.h"
#include <global-objects.h>
#include <JsonMenu.h>
+#include <LIconCache.h>
void DesktopContextMenu::SettingsChanged(DesktopSettings::File file){
if(file == DesktopSettings::ContextMenu){ UpdateMenu(false); }
@@ -78,11 +79,11 @@ void DesktopContextMenu::UpdateMenu(bool fast){
}
// === PRIVATE ===
-void DesktopContextMenu::AddWindowToMenu(NativeWindow *win){
- QString label = win->property(NativeWindow::ShortTitle).toString();
- if(label.isEmpty()){ label = win->property(NativeWindow::Title).toString(); }
- if(label.isEmpty()){ label = win->property(NativeWindow::Name).toString(); }
- QAction *tmp = winMenu->addAction( win->property(NativeWindow::Icon).value<QIcon>(), label, win, SLOT(toggleVisibility()) );
+void DesktopContextMenu::AddWindowToMenu(NativeWindowObject *win){
+ QString label = win->property(NativeWindowObject::ShortTitle).toString();
+ if(label.isEmpty()){ label = win->property(NativeWindowObject::Title).toString(); }
+ if(label.isEmpty()){ label = win->property(NativeWindowObject::Name).toString(); }
+ QAction *tmp = winMenu->addAction( win->property(NativeWindowObject::Icon).value<QIcon>(), label, win, SLOT(toggleVisibility()) );
//Need to change the visual somehow to indicate whether it is visible or not
//bool visible = win->property(NativeWindow::Visible).toBool();
// TODO
@@ -176,14 +177,8 @@ void DesktopContextMenu::updateWinMenu(){
LIconCache::instance()->loadIcon( winMenu, "preferences-system-windows");
}
winMenu->clear();
- QList<NativeWindow*> wins = Lumina::NWS->currentWindows();
- unsigned int wkspace = Lumina::NWS->currentWorkspace();
+ QList<NativeWindowObject*> wins = RootDesktopObject::instance()->windowObjects();
for(int i=0; i<wins.length(); i++){
- //First check if this window is in the current workspace (or is "sticky")
- if(wins.at(i)->property(NativeWindow::Workspace).toUInt() != wkspace
- && wins.at(i)->property(NativeWindow::States).value< QList<NativeWindow::State> >().contains(NativeWindow::S_STICKY) ){
- continue;
- }
AddWindowToMenu(wins.at(i));
}
}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.h
index 78756e8c..b20087ef 100644
--- a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/ContextMenu.h
@@ -21,7 +21,7 @@ private:
QMenu *appMenu, *winMenu;
bool usewinmenu;
- void AddWindowToMenu(NativeWindow*);
+ void AddWindowToMenu(NativeWindowObject*);
public:
DesktopContextMenu(QWidget *parent = 0);
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp
new file mode 100644
index 00000000..24ad3fda
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp
@@ -0,0 +1,48 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "NativeWindow.h"
+
+// === PUBLIC ===
+NativeWindow::NativeWindow( NativeWindowObject *obj ) : QFrame(0, Qt::Window | Qt::FramelessWindowHint){
+ WIN = obj;
+ createFrame();
+}
+
+NativeWindow::~NativeWindow(){
+
+}
+
+// === PRIVATE ===
+void NativeWindow::createFrame(){
+ //Initialize the widgets
+ closeB = new QToolButton(this);
+ closeB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ minB = new QToolButton(this);
+ minB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ maxB = new QToolButton(this);
+ maxB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ otherB = new QToolButton(this);
+ otherB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ toolbarL = new QHBoxLayout(this);
+ vlayout = new QVBoxLayout(this);
+ vlayout.align
+ titleLabel = new QLabel(this);
+ titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ //Now put the widgets in the right places
+ toolbarL->addWidget(otherB);
+ toolbarL->addWidget(titleLabel);
+ toolbarL->addWidget(minB);
+ toolbarL->addWidget(maxB);
+ toolbarL->addWidget(closeB);
+ vlayout->addLayout(toolbarL);
+ vlayout->addStretch();
+ this->setLayout(vlayout);
+
+ //
+}
+
+// === PRIVATE SLOTS ===
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h
new file mode 100644
index 00000000..ffdb364a
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h
@@ -0,0 +1,34 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifndef _LUMINA_DESKTOP_NATIVE_WINDOW_WIDGET_H
+#define _LUMINA_DESKTOP_NATIVE_WINDOW_WIDGET_H
+
+#include <global-includes.h>
+
+class NativeWindow : public QFrame{
+ Q_OBJECT
+public:
+ NativeWindow(NativeWindowObject *obj);
+ ~NativeWindow();
+
+private:
+ //Core object
+ NativeWindowObject *WIN;
+ // Interface items
+ void createFrame();
+ QToolButton *closeB, *minB, *maxB, *otherB;
+ QHBoxLayout *toolbarL;
+ QVBoxLayout *vlayout;
+ QLabel *titleLabel;
+ // Info cache variables
+ QRect oldgeom;
+
+private slots:
+
+};
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.cpp
new file mode 100644
index 00000000..9e22a143
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.cpp
@@ -0,0 +1,120 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include <global-objects.h>
+#include "RootDesktop.h"
+
+// === PUBLIC ===
+RootDesktop::RootDesktop(QWindow *) : QWidget(0, Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnBottomHint){
+
+}
+
+RootDesktop::~RootDesktop(){
+
+}
+
+void RootDesktop::start(){
+ //qDebug() << "Starting RootDesktop" << this->geometry() << this->isVisible();
+ bgTimer = new QTimer(this);
+ bgTimer->setInterval(50);
+ bgTimer->setSingleShot(true);
+ connect(bgTimer, SIGNAL(timeout()), this, SLOT(bgTimerUpdate()) );
+
+ cmenu = new DesktopContextMenu(this);
+
+ //Setup the connections to the main objects
+ connect(RootDesktopObject::instance(), SIGNAL(screensChanged()), this, SLOT(on_screensChanged()) );
+ connect(RootDesktopObject::instance(), SIGNAL(panelsChanged()), this, SLOT(on_panelsChanged()) );
+ connect(RootDesktopObject::instance(), SIGNAL(windowsChanged()), this, SLOT(on_windowsChanged()) );
+ connect(RootDesktopObject::instance(), SIGNAL(trayWindowsChanged()), this, SLOT(on_trayWindowsChanged()) );
+
+ connect(cmenu, SIGNAL(LockSession()), RootDesktopObject::instance(), SIGNAL(lockScreen()) );
+ connect(cmenu, SIGNAL(showLeaveDialog()), RootDesktopObject::instance(), SIGNAL(startLogout()) );
+ connect(cmenu, SIGNAL(LaunchStandardApplication(QString)), RootDesktopObject::instance(), SIGNAL(launchApplication(QString)) );
+ connect(cmenu, SIGNAL(LaunchApplication(QString)), RootDesktopObject::instance(), SIGNAL(launchApplication(QString)) );
+
+ on_screensChanged(); //make sure this is setup right away (sets up connections
+ QTimer::singleShot(0, this, SLOT(on_panelsChanged()) );
+ QTimer::singleShot(2, this, SLOT(on_windowsChanged()) );
+ QTimer::singleShot(4, this, SLOT(on_trayWindowsChanged()) );
+
+ //Now start the first-run of the background change system
+ cmenu->start();
+ bgTimer->start();
+ this->show();
+}
+
+// === PRIVATE ===
+//QImage bgimage;
+
+
+// === PRIVATE SLOTS ===
+//RootDesktopObject connections
+void RootDesktop::on_screensChanged(){
+ QStringList screens = RootDesktopObject::instance()->screens();
+ //qDebug() << "Screens Changed:" << lastscreens << screens;
+ for(int i=0; i<screens.length(); i++){
+ if(!lastscreens.contains(screens[i])){
+ connect(RootDesktopObject::instance()->screen(screens[i]), SIGNAL(backgroundChanged()), this, SLOT(on_screen_bg_changed()) );
+ }
+ }
+ on_screen_bg_changed(); //start the timer to update the backgrounds now
+ lastscreens = screens; //save this for later
+}
+
+void RootDesktop::on_panelsChanged(){
+
+}
+
+void RootDesktop::on_windowsChanged(){
+
+}
+
+void RootDesktop::on_trayWindowsChanged(){
+
+}
+
+void RootDesktop::on_screen_bg_changed(){
+ if(!bgTimer->isActive()){ bgTimer->start(); }
+}
+
+//Internal use
+void RootDesktop::bgTimerUpdate(){
+ //qDebug() << "bgTimerUpdate";
+ //QtConcurrent::run(this, &RootDesktop::updateBG, this);
+ updateBG(this);
+}
+
+void RootDesktop::updateBG(RootDesktop* obj){
+
+ QImage tmp(obj->size(), QImage::Format_ARGB32_Premultiplied);
+ QStringList scr = RootDesktopObject::instance()->screens();
+ //qDebug() << "updateBG" << scr << tmp.size() << obj->geometry();
+ QPainter imgpaint(&tmp);
+ for(int i=0; i<scr.length(); i++){
+ ScreenObject * screen = RootDesktopObject::instance()->screen(scr[i]);
+ QString file = screen->background(); //in URL format
+ //qDebug() << "Got BG File:" << file << QUrl(file).toLocalFile();
+ QImage img(QUrl(file).toLocalFile());
+ //qDebug() << " - BG File is null:" << img.isNull();
+ imgpaint.drawImage(screen->geometry(), img, QRect(0,0,img.width(), img.height()) );
+ }
+ bgimage = tmp;
+ //QTimer::singleShot(0, obj, SLOT(update()) );
+ obj->update();
+}
+
+// === PROTECTED ===
+void RootDesktop::paintEvent(QPaintEvent *ev){
+ //qDebug() << "Paint Event:" << bgimage.isNull();
+ if (!bgimage.isNull()) {
+ //qDebug() << "Wallpaper paint Event:" << ev->rect();
+ QPainter painter(this);
+ painter.drawImage(ev->rect(), bgimage, ev->rect());
+ }else{
+ QWidget::paintEvent(ev);
+ }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.h
new file mode 100644
index 00000000..16ce0e47
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/RootDesktop.h
@@ -0,0 +1,44 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2018, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifndef _LUMINA_DESKTOP_WIDGETS_ROOT_DESKTOP_H
+#define _LUMINA_DESKTOP_WIDGETS_ROOT_DESKTOP_H
+
+#include <global-includes.h>
+#include "ContextMenu.h"
+
+class RootDesktop : public QWidget{
+ Q_OBJECT
+public:
+ RootDesktop(QWindow *root);
+ ~RootDesktop();
+
+ void start();
+
+private:
+ QImage bgimage;
+ QStringList lastscreens;
+ QTimer *bgTimer;
+ DesktopContextMenu *cmenu;
+
+private slots:
+ //RootDesktopObject connections
+ void on_screensChanged();
+ void on_panelsChanged();
+ void on_windowsChanged();
+ void on_trayWindowsChanged();
+ void on_screen_bg_changed();
+
+ //Internal use
+ void bgTimerUpdate();
+ void updateBG(RootDesktop* obj); //designed to be run in a background thread
+
+protected:
+ virtual void paintEvent(QPaintEvent *ev);
+
+};
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/src-widgets.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/src-widgets.pri
new file mode 100644
index 00000000..7994bb93
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/src-widgets.pri
@@ -0,0 +1,8 @@
+#update the includepath so we can just #include as needed without paths
+INCLUDEPATH *= $${PWD}
+
+SOURCES *= $${PWD}/RootDesktop.cpp \
+ $${PWD}/ContextMenu.cpp
+
+HEADERS *= $${PWD}/RootDesktop.h \
+ $${PWD}/ContextMenu.h
bgstack15