Added support for shell switching

This commit is contained in:
Ivan Čukić 2013-09-02 11:51:44 +02:00
commit 3ba0708120
54 changed files with 3960 additions and 441 deletions

View File

@ -1,4 +1,3 @@
add_subdirectory(calendar)
add_subdirectory(core)
add_subdirectory(dirmodel)
add_subdirectory(draganddrop)
@ -6,4 +5,6 @@ add_subdirectory(krunnermodel)
add_subdirectory(qtextracomponents)
add_subdirectory(plasmacomponents)
add_subdirectory(plasmaextracomponents)
add_subdirectory(platformcomponents)
add_subdirectory(locale)
add_subdirectory(calendar)

View File

@ -0,0 +1,37 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(CheckCXXSourceCompiles)
add_definitions("-std=c++11")
set(CMAKE_MODULE_PATH
/opt/kf5/usr/kde/share/ECM-0.0.8/find-modules/
${CMAKE_MODULE_PATH}
)
set(platformcomponents_SRCS
platformextensionplugin.cpp
application.cpp
)
include(CheckIncludeFiles)
include(CheckFunctionExists)
include(CheckCXXSourceCompiles)
find_package(Qt5 5.2.0 REQUIRED NO_MODULE COMPONENTS Core DBus Declarative Quick Qml)
add_library(platformcomponentsplugin SHARED ${platformcomponents_SRCS})
qt5_use_modules(platformcomponentsplugin Core Widgets Declarative DBus Quick Qml)
target_link_libraries(
platformcomponentsplugin
Qt5::Core
Qt5::DBus
Qt5::Declarative
)
install(TARGETS platformcomponentsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/platformcomponents)
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/platformcomponents)

View File

@ -0,0 +1,114 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "application.h"
#include "application_p.h"
#include "utils/d_ptr_implementation.h"
#include <QDebug>
Application::Private::Private(Application * parent)
: q(parent)
{
connect(
&process, SIGNAL(stateChanged(QProcess::ProcessState)),
this, SLOT(stateChanged(QProcess::ProcessState))
);
process.setProcessChannelMode(QProcess::MergedChannels);
}
void Application::Private::stateChanged(QProcess::ProcessState newState)
{
//running = (newState != QProcess::NotRunning);
//q->runningChanged(running);
}
Application::Application(QObject * parent)
: QObject(parent), d(this)
{
}
Application::~Application()
{
terminate();
}
bool Application::running() const
{
return d->running;
}
QString Application::application() const
{
return d->application;
}
void Application::setApplication(const QString & application)
{
qDebug() << "setting the application to" << application;
// TODO: make an application whitelist
// and ensure those are invoked from the system
// prefix (plasma's)
if (d->process.state() != QProcess::NotRunning) {
return;
}
qDebug() << "setting the application to" << application;
d->application = application;
emit applicationChanged(application);
if (d->running) {
start();
}
}
void Application::setRunning(bool run)
{
qDebug() << "running?" << run;
d->running = run;
if (run)
start();
else
terminate();
}
void Application::start()
{
qDebug() << "starting?";
if (d->application.isEmpty()) return;
qDebug() << "starting.";
d->process.start(d->application);
qDebug() <<
d->process.waitForStarted();
qDebug() << "do we have an error " << d->process.error();
}
void Application::terminate()
{
d->process.terminate();
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef APPLICATION_H
#define APPLICATION_H
#include <QObject>
#include <QString>
#include "utils/d_ptr.h"
/**
* Class which handles an application execution.
*
* Example:
* <code>
* Application {
* application: "xterm"
* running: terminalRunningCheckbox.checked
* }
* </code>
*/
class Application: public QObject {
Q_OBJECT
/**
* The name or path of the applications
*/
Q_PROPERTY(QString application READ application WRITE setApplication NOTIFY applicationChanged);
/**
* Indicates whether the user wants the application to be running or not.
* It does not refer to the actual state of the application.
*/
Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged);
public:
Application(QObject * parent = Q_NULLPTR);
~Application();
QString application() const;
bool running() const;
public:
void setApplication(const QString & application);
void setRunning(bool run);
public Q_SLOTS:
void start();
void terminate();
Q_SIGNALS:
void applicationChanged(const QString & application);
void runningChanged(bool running);
private:
D_PTR;
};
#endif /* APPLICATION_H */

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef APPLICATION_P_H
#define APPLiCAtION_P_H
#include "application.h"
#include <QProcess>
class Application::Private: public QObject {
Q_OBJECT
public:
Private(Application *);
QString application;
QProcess process;
bool running;
private Q_SLOTS:
void stateChanged(QProcess::ProcessState newState);
private:
Application * const q;
};
#endif /* APPLICATION_P_H */

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QtQml>
#include <QtQml/QQmlExtensionPlugin>
#include <QDebug>
#include "application.h"
class PlatformComponentsPlugin: public QQmlExtensionPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.plasma.platformcomponents")
public:
PlatformComponentsPlugin(QObject * parent = Q_NULLPTR)
: QQmlExtensionPlugin(parent)
{
qDebug() << "instantiated plugin object";
}
void registerTypes(const char * uri) Q_DECL_OVERRIDE
{
qDebug() << "plugin loaded, registering types " << uri;
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.platformcomponents"));
qmlRegisterType<Application> (uri, 1, 0, "Application");
}
};
#include "platformextensionplugin.moc"

View File

@ -0,0 +1,2 @@
module org.kde.plasma.platformcomponents
plugin platformcomponentsplugin

View File

@ -38,8 +38,6 @@ public:
~d_ptr();
T * operator->() const;
T * get() const;
};
#define D_PTR \

View File

@ -47,12 +47,6 @@ T * d_ptr<T>::operator->() const
return d.get();
}
template<typename T>
T * d_ptr<T>::get() const
{
return d.get();
}
} // namespace utils
#endif

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SHARED_SINGLETON_P_H
#define SHARED_SINGLETON_P_H
#include <memory>
namespace utils {
template <typename Target>
class SharedSingleton {
public:
static std::shared_ptr<Target> instance()
{
// TODO: Make this thread safe
auto ptr = s_instance.lock();
if (!ptr) {
ptr = std::make_shared<Target>();
s_instance = ptr;
}
return ptr;
}
private:
static std::weak_ptr<Target> s_instance;
};
template <typename Target>
std::weak_ptr<Target> SharedSingleton<Target>::s_instance;
} // namespace utils
#endif /* SHARED_SINGLETON_P_H */

View File

