diff options
Diffstat (limited to 'src-qt5/core/lumina-desktop-unified/src-desktop')
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 |