diff --git a/autotests/integration/scripting/screenedge_test.cpp b/autotests/integration/scripting/screenedge_test.cpp index 7ca92b56e..9c188cc2f 100644 --- a/autotests/integration/scripting/screenedge_test.cpp +++ b/autotests/integration/scripting/screenedge_test.cpp @@ -1,217 +1,242 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin 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, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "cursor.h" #include "effectloader.h" #include "platform.h" #include "wayland_server.h" #include "workspace.h" #include "scripting/scripting.h" #include "effect_builtins.h" #include "workspace.h" #define private public #include "screenedge.h" #undef private #include Q_DECLARE_METATYPE(KWin::ElectricBorder) using namespace KWin; static const QString s_socketName = QStringLiteral("wayland_test_kwin_scripting_screenedge-0"); class ScreenEdgeTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testEdge_data(); void testEdge(); void testEdgeUnregister(); + void testDeclarativeTouchEdge(); private: void triggerConfigReload(); }; void ScreenEdgeTest::initTestCase() { QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); // empty config to have defaults auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); // disable all effects to prevent them grabbing edges KConfigGroup plugins(config, QStringLiteral("Plugins")); ScriptedEffectLoader loader; const auto builtinNames = BuiltInEffects::availableEffectNames() << loader.listOfKnownEffects(); for (QString name : builtinNames) { plugins.writeEntry(name + QStringLiteral("Enabled"), false); } // disable electric border pushback config->group("Windows").writeEntry("ElectricBorderPushbackPixels", 0); config->sync(); kwinApp()->setConfig(config); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QVERIFY(Scripting::self()); ScreenEdges::self()->setTimeThreshold(0); ScreenEdges::self()->setReActivationThreshold(0); } void ScreenEdgeTest::init() { KWin::Cursor::setPos(640, 512); if (workspace()->showingDesktop()) { workspace()->slotToggleShowDesktop(); } QVERIFY(!workspace()->showingDesktop()); } void ScreenEdgeTest::cleanup() { // try to unload the script const QStringList scripts = {QFINDTESTDATA("./scripts/screenedge.js"), QFINDTESTDATA("./scripts/screenedgeunregister.js")}; for (const QString &script: scripts) { if (!script.isEmpty()) { if (Scripting::self()->isScriptLoaded(script)) { QVERIFY(Scripting::self()->unloadScript(script)); QTRY_VERIFY(!Scripting::self()->isScriptLoaded(script)); } } } } void ScreenEdgeTest::testEdge_data() { QTest::addColumn("edge"); QTest::addColumn("triggerPos"); QTest::newRow("Top") << KWin::ElectricTop << QPoint(512, 0); QTest::newRow("TopRight") << KWin::ElectricTopRight << QPoint(1279, 0); QTest::newRow("Right") << KWin::ElectricRight << QPoint(1279, 512); QTest::newRow("BottomRight") << KWin::ElectricBottomRight << QPoint(1279, 1023); QTest::newRow("Bottom") << KWin::ElectricBottom << QPoint(512, 1023); QTest::newRow("BottomLeft") << KWin::ElectricBottomLeft << QPoint(0, 1023); QTest::newRow("Left") << KWin::ElectricLeft << QPoint(0, 512); QTest::newRow("TopLeft") << KWin::ElectricTopLeft << QPoint(0, 0); //repeat a row to show previously unloading and re-registering works QTest::newRow("Top") << KWin::ElectricTop << QPoint(512, 0); } void ScreenEdgeTest::testEdge() { const QString scriptToLoad = QFINDTESTDATA("./scripts/screenedge.js"); QVERIFY(!scriptToLoad.isEmpty()); // mock the config auto config = kwinApp()->config(); QFETCH(KWin::ElectricBorder, edge); config->group(QLatin1String("Script-") + scriptToLoad).writeEntry("Edge", int(edge)); config->sync(); QVERIFY(!Scripting::self()->isScriptLoaded(scriptToLoad)); const int id = Scripting::self()->loadScript(scriptToLoad); QVERIFY(id != -1); QVERIFY(Scripting::self()->isScriptLoaded(scriptToLoad)); auto s = Scripting::self()->findScript(scriptToLoad); QVERIFY(s); QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged); QVERIFY(runningChangedSpy.isValid()); s->run(); QVERIFY(runningChangedSpy.wait()); QCOMPARE(runningChangedSpy.count(), 1); QCOMPARE(runningChangedSpy.first().first().toBool(), true); // triggering the edge will result in show desktop being triggered QSignalSpy showDesktopSpy(workspace(), &Workspace::showingDesktopChanged); QVERIFY(showDesktopSpy.isValid()); // trigger the edge QFETCH(QPoint, triggerPos); KWin::Cursor::setPos(triggerPos); QCOMPARE(showDesktopSpy.count(), 1); QVERIFY(workspace()->showingDesktop()); } void ScreenEdgeTest::triggerConfigReload() { workspace()->slotReconfigure(); } void ScreenEdgeTest::testEdgeUnregister() { const QString scriptToLoad = QFINDTESTDATA("./scripts/screenedgeunregister.js"); QVERIFY(!scriptToLoad.isEmpty()); Scripting::self()->loadScript(scriptToLoad); auto s = Scripting::self()->findScript(scriptToLoad); auto configGroup = s->config(); configGroup.writeEntry("Edge", int(KWin::ElectricLeft)); configGroup.sync(); const QPoint triggerPos = QPoint(0, 512); QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged); s->run(); QVERIFY(runningChangedSpy.wait()); QSignalSpy showDesktopSpy(workspace(), &Workspace::showingDesktopChanged); QVERIFY(showDesktopSpy.isValid()); //trigger the edge KWin::Cursor::setPos(triggerPos); QCOMPARE(showDesktopSpy.count(), 1); //reset KWin::Cursor::setPos(500,500); workspace()->slotToggleShowDesktop(); showDesktopSpy.clear(); //trigger again, to show that retriggering works KWin::Cursor::setPos(triggerPos); QCOMPARE(showDesktopSpy.count(), 1); //reset KWin::Cursor::setPos(500,500); workspace()->slotToggleShowDesktop(); showDesktopSpy.clear(); //make the script unregister the edge configGroup.writeEntry("mode", "unregister"); triggerConfigReload(); KWin::Cursor::setPos(triggerPos); QCOMPARE(showDesktopSpy.count(), 0); //not triggered //force the script to unregister a non-registered edge to prove it doesn't explode triggerConfigReload(); } +void ScreenEdgeTest::testDeclarativeTouchEdge() +{ + const QString scriptToLoad = QFINDTESTDATA("./scripts/screenedgetouch.qml"); + QVERIFY(!scriptToLoad.isEmpty()); + QVERIFY(Scripting::self()->loadDeclarativeScript(scriptToLoad) != -1); + QVERIFY(Scripting::self()->isScriptLoaded(scriptToLoad)); + + auto s = Scripting::self()->findScript(scriptToLoad); + QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged); + s->run(); + QTRY_COMPARE(runningChangedSpy.count(), 1); + + QSignalSpy showDesktopSpy(workspace(), &Workspace::showingDesktopChanged); + QVERIFY(showDesktopSpy.isValid()); + + // Trigger the edge through touch + quint32 timestamp = 0; + kwinApp()->platform()->touchDown(0, QPointF(0, 50), timestamp++); + kwinApp()->platform()->touchMotion(0, QPointF(500, 50), timestamp++); + kwinApp()->platform()->touchUp(0, timestamp++); + + QVERIFY(showDesktopSpy.wait()); +} + WAYLANDTEST_MAIN(ScreenEdgeTest) #include "screenedge_test.moc" diff --git a/autotests/integration/scripting/scripts/screenedgetouch.qml b/autotests/integration/scripting/scripts/screenedgetouch.qml new file mode 100644 index 000000000..04c136aeb --- /dev/null +++ b/autotests/integration/scripting/scripts/screenedgetouch.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0; +import org.kde.kwin 2.0; + +ScreenEdgeItem { + edge: ScreenEdgeItem.LeftEdge + mode: ScreenEdgeItem.Touch + onActivated: { + workspace.slotToggleShowDesktop(); + } +} diff --git a/scripting/screenedgeitem.cpp b/scripting/screenedgeitem.cpp index 49369c30c..8dbf7bda1 100644 --- a/scripting/screenedgeitem.cpp +++ b/scripting/screenedgeitem.cpp @@ -1,85 +1,118 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin 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, see . *********************************************************************/ #include "screenedgeitem.h" #include #include "screenedge.h" +#include + namespace KWin { ScreenEdgeItem::ScreenEdgeItem(QObject* parent) : QObject(parent) , m_enabled(true) , m_edge(NoEdge) + , m_action(new QAction(this)) { + connect(m_action, &QAction::triggered, this, &ScreenEdgeItem::activated); } ScreenEdgeItem::~ScreenEdgeItem() { } void ScreenEdgeItem::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } disableEdge(); m_enabled = enabled; enableEdge(); emit enabledChanged(); } void ScreenEdgeItem::setEdge(Edge edge) { if (m_edge == edge) { return; } disableEdge(); m_edge = edge; enableEdge(); emit edgeChanged(); } void ScreenEdgeItem::enableEdge() { if (!m_enabled || m_edge == NoEdge) { return; } - ScreenEdges::self()->reserve(static_cast(m_edge), this, "borderActivated"); + switch (m_mode) { + case Mode::Pointer: + ScreenEdges::self()->reserve(static_cast(m_edge), this, "borderActivated"); + break; + case Mode::Touch: + ScreenEdges::self()->reserveTouch(static_cast(m_edge), m_action); + break; + default: + Q_UNREACHABLE(); + } } void ScreenEdgeItem::disableEdge() { if (!m_enabled || m_edge == NoEdge) { return; } - ScreenEdges::self()->unreserve(static_cast(m_edge), this); + switch (m_mode) { + case Mode::Pointer: + ScreenEdges::self()->unreserve(static_cast(m_edge), this); + break; + case Mode::Touch: + ScreenEdges::self()->unreserveTouch(static_cast(m_edge), m_action); + break; + default: + Q_UNREACHABLE(); + } } bool ScreenEdgeItem::borderActivated(ElectricBorder edge) { if (edge != static_cast(m_edge) || !m_enabled) { return false; } emit activated(); return true; } +void ScreenEdgeItem::setMode(Mode mode) +{ + if (m_mode == mode) { + return; + } + disableEdge(); + m_mode = mode; + enableEdge(); + emit modeChanged(); +} + } // namespace diff --git a/scripting/screenedgeitem.h b/scripting/screenedgeitem.h index f9984d500..bb67acf05 100644 --- a/scripting/screenedgeitem.h +++ b/scripting/screenedgeitem.h @@ -1,108 +1,129 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin 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, see . *********************************************************************/ #ifndef KWIN_SCREENEDGEITEM_H #define KWIN_SCREENEDGEITEM_H #include #include +class QAction; + namespace KWin { /** * @brief Qml export for reserving a Screen Edge. * * The edge is controlled by the @c enabled property and the @c edge * property. If the edge is enabled and gets triggered the @link activated() * signal gets emitted. * * Example usage: * @code * ScreenEdgeItem { * edge: ScreenEdgeItem.LeftEdge * onActivated: doSomething() * } * @endcode * */ class ScreenEdgeItem : public QObject { Q_OBJECT Q_ENUMS(Edge) + Q_ENUMS(Mode) /** * @brief Whether the edge is currently enabled, that is reserved. Default value is @c true. * */ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) /** * @brief Which of the screen edges is to be reserved. Default value is @c NoEdge. * */ Q_PROPERTY(Edge edge READ edge WRITE setEdge NOTIFY edgeChanged) + /** + * @brief The operation mode for this edge. Default value is @c Mode::Pointer + **/ + Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY modeChanged) public: enum Edge { TopEdge, TopRightEdge, RightEdge, BottomRightEdge, BottomEdge, BottomLeftEdge, LeftEdge, TopLeftEdge, EDGE_COUNT, NoEdge }; + /** + * Enum describing the operation modes of the edge. + **/ + enum class Mode { + Pointer, + Touch + }; explicit ScreenEdgeItem(QObject *parent = 0); virtual ~ScreenEdgeItem(); bool isEnabled() const; Edge edge() const; + Mode mode() const { + return m_mode; + } public Q_SLOTS: void setEnabled(bool enabled); void setEdge(Edge edge); + void setMode(Mode mode); Q_SIGNALS: void enabledChanged(); void edgeChanged(); + void modeChanged(); void activated(); private Q_SLOTS: bool borderActivated(ElectricBorder edge); private: void enableEdge(); void disableEdge(); bool m_enabled; Edge m_edge; + Mode m_mode = Mode::Pointer; + QAction *m_action; }; inline bool ScreenEdgeItem::isEnabled() const { return m_enabled; } inline ScreenEdgeItem::Edge ScreenEdgeItem::edge() const { return m_edge; } } // namespace KWin #endif // KWIN_SCREENEDGEITEM_H