@ -70,6 +70,11 @@ Plasma::Package Corona::package() const
return d->package;
}
void Corona::setPackage(const QString & package)
{
d->package.setPath(package);
}
void Corona::saveLayout(const QString &configName) const
{
KSharedConfigPtr c;

View File

@ -58,6 +58,11 @@ public:
**/
Plasma::Package package() const;
/**
* Setting the package name
*/
void setPackage(const QString & package);
/**
* @return all containments on this Corona
*/

View File

@ -64,12 +64,14 @@ add_executable(plasma-shell
main.cpp
configview.cpp
containmentconfigview.cpp
# desktopcorona.cpp
shellcorona.cpp
currentcontainmentactionsmodel.cpp
panelshadows.cpp
desktopcorona.cpp
panelview.cpp
shellpluginloader.cpp
shellpackage.cpp
shellmanager.cpp
lookandfeelpackage.cpp
view.cpp
panelconfigview.cpp

View File

@ -1,355 +0,0 @@
/*
* Copyright 2008 Aaron Seigo <aseigo@kde.org>
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "desktopcorona.h"
#include <QApplication>
#include <QDebug>
#include <QDesktopWidget>
#include <QQmlContext>
#include <QTimer>
#include <KLocalizedString>
#include <Plasma/Package>
#include "containmentconfigview.h"
#include "panelview.h"
#include "view.h"
#include "scripting/desktopscriptengine.h"
#include "widgetexplorer/widgetexplorerview.h"
static const QString s_panelTemplatesPath("plasma-layout-templates/panels/*");
DesktopCorona::DesktopCorona(QObject *parent)
: Plasma::Corona(parent),
m_desktopWidget(QApplication::desktop()),
m_widgetExplorerView(0)
{
m_desktopDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(package().filePath("defaults")), "Desktop");
m_appConfigSyncTimer = new QTimer(this);
m_appConfigSyncTimer->setSingleShot(true);
connect(m_appConfigSyncTimer, &QTimer::timeout,
this, &DesktopCorona::syncAppConfig);
connect(m_desktopWidget, SIGNAL(resized(int)),
this, SLOT(screenResized(int)));
connect(m_desktopWidget, SIGNAL(screenCountChanged(int)),
this, SLOT(screenCountChanged(int)));
connect(m_desktopWidget, SIGNAL(workAreaResized(int)),
this, SLOT(workAreaResized(int)));
connect(this, &DesktopCorona::containmentAdded, this, &DesktopCorona::handleContainmentAdded);
connect(this, SIGNAL(screenOwnerChanged(int, int, Plasma::Containment *)),
this, SLOT(updateScreenOwner(int, int, Plasma::Containment *)));
checkViews();
//QTimer::singleShot(600, this, SLOT(showWidgetExplorer())); // just for easier debugging
}
DesktopCorona::~DesktopCorona()
{
}
KSharedConfig::Ptr DesktopCorona::applicationConfig()
{
return KSharedConfig::openConfig();
}
void DesktopCorona::requestApplicationConfigSync()
{
// constant controlling how long between requesting a configuration sync
// and one happening should occur. currently 10 seconds
static const int CONFIG_SYNC_TIMEOUT = 10000;
m_appConfigSyncTimer->start(CONFIG_SYNC_TIMEOUT);
}
void DesktopCorona::loadDefaultLayout()
{
WorkspaceScripting::DesktopScriptEngine scriptEngine(this, true);
connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError,
this, &DesktopCorona::printScriptError);
connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print,
this, &DesktopCorona::printScriptMessage);
QString script = package().filePath("defaultlayout");
QFile file(script);
if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
QString code = file.readAll();
qDebug() << "evaluating startup script:" << script;
scriptEngine.evaluateScript(code);
}
}
void DesktopCorona::processUpdateScripts()
{
foreach (const QString &script, WorkspaceScripting::ScriptEngine::pendingUpdateScripts(this)) {
WorkspaceScripting::DesktopScriptEngine scriptEngine(this, false);
scriptEngine.evaluateScript(script);
}
}
void DesktopCorona::checkScreens(bool signalWhenExists)
{
// quick sanity check to ensure we have containments for each screen
int num = numScreens();
for (int i = 0; i < num; ++i) {
checkScreen(i, signalWhenExists);
}
}
void DesktopCorona::checkScreen(int screen, bool signalWhenExists)
{
// signalWhenExists is there to allow PlasmaApp to know when to create views
// it does this only on containment addition, but in the case of a screen being
// added and the containment already existing for that screen, no signal is emitted
// and so PlasmaApp does not know that it needs to create a view for it. to avoid
// taking care of that case in PlasmaApp (which would duplicate some of the code below,
// DesktopCorona will, when signalWhenExists is true, emit a containmentAdded signal
// even if the containment actually existed prior to this method being called.
//
//note: hte signal actually triggers view creation only for panels, atm.
//desktop views are created in response to containment's screenChanged signal instead, which is
//buggy (sometimes the containment thinks it's already on the screen, so no view is created)
//TODO: restore activities
//Activity *currentActivity = activity(m_activityController->currentActivity());
//ensure the desktop(s) have a containment and view
checkDesktop(/*currentActivity,*/ signalWhenExists, screen);
//ensure the panels get views too
if (signalWhenExists) {
foreach (Plasma::Containment * c, containments()) {
if (c->screen() != screen) {
continue;
}
Plasma::Types::ContainmentType t = c->containmentType();
if (t == Plasma::Types::PanelContainment ||
t == Plasma::Types::CustomPanelContainment) {
emit containmentAdded(c);
}
}
}
}
void DesktopCorona::checkDesktop(/*Activity *activity,*/ bool signalWhenExists, int screen)
{
Plasma::Containment *c = /*activity->*/containmentForScreen(screen);
//TODO: remove following when activities are restored
if (!c) {
c = createContainment(m_desktopDefaultsConfig.readEntry("Containment", "org.kde.desktopcontainment"));
}
if (!c) {
return;
}
c->setScreen(screen);
if (screen >= 0 || m_views.count() >= screen + 1) {
m_views[screen]->setContainment(c);
} else {
qWarning() << "Invalid screen";
}
c->flushPendingConstraintsEvents();
requestApplicationConfigSync();
if (signalWhenExists) {
emit containmentAdded(c);
}
}
int DesktopCorona::numScreens() const
{
return m_desktopWidget->screenCount();
}
QRect DesktopCorona::screenGeometry(int id) const
{
return m_desktopWidget->screenGeometry(id);
}
QRegion DesktopCorona::availableScreenRegion(int id) const
{
return m_desktopWidget->availableGeometry(id);
}
QRect DesktopCorona::availableScreenRect(int id) const
{
return m_desktopWidget->availableGeometry(id);
}
PanelView *DesktopCorona::panelView(Plasma::Containment *containment) const
{
return m_panelViews.value(containment);
}
///// SLOTS
void DesktopCorona::screenCountChanged(int newCount)
{
qDebug() << "New screen count" << newCount;
checkViews();
}
void DesktopCorona::screenResized(int screen)
{
qDebug() << "Screen resized" << screen;
}
void DesktopCorona::workAreaResized(int screen)
{
qDebug() << "Work area resized" << screen;
}
void DesktopCorona::checkViews()
{
if (m_views.count() == m_desktopWidget->screenCount()) {
return;
} else if (m_views.count() < m_desktopWidget->screenCount()) {
for (int i = m_views.count(); i < m_desktopWidget->screenCount(); ++i) {
View *view = new View(this);
QSurfaceFormat format;
view->show();
m_views << view;
}
} else {
for (int i = m_desktopWidget->screenCount(); i < m_views.count(); ++i) {
View *view = m_views.last();
view->deleteLater();
m_views.pop_back();
}
}
//check every containment is in proper view
for (int i = 0; i < m_desktopWidget->screenCount(); ++i) {
qDebug() << "TODO: Implement loading containments into the views";
}
}
void DesktopCorona::checkLoadingDesktopsComplete()
{
Plasma::Containment *c = qobject_cast<Plasma::Containment *>(sender());
if (c) {
disconnect(c, &Plasma::Containment::uiReadyChanged,
this, &DesktopCorona::checkLoadingDesktopsComplete);
m_loadingDesktops.remove(c);
}
if (m_loadingDesktops.isEmpty()) {
foreach (Plasma::Containment *cont, m_waitingPanels) {
m_panelViews[cont] = new PanelView(this);
m_panelViews[cont]->setContainment(cont);
}
m_waitingPanels.clear();
}
}
void DesktopCorona::updateScreenOwner(int wasScreen, int isScreen, Plasma::Containment *containment)
{
qDebug() << "Was screen" << wasScreen << "Is screen" << isScreen <<"Containment" << containment << containment->title();
if (containment->formFactor() == Plasma::Types::Horizontal ||
containment->formFactor() == Plasma::Types::Vertical) {
if (isScreen >= 0) {
m_waitingPanels << containment;
} else {
if (m_panelViews.contains(containment)) {
m_panelViews[containment]->setContainment(0);
m_panelViews[containment]->deleteLater();
m_panelViews.remove(containment);
}
}
checkLoadingDesktopsComplete();
//Desktop view
} else {
if (containment->isUiReady()) {
checkLoadingDesktopsComplete();
} else {
m_loadingDesktops.insert(containment);
connect(containment, &Plasma::Containment::uiReadyChanged,
this, &DesktopCorona::checkLoadingDesktopsComplete);
}
if (isScreen < 0 || m_views.count() < isScreen + 1) {
qWarning() << "Invalid screen";
return;
}
m_views[isScreen]->setContainment(containment);
}
}
void DesktopCorona::handleContainmentAdded(Plasma::Containment* c)
{
connect(c, &Plasma::Containment::showAddWidgetsInterface,
this, &DesktopCorona::showWidgetExplorer);
}
void DesktopCorona::showWidgetExplorer()
{
if (!m_widgetExplorerView) {
QString expqml = package().filePath("widgetexplorer");
qDebug() << "Script to load for WidgetExplorer: " << expqml;
m_widgetExplorerView = new WidgetExplorerView(expqml);
m_widgetExplorerView->init();
}
Plasma::Containment *c = 0;
c = dynamic_cast<Plasma::Containment*>(sender());
if (c) {
qDebug() << "Found containment.";
m_widgetExplorerView->setContainment(c);
} else {
// FIXME: try harder to find a suitable containment?
qWarning() << "containment not set, don't know where to add the applet.";
}
m_widgetExplorerView->show();
}
void DesktopCorona::syncAppConfig()
{
qDebug() << "Syncing plasma-shellrc config";
applicationConfig()->sync();
}
void DesktopCorona::printScriptError(const QString &error)
{
qWarning() << error;
}
void DesktopCorona::printScriptMessage(const QString &message)
{
qDebug() << message;
}
#include "moc_desktopcorona.cpp"

View File

@ -20,8 +20,8 @@
#include <qcommandlineparser.h>
#include <klocalizedstring.h>
#include "desktopcorona.h"
#include "shellpluginloader.h"
#include "shellmanager.h"
#include <QtQml/QQmlDebuggingEnabler>
@ -49,14 +49,16 @@ int main(int argc, char** argv)
QQmlDebuggingEnabler enabler;
}
Plasma::PluginLoader::setPluginLoader(new ShellPluginLoader);
DesktopCorona *corona = new DesktopCorona();
corona->loadLayout();
if (corona->containments().isEmpty()) {
corona->loadDefaultLayout();
}
corona->processUpdateScripts();
corona->checkScreens();
// Plasma::PluginLoader::setPluginLoader(new ShellPluginLoader);
// DesktopCorona *corona = new DesktopCorona();
// corona->loadLayout();
// if (corona->containments().isEmpty()) {
// corona->loadDefaultLayout();
// }
// corona->processUpdateScripts();
// corona->checkScreens();
ShellManager::instance();
return app.exec();
}

View File

@ -17,7 +17,7 @@
*/
#include "panelview.h"
#include "desktopcorona.h"
#include "shellcorona.h"
#include "panelshadows_p.h"
#include <QAction>
@ -33,7 +33,7 @@
#include <Plasma/Containment>
#include <Plasma/Package>
PanelView::PanelView(DesktopCorona *corona, QWindow *parent)
PanelView::PanelView(ShellCorona *corona, QWindow *parent)
: View(corona, parent),
m_offset(0),
m_maxLength(0),
@ -167,11 +167,11 @@ void PanelView::setThickness(int value)
int PanelView::length() const
{
if (formFactor() == Plasma::Types::Vertical) {
return config().readEntry<int>("length", screen()->size().height());
} else {
return config().readEntry<int>("length", screen()->size().width());
}
return config().readEntry<int>("length",
formFactor() == Plasma::Types::Vertical ?
screen()->size().height() :
screen()->size().width()
);
}
void PanelView::setLength(int value)

View File

@ -24,7 +24,7 @@
#include "panelconfigview.h"
#include <QtCore/qpointer.h>
class DesktopCorona;
class ShellCorona;
class PanelView : public View
{
@ -38,7 +38,7 @@ class PanelView : public View
Q_PROPERTY(QScreen *screen READ screen NOTIFY screenChanged)
public:
explicit PanelView(DesktopCorona *corona, QWindow *parent = 0);
explicit PanelView(ShellCorona *corona, QWindow *parent = 0);
virtual ~PanelView();
virtual KConfigGroup config() const;
@ -85,7 +85,7 @@ private:
int m_minLength;
Qt::Alignment m_alignment;
QPointer<PanelConfigView> m_panelConfigView;
DesktopCorona *m_corona;
ShellCorona *m_corona;
};
#endif // PANELVIEW_H

View File

@ -1,5 +1,6 @@
plasma_install_package(desktop org.kde.desktop plasma/shells shell)
plasma_install_package(blank org.kde.blank plasma/shells shell)
plasma_install_package(lookandfeel org.kde.lookandfeel plasma/look-and-feel lookandfeel)
add_subdirectory(wallpapers)

View File

@ -0,0 +1,3 @@
#! /usr/bin/env bash
$XGETTEXT `find . -name \*.qml` -L Java -o $podir/plasma_shell_org.kde.desktop.pot
rm -f rc.cpp

View File

@ -0,0 +1,45 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
Row {
id: root
property alias reason: messageText.text
clip: true
PlasmaCore.IconItem {
id: icon
anchors.verticalCenter: parent
width: theme.hugeIconSize
height: width
source: "dialog-error"
}
PlasmaComponents.Label {
id: messageText
anchors.verticalCenter: parent
width: parent.width - icon.width
wrapMode: Text.Wrap
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Window 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
id: root
property Item applet
property Item compactRepresentation
onAppletChanged: {
applet.parent = appletParent
applet.anchors.fill = applet.parent
}
onCompactRepresentationChanged: {
compactRepresentation.parent = root
compactRepresentation.anchors.fill = root
}
PlasmaCore.Dialog {
id: popupWindow
//windowFlags: Qt.Popup
color: Qt.rgba(0,0,0,0)
visible: plasmoid.expanded
visualParent: root
mainItem: Item {
id: appletParent
width: applet && applet.implicitWidth > 0 ? applet.implicitWidth : theme.mSize(theme.defaultFont).width * 35
height: applet && applet.implicitHeight > 0 ? applet.implicitHeight : theme.mSize(theme.defaultFont).height * 25
onWidthChanged: applet.width = width
onHeightChanged: applet.height = height
}
onActiveWindowChanged: {
if (!activeWindow) {
// plasmoid.expanded = false
}
}
onVisibleChanged: {
if (!visible) {
plasmoid.expanded = false
}
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
PlasmaCore.IconItem {
id: icon
source: plasmoid.icon ? plasmoid.icon : "plasma"
active: mouseArea.containsMouse
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: plasmoid.expanded = !plasmoid.expanded
}
}

View File

@ -0,0 +1,237 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Controls 1.0 as QtControls
import QtQuick.Layouts 1.0
import org.kde.plasma.configuration 2.0
//TODO: all of this will be done with desktop components
Rectangle {
id: root
property int _m: theme.defaultFont.pointSize
//BEGIN properties
color: syspal.window
width: 640
height: 480
//END properties
//BEGIN model
property ConfigModel globalConfigModel: globalAppletConfigModel
ConfigModel {
id: globalAppletConfigModel
ConfigCategory {
name: "Keyboard shortcuts"
icon: "preferences-desktop-keyboard"
source: "ConfigurationShortcuts.qml"
}
}
//END model
//BEGIN functions
function saveConfig() {
for (var key in plasmoid.configuration) {
if (main.currentPage["cfg_"+key] !== undefined) {
plasmoid.configuration[key] = main.currentPage["cfg_"+key]
}
}
}
function restoreConfig() {
for (var key in plasmoid.configuration) {
if (main.currentPage["cfg_"+key] !== undefined) {
main.currentPage["cfg_"+key] = plasmoid.configuration[key]
}
}
}
//END functions
//BEGIN connections
Component.onCompleted: {
if (configDialog.configModel && configDialog.configModel.count > 0) {
main.sourceFile = configDialog.configModel.get(0).source
} else {
main.sourceFile = globalConfigModel.get(0).source
}
root.restoreConfig()
// root.width = mainColumn.implicitWidth
// root.height = mainColumn.implicitHeight
}
//END connections
//BEGIN UI components
SystemPalette {id: syspal}
ColumnLayout {
id: mainColumn
anchors.fill: parent
property int implicitWidth: Math.max(contentRow.implicitWidth, buttonsRow.implicitWidth) + 8
property int implicitHeight: contentRow.implicitHeight + buttonsRow.implicitHeight + 8
RowLayout {
id: contentRow
anchors {
left: parent.left
right: parent.right
}
Layout.fillHeight: true
Layout.preferredHeight: parent.height - buttonsRow.height
QtControls.ScrollView{
id: categoriesScroll
frameVisible: true
anchors {
top: parent.top
bottom: parent.bottom
}
visible: (configDialog.configModel ? configDialog.configModel.count : 0) + globalConfigModel.count > 1
width: visible ? 100 : 0
implicitWidth: width
Flickable {
id: categoriesView
contentWidth: width
contentHeight: childrenRect.height
anchors.fill: parent
property Item currentItem
Rectangle {
id: categories
width: parent.width
height: Math.max(categoriesView.height, categoriesColumn.height)
color: syspal.base
Rectangle {
color: syspal.highlight
width: parent.width
height: theme.iconSizes.IconSizeHuge
y: index * height
Behavior on y {
NumberAnimation {
duration: 250
easing.type: "InOutQuad"
}
}
}
Column {
id: categoriesColumn
width: parent.width
Repeater {
model: configDialog.configModel
delegate: ConfigCategoryDelegate {
onClicked: categoriesView.currentIndex = index
}
}
Repeater {
model: globalConfigModel
delegate: ConfigCategoryDelegate {}
}
}
}
}
}
QtControls.ScrollView{
id: pageScroll
anchors {
top: parent.top
bottom: parent.bottom
}
Layout.fillWidth: true
Flickable {
contentWidth: width
contentHeight: main.height
Item {
width: parent.width
height: childrenRect.height
QtControls.StackView {
id: main
anchors.fill: parent
anchors.margins: 12
property string sourceFile
Timer {
id: pageSizeSync
interval: 100
onTriggered: {
// root.width = mainColumn.implicitWidth
// root.height = mainColumn.implicitHeight
}
}
onImplicitWidthChanged: pageSizeSync.restart()
onImplicitHeightChanged: pageSizeSync.restart()
onSourceFileChanged: {
print("Source file changed in flickable" + sourceFile);
replace(Qt.resolvedUrl(sourceFile))
/*
* This is not needed on a desktop shell that has ok/apply/cancel buttons, i'll leave it here only for future reference until we have a prototype for the active shell.
* root.pageChanged will start a timer, that in turn will call saveConfig() when triggered
for (var prop in currentPage) {
if (prop.indexOf("cfg_") === 0) {
currentPage[prop+"Changed"].connect(root.pageChanged)
}
}*/
}
}
}
}
}
}
RowLayout {
id: buttonsRow
anchors {
right: parent.right
rightMargin: spacing
}
QtControls.Button {
iconSource: "dialog-ok"
text: "Ok"
onClicked: {
if (main.currentPage.saveConfig !== undefined) {
main.currentPage.saveConfig()
} else {
root.saveConfig()
}
configDialog.close()
}
}
QtControls.Button {
iconSource: "dialog-ok-apply"
text: "Apply"
onClicked: {
if (main.currentPage.saveConfig !== undefined) {
main.currentPage.saveConfig()
} else {
root.saveConfig()
}
}
}
QtControls.Button {
iconSource: "dialog-cancel"
text: "Cancel"
onClicked: configDialog.close()
}
}
}
//END UI components
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Controls 1.0 as QtControls
import org.kde.plasma.core 2.0 as PlasmaCore
MouseArea {
id: delegate
anchors {
left: parent.left
right: parent.right
}
//BEGIN properties
width: childrenRect.width
height: childrenRect.height
property bool current: model.source == main.sourceFile
//END properties
//BEGIN connections
onClicked: {
print("model source: " + model.source + " " + main.sourceFile);
if (delegate.current) {
return
} else {
if (typeof(categoriesView.currentIndex) != "undefined") {
categoriesView.currentIndex = index;
}
main.sourceFile = model.source
root.restoreConfig()
}
}
onCurrentChanged: {
if (current) {
categoriesView.currentIndex = index
}
}
//END connections
//BEGIN UI components
Column {
spacing: 4
anchors {
left: parent.left
right: parent.right
topMargin: _m
}
PlasmaCore.IconItem {
anchors.horizontalCenter: parent.horizontalCenter
width: theme.IconSizeHuge
height: width
source: model.icon
}
QtControls.Label {
anchors {
left: parent.left
right: parent.right
}
text: model.name
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
}
}
//END UI components
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
PlasmaComponents.Label {
text: "Containment actions go here"
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.configuration 2.0
import QtQuick.Controls 1.0 as QtControls
Column {
id: root
spacing: _m
PlasmaExtras.Title {
text: "Plugins"
}
//BEGIN functions
function saveConfig() {
for (var key in configDialog.wallpaperConfiguration) {
if (main.currentPage["cfg_"+key] !== undefined) {
configDialog.wallpaperConfiguration[key] = main.currentPage["cfg_"+key]
}
}
configDialog.applyWallpaper()
}
function restoreConfig() {
for (var key in configDialog.wallpaperConfiguration) {
if (main.currentPage["cfg_"+key] !== undefined) {
main.currentPage["cfg_"+key] = configDialog.wallpaperConfiguration[key]
}
}
}
//END functions
ListView {
id: categoriesView
anchors {
left: parent.left
right: parent.right
}
height: 100
orientation: ListView.Horizontal
model: configDialog.wallpaperConfigModel
delegate: ConfigCategoryDelegate {
id: delegate
current: categoriesView.currentIndex == index
anchors {
top: parent.top
bottom: parent.bottom
left: undefined
right: undefined
}
width: 64
onClicked: {
configDialog.currentWallpaper = model.pluginName
if (categoriesView.currentIndex == index) {
return
} else {
categoriesView.currentIndex = index;
main.sourceFile = model.source
root.restoreConfig()
}
}
onCurrentChanged: {
categoriesView.currentIndex = index
if (current) {
categoriesView.currentIndex = index
}
}
Component.onCompleted: {
if (configDialog.currentWallpaper == model.pluginName) {
loadWallpaperTimer.pendingCurrent = model
loadWallpaperTimer.restart()
}
}
}
highlight: Rectangle {
color: theme.highlightColor
}
Timer {
id: loadWallpaperTimer
interval: 100
property variant pendingCurrent
onTriggered: {
if (pendingCurrent) {
categoriesView.currentIndex = pendingCurrent.index
main.sourceFile = pendingCurrent.source
root.restoreConfig()
}
}
}
}
PlasmaComponents.PageStack {
id: main
anchors {
left: categoriesView.left;
right: parent.right;
}
width: implicitWidth
height: implicitHeight
property string sourceFile
onSourceFileChanged: {
if (sourceFile != "") {
main.opacity = 1;
replace(Qt.resolvedUrl(sourceFile))
//main.width = mainColumn.implicitWidth
main.height = mainColumn.implicitHeight
} else {
main.opacity = 0
}
}
Behavior on opacity { NumberAnimation {} }
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
id: iconsPage
width: childrenRect.width
height: childrenRect.height
PlasmaComponents.Button {
iconSource: "settings"
text: "None"
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
AppletConfiguration {
id: root
//BEGIN model
globalConfigModel: globalContainmentConfigModel
ConfigModel {
id: globalContainmentConfigModel
ConfigCategory {
name: "Appearance"
icon: "preferences-desktop-wallpaper"
source: "ConfigurationContainmentAppearance.qml"
}
ConfigCategory {
name: "Mouse Actions"
icon: "preferences-desktop-mouse"
source: "ConfigurationContainmentActions.qml"
}
}
//END model
}

View File

@ -0,0 +1,186 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
import org.kde.qtextracomponents 2.0 as QtExtras
import "panelconfiguration"
//TODO: all of this will be done with desktop components
PlasmaCore.FrameSvgItem {
id: dialogRoot
//BEGIN Properties
width: 640
height: 64
imagePath: "dialogs/background"
state: {
switch (panel.location) {
//TopEdge
case 3:
return "TopEdge"
//LeftEdge
case 5:
return "LeftEdge"
//RightEdge
case 6:
return "RightEdge"
//BottomEdge
case 4:
default:
return "BottomEdge"
}
}
property bool vertical: (panel.location == 5 || panel.location == 6)
//END properties
//BEGIN Connections
Connections {
target: panel
onOffsetChanged: ruler.offset = panel.offset
onMinimumLengthChanged: ruler.minimumLength = panel.minimumLength
onMaximumLengthChanged: ruler.maximumLength = panel.maximumLength
}
//END Connections
//BEGIN UI components
Ruler {
id: ruler
state: dialogRoot.state
}
ToolBar {
id: toolBar
}
//END UI components
//BEGIN Animations
//when EdgeHandle is released animate to old panel position
ParallelAnimation {
id: panelResetAnimation
NumberAnimation {
target: panel
properties: (panel.location == 5 || panel.location == 6) ? "x" : "y"
to: {
switch (panel.location) {
//TopEdge
case 3:
return 0
break
//LeftEdge
case 5:
return 0
break;
//RightEdge
case 6:
return panel.screenGeometry.x + panel.screenGeometry.width - panel.width
break;
//BottomEdge
case 4:
default:
return panel.screenGeometry.y + panel.screenGeometry.height - panel.height
}
}
duration: 150
}
NumberAnimation {
target: configDialog
properties: (panel.location == 5 || panel.location == 6) ? "x" : "y"
to: {
panel.height
switch (panel.location) {
//TopEdge
case 3:
return panel.height
break
//LeftEdge
case 5:
return panel.width
break;
//RightEdge
case 6:
return panel.screenGeometry.x + panel.screenGeometry.width - panel.width - configDialog.width
break;
//BottomEdge
case 4:
default:
return panel.screenGeometry.y + panel.screenGeometry.height - panel.height - configDialog.height
}
}
duration: 150
}
}
//END Animations
//BEGIN States
states: [
State {
name: "TopEdge"
PropertyChanges {
target: dialogRoot
enabledBorders: "TopBorder|BottomBorder"
}
PropertyChanges {
target: dialogRoot
implicitHeight: ruler.implicitHeight + toolBar.implicitHeight
}
},
State {
name: "BottomEdge"
PropertyChanges {
target: dialogRoot
enabledBorders: "TopBorder|BottomBorder"
}
PropertyChanges {
target: dialogRoot
implicitHeight: ruler.implicitHeight + toolBar.implicitHeight
}
},
State {
name: "LeftEdge"
PropertyChanges {
target: dialogRoot
enabledBorders: "LeftBorder|RightBorder"
}
PropertyChanges {
target: dialogRoot
implicitWidth: ruler.implicitWidth + toolBar.implicitWidth
}
},
State {
name: "RightEdge"
PropertyChanges {
target: dialogRoot
enabledBorders: "LeftBorder|RightBorder"
}
PropertyChanges {
target: dialogRoot
implicitWidth: ruler.implicitWidth + toolBar.implicitWidth
}
}
]
//END States
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
import org.kde.qtextracomponents 2.0 as QtExtras
PlasmaComponents.ToolButton {
flat: false
text: "Screen edge"
QtExtras.MouseEventListener {
anchors.fill: parent
property int lastX
property int lastY
property int startMouseX
property int startMouseY
onPressed: {
lastX = mouse.screenX
lastY = mouse.screenY
startMouseX = mouse.x
startMouseY = mouse.y
}
onPositionChanged: {
switch (panel.location) {
//TopEdge
case 3:
configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y
panel.y = configDialog.y - panel.height
break
//LeftEdge
case 5:
configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x
panel.x = configDialog.x - panel.width
break;
//RightEdge
case 6:
configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x
panel.x = configDialog.x + configDialog.width
break;
//BottomEdge
case 4:
default:
configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y
panel.y = configDialog.y + configDialog.height
}
lastX = mouse.screenX
lastY = mouse.screenY
var screenAspect = panel.screenGeometry.height / panel.screenGeometry.width
var newLocation = panel.location
//If the mouse is in an internal rectangle, do nothing
if ((mouse.screenX > panel.screenGeometry.x + panel.screenGeometry.width/3 &&
mouse.screenX < panel.screenGeometry.x + panel.screenGeometry.width/3*2) &&
(mouse.screenY > panel.screenGeometry.y + panel.screenGeometry.height/3 &&
mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height/3*2)) {
return;
}
if (mouse.screenY < panel.screenGeometry.y+(mouse.screenX-panel.screenGeometry.x)*screenAspect) {
if (mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height-(mouse.screenX-panel.screenGeometry.x)*screenAspect) {
if (panel.location == 3) {
return;
} else {
newLocation = 3; //FIXME: Plasma::TopEdge;
}
} else if (panel.location == 6) {
return;
} else {
newLocation = 6; //FIXME: Plasma::RightEdge;
}
} else {
if (mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height-(mouse.screenX-panel.screenGeometry.x)*screenAspect) {
if (panel.location == 5) {
return;
} else {
newLocation = 5; //FIXME: Plasma::LeftEdge;
}
} else if(panel.location == 4) {
return;
} else {
newLocation = 4; //FIXME: Plasma::BottomEdge;
}
}
panel.location = newLocation
print("New Location: " + newLocation);
}
onReleased: panelResetAnimation.running = true
}
}

View File

@ -0,0 +1,352 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
import org.kde.qtextracomponents 2.0 as QtExtras
PlasmaCore.FrameSvgItem {
id: root
property alias offset: offsetHandle.value
property alias minimumLength: minimumLengthHandle.value
property alias maximumLength: maximumLengthHandle.value
imagePath: "widgets/containment-controls"
state: "BottomEdge"
implicitWidth: offsetHandle.width + minimumLengthHandle.width
implicitHeight: offsetHandle.height + minimumLengthHandle.height
onMinimumLengthChanged: leftMinimumLengthHandle.value = minimumLength
onMaximumLengthChanged: leftMaximumLengthHandle.value = maximumLength
Component.onCompleted: {
offsetHandle.value = panel.offset
minimumLengthHandle.value = panel.minimumLength
maximumLengthHandle.value = panel.maximumLength
leftMinimumLengthHandle.value = panel.minimumLength
leftMaximumLengthHandle.value = panel.maximumLength
}
PlasmaCore.Svg {
id: containmentControlsSvg
imagePath: "widgets/containment-controls"
}
PlasmaCore.SvgItem {
id: centerMark
svg: containmentControlsSvg
elementId: dialogRoot.vertical ? "vertical-centerindicator" : "horizontal-centerindicator"
visible: panel.alignment == Qt.AlignCenter
width: dialogRoot.vertical ? parent.width : naturalSize.width
height: dialogRoot.vertical ? naturalSize.height : parent.height
anchors.centerIn: parent
}
SliderHandle {
id: offsetHandle
graphicElementName: "offsetslider"
onValueChanged: panel.offset = value
}
SliderHandle {
id: minimumLengthHandle
alignment: panel.alignment | Qt.AlignLeft
visible: panel.alignment != Qt.AlignRight
offset: panel.offset
graphicElementName: "minslider"
onValueChanged: panel.minimumLength = value
}
SliderHandle {
id: maximumLengthHandle
alignment: panel.alignment | Qt.AlignLeft
visible: panel.alignment != Qt.AlignRight
offset: panel.offset
graphicElementName: "maxslider"
onValueChanged: panel.maximumLength = value
}
SliderHandle {
id: leftMinimumLengthHandle
alignment: panel.alignment | Qt.AlignRight
visible: panel.alignment != Qt.AlignLeft
offset: panel.offset
graphicElementName: "maxslider"
onValueChanged: panel.minimumLength = value
}
SliderHandle {
id: leftMaximumLengthHandle
alignment: panel.alignment | Qt.AlignRight
visible: panel.alignment != Qt.AlignLeft
offset: panel.offset
graphicElementName: "minslider"
onValueChanged: panel.maximumLength = value
}
states: [
State {
name: "TopEdge"
PropertyChanges {
target: root
prefix: "north"
height: root.implicitHeight
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: undefined
left: root.parent.left
right: root.parent.right
}
}
AnchorChanges {
target: offsetHandle
anchors {
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
AnchorChanges {
target: minimumLengthHandle
anchors {
top: root.top
bottom: undefined
left: undefined
right: undefined
}
}
AnchorChanges {
target: maximumLengthHandle
anchors {
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
AnchorChanges {
target: leftMinimumLengthHandle
anchors {
top: root.top
bottom: undefined
left: undefined
right: undefined
}
}
AnchorChanges {
target: leftMaximumLengthHandle
anchors {
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
},
State {
name: "BottomEdge"
PropertyChanges {
target: root
prefix: "south"
height: root.implicitHeight
}
AnchorChanges {
target: root
anchors {
top: undefined
bottom: root.parent.bottom
left: root.parent.left
right: root.parent.right
}
}
AnchorChanges {
target: offsetHandle
anchors {
top: root.top
bottom: undefined
left: undefined
right: undefined
}
}
AnchorChanges {
target: minimumLengthHandle
anchors {
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
AnchorChanges {
target: maximumLengthHandle
anchors {
top: root.top
bottom: undefined
left: undefined
right: undefined
}
}
AnchorChanges {
target: leftMinimumLengthHandle
anchors {
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
AnchorChanges {
target: leftMaximumLengthHandle
anchors {
top: root.top
bottom: undefined
left: undefined
right: undefined
}
}
},
State {
name: "LeftEdge"
PropertyChanges {
target: root
prefix: "west"
width: root.implicitWidth
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: root.parent.bottom
left: root.parent.left
right: undefined
}
}
AnchorChanges {
target: offsetHandle
anchors {
top: undefined
bottom: undefined
left: undefined
right: root.right
}
}
AnchorChanges {
target: minimumLengthHandle
anchors {
top: undefined
bottom: undefined
left: root.left
right: undefined
}
}
AnchorChanges {
target: maximumLengthHandle
anchors {
top: undefined
bottom: undefined
left: undefined
right: root.right
}
}
AnchorChanges {
target: leftMinimumLengthHandle
anchors {
top: undefined
bottom: undefined
left: root.left
right: undefined
}
}
AnchorChanges {
target: leftMaximumLengthHandle
anchors {
top: undefined
bottom: undefined
left: undefined
right: root.right
}
}
},
State {
name: "RightEdge"
PropertyChanges {
target: root
prefix: "east"
width: root.implicitWidth
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: root.parent.bottom
left: undefined
right: root.parent.right
}
}
AnchorChanges {
target: offsetHandle
anchors {
top: undefined
bottom: undefined
left: parent.left
right: undefined
}
}
AnchorChanges {
target: minimumLengthHandle
anchors {
top: undefined
bottom: undefined
left: undefined
right: parent.right
}
}
AnchorChanges {
target: maximumLengthHandle
anchors {
top: undefined
bottom: undefined
left: parent.left
right: undefined
}
}
AnchorChanges {
target: leftMinimumLengthHandle
anchors {
top: undefined
bottom: undefined
left: undefined
right: parent.right
}
}
AnchorChanges {
target: leftMaximumLengthHandle
anchors {
top: undefined
bottom: undefined
left: parent.left
right: undefined
}
}
}
]
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
import org.kde.qtextracomponents 2.0 as QtExtras
PlasmaComponents.ToolButton {
flat: false
text: "Height"
QtExtras.MouseEventListener {
anchors.fill: parent
property int startMouseX
property int startMouseY
onPressed: {
startMouseX = mouse.x
startMouseY = mouse.y
}
onPositionChanged: {
switch (panel.location) {
//TopEdge
case 3:
configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y
panel.thickness = configDialog.y - panel.y
break;
//LeftEdge
case 5:
configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x
panel.thickness = configDialog.x - panel.x
break;
//RightEdge
case 6:
configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x
panel.thickness = (panel.x + panel.width) - (configDialog.x + configDialog.width)
break;
//BottomEdge
case 4:
default:
configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y
panel.thickness = (panel.y + panel.height) - (configDialog.y + configDialog.height)
}
}
}
}

View File

@ -0,0 +1,166 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
import org.kde.qtextracomponents 2.0 as QtExtras
PlasmaCore.SvgItem {
id: root
svg: containmentControlsSvg
state: parent.state
width: naturalSize.width
height: naturalSize.height
//value expressed by this slider
property int value
//name of the graphics to load
property string graphicElementName
//where the point "0" is
property int offset: 0
//handle type: behave in different ways based on the alignment
property int alignment: panel.alignment
function syncPos() {
if (dialogRoot.vertical) {
if (alignment == Qt.AlignRight) {
y = root.parent.height - (value + offset + root.height/2)
} else if (alignment == Qt.AlignLeft) {
y = value + offset - root.height/2
} else {
if (root.alignment & Qt.AlignRight) {
y = root.parent.height/2 - value/2 - offset + root.height/2
} else if (root.alignment & Qt.AlignLeft) {
y = root.parent.height/2 + value/2 + offset - root.height/2
} else {
y = root.parent.height/2 + value + offset -root.height/2
}
}
} else {
if (alignment == Qt.AlignRight) {
x = root.parent.width - (value + offset + root.width/2)
} else if (alignment == Qt.AlignLeft) {
x = value + offset - root.width/2
} else {
if (root.alignment & Qt.AlignRight) {
x = root.parent.width/2 - value/2 + offset - root.width/2
} else if (root.alignment & Qt.AlignLeft) {
x = root.parent.width/2 + value/2 + offset -root.width/2
} else {
x = root.parent.width/2 + value + offset -root.width/2
}
}
}
}
onValueChanged: syncPos()
onOffsetChanged: syncPos()
onAlignmentChanged: syncPos()
Connections {
target: root.parent
onWidthChanged: syncPos()
onHeightChanged: syncPos()
}
MouseArea {
drag {
target: parent
axis: (dialogRoot.vertical) ? Drag.YAxis : Drag.XAxis
}
anchors.fill: parent
onPositionChanged: {
if (dialogRoot.vertical) {
if (root.alignment == Qt.AlignRight) {
root.value = root.parent.height - (parent.y + offset + root.height/2)
} else if (alignment == Qt.AlignLeft) {
root.value = parent.y - offset + root.height/2
//Center
} else {
if (root.alignment & Qt.AlignRight) {
root.value = (root.parent.height/2 - parent.y + offset)*2 + root.height/2
} else if (root.alignment & Qt.AlignLeft) {
root.value = (parent.y - offset - root.parent.height/2)*2 + root.height/2
} else {
var value = parent.y - root.parent.height/2 - offset + root.height/2
//Snap
if (Math.abs(value) < 5) {
root.value = 0
} else {
root.value = value
}
}
}
} else {
if (root.alignment == Qt.AlignRight) {
root.value = root.parent.width - (parent.x + offset + root.width/2)
} else if (alignment == Qt.AlignLeft) {
root.value = parent.x - offset + root.width/2
//Center
} else {
if (root.alignment & Qt.AlignRight) {
root.value = (root.parent.width/2 - parent.x + offset)*2 + root.width/2
} else if (root.alignment & Qt.AlignLeft) {
root.value = (parent.x - offset - root.parent.width/2)*2 + root.width/2
} else {
var value = parent.x - root.parent.width/2 - offset + root.width/2
//Snap
if (Math.abs(value) < 5) {
root.value = 0
} else {
root.value = value
}
}
}
}
}
}
states: [
State {
name: "TopEdge"
PropertyChanges {
target: root
elementId: "north-" + graphicElementName
}
},
State {
name: "BottomEdge"
PropertyChanges {
target: root
elementId: "south-" + graphicElementName
}
},
State {
name: "LeftEdge"
PropertyChanges {
target: root
elementId: "west-" + graphicElementName
}
},
State {
name: "RightEdge"
PropertyChanges {
target: root
elementId: "east-" + graphicElementName
}
}
]
}

View File

@ -0,0 +1,232 @@
/*
* Copyright 2013 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.configuration 2.0
Item {
id: root
state: parent.state
implicitWidth: childrenRect.width + 20
implicitHeight: childrenRect.height + 20
PlasmaComponents.ButtonRow {
id: row
spacing: 0
exclusive: false
visible: !dialogRoot.vertical
anchors {
centerIn: parent
}
EdgeHandle {
id: edgeHandle
}
SizeHandle {
id: sizeHandle
}
}
//FIXME: remove this duplication, use desktopcomponents linear layouts that can switch between horizontal and vertical
PlasmaComponents.ButtonColumn {
id: column
spacing: 0
exclusive: false
visible: dialogRoot.vertical
anchors {
centerIn: parent
}
EdgeHandle {
width: 80
}
SizeHandle {
width: 80
}
}
PlasmaComponents.Button {
id: moreSettingsButton
property QtObject contextMenu
text: "More settings"
onClicked: {
if (!contextMenu) {
contextMenu = contextMenuComponent.createObject(moreSettingsButton)
}
contextMenu.open()
}
}
Component {
id: contextMenuComponent
PlasmaComponents.ContextMenu {
visualParent: moreSettingsButton
PlasmaComponents.MenuItem {
id: leftToggle
text: "Left"
checkable: true
checked: panel.alignment == Qt.AlignLeft
onClicked: panel.alignment = Qt.AlignLeft
onToggled: {
if (checked) {
centerToggle.checked = false
rightToggle.checked = false
}
}
}
PlasmaComponents.MenuItem {
id: centerToggle
text: "Center"
checkable: true
checked: panel.alignment == Qt.AlignCenter
onClicked: panel.alignment = Qt.AlignCenter
onToggled: {
if (checked) {
leftToggle.checked = false
rightToggle.checked = false
}
}
}
PlasmaComponents.MenuItem {
id: rightToggle
text: "Right"
checkable: true
checked: panel.alignment == Qt.AlignRight
onClicked: panel.alignment = Qt.AlignRight
onToggled: {
if (checked) {
centerToggle.checked = false
leftToggle.checked = false
}
}
}
}
}
//BEGIN States
states: [
State {
name: "TopEdge"
PropertyChanges {
target: root
height: root.implicitHeight
}
AnchorChanges {
target: root
anchors {
top: undefined
bottom: root.parent.bottom
left: root.parent.left
right: root.parent.right
}
}
AnchorChanges {
target: moreSettingsButton
anchors {
verticalCenter: root.verticalCenter
horizontalCenter: undefined
top: undefined
bottom: undefined
left: undefined
right: root.right
}
}
},
State {
name: "BottomEdge"
PropertyChanges {
target: root
height: root.implicitHeight
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: undefined
left: root.parent.left
right: root.parent.right
}
}
AnchorChanges {
target: moreSettingsButton
anchors {
verticalCenter: root.verticalCenter
horizontalCenter: undefined
top: undefined
bottom: undefined
left: undefined
right: root.right
}
}
},
State {
name: "LeftEdge"
PropertyChanges {
target: root
width: root.implicitWidth
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: root.parent.bottom
left: undefined
right: root.parent.right
}
}
AnchorChanges {
target: moreSettingsButton
anchors {
verticalCenter: undefined
horizontalCenter: root.verticalCenter
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
},
State {
name: "RightEdge"
PropertyChanges {
target: root
width: root.implicitWidth
}
AnchorChanges {
target: root
anchors {
top: root.parent.top
bottom: root.parent.bottom
left: root.parent.left
right: undefined
}
}
AnchorChanges {
target: moreSettingsButton
anchors {
verticalCenter: undefined
horizontalCenter: root.verticalCenter
top: undefined
bottom: root.bottom
left: undefined
right: undefined
}
}
}
]
//END States
}

View File

@ -0,0 +1,17 @@
[Desktop]
Containment=org.kde.desktop
ToolBox=org.kde.toolbox
RuntimePlatform=
[Desktop][ContainmentActions]
Ctrl;LeftButton=org.kde.standardmenu
MiddleButton=org.kde.paste
[Panel]
Containment=org.kde.panel
ToolBox=org.kde.paneltoolbox
[Panel][ContainmentActions]
Ctrl;LeftButton=org.kde.standardmenu
[Theme]
Theme=default

View File

@ -0,0 +1,197 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.draganddrop 2.0
import org.kde.qtextracomponents 2.0
PlasmaCore.FrameSvgItem {
id: background
width: list.delegateWidth
height: list.delegateHeight
property variant icon: decoration
property string title: name
property string description: model.description
property string author: model.author
property string email: model.email
property string license: model.license
property string pluginName: model.pluginName
property bool local: model.local
ListView.onRemove: SequentialAnimation {
PropertyAction {
target: background
property: "ListView.delayRemove"
value: true
}
NumberAnimation {
target: background
property: widgetExplorer.orientation == Qt.Horizontal ? "y" : "x"
to: widgetExplorer.orientation == Qt.Horizontal ? list.delegateHeight : list.delegateWidth
duration: 150
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: background
property: widgetExplorer.orientation == Qt.Horizontal ? "y" : "x"
to: widgetExplorer.orientation == Qt.Horizontal ? list.delegateHeight : list.delegateWidth
duration: 150
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: background
property: widgetExplorer.orientation == Qt.Horizontal ? "width" : "height"
from: widgetExplorer.orientation == Qt.Horizontal ? list.delegateWidth : list.delegateHeight
to: 0
duration: 150
easing.type: Easing.InOutQuad
}
PropertyAction {
target: background
property: "ListView.delayRemove"
value: false
}
}
ListView.onAdd: SequentialAnimation {
PropertyAction {
target: background
property: "y"
value: widgetExplorer.orientation == Qt.Horizontal ? -list.delegateHeight : -list.delegateWidth
}
NumberAnimation {
target: background
property: widgetExplorer.orientation == Qt.Horizontal ? "width" : "height"
from: 0
to: widgetExplorer.orientation == Qt.Horizontal ? list.delegateWidth : list.delegateHeight
duration: 150
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: background
property: widgetExplorer.orientation == Qt.Horizontal ? "y" : "x"
to: 0
duration: 150
easing.type: Easing.InOutQuad
}
}
imagePath: "widgets/viewitem"
prefix: "normal"
DragArea {
anchors.fill: parent
supportedActions: Qt.MoveAction | Qt.LinkAction
onDragStarted: tooltipDialog.visible = false
delegateImage: background.icon
mimeData {
source: parent
}
Component.onCompleted: mimeData.setData("text/x-plasmoidservicename", pluginName)
QIconItem {
id: iconWidget
anchors.verticalCenter: parent.verticalCenter
x: y
width: theme.hugeIconSize
height: width
icon: background.icon
}
Column {
anchors {
left: iconWidget.right
right: parent.right
verticalCenter: parent.verticalCenter
leftMargin: background.margins.left
rightMargin: background.margins.right
}
spacing: 4
PlasmaExtras.Heading {
id: titleText
level: 4
text: title
// font {
// weight: Font.Bold
// pointSize: theme.smallestFont.pointSize
// }
anchors {
left: parent.left
right: parent.right
}
height: paintedHeight
wrapMode: Text.WordWrap
//go with nowrap only if there is a single word too long
onPaintedWidthChanged: {
wrapTimer.restart()
}
Timer {
id: wrapTimer
interval: 200
onTriggered: {
//give it some pixels of tolerance
if (titleText.paintedWidth > titleText.width + 3) {
titleText.wrapMode = Text.NoWrap
titleText.elide = Text.ElideRight
} else {
titleText.wrapMode = Text.WordWrap
titleText.elide = Text.ElideNone
}
}
}
}
PlasmaComponents.Label {
text: description
font.pointSize: theme.smallestFont.pointSize
anchors {
left: parent.left
right: parent.right
}
//elide: Text.ElideRight
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignTop
maximumLineCount: 3
}
}
QIconItem {
icon: running ? "dialog-ok-apply" : undefined
visible: running
width: theme.smallIconSize
height: width
anchors {
right: parent.right
bottom: parent.bottom
rightMargin: background.margins.right
bottomMargin: background.margins.bottom
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onDoubleClicked: widgetExplorer.addApplet(pluginName)
onEntered: tooltipDialog.appletDelegate = background
onExited: tooltipDialog.appletDelegate = null
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
//import org.kde.plasma.core 2.0 as PlasmaCore
//import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Rectangle {
id: root
color: "pink"
width: 400
height: 800
ListView {
anchors.fill: parent
model: widgetExplorer.widgetsModel
header: PlasmaExtras.Title { text: "Add Widgets" }
delegate: Item {
width: parent.width
height: 48
Text { text: "Applet: " + pluginName }
MouseArea {
anchors.fill: parent
onClicked: widgetExplorer.addApplet(pluginName)
}
}
}
Component.onCompleted: {
print("WidgetExplorer QML loaded");
print(" found " + widgetExplorer.widgetsModel.count + " widgets");
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.qtextracomponents 2.0
MouseArea {
id: main
hoverEnabled: true
onEntered: toolTipHideTimer.running = false
onExited: toolTipHideTimer.running = true
width: childrenRect.width
height: 200
property variant icon
property string title
property string description
property string author
property string email
property string license
property string pluginName
property bool local
onClicked: tooltipDialog.visible = false
Connections {
target: tooltipDialog
onAppletDelegateChanged: {
if (!tooltipDialog.appletDelegate) {
return
}
icon = tooltipDialog.appletDelegate.icon
title = tooltipDialog.appletDelegate.title
description = tooltipDialog.appletDelegate.description
author = tooltipDialog.appletDelegate.author
email = tooltipDialog.appletDelegate.email
license = tooltipDialog.appletDelegate.license
pluginName = tooltipDialog.appletDelegate.pluginName
local = tooltipDialog.appletDelegate.local
}
}
QIconItem {
id: tooltipIconWidget
anchors.left: parent.left
anchors.top: parent.top
width: theme.hugeIconSize
height: width
icon: main.icon
}
Column {
id: nameColumn
spacing: 8
anchors {
left: tooltipIconWidget.right
leftMargin: 8
top: parent.top
right: parent.right
}
PlasmaComponents.Label {
text: title
font.bold:true
anchors.left: parent.left
anchors.right: parent.right
height: paintedHeight
wrapMode: Text.Wrap
}
PlasmaComponents.Label {
text: description
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.Wrap
}
}
Grid {
anchors.top: tooltipIconWidget.bottom
anchors.topMargin: 16
anchors.bottom: uninstallButton.top
anchors.bottomMargin: 4
rows: 3
columns: 2
spacing: 4
PlasmaComponents.Label {
text: i18n("License:")
}
PlasmaComponents.Label {
id: licenseText
text: license
wrapMode: Text.Wrap
}
PlasmaComponents.Label {
text: i18n("Author:")
}
PlasmaComponents.Label {
text: author
wrapMode: Text.Wrap
}
PlasmaComponents.Label {
text: i18n("Email:")
}
PlasmaComponents.Label {
text: email
}
}
PlasmaComponents.Button {
id: uninstallButton
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
opacity: local ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: 250 }
}
iconSource: "application-exit"
text: i18n("Uninstall")
onClicked: {
widgetExplorer.uninstall(pluginName)
tooltipDialog.visible = false
}
}
}

View File

@ -0,0 +1,378 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.qtextracomponents 2.0
Item {
id: main
width: 240
height: 800
//this is used to perfectly align the filter field and delegates
property int cellWidth: theme.defaultFont.pixelSize * 20
PlasmaCore.FrameSvgItem {
imagePath: "dialogs/background"
anchors.fill: parent
anchors.margins: margins
//color: "orange"
//opacity: 0.3
}
property int minimumWidth: cellWidth + (
widgetExplorer.orientation == Qt.Horizontal
? 0
: (scrollBar.width + 4 * 2) // 4 * 2 == left and right margins
)
property int minimumHeight: topBar.height + list.delegateHeight + (widgetExplorer.orientation == Qt.Horizontal ? scrollBar.height : 0) + 4
property Item getWidgetsButton
property Item categoryButton
function i18n(inp) { return inp; }
PlasmaComponents.ContextMenu {
id: categoriesDialog
visualParent: main.categoryButton
}
Repeater {
parent: categoriesDialog
model: widgetExplorer.filterModel
delegate: PlasmaComponents.MenuItem {
text: display
separator: model["separator"] != undefined ? model["separator"] : false
onClicked: {
list.contentX = 0
list.contentY = 0
main.categoryButton.text = display
widgetExplorer.widgetsModel.filterQuery = model["filterData"]
widgetExplorer.widgetsModel.filterType = model["filterType"]
}
Component.onCompleted: {
parent = categoriesDialog
}
}
}
PlasmaComponents.ContextMenu {
id: getWidgetsDialog
visualParent: main.getWidgetsButton
}
Repeater {
parent: getWidgetsDialog
model: widgetExplorer.widgetsMenuActions
delegate: PlasmaComponents.MenuItem {
icon: modelData.icon
text: modelData.text
separator: modelData.separator
onClicked: modelData.trigger()
Component.onCompleted: {
parent = getWidgetsDialog
}
}
}
PlasmaCore.Dialog {
id: tooltipDialog
property Item appletDelegate
Component.onCompleted: {
tooltipDialog.setAttribute(Qt.WA_X11NetWmWindowTypeToolTip, true)
tooltipDialog.windowFlags = Qt.Window|Qt.WindowStaysOnTopHint|Qt.X11BypassWindowManagerHint
}
onAppletDelegateChanged: {
if (!appletDelegate) {
toolTipHideTimer.restart()
toolTipShowTimer.running = false
} else if (tooltipDialog.visible) {
var point = main.tooltipPosition()
tooltipDialog.x = point.x
tooltipDialog.y = point.y
} else {
toolTipShowTimer.restart()
toolTipHideTimer.running = false
}
}
mainItem: Tooltip { id: tooltipWidget }
Behavior on x {
enabled: widgetExplorer.orientation == Qt.Horizontal
NumberAnimation { duration: 250 }
}
Behavior on y {
enabled: widgetExplorer.orientation == Qt.Vertical
NumberAnimation { duration: 250 }
}
}
Timer {
id: toolTipShowTimer
interval: 500
repeat: false
onTriggered: {
var point = main.tooltipPosition()
tooltipDialog.x = point.x
tooltipDialog.y = point.y
tooltipDialog.visible = true
}
}
Timer {
id: toolTipHideTimer
interval: 1000
repeat: false
onTriggered: tooltipDialog.visible = false
}
function tooltipPosition() {
return widgetExplorer.tooltipPosition(tooltipDialog.appletDelegate, tooltipDialog.width, tooltipDialog.height);
}
Loader {
id: topBar
property Item categoryButton
sourceComponent: (widgetExplorer.orientation == Qt.Horizontal) ? horizontalTopBarComponent : verticalTopBarComponent
height: item.height + 2
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: widgetExplorer.orientation == Qt.Horizontal ? 4 : 0
leftMargin: 4
}
}
Component {
id: horizontalTopBarComponent
Item {
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: filterField.height
Row {
spacing: 5
anchors {
left: parent.left
leftMargin: 2
}
PlasmaComponents.TextField {
id: filterField
width: list.width / Math.floor(list.width / cellWidth) - 4
clearButtonShown: true
placeholderText: i18n("Enter search term...")
onTextChanged: {
list.contentX = 0
list.contentY = 0
widgetExplorer.widgetsModel.searchTerm = text
}
Component.onCompleted: forceActiveFocus()
}
PlasmaComponents.Button {
id: categoryButton
text: i18n("Categories")
onClicked: categoriesDialog.open(0, categoryButton.height)
}
}
Row {
anchors.right: parent.right
spacing: 5
PlasmaComponents.Button {
id: getWidgetsButton
iconSource: "get-hot-new-stuff"
text: i18n("Get new widgets")
onClicked: getWidgetsDialog.open()
}
Repeater {
model: widgetExplorer.extraActions.length
PlasmaComponents.Button {
iconSource: widgetExplorer.extraActions[modelData].icon
text: widgetExplorer.extraActions[modelData].text
onClicked: {
widgetExplorer.extraActions[modelData].trigger()
}
}
}
PlasmaComponents.ToolButton {
iconSource: "window-close"
onClicked: widgetExplorer.closeClicked()
}
}
Component.onCompleted: {
main.getWidgetsButton = getWidgetsButton
main.categoryButton = categoryButton
}
}
}
Component {
id: verticalTopBarComponent
Column {
anchors.top: parent.top
anchors.left:parent.left
anchors.right: parent.right
spacing: 4
PlasmaComponents.ToolButton {
anchors.right: parent.right
iconSource: "window-close"
onClicked: widgetExplorer.closeClicked()
}
PlasmaComponents.TextField {
anchors {
left: parent.left
right: parent.right
}
clearButtonShown: true
placeholderText: i18n("Enter search term...")
onTextChanged: {
list.contentX = 0
list.contentY = 0
widgetExplorer.widgetsModel.searchTerm = text
}
Component.onCompleted: forceActiveFocus()
}
PlasmaComponents.Button {
anchors {
left: parent.left
right: parent.right
}
id: categoryButton
text: i18n("Categories")
onClicked: categoriesDialog.open(0, categoryButton.height)
}
Component.onCompleted: {
main.categoryButton = categoryButton
}
}
}
MouseEventListener {
id: listParent
anchors {
top: topBar.bottom
left: parent.left
right: widgetExplorer.orientation == Qt.Horizontal
? parent.right
: (scrollBar.visible ? scrollBar.left : parent.right)
bottom: widgetExplorer.orientation == Qt.Horizontal ? scrollBar.top : bottomBar.top
leftMargin: 4
bottomMargin: 4
}
onWheelMoved: {
//use this only if the wheel orientation is vertical and the list orientation is horizontal, otherwise will be the list itself managing the wheel
if (wheel.orientation == Qt.Vertical && list.orientation == ListView.Horizontal) {
var delta = wheel.delta > 0 ? 20 : -20
list.contentX = Math.min(Math.max(0, list.contentWidth - list.width),
Math.max(0, list.contentX - delta))
}
}
ListView {
id: list
property int delegateWidth: (widgetExplorer.orientation == Qt.Horizontal) ? (list.width / Math.floor(list.width / cellWidth)) : list.width
property int delegateHeight: theme.defaultFont.pixelSize * 7 - 4
anchors.fill: parent
orientation: widgetExplorer.orientation == Qt.Horizontal ? ListView.Horizontal : ListView.Vertical
snapMode: ListView.SnapToItem
model: widgetExplorer.widgetsModel
clip: widgetExplorer.orientation == Qt.Vertical
delegate: AppletDelegate {}
}
}
PlasmaComponents.ScrollBar {
id: scrollBar
orientation: widgetExplorer.orientation == Qt.Horizontal ? ListView.Horizontal : ListView.Vertical
anchors {
top: widgetExplorer.orientation == Qt.Horizontal ? undefined : listParent.top
bottom: widgetExplorer.orientation == Qt.Horizontal ? parent.bottom : bottomBar.top
left: widgetExplorer.orientation == Qt.Horizontal ? parent.left : undefined
right: parent.right
}
flickableItem: list
}
Loader {
id: bottomBar
sourceComponent: (widgetExplorer.orientation == Qt.Horizontal) ? undefined : verticalBottomBarComponent
//height: item.height
height: 48 // FIXME
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
leftMargin: 4
}
}
Component {
id: verticalBottomBarComponent
Column {
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
spacing: 4
PlasmaComponents.Button {
anchors {
left: parent.left
right: parent.right
}
id: getWidgetsButton
iconSource: "get-hot-new-stuff"
text: i18n("Get new widgets")
onClicked: getWidgetsDialog.open()
}
Repeater {
model: widgetExplorer.extraActions.length
PlasmaComponents.Button {
anchors {
left: parent.left
right: parent.right
}
iconSource: widgetExplorer.extraActions[modelData].icon
text: widgetExplorer.extraActions[modelData].text
onClicked: {
widgetExplorer.extraActions[modelData].trigger()
}
}
}
Component.onCompleted: {
main.getWidgetsButton = getWidgetsButton
}
}
}
}

View File

@ -0,0 +1,16 @@
// var panel = new Panel
// panel.screen = 0
// panel.location = 'top'
// panel.addWidget("org.kde.testapplet")
// panel.addWidget("org.kde.testcomponentsapplet")
//
// for (var i = 0; i < screenCount; ++i) {
// var desktop = new Activity
// desktop.name = i18n("Desktop")
// desktop.screen = i
// desktop.wallpaperPlugin = 'org.kde.image'
//
// desktop.addWidget("org.kde.testapplet")
// var testComponents = desktop.addWidget("org.kde.testcomponentsapplet")
// }

View File

@ -0,0 +1,67 @@
/* vim:set foldmethod=marker:
*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2
import org.kde.plasma.hardware 0.1 as Hardware
import org.kde.plasma.platformcomponents 1.0 as Platform
import QtQuick.Window 2.0
Item {
id: main
property string shell : "org.kde.blank"
property bool willing : hardware.touchscreenPresent
property int priority : 0
// This is not needed, but allows the
// handler to know whether its shell is loaded
property bool loaded : false
Hardware.Interface { id: hardware }
Platform.Application {
application: "xterm"
running: main.loaded
}
// TODO: This is not needed - just for testing purposes
Window {
id: blankDialog
visible: main.loaded
width: 500
height: 500
Rectangle {
anchors.fill: parent
color: "white"
}
Text {
anchors.fill: parent
font.pointSize: 32
text: "Blank"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
//import org.kde.plasma 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
Rectangle {
id: root
color: "gray"
width: 1024
height: 768
property Item containment
onContainmentChanged: {
print("New Containment: " + containment)
//containment.parent = root
containment.visible = true
containment.anchors.fill = root
}
Component.onCompleted: {
print("View QML loaded")
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.0
//import org.kde.plasma 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
PlasmaCore.FrameSvgItem {
id: root
width: 640
height: 32
imagePath: "widgets/panel-background"
property Item containment
onContainmentChanged: {
print("New panel Containment: " + containment)
//containment.parent = root
containment.visible = true
containment.anchors.fill = root
}
Component.onCompleted: {
print("PanelView QML loaded")
}
}

View File

@ -0,0 +1,17 @@
[Desktop Entry]
Comment=Blank desktop view furniture
Encoding=UTF-8
Keywords=
Name=Blank Desktop
Type=Service
X-KDE-ServiceTypes=Plasma/Shell
X-KDE-ParentApp=
X-KDE-PluginInfo-Author=Marco Martin
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Email=mart@kde.org
X-KDE-PluginInfo-License=GPLv2+
X-KDE-PluginInfo-Name=org.kde.blank
X-KDE-PluginInfo-Version=
X-KDE-PluginInfo-Website=
X-Plasma-MainScript=layout.js

View File

@ -26,7 +26,7 @@
#include <Plasma/Containment>
#include <QScreen>
#include "desktopcorona.h"
#include "shellcorona.h"
#include "panelview.h"
#include "scriptengine.h"
#include "widget.h"
@ -37,7 +37,7 @@ namespace WorkspaceScripting
Panel::Panel(Plasma::Containment *containment, QObject *parent)
: Containment(containment, parent)
{
m_corona = qobject_cast<DesktopCorona *>(containment->corona());
m_corona = qobject_cast<ShellCorona *>(containment->corona());
}
Panel::~Panel()

View File

@ -27,7 +27,7 @@
#include "containment.h"
class PanelView;
class DesktopCorona;
class ShellCorona;
namespace WorkspaceScripting
{
@ -88,7 +88,7 @@ public Q_SLOTS:
private:
PanelView *panel() const;
DesktopCorona *m_corona;
ShellCorona *m_corona;
};
}

420
src/shell/shellcorona.cpp Normal file
View File

@ -0,0 +1,420 @@
/*
* Copyright 2008 Aaron Seigo <aseigo@kde.org>
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
* Copyright 2013 Ivan Cukic <ivan.cukic@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "shellcorona.h"
#include <QApplication>
#include <QDebug>
#include <QDesktopWidget>
#include <QQmlContext>
#include <QTimer>
#include <KLocalizedString>
#include <Plasma/Package>
#include "containmentconfigview.h"
#include "panelview.h"
#include "view.h"
#include "scripting/desktopscriptengine.h"
#include "widgetexplorer/widgetexplorerview.h"
#include "configview.h"
class ShellCorona::Private {
public:
Private()
: desktopWidget(QApplication::desktop()),
widgetExplorerView(nullptr)
{
appConfigSyncTimer.setSingleShot(true);
// constant controlling how long between requesting a configuration sync
// and one happening should occur. currently 10 seconds
appConfigSyncTimer.setInterval(10000);
}
QString shell;
QDesktopWidget * desktopWidget;
QList <View *> views;
WidgetExplorerView * widgetExplorerView;
QHash <Plasma::Containment *, PanelView *> panelViews;
KConfigGroup desktopDefaultsConfig;
WorkspaceScripting::DesktopScriptEngine * scriptEngine;
QList<Plasma::Containment *> waitingPanels;
QSet<Plasma::Containment *> loadingDesktops;
QTimer appConfigSyncTimer;
};
WorkspaceScripting::DesktopScriptEngine * ShellCorona::scriptEngine() const
{
return d->scriptEngine;
}
ShellCorona::ShellCorona(QObject *parent)
: Plasma::Corona(parent), d(new Private())
{
d->desktopDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(package().filePath("defaults")), "Desktop");
connect(&d->appConfigSyncTimer, &QTimer::timeout,
this, &ShellCorona::syncAppConfig);
connect(d->desktopWidget, &QDesktopWidget::resized,
this, &ShellCorona::screenResized );
connect(d->desktopWidget, &QDesktopWidget::screenCountChanged,
this, &ShellCorona::screenCountChanged);
connect(d->desktopWidget, &QDesktopWidget::workAreaResized,
this, &ShellCorona::workAreaResized);
connect(this, &ShellCorona::containmentAdded,
this, &ShellCorona::handleContainmentAdded);
connect(this, &ShellCorona::screenOwnerChanged,
this, &ShellCorona::updateScreenOwner);
d->scriptEngine = new WorkspaceScripting::DesktopScriptEngine(this, true);
connect(d->scriptEngine, &WorkspaceScripting::ScriptEngine::printError,
this, &ShellCorona::printScriptError);
connect(d->scriptEngine, &WorkspaceScripting::ScriptEngine::print,
this, &ShellCorona::printScriptMessage);
checkViews();
//QTimer::singleShot(600, this, SLOT(showWidgetExplorer())); // just for easier debugging
}
ShellCorona::~ShellCorona()
{
}
void ShellCorona::setShell(const QString & shell)
{
if (d->shell == shell) return;
unload();
setPackage(shell);
d->shell = shell;
load();
}
QString ShellCorona::shell() const
{
return d->shell;
}
void ShellCorona::load()
{
if (d->shell.isEmpty()) return;
loadLayout(d->shell);
if (containments().isEmpty()) {
loadDefaultLayout();
}
processUpdateScripts();
checkScreens();
}
void ShellCorona::unload()
{
if (d->shell.isEmpty()) return;
qDeleteAll(containments());
}
KSharedConfig::Ptr ShellCorona::applicationConfig()
{
return KSharedConfig::openConfig();
}
void ShellCorona::requestApplicationConfigSync()
{
d->appConfigSyncTimer.start();
}
void ShellCorona::loadDefaultLayout()
{
QString script = package().filePath("defaultlayout");
qDebug() << "This is the default layout we are using:";
QFile file(script);
if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
QString code = file.readAll();
qDebug() << "evaluating startup script:" << script;
d->scriptEngine->evaluateScript(code);
}
}
void ShellCorona::processUpdateScripts()
{
foreach (const QString &script, WorkspaceScripting::ScriptEngine::pendingUpdateScripts(this)) {
d->scriptEngine->evaluateScript(script);
}
}
void ShellCorona::checkScreens(bool signalWhenExists)
{
// quick sanity check to ensure we have containments for each screen
int num = numScreens();
for (int i = 0; i < num; ++i) {
checkScreen(i, signalWhenExists);
}
}
void ShellCorona::checkScreen(int screen, bool signalWhenExists)
{
// signalWhenExists is there to allow PlasmaApp to know when to create views
// it does this only on containment addition, but in the case of a screen being
// added and the containment already existing for that screen, no signal is emitted
// and so PlasmaApp does not know that it needs to create a view for it. to avoid
// taking care of that case in PlasmaApp (which would duplicate some of the code below,
// ShellCorona will, when signalWhenExists is true, emit a containmentAdded signal
// even if the containment actually existed prior to this method being called.
//
//note: hte signal actually triggers view creation only for panels, atm.
//desktop views are created in response to containment's screenChanged signal instead, which is
//buggy (sometimes the containment thinks it's already on the screen, so no view is created)
//TODO: restore activities
//Activity *currentActivity = activity(d->activityController->currentActivity());
//ensure the desktop(s) have a containment and view
checkDesktop(/*currentActivity,*/ signalWhenExists, screen);
//ensure the panels get views too
if (signalWhenExists) {
foreach (Plasma::Containment * c, containments()) {
if (c->screen() != screen) {
continue;
}
Plasma::Types::ContainmentType t = c->containmentType();
if (t == Plasma::Types::PanelContainment ||
t == Plasma::Types::CustomPanelContainment) {
emit containmentAdded(c);
}
}
}
}
void ShellCorona::checkDesktop(/*Activity *activity,*/ bool signalWhenExists, int screen)
{
Plasma::Containment *c = /*activity->*/containmentForScreen(screen);
//TODO: remove following when activities are restored
if (!c) {
c = createContainment(d->desktopDefaultsConfig.readEntry("Containment", "org.kde.desktopcontainment"));
}
if (!c) {
return;
}
c->setScreen(screen);
if (screen >= 0 || d->views.count() >= screen + 1) {
d->views[screen]->setContainment(c);
} else {
qWarning() << "Invalid screen";
}
c->flushPendingConstraintsEvents();
requestApplicationConfigSync();
if (signalWhenExists) {
emit containmentAdded(c);
}
}
int ShellCorona::numScreens() const
{
return d->desktopWidget->screenCount();
}
QRect ShellCorona::screenGeometry(int id) const
{
return d->desktopWidget->screenGeometry(id);
}
QRegion ShellCorona::availableScreenRegion(int id) const
{
return d->desktopWidget->availableGeometry(id);
}
QRect ShellCorona::availableScreenRect(int id) const
{
return d->desktopWidget->availableGeometry(id);
}
PanelView *ShellCorona::panelView(Plasma::Containment *containment) const
{
return d->panelViews.value(containment);
}
///// SLOTS
void ShellCorona::screenCountChanged(int newCount)
{
qDebug() << "New screen count" << newCount;
checkViews();
}
void ShellCorona::screenResized(int screen)
{
qDebug() << "Screen resized" << screen;
}
void ShellCorona::workAreaResized(int screen)
{
qDebug() << "Work area resized" << screen;
}
void ShellCorona::checkViews()
{
if (d->views.count() == d->desktopWidget->screenCount()) {
return;
} else if (d->views.count() < d->desktopWidget->screenCount()) {
for (int i = d->views.count(); i < d->desktopWidget->screenCount(); ++i) {
View *view = new View(this);
QSurfaceFormat format;
view->show();
d->views << view;
}
} else {
for (int i = d->desktopWidget->screenCount(); i < d->views.count(); ++i) {
View *view = d->views.last();
view->deleteLater();
d->views.pop_back();
}
}
//check every containment is in proper view
for (int i = 0; i < d->desktopWidget->screenCount(); ++i) {
qDebug() << "TODO: Implement loading containments into the views";
}
}
void ShellCorona::checkLoadingDesktopsComplete()
{
Plasma::Containment *c = qobject_cast<Plasma::Containment *>(sender());
if (c) {
disconnect(c, &Plasma::Containment::uiReadyChanged,
this, &ShellCorona::checkLoadingDesktopsComplete);
d->loadingDesktops.remove(c);
}
if (d->loadingDesktops.isEmpty()) {
foreach (Plasma::Containment *cont, d->waitingPanels) {
d->panelViews[cont] = new PanelView(this);
d->panelViews[cont]->setContainment(cont);
}
d->waitingPanels.clear();
}
}
void ShellCorona::updateScreenOwner(int wasScreen, int isScreen, Plasma::Containment *containment)
{
qDebug() << "Was screen" << wasScreen << "Is screen" << isScreen <<"Containment" << containment << containment->title();
if (containment->formFactor() == Plasma::Types::Horizontal ||
containment->formFactor() == Plasma::Types::Vertical) {
if (isScreen >= 0) {
d->waitingPanels << containment;
} else {
if (d->panelViews.contains(containment)) {
d->panelViews[containment]->setContainment(0);
d->panelViews[containment]->deleteLater();
d->panelViews.remove(containment);
}
}
checkLoadingDesktopsComplete();
//Desktop view
} else {
if (containment->isUiReady()) {
checkLoadingDesktopsComplete();
} else {
d->loadingDesktops.insert(containment);
connect(containment, &Plasma::Containment::uiReadyChanged,
this, &ShellCorona::checkLoadingDesktopsComplete);
}
if (isScreen < 0 || d->views.count() < isScreen + 1) {
qWarning() << "Invalid screen";
return;
}
d->views[isScreen]->setContainment(containment);
}
}
void ShellCorona::handleContainmentAdded(Plasma::Containment* c)
{
connect(c, &Plasma::Containment::showAddWidgetsInterface,
this, &ShellCorona::showWidgetExplorer);
}
void ShellCorona::showWidgetExplorer()
{
if (!d->widgetExplorerView) {
QString expqml = package().filePath("widgetexplorer");
qDebug() << "Script to load for WidgetExplorer: " << expqml;
d->widgetExplorerView = new WidgetExplorerView(expqml);
d->widgetExplorerView->init();
}
Plasma::Containment *c = 0;
c = dynamic_cast<Plasma::Containment*>(sender());
if (c) {
qDebug() << "Found containment.";
d->widgetExplorerView->setContainment(c);
} else {
// FIXME: try harder to find a suitable containment?
qWarning() << "containment not set, don't know where to add the applet.";
}
d->widgetExplorerView->show();
}
void ShellCorona::syncAppConfig()
{
qDebug() << "Syncing plasma-shellrc config";
applicationConfig()->sync();
}
void ShellCorona::printScriptError(const QString &error)
{
qWarning() << error;
}
void ShellCorona::printScriptMessage(const QString &message)
{
qDebug() << message;
}
// Desktop corona handler
#include "moc_shellcorona.cpp"

View File

@ -14,57 +14,40 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
class * Free Software Foundation, Inc.,
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DESKTOPCORONA_H
#define DESKTOPCORONA_H
#ifndef SHELLCORONA_H
#define SHELLCORONA_H
#include "plasma/corona.h"
#include "configview.h"
class QDesktopWidget;
class QQuickView;
class PanelView;
class View;
class WidgetExplorerView;
namespace Plasma
{
class Applet;
} // namespace Plasma
class PanelView;
namespace WorkspaceScripting {
class DesktopScriptEngine;
}
class DesktopCorona : public Plasma::Corona
class ShellCorona : public Plasma::Corona
{
Q_OBJECT
Q_PROPERTY(QString shell READ shell WRITE setShell)
public:
explicit DesktopCorona(QObject * parent = 0);
~DesktopCorona();
explicit ShellCorona(QObject * parent = 0);
~ShellCorona();
/**
* Where to save global configuration that doesn't have anything to do with the scene (e.g. views)
*/
KSharedConfig::Ptr applicationConfig();
/**
* Request saving applicationConfig on disk, it's event compressed, not immediate
*/
void requestApplicationConfigSync();
/**
* Loads the default (system wide) layout for this user
**/
void loadDefaultLayout();
/**
* Execute any update script
*/
void processUpdateScripts();
WorkspaceScripting::DesktopScriptEngine * scriptEngine() const;
/**
* Ensures we have the necessary containments for every screen
*/
@ -84,6 +67,22 @@ public:
PanelView *panelView(Plasma::Containment *containment) const;
public Q_SLOTS:
/**
* Request saving applicationConfig on disk, it's event compressed, not immediate
*/
void requestApplicationConfigSync();
/**
* Sets the shell that the corona should display
*/
void setShell(const QString & shell);
/**
* Gets the currently shown shell
*/
QString shell() const;
protected Q_SLOTS:
void screenCountChanged(int newCount);
void screenResized(int screen);
@ -95,23 +94,38 @@ protected Q_SLOTS:
void printScriptError(const QString &error);
void printScriptMessage(const QString &message);
/**
* Loads the layout and performs the needed checks
*/
void load();
/**
* Unloads everything
*/
void unload();
/**
* Loads the default (system wide) layout for this user
**/
void loadDefaultLayout();
/**
* Execute any update script
*/
void processUpdateScripts();
private Q_SLOTS:
void checkLoadingDesktopsComplete();
void handleContainmentAdded(Plasma::Containment *c);
void showWidgetExplorer();
void syncAppConfig();
void checkLoadingDesktopsComplete();
private:
QDesktopWidget *m_desktopWidget;
QList <View *> m_views;
WidgetExplorerView *m_widgetExplorerView;
QList<Plasma::Containment *> m_waitingPanels;
QSet<Plasma::Containment *> m_loadingDesktops;
QHash<Plasma::Containment *, PanelView *> m_panelViews;
KConfigGroup m_desktopDefaultsConfig;
QTimer *m_appConfigSyncTimer;
class Private;
const QScopedPointer<Private> d;
};
#endif
#endif // SHELLCORONA_H

205
src/shell/shellmanager.cpp Normal file
View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "shellmanager.h"
#include <algorithm>
#include <QDebug>
#include <QDir>
#include <QList>
#include <QTimer>
#include <QQmlEngine>
#include <QQmlComponent>
#include <config-prefix.h>
#include "shellcorona.h"
#include "shellpluginloader.h"
static const QString s_shellsDir(
QString(CMAKE_INSTALL_PREFIX) + "/" + DATA_INSTALL_DIR + "/" + "plasma/shells/");
static const QString s_shellLoaderPath = QString("/contents/loader.qml");
//
// ShellManager
//
class ShellManager::Private {
public:
Private()
: currentHandler(nullptr)
{
shellUpdateDelay.setInterval(100);
shellUpdateDelay.setSingleShot(true);
}
QList<QObject *> handlers;
QObject * currentHandler;
QTimer shellUpdateDelay;
ShellCorona * corona;
};
ShellManager::ShellManager()
: d(new Private())
{
ShellPluginLoader::init();
connect(
&d->shellUpdateDelay, &QTimer::timeout,
this, &ShellManager::updateShell
);
d->corona = new ShellCorona(this);
connect(
this, &ShellManager::shellChanged,
d->corona, &ShellCorona::setShell
);
loadHandlers();
}
ShellManager::~ShellManager()
{
// if (d->currentHandler)
// d->currentHandler->unload();
}
void ShellManager::loadHandlers()
{
// TODO: Use corona's qml engine when it switches from QScriptEngine
static QQmlEngine * engine = new QQmlEngine(this);
for (const auto & dir: QDir(s_shellsDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
const QString qmlFile = s_shellsDir + dir + s_shellLoaderPath;
// qDebug() << "Making a new instance of " << qmlFile;
QQmlComponent handlerComponent(engine,
QUrl::fromLocalFile(qmlFile)
);
auto handler = handlerComponent.create();
// Writing out the errors
for (const auto & error: handlerComponent.errors()) {
qWarning() << "Error: " << error;
}
if (handler) {
registerHandler(handler);
}
}
updateShell();
}
void ShellManager::registerHandler(QObject * handler)
{
// qDebug() << "We got the handler: " << handler->property("shell").toString();
connect(
handler, &QObject::destroyed,
this, &ShellManager::deregisterHandler
);
connect(
handler, SIGNAL(willingChanged()),
this, SLOT(requestShellUpdate())
);
connect(
handler, SIGNAL(priorityChanged()),
this, SLOT(requestShellUpdate())
);
d->handlers.push_back(handler);
}
void ShellManager::deregisterHandler(QObject * handler)
{
if (d->handlers.contains(handler)) {
d->handlers.removeAll(handler);
handler->disconnect(this);
}
if (d->currentHandler == handler)
d->currentHandler = nullptr;
}
void ShellManager::requestShellUpdate()
{
d->shellUpdateDelay.start();
}
void ShellManager::updateShell()
{
d->shellUpdateDelay.stop();
if (d->handlers.isEmpty()) {
qFatal("We have no shell handlers installed");
return;
}
// Finding the handler that has the priority closest to zero.
// We will return a handler even if there are no willing ones.
auto handler =* std::min_element(d->handlers.cbegin(), d->handlers.cend(),
[] (QObject * left, QObject * right)
{
auto willing = [] (QObject * handler)
{
return handler->property("willing").toBool();
};
auto priority = [] (QObject * handler)
{
return handler->property("priority").toInt();
};
return
// If one is willing and the other is not,
// return it - it has the priority
willing(left) && !willing(right) ? true :
!willing(left) && willing(right) ? false :
// otherwise just compare the priorities
priority(left) < priority(right);
}
);
if (handler == d->currentHandler) return;
// Activating the new handler and killing the old one
if (d->currentHandler) {
d->currentHandler->setProperty("loaded", false);
}
d->currentHandler = handler;
d->currentHandler->setProperty("loaded", true);
// d->corona->setShell(d->currentHandler->property("shell").toString());
emit shellChanged(d->currentHandler->property("shell").toString());
}
ShellManager * ShellManager::instance()
{
static ShellManager manager;
return &manager;
}

52
src/shell/shellmanager.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SHELLMANAGER_H
#define SHELLMANAGER_H
#include <QObject>
class ShellManager: public QObject {
Q_OBJECT
public:
static ShellManager * instance();
~ShellManager();
void loadHandlers();
protected Q_SLOTS:
void registerHandler(QObject * handler);
void deregisterHandler(QObject * handler);
public Q_SLOTS:
void requestShellUpdate();
void updateShell();
Q_SIGNALS:
void shellChanged(const QString & shell);
private:
ShellManager();
class Private;
const QScopedPointer<Private> d;
};
#endif /* SHELLMANAGER_H */

View File

@ -55,10 +55,10 @@ void ShellPackageStructure::initPackage(Plasma::Package *package)
package->addFileDefinition("containmentconfigurationui", "configuration/ContainmentConfiguration.qml", i18n("QML component for the configuration dialog for containments"));
package->addFileDefinition("panelconfigurationui", "configuration/PanelConfiguration.qml", i18n("Panel configuration UI"));
//WIdget explorer
//Widget explorer
package->addFileDefinition("widgetexplorer", "explorer/WidgetExplorer.qml", i18n("Widgets explorer UI"));
//package->setRequired("mainscript", true);
}

View File

@ -30,8 +30,6 @@ ShellPluginLoader::~ShellPluginLoader()
{
}
Plasma::Package ShellPluginLoader::internalLoadPackage(const QString &packageFormat, const QString &specialization)
{
Q_UNUSED(specialization)
@ -43,3 +41,8 @@ Plasma::Package ShellPluginLoader::internalLoadPackage(const QString &packageFor
return Plasma::Package();
}
}
void ShellPluginLoader::init()
{
Plasma::PluginLoader::setPluginLoader(new ShellPluginLoader);
}

View File

@ -25,10 +25,12 @@
class ShellPluginLoader: public Plasma::PluginLoader
{
public:
public:
ShellPluginLoader();
~ShellPluginLoader();
static void init();
protected:
Plasma::Package internalLoadPackage(const QString &packageFormat, const QString &specialization);

View File

@ -100,17 +100,17 @@ void View::setContainment(Plasma::Containment *cont)
emit containmentChanged();
if (!cont) {
if (cont) {
connect(cont, &Plasma::Containment::locationChanged,
this, &View::locationChanged);
connect(cont, &Plasma::Containment::formFactorChanged,
this, &View::formFactorChanged);
connect(cont, &Plasma::Containment::configureRequested,
this, &View::showConfigurationInterface);
} else {
return;
}
connect(cont, &Plasma::Containment::locationChanged,
this, &View::locationChanged);
connect(cont, &Plasma::Containment::formFactorChanged,
this, &View::formFactorChanged);
connect(cont, &Plasma::Containment::configureRequested,
this, &View::showConfigurationInterface);
QObject *graphicObject = m_containment.data()->property("graphicObject").value<QObject *>();
if (graphicObject) {