diff options
author | Antti Hölttä <AHoelttae@luxoft.com> | 2018-07-23 14:53:11 +0200 |
---|---|---|
committer | Antti Hölttä <AHoelttae@luxoft.com> | 2019-03-18 16:29:30 +0100 |
commit | ffd151ecf3971fd8601d8b1d4e2f04fb5793e364 (patch) | |
tree | cd3b947f04781d59bc61306dd34ef15f8c3a24fd /plugin |
Initial commit, demoable poc
Sort of working with 4 dir navigation, check demoapp
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/cursornavigation.cpp | 87 | ||||
-rw-r--r-- | plugin/cursornavigation.h | 45 | ||||
-rw-r--r-- | plugin/cursornavigationalgorithm.cpp | 13 | ||||
-rw-r--r-- | plugin/cursornavigationalgorithm.h | 26 | ||||
-rw-r--r-- | plugin/cursornavigationattached.cpp | 82 | ||||
-rw-r--r-- | plugin/cursornavigationattached.h | 54 | ||||
-rw-r--r-- | plugin/inputadapter.cpp | 83 | ||||
-rw-r--r-- | plugin/inputadapter.h | 40 | ||||
-rw-r--r-- | plugin/inputtypes.cpp | 23 | ||||
-rw-r--r-- | plugin/inputtypes.h | 33 | ||||
-rw-r--r-- | plugin/itemregister.cpp | 36 | ||||
-rw-r--r-- | plugin/itemregister.h | 30 | ||||
-rw-r--r-- | plugin/plugin.cpp | 13 | ||||
-rw-r--r-- | plugin/plugin.h | 16 | ||||
-rw-r--r-- | plugin/plugin.pro | 36 | ||||
-rw-r--r-- | plugin/qmldir | 2 | ||||
-rw-r--r-- | plugin/spatialnavigation4dir.cpp | 161 | ||||
-rw-r--r-- | plugin/spatialnavigation4dir.h | 17 |
18 files changed, 797 insertions, 0 deletions
diff --git a/plugin/cursornavigation.cpp b/plugin/cursornavigation.cpp new file mode 100644 index 0000000..5cb9545 --- /dev/null +++ b/plugin/cursornavigation.cpp @@ -0,0 +1,87 @@ +#include "cursornavigation.h" +#include "cursornavigationalgorithm.h" +#include "spatialnavigation4dir.h" +#include <QQuickWindow> +#include <QQuickItem> + +const char CursorNavigation::windowPropertyName[] = "cursor_navigation"; + +CursorNavigation::CursorNavigation(QQuickWindow *parent) +:QObject(parent) +,m_inputAdapter(parent, this) +,m_currentItem(nullptr) +{ + m_algorithms.push_back(new SpatialNavigation4Dir(&m_itemRegister)); +} + +bool CursorNavigation::inputCommand(CursorNavigationCommand cmd) +{ + QQuickItem *nextItem; + + for (auto alg : m_algorithms) { + nextItem = alg->getNextCandidate(m_itemRegister.items(), m_currentItem, cmd); + if (nextItem) + break; + } + + if (nextItem) { + if (m_currentItem) { + CursorNavigationAttached *current=cursorNavigationAttachment(m_currentItem); + Q_ASSERT(current); + current->setHasCursor(false); + } + CursorNavigationAttached *next=cursorNavigationAttachment(nextItem); + Q_ASSERT(next); + next->setHasCursor(true); + m_currentItem = nextItem; + } +} + +CursorNavigationAttached *CursorNavigation::qmlAttachedProperties(QObject *object) +{ + // if the object is a window, use its contentItem instead + if (auto win = qobject_cast<QQuickWindow *>(object)) { + object = win->contentItem(); + } + + if (!qobject_cast<QQuickItem *>(object)) { + qWarning("Cannot manage focus for a non-Item!"); + return nullptr; + } + + QQuickItem *item = static_cast<QQuickItem *>(object); + + // TODO: what if an object, with an already attached object, gets reparented (say, in another window?) + // with or without a focus system. + + return new CursorNavigationAttached(item); +} + +CursorNavigation *CursorNavigation::cursorNavigationForWindow(QQuickWindow *window) +{ + if (!window) + return nullptr; + + const QVariant &oldCursorNavigation = window->property(windowPropertyName); + if (!oldCursorNavigation.isNull()) + return oldCursorNavigation.value<CursorNavigation *>(); + + CursorNavigation *cursorNavigation = new CursorNavigation(window); + window->setProperty(windowPropertyName, QVariant::fromValue(cursorNavigation)); + + //why would the context property be needed? + /*if (QQmlEngine *engine = cn->qmlEngine(window)) { + engine->rootContext()->setContextProperty("_cursorNavigation", cn); + } else { + qDebug() << "Couldn't find QQmlEngine"; + }*/ + + return cursorNavigation; +} + +CursorNavigationAttached *CursorNavigation::cursorNavigationAttachment(QQuickItem *item) +{ + Q_ASSERT(item); + return static_cast<CursorNavigationAttached *>(qmlAttachedPropertiesObject<CursorNavigation>(item, false)); +} + diff --git a/plugin/cursornavigation.h b/plugin/cursornavigation.h new file mode 100644 index 0000000..a9309fb --- /dev/null +++ b/plugin/cursornavigation.h @@ -0,0 +1,45 @@ +#ifndef CURSORNAVIGATION_H +#define CURSORNAVIGATION_H + +#include "cursornavigationattached.h" +#include "itemregister.h" +#include "inputtypes.h" +#include "inputadapter.h" + +#include <QObject> +#include <qqml.h> + +class QQuickItem; +class CursorNavigationAlgorithm; + +class CursorNavigation : public QObject +{ + Q_OBJECT + +public: + CursorNavigation(QQuickWindow *parent); + + bool inputCommand(CursorNavigationCommand cmd); + + static CursorNavigationAttached *qmlAttachedProperties(QObject *object); + + static CursorNavigation *cursorNavigationForWindow(QQuickWindow *window); + +private: + void setCursorOnItem(QQuickItem *item); + static CursorNavigationAttached *cursorNavigationAttachment(QQuickItem *item); + +private: + static const char windowPropertyName[]; + InputAdapter m_inputAdapter; + QQuickItem *m_currentItem; //item that currently has the cursor + QList<CursorNavigationAlgorithm*> m_algorithms; + ItemRegister m_itemRegister; + + + friend class CursorNavigationAttached; +}; + +QML_DECLARE_TYPEINFO(CursorNavigation, QML_HAS_ATTACHED_PROPERTIES) + +#endif // CURSORNAVIGATION_H diff --git a/plugin/cursornavigationalgorithm.cpp b/plugin/cursornavigationalgorithm.cpp new file mode 100644 index 0000000..d21c81c --- /dev/null +++ b/plugin/cursornavigationalgorithm.cpp @@ -0,0 +1,13 @@ +#include "cursornavigationalgorithm.h" +#include "itemregister.h" + +CursorNavigationAlgorithm::CursorNavigationAlgorithm(ItemRegister *itemRegister) +:m_itemRegister(itemRegister) +{ + +} + +CursorNavigationAlgorithm::~CursorNavigationAlgorithm() +{ + +} diff --git a/plugin/cursornavigationalgorithm.h b/plugin/cursornavigationalgorithm.h new file mode 100644 index 0000000..02f247a --- /dev/null +++ b/plugin/cursornavigationalgorithm.h @@ -0,0 +1,26 @@ +#ifndef CURSORNAVIGATIONALGORITHM_H +#define CURSORNAVIGATIONALGORITHM_H + +#include <QList> +#include "inputtypes.h" + +class ItemRegister; +class QQuickItem; + +class CursorNavigationAlgorithm +{ +public: + CursorNavigationAlgorithm(ItemRegister *itemRegister); + + virtual ~CursorNavigationAlgorithm(); + + virtual QQuickItem* getNextCandidate(const QList<QQuickItem*> &candidates, + const QQuickItem *currentItem, + const CursorNavigationCommand& cmd) = 0; + +private: + ItemRegister *m_itemRegister; + +}; + +#endif // CURSORNAVIGATIONALGORITHM_H diff --git a/plugin/cursornavigationattached.cpp b/plugin/cursornavigationattached.cpp new file mode 100644 index 0000000..50b3686 --- /dev/null +++ b/plugin/cursornavigationattached.cpp @@ -0,0 +1,82 @@ +#include "cursornavigationattached.h" +#include "cursornavigation.h" +#include <QQuickItem> +#include <QQuickWindow> + +CursorNavigationAttached::CursorNavigationAttached(QQuickItem *parent) +:QObject(parent), +m_acceptsCursor(true), +m_cursorNavigation(nullptr), +m_hasCursor(false) +{ + connect(parent, &QQuickItem::windowChanged, this, &CursorNavigationAttached::onWindowChanged); + + if (item()->window()) + { + qDebug() << "Item has a window already"; + onWindowChanged(item()->window()); + } +} + +bool CursorNavigationAttached::acceptsCursor() const +{ + return m_acceptsCursor; +} + +void CursorNavigationAttached::setAcceptsCursor(bool acceptsCursor) +{ + if (acceptsCursor != m_acceptsCursor) { + m_acceptsCursor=acceptsCursor; + emit acceptsCursorChanged(m_acceptsCursor); + } +} + +bool CursorNavigationAttached::hasCursor() const +{ + return m_hasCursor; +} + +bool CursorNavigationAttached::trapsCursor() const +{ + return m_trapsCursor; +} + +void CursorNavigationAttached::setTrapsCursor(bool trapsCursor) +{ + if (trapsCursor != m_trapsCursor) { + m_trapsCursor=trapsCursor; + emit trapsCursorChanged(m_trapsCursor); + } +} + +void CursorNavigationAttached::onWindowChanged(QQuickWindow *window) +{ + qDebug() << "window changed, window = " << window; + if (m_cursorNavigation) + m_cursorNavigation->m_itemRegister.unregisterItem(item()); + + if (window) { + m_cursorNavigation = CursorNavigation::cursorNavigationForWindow(window); + } else { + m_cursorNavigation = nullptr; + } + + if (m_cursorNavigation) + m_cursorNavigation->m_itemRegister.registerItem(item()); + + //emit focusManagerChanged(); +} + +QQuickItem *CursorNavigationAttached::item() const +{ + Q_ASSERT(qobject_cast<QQuickItem *>(parent())); + return static_cast<QQuickItem *>(parent()); +} + +void CursorNavigationAttached::setHasCursor(bool hasCursor) +{ + if (hasCursor != m_hasCursor) { + m_hasCursor=hasCursor; + emit hasCursorChanged(m_hasCursor); + } +} diff --git a/plugin/cursornavigationattached.h b/plugin/cursornavigationattached.h new file mode 100644 index 0000000..2be0f72 --- /dev/null +++ b/plugin/cursornavigationattached.h @@ -0,0 +1,54 @@ +#ifndef CURSORNAVIGATIONATTACHED_H +#define CURSORNAVIGATIONATTACHED_H + +//#include <qqml.h> +#include <QObject> + +class CursorNavigation; +class QQuickItem; +class QQuickWindow; + +class CursorNavigationAttached : public QObject +{ + Q_OBJECT + //is available for cursor navigation + Q_PROPERTY(bool acceptsCursor READ acceptsCursor WRITE setAcceptsCursor NOTIFY acceptsCursorChanged) + //is available for cursor navigation + Q_PROPERTY(bool hasCursor READ hasCursor NOTIFY hasCursorChanged) + //traps cursor. a trapped cursor can not be traversed outside of the item that traps it + Q_PROPERTY(bool trapsCursor READ trapsCursor WRITE setTrapsCursor NOTIFY trapsCursorChanged) + //proxy cursor to other items + //Q_PROPERTY(QQmlListProperty<QQuickItem> preferredCursorTargets READ preferredCursorTargetsQML) + +public: + CursorNavigationAttached(QQuickItem *parent); + + bool acceptsCursor() const; + void setAcceptsCursor(bool acceptsCursor); + + bool hasCursor() const; + + bool trapsCursor() const; + void setTrapsCursor(bool trapsCursor); + +signals: + void acceptsCursorChanged(bool acceptsCursor); + void hasCursorChanged(bool hasCursor); + void trapsCursorChanged(bool trapsCursor); + +private slots: + void onWindowChanged(QQuickWindow *window); + +private: + QQuickItem *item() const; + void setHasCursor(bool hasCursor); + + CursorNavigation *m_cursorNavigation; + bool m_acceptsCursor; + bool m_hasCursor; + bool m_trapsCursor; + + friend class CursorNavigation; +}; + +#endif // CURSORNAVIGATIONATTACHED_H diff --git a/plugin/inputadapter.cpp b/plugin/inputadapter.cpp new file mode 100644 index 0000000..5814e5d --- /dev/null +++ b/plugin/inputadapter.cpp @@ -0,0 +1,83 @@ +#include "inputadapter.h" +#include "cursornavigation.h" +#include <QEvent> +#include <QKeyEvent> +#include <QMouseEvent> +#include <QWheelEvent> + +InputAdapter::InputAdapter(QObject *target, CursorNavigation *cursorNavigation) + : QObject(cursorNavigation) + ,m_target(target) + ,m_cursorNavigation(cursorNavigation) +{ + if (m_target) + m_target->installEventFilter(this); +} + +bool InputAdapter::eventFilter(QObject *object, QEvent *event) +{ + if (object != m_target) + return false; + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + return handleKeyEvent(keyEvent); + } else if (event->type() == QEvent::Wheel) { + QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event); + return handleWheelEvent(wheelEvent); + } else if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return handleMouseEvent(mouseEvent); + } + return false; +} + +bool InputAdapter::handleKeyEvent(QKeyEvent *event) +{ + CursorNavigationCommand cmd; + //detect arrow keys, tabs, enter and esc + switch (event->key()) + { + case Qt::Key_Left: + cmd = CursorNavigationCommand::Left; + break; + case Qt::Key_Right: + cmd = CursorNavigationCommand::Right; + break; + case Qt::Key_Up: + cmd = CursorNavigationCommand::Up; + break; + case Qt::Key_Down: + cmd = CursorNavigationCommand::Down; + break; + case Qt::Key_Return: + case Qt::Key_Enter: + cmd.action = CursorNavigationCommand::Activate; + break; + case Qt::BackButton: + case Qt::Key_Escape: + cmd.action = CursorNavigationCommand::Escape; + break; + case Qt::Key_Tab: + cmd.action = CursorNavigationCommand::Escape; + break; + case Qt::Key_Backtab: + cmd.action = CursorNavigationCommand::Escape; + break; + default: + return false; + } + + return m_cursorNavigation->inputCommand(cmd); +} + +bool InputAdapter::handleMouseEvent(QMouseEvent *event) +{ + //interpret mouse movement as omnnidirectional joystick movements for testing purposes + return false; +} + +bool InputAdapter::handleWheelEvent(QWheelEvent *event) +{ + //turn wheel events into tabs + return false; +} diff --git a/plugin/inputadapter.h b/plugin/inputadapter.h new file mode 100644 index 0000000..0613751 --- /dev/null +++ b/plugin/inputadapter.h @@ -0,0 +1,40 @@ +#ifndef INPUTADAPTER_H +#define INPUTADAPTER_H + +#include <QObject> +#include "inputtypes.h" + +class CursorNavigation; +class QQuickWindow; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; + +/* filter various input events and translate them for the cursor navigation. + * it is possible to interpret mouse events as joystick/swipe events + * Set instance of this class as an input filter to the window or component that + * is being tracked. + * Events are passed forward to the CursorNavigation class, which should accept + * the event or reject it. When rejected, event is passed on. + */ + +class InputAdapter : public QObject +{ + Q_OBJECT +public: + InputAdapter(QObject *target, CursorNavigation *cursorNavigation); + +protected: + bool eventFilter(QObject *object, QEvent *event) override; + +private: + bool handleKeyEvent(QKeyEvent *ev); + bool handleMouseEvent(QMouseEvent *ev); + bool handleWheelEvent(QWheelEvent *ev); + + QObject *const m_target; + CursorNavigation *m_cursorNavigation; + +}; + +#endif // INPUTADAPTER_H diff --git a/plugin/inputtypes.cpp b/plugin/inputtypes.cpp new file mode 100644 index 0000000..770b03d --- /dev/null +++ b/plugin/inputtypes.cpp @@ -0,0 +1,23 @@ +#include "inputtypes.h" + +const CursorNavigationCommand CursorNavigationCommand::Up(1.0, 270.0); +const CursorNavigationCommand CursorNavigationCommand::Down(1.0, 90.0); +const CursorNavigationCommand CursorNavigationCommand::Left(1.0, 180.0); +const CursorNavigationCommand CursorNavigationCommand::Right(1.0, 0.0); + +CursorNavigationCommand::CursorNavigationCommand() + :magnitude(-1), angle(-1), action(NoAction) +{} + +CursorNavigationCommand::CursorNavigationCommand(float magnitude, int angle) + :magnitude(magnitude), angle(angle), action(NoAction) +{} + +//test if this commands angle is between given angles. clockwise from begin to end +bool CursorNavigationCommand::angleIsBetween(int begin, int end) const +{ + if (begin > end) + return angle >= begin || angle <= end; + else + return angle >= begin && angle <= end; +} diff --git a/plugin/inputtypes.h b/plugin/inputtypes.h new file mode 100644 index 0000000..bf7e82e --- /dev/null +++ b/plugin/inputtypes.h @@ -0,0 +1,33 @@ +#ifndef INPUTTYPES_H +#define INPUTTYPES_H + +struct CursorNavigationCommand +{ + enum Action + { + NoAction, + Forward, //tab + Back, //ctrl-tab + Activate, //enter/click on item + Escape //leave scope + }; + + CursorNavigationCommand(); + + CursorNavigationCommand(float magnitude, int angle); + + //test if this commands angle is between given angles. clockwise from begin to end + bool angleIsBetween(int begin, int end) const; + + float magnitude; //0.0 to 1.0 + int angle; //0 to 359 + Action action; + + static const CursorNavigationCommand Up; + static const CursorNavigationCommand Down; + static const CursorNavigationCommand Left; + static const CursorNavigationCommand Right; + +}; + +#endif // INPUTTYPES_H diff --git a/plugin/itemregister.cpp b/plugin/itemregister.cpp new file mode 100644 index 0000000..f4555f1 --- /dev/null +++ b/plugin/itemregister.cpp @@ -0,0 +1,36 @@ +#include "itemregister.h" +#include <QQuickItem> + +ItemRegister::ItemRegister() +{ + +} + +void ItemRegister::registerItem(QQuickItem* item) +{ + if (!item) + return; + + m_items.append(item); + connect(item, &QQuickItem::destroyed, this, &ItemRegister::onItemDestroyed); +} + +void ItemRegister::unregisterItem(QQuickItem* item) +{ + if (!item) + return; + + disconnect(item, &QQuickItem::destroyed, this, &ItemRegister::onItemDestroyed); + m_items.removeOne(item); +} + +const QList<QQuickItem*> ItemRegister::items() const +{ + return m_items; +} + +void ItemRegister::onItemDestroyed(QObject *obj) +{ + QQuickItem *item=static_cast<QQuickItem*>(obj); + m_items.removeOne(item); +} diff --git a/plugin/itemregister.h b/plugin/itemregister.h new file mode 100644 index 0000000..479bca7 --- /dev/null +++ b/plugin/itemregister.h @@ -0,0 +1,30 @@ +#ifndef ITEMREGISTER_H +#define ITEMREGISTER_H + +#include <QObject> + +class QQuickItem; + +//keeps track of items that are cursor navigable +class ItemRegister : public QObject +{ + Q_OBJECT + +public: + ItemRegister(); + + void registerItem(QQuickItem* item); + void unregisterItem(QQuickItem* item); + + const QList<QQuickItem*> items() const; + +private Q_SLOTS: + void onItemDestroyed(QObject *obj); + +private: + //for now the data structure is just a list. could be replaced with something more efficient for the final purpose + QList<QQuickItem*> m_items; + +}; + +#endif // ITEMREGISTER_H diff --git a/plugin/plugin.cpp b/plugin/plugin.cpp new file mode 100644 index 0000000..26d156a --- /dev/null +++ b/plugin/plugin.cpp @@ -0,0 +1,13 @@ +#include "plugin.h" +#include "cursornavigation.h" +#include "qqml.h" + +CursorNavigationPlugin::CursorNavigationPlugin() +{ +} + +void CursorNavigationPlugin::registerTypes(const char *uri) +{ + qmlRegisterUncreatableType<CursorNavigation>(uri, 1, 0, "CursorNavigation", + QStringLiteral("CursorNavigation is not creatable, use the attached properties.")); +} diff --git a/plugin/plugin.h b/plugin/plugin.h new file mode 100644 index 0000000..237c7b2 --- /dev/null +++ b/plugin/plugin.h @@ -0,0 +1,16 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include <QQmlExtensionPlugin> + +class CursorNavigationPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + CursorNavigationPlugin(); + void registerTypes(const char *uri); +}; + +#endif // PLUGIN_H diff --git a/plugin/plugin.pro b/plugin/plugin.pro new file mode 100644 index 0000000..72c6b96 --- /dev/null +++ b/plugin/plugin.pro @@ -0,0 +1,36 @@ +TARGET = cursornavigationplugin +TEMPLATE = lib + +QT -= gui +QT += qml quick + +CONFIG += plugin + +DEFINES += PLUGIN_LIBRARY + +SOURCES += \ + plugin.cpp \ + cursornavigation.cpp \ + cursornavigationattached.cpp \ + itemregister.cpp \ + inputadapter.cpp \ + cursornavigationalgorithm.cpp \ + spatialnavigation4dir.cpp \ + inputtypes.cpp + +HEADERS += \ + plugin.h \ + cursornavigation.h \ + cursornavigationattached.h \ + itemregister.h \ + inputadapter.h \ + inputtypes.h \ + cursornavigationalgorithm.h \ + spatialnavigation4dir.h + +pluginfiles.files += qmldir + +target.path = $$[QT_INSTALL_QML]/CursorNavigation +pluginfiles.path = $$[QT_INSTALL_QML]/CursorNavigation + +INSTALLS += target pluginfiles diff --git a/plugin/qmldir b/plugin/qmldir new file mode 100644 index 0000000..02cff60 --- /dev/null +++ b/plugin/qmldir @@ -0,0 +1,2 @@ +module CursorNavigation +plugin cursornavigationplugin diff --git a/plugin/spatialnavigation4dir.cpp b/plugin/spatialnavigation4dir.cpp new file mode 100644 index 0000000..fa1657e --- /dev/null +++ b/plugin/spatialnavigation4dir.cpp @@ -0,0 +1,161 @@ +#include "spatialnavigation4dir.h" +#include <QQuickItem> +#include <QDebug> +#include <algorithm> +#include <functional> + +//we only compare distances to eachother so no need to calculate expensive +//square roots. centerpoint comparison is just enough for now too +float distanceSquared(const QRectF& item1, const QRectF& item2) +{ + QPointF p1=item1.center(); + QPointF p2=item2.center(); + float dx=p1.x()-p2.x(); + float dy=p1.y()-p2.y(); + return dx*dx+dy*dy; +} + +SpatialNavigation4Dir::SpatialNavigation4Dir(ItemRegister *itemRegister) + :CursorNavigationAlgorithm (itemRegister) +{ + +} + +QQuickItem* SpatialNavigation4Dir::getNextCandidate(const QList<QQuickItem*> &candidates, + const QQuickItem *currentItem, + const CursorNavigationCommand &cmd) +{ + if (candidates.isEmpty()) + return nullptr; + + qDebug() << "spatial chooser called, no of candidates=" << candidates.count(); + + if (!currentItem && candidates.size()) { + qDebug() << "the spatial chooser falling back to first child" << candidates.first(); + return candidates.first(); + } + + //picking the next item according to the current items location and the command: + //-check direction + //-choose candidates in that general direction currentItem our current item (up, down, left or right) + //-currentItem those pick ones inside of current items projection + // -currentItem those within the projection pick the closest one + //-if no hits within the projection, then take the closest with distance just in the general direction + //this algorithm uses the scene coordinates of the items + + std::function<bool(const QRectF&)> isInDirection; + std::function<bool(const QRectF&)> isInProjection; + + //scene coords of the current item + const QRectF currentItemSceneRect = currentItem->mapRectToScene(QRectF( 0, 0, + currentItem->width(), currentItem->height() )); + + //NOTICE: overlapping candidates will be ignored for now (TODO, this needs to be changed) + + if (cmd.angleIsBetween(315, 45) || cmd.angleIsBetween(135, 225) ) { + //if (cmd == CursorNavigationCommand::Right || cmd == CursorNavigationCommand::Left) { + + isInProjection = [¤tItemSceneRect](const QRectF &itemRect) { + return !( currentItemSceneRect.y() > itemRect.y()+itemRect.height() || + currentItemSceneRect.y()+currentItemSceneRect.height() < itemRect.y() ); + }; + if (cmd.angleIsBetween(315, 45)) { + //if (cmd == Command_Right) { + isInDirection = [¤tItemSceneRect](const QRectF &itemRect) { + return currentItemSceneRect.x()+currentItemSceneRect.width() <= itemRect.x(); + }; + } else { + isInDirection = [¤tItemSceneRect](const QRectF &itemRect) { + return currentItemSceneRect.x() >= itemRect.x()+itemRect.width(); + }; + } + + } else if (cmd.angleIsBetween(225, 315) || cmd.angleIsBetween(45, 135)) { + //} else if (cmd == Command_Up || cmd == Command_Down) { + isInProjection = [¤tItemSceneRect](const QRectF &itemRect) { + return !( currentItemSceneRect.x() > itemRect.x()+itemRect.width() || + currentItemSceneRect.x()+currentItemSceneRect.width() < itemRect.x() ); + }; + if (cmd.angleIsBetween(45, 135)) { + //if (cmd == Command_Down) { + isInDirection = [¤tItemSceneRect](const QRectF &itemRect) { + return currentItemSceneRect.y()+currentItemSceneRect.height() <= itemRect.y(); + }; + } else { + isInDirection = [currentItemSceneRect](const QRectF &itemRect) { + return currentItemSceneRect.y() >= itemRect.y()+itemRect.height(); + }; + } + } else { + return nullptr; + } + + std::pair<QQuickItem*,int> closest(nullptr,0); + + //qDebug() << "current: x=" << currentItemSceneRect.x() << " y=" << currentItemSceneRect.y(); + + for (auto candidate : candidates) + { + if (!candidate->isVisible() || !candidate->isEnabled()) { + //qDebug() << "skipping a invisible/disabled item"; + continue; + } + + //scene coords of the candidate + QRectF candidateSceneRect = candidate->mapRectToScene( + QRect( 0, 0, + candidate->width(), candidate->height() )); + + //qDebug() << "x=" << candidateSceneRect.x() << " y=" << candidateSceneRect.y(); + + if (isInDirection(candidateSceneRect) && isInProjection(candidateSceneRect)) + { + //qDebug() << " is in direction and projection..."; + int dist = distanceSquared(currentItemSceneRect,candidateSceneRect); + if (closest.second > dist || !closest.first) + { + closest.second = dist; + closest.first = candidate; + } + } + } + + if (closest.first) + { + qDebug() << "chosen one: " << closest.first->mapRectToScene( + QRect( 0, 0, + closest.first->width(), closest.first->height() )); + } + + if (!closest.first) { + //qDebug() << Q_FUNC_INFO << " looking for a candidate in the general direction..."; + + for (auto candidate : candidates) + { + if (!candidate->isVisible() || !candidate->isEnabled()) { + //qDebug() << "skipping a invisible/disabled item"; + continue; + } + + //scene coords of the candidate + QRectF candidateSceneRect = candidate->mapRectToScene( + QRect( 0, 0, + candidate->width(), candidate->height() )); + + if (isInDirection(candidateSceneRect)) + { + int dist = distanceSquared(currentItemSceneRect,candidateSceneRect); + if (closest.second > dist || !closest.first) + { + closest.second = dist; + closest.first = candidate; + } + } + } + } + + qDebug() << Q_FUNC_INFO << " selected candidate:" << closest.first; + + return closest.first; + +} diff --git a/plugin/spatialnavigation4dir.h b/plugin/spatialnavigation4dir.h new file mode 100644 index 0000000..a91c505 --- /dev/null +++ b/plugin/spatialnavigation4dir.h @@ -0,0 +1,17 @@ +#ifndef SPATIALNAVIGATION_H +#define SPATIALNAVIGATION_H + +#include "cursornavigationalgorithm.h" + +class SpatialNavigation4Dir : public CursorNavigationAlgorithm +{ +public: + SpatialNavigation4Dir(ItemRegister *itemRegister); + + virtual QQuickItem* getNextCandidate(const QList<QQuickItem*> &candidates, + const QQuickItem *currentItem, + const CursorNavigationCommand &cmd); + +}; + +#endif // SPATIALNAVIGATION_H |