summaryrefslogtreecommitdiffstats
path: root/plugin
diff options
context:
space:
mode:
authorAntti Hölttä <AHoelttae@luxoft.com>2018-11-13 15:40:13 +0100
committerAntti Hölttä <AHoelttae@luxoft.com>2019-03-18 16:33:53 +0100
commitc9a97de34ca56d4cab671beeeb6d27a66ac3da4e (patch)
tree652a2fc55284fa22506795eccb2e043aa18446b5 /plugin
parent67dd94b0daecc445bbd3af7b5956ddcbdbdb5f39 (diff)
Add 360 algorithm
Add algorithm that allows navigation to any direction. Works somewhat well now. Example app has a page with a gamepad/stick support.
Diffstat (limited to 'plugin')
-rw-r--r--plugin/cursornavigation.cpp121
-rw-r--r--plugin/cursornavigation.h15
-rw-r--r--plugin/cursornavigationattached.cpp36
-rw-r--r--plugin/cursornavigationattached.h3
-rw-r--r--plugin/inputadapter.cpp18
-rw-r--r--plugin/inputtypes.cpp33
-rw-r--r--plugin/inputtypes.h37
-rw-r--r--plugin/plugin.pro6
-rw-r--r--plugin/spatialnavigation360.cpp210
-rw-r--r--plugin/spatialnavigation360.h29
-rw-r--r--plugin/spatialnavigation4dir.cpp17
11 files changed, 415 insertions, 110 deletions
diff --git a/plugin/cursornavigation.cpp b/plugin/cursornavigation.cpp
index 0011cb2..4eaff64 100644
--- a/plugin/cursornavigation.cpp
+++ b/plugin/cursornavigation.cpp
@@ -3,6 +3,7 @@
#include "spatialnavigation4dir.h"
#include <QQuickWindow>
#include <QQuickItem>
+#include <QtMath>
const char CursorNavigation::windowPropertyName[] = "cursor_navigation";
@@ -15,13 +16,71 @@ CursorNavigation::CursorNavigation(QQuickWindow *parent)
{
m_rootItem->setParent(m_window->contentItem());
- m_algorithms.push_back(new SpatialNavigation4Dir());
+ //m_algorithms.push_back(new SpatialNavigation4Dir());
connect(m_window, &QQuickWindow::activeFocusItemChanged, this, &CursorNavigation::onActiveFocusItemChanged);
onActiveFocusItemChanged();
}
-bool CursorNavigation::inputCommand(const CursorNavigationCommand &cmd)
+void CursorNavigation::move(qreal angle, qreal tolerance, bool discrete)
+{
+ qreal a = qDegreesToRadians(angle);
+ qreal t = qDegreesToRadians(qFabs(std::fmod(tolerance, 180)));
+ qWarning() << "move, angle = " << a << " tolerance = " << t << " discrete = " << discrete;
+ CursorNavigationCommand cmd(a, t);
+ handleMove(cmd, discrete);
+}
+
+void CursorNavigation::move(const QVector2D& vector, qreal tolerance, bool discrete)
+{
+ qreal angle = qAtan2(vector.y(), vector.x());
+ qreal t = qDegreesToRadians(qFabs(std::fmod(tolerance, 180)));
+ qWarning() << "move(vector2d), angle = " << angle << " tolerance = " << t << " discrete = " << discrete;
+ CursorNavigationCommand cmd(angle, tolerance);
+ handleMove(cmd, discrete);
+}
+
+void CursorNavigation::action(Action action)
+{
+ qWarning() << "handleActionCommand, action= " << action;
+ switch (action) {
+ case Forward:
+ break;
+ case Back:
+ break;
+ case Activate:
+ break;
+ case Escape: {
+ /* if item has escapeTrgate defined, set focus to that. otherwise leave
+ * scope, ie. go back to parent's parent in the hierarchy and set focus
+ * (back) to it (setting the focus further to one of its children
+ * depends on the focus mechanism).
+ * if we are already at the root item's children, nothing happens
+ */
+ if (!m_currentItem)
+ break;
+
+ QQuickItem *escapeTarget = m_currentItem->m_parentNavigable->escapeTarget();
+ if (!escapeTarget) {
+ if (m_currentItem->m_parentNavigable == m_rootItem) {
+ break;
+ }
+ escapeTarget = m_currentItem->m_parentNavigable->m_parentNavigable->item();
+ }
+ qWarning() << "escaping, target = " << escapeTarget;
+ setCursorOnItem(nullptr);
+ escapeTarget->forceActiveFocus();
+ onActiveFocusItemChanged();
+ //escapeTarget->setFocus(true);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+/*bool CursorNavigation::inputCommand(const CursorNavigationCommand &cmd)
{
if (cmd.action == CursorNavigationCommand::NoAction) {
@@ -29,7 +88,7 @@ bool CursorNavigation::inputCommand(const CursorNavigationCommand &cmd)
} else {
return handleActionCommand(cmd);
}
-}
+}*/
CursorNavigationAttached *CursorNavigation::qmlAttachedProperties(QObject *object)
{
@@ -139,7 +198,7 @@ void CursorNavigation::unregisterItem(CursorNavigationAttached* item)
item->m_parentNavigable->m_children.removeOne(item);
}
-bool CursorNavigation::handleDirectionCommand(const CursorNavigationCommand &cmd)
+bool CursorNavigation::handleMove(const CursorNavigationCommand &cmd, bool discrete)
{
qWarning() << "handleDirectionCommand";
CursorNavigationAttached *nextItem = nullptr;
@@ -148,54 +207,24 @@ bool CursorNavigation::handleDirectionCommand(const CursorNavigationCommand &cmd
m_currentItem->m_parentNavigable->m_children :
m_rootItem->m_children;
- for (auto alg : m_algorithms) {
+ if (discrete) {
+ nextItem = m_navigation4Dir.getNextCandidate(candidates, m_currentItem, cmd);
+ } else {
+ nextItem = m_navigation360.getNextCandidate(candidates, m_currentItem, cmd);
+ }
+
+ if (nextItem) {
+ setCursorOnItem(nextItem);
+ }
+
+/* for (auto alg : m_algorithms) {
nextItem = alg->getNextCandidate(candidates, m_currentItem, cmd);
if (nextItem) {
setCursorOnItem(nextItem);
break;
}
- }
+ }*/
return true;
}
-bool CursorNavigation::handleActionCommand(const CursorNavigationCommand &cmd)
-{
- qWarning() << "handleActionCommand, cmd.action= " << cmd.action;
- switch (cmd.action) {
- case CursorNavigationCommand::Forward:
- break;
- case CursorNavigationCommand::Back:
- break;
- case CursorNavigationCommand::Activate:
- break;
- case CursorNavigationCommand::Escape: {
- /* if item has escapeTrgate defined, set focus to that. otherwise leave
- * scope, ie. go back to parent's parent in the hierarchy and set focus
- * (back) to it (setting the focus further to one of its children
- * depends on the focus mechanism).
- * if we are already at the root item's children, nothing happens
- */
- if (!m_currentItem)
- break;
-
- QQuickItem *escapeTarget = m_currentItem->m_parentNavigable->escapeTarget();
- if (!escapeTarget) {
- if (m_currentItem->m_parentNavigable == m_rootItem) {
- break;
- }
- escapeTarget = m_currentItem->m_parentNavigable->m_parentNavigable->item();
- }
- qWarning() << "escaping, target = " << escapeTarget;
- setCursorOnItem(nullptr);
- escapeTarget->forceActiveFocus();
- onActiveFocusItemChanged();
- //escapeTarget->setFocus(true);
- break;
- }
-
- default:
- break;
- }
-}
-
diff --git a/plugin/cursornavigation.h b/plugin/cursornavigation.h
index 96c5e67..956d900 100644
--- a/plugin/cursornavigation.h
+++ b/plugin/cursornavigation.h
@@ -4,13 +4,14 @@
#include "cursornavigationattached.h"
#include "inputtypes.h"
#include "inputadapter.h"
+#include "spatialnavigation360.h"
+#include "spatialnavigation4dir.h"
#include <QObject>
#include <qqml.h>
#include <QStack>
class QQuickItem;
-class CursorNavigationAlgorithm;
class CursorNavigation : public QObject
{
@@ -19,7 +20,10 @@ class CursorNavigation : public QObject
public:
CursorNavigation(QQuickWindow *parent);
- bool inputCommand(const CursorNavigationCommand &cmd);
+ //bool inputCommand(const CursorNavigationCommand &cmd);
+ void move(qreal angle, qreal tolerance, bool discrete);
+ void move(const QVector2D& vector, qreal tolerance, bool discrete);
+ void action(Action action);
static CursorNavigationAttached *qmlAttachedProperties(QObject *object);
@@ -33,15 +37,16 @@ private:
void registerItem(CursorNavigationAttached* item);
void unregisterItem(CursorNavigationAttached* item);
- bool handleDirectionCommand(const CursorNavigationCommand &cmd);
- bool handleActionCommand(const CursorNavigationCommand &cmd);
+ bool handleMove(const CursorNavigationCommand &cmd, bool discrete);
private:
static const char windowPropertyName[];
QQuickWindow *m_window;
InputAdapter m_inputAdapter;
CursorNavigationAttached *m_currentItem; //item that currently has the cursor
- QList<CursorNavigationAlgorithm*> m_algorithms;
+ //QList<CursorNavigationAlgorithm*> m_algorithms;
+ SpatialNavigation360 m_navigation360;
+ SpatialNavigation4Dir m_navigation4Dir;
//a root item that is not tied to any actual QQuickItem
CursorNavigationAttached *m_rootItem;
//QStack<CursorNavigationAttached*> m_scopeStack;
diff --git a/plugin/cursornavigationattached.cpp b/plugin/cursornavigationattached.cpp
index c015cd3..0f7afdc 100644
--- a/plugin/cursornavigationattached.cpp
+++ b/plugin/cursornavigationattached.cpp
@@ -68,54 +68,56 @@ void CursorNavigationAttached::setEscapeTarget(QQuickItem *escapeTarget)
emit escapeTargetChanged(m_escapeTarget);
}
-void CursorNavigationAttached::move(int angle, float magnitude)
+void CursorNavigationAttached::move(qreal angle, qreal tolerance)
{
- CursorNavigationCommand cmd(magnitude, angle);
- m_cursorNavigation->inputCommand(cmd);
+ qWarning() << "move";
+ m_cursorNavigation->move(angle, tolerance, false);
+}
+
+void CursorNavigationAttached::move(QVector2D vector, qreal tolerance)
+{
+ qWarning() << "move";
+ m_cursorNavigation->move(vector, tolerance, false);
}
void CursorNavigationAttached::moveUp()
{
- m_cursorNavigation->inputCommand(CursorNavigationCommand::Up);
+ m_cursorNavigation->move(-90, 0, true);
}
void CursorNavigationAttached::moveDown()
{
- m_cursorNavigation->inputCommand(CursorNavigationCommand::Down);
+ m_cursorNavigation->move(90, 0, true);
}
void CursorNavigationAttached::moveRight()
{
- m_cursorNavigation->inputCommand(CursorNavigationCommand::Right);
+ m_cursorNavigation->move(0, 0, true);
}
void CursorNavigationAttached::moveLeft()
{
- m_cursorNavigation->inputCommand(CursorNavigationCommand::Left);
+ m_cursorNavigation->move(180, 0, true);
}
void CursorNavigationAttached::activate()
{
- CursorNavigationCommand cmd(CursorNavigationCommand::Activate);
- m_cursorNavigation->inputCommand(cmd);
+ m_cursorNavigation->action(Activate);
}
void CursorNavigationAttached::forward()
{
- CursorNavigationCommand cmd(CursorNavigationCommand::Forward);
- m_cursorNavigation->inputCommand(cmd);
+ m_cursorNavigation->action(Forward);
}
void CursorNavigationAttached::back()
{
- CursorNavigationCommand cmd(CursorNavigationCommand::Back);
- m_cursorNavigation->inputCommand(cmd);
+ m_cursorNavigation->action(Back);
}
void CursorNavigationAttached::escape()
{
- CursorNavigationCommand cmd(CursorNavigationCommand::Escape);
- m_cursorNavigation->inputCommand(cmd);
+ m_cursorNavigation->action(Escape);
}
void CursorNavigationAttached::onWindowChanged(QQuickWindow *window)
@@ -155,7 +157,3 @@ void CursorNavigationAttached::setHasCursor(bool hasCursor)
}
}
-/*QList<CursorNavigationAttached *> &CursorNavigationAttached::siblings()
-{
- return m_parentNavigable->m_children;
-}*/
diff --git a/plugin/cursornavigationattached.h b/plugin/cursornavigationattached.h
index 344b4fa..ee32f76 100644
--- a/plugin/cursornavigationattached.h
+++ b/plugin/cursornavigationattached.h
@@ -40,7 +40,8 @@ public slots:
void setTrapsCursor(bool trapsCursor);
void setEscapeTarget(QQuickItem * escapeTarget);
- void move(int angle, float magnitude);
+ void move(qreal angle, qreal tolerance = 0);
+ void move(QVector2D vector, qreal tolerance = 0);
void moveUp();
void moveDown();
void moveRight();
diff --git a/plugin/inputadapter.cpp b/plugin/inputadapter.cpp
index 99eeb4f..89fa1a8 100644
--- a/plugin/inputadapter.cpp
+++ b/plugin/inputadapter.cpp
@@ -38,36 +38,36 @@ bool InputAdapter::handleKeyEvent(QKeyEvent *event)
switch (event->key())
{
case Qt::Key_Left:
- cmd = CursorNavigationCommand::Left;
+ m_cursorNavigation->move(180, 0, true);
break;
case Qt::Key_Right:
- cmd = CursorNavigationCommand::Right;
+ m_cursorNavigation->move(0, 0, true);
break;
case Qt::Key_Up:
- cmd = CursorNavigationCommand::Up;
+ m_cursorNavigation->move(270, 0, true);
break;
case Qt::Key_Down:
- cmd = CursorNavigationCommand::Down;
+ m_cursorNavigation->move(90, 0, true);
break;
case Qt::Key_Return:
case Qt::Key_Enter:
- cmd.action = CursorNavigationCommand::Activate;
+ m_cursorNavigation->action(Activate);
break;
case Qt::BackButton:
case Qt::Key_Escape:
- cmd.action = CursorNavigationCommand::Escape;
+ m_cursorNavigation->action(Escape);
break;
case Qt::Key_Tab:
- cmd.action = CursorNavigationCommand::Forward;
+ m_cursorNavigation->action(Forward);
break;
case Qt::Key_Backtab:
- cmd.action = CursorNavigationCommand::Back;
+ m_cursorNavigation->action(Back);
break;
default:
return false;
}
- return m_cursorNavigation->inputCommand(cmd);
+ return true;
}
bool InputAdapter::handleMouseEvent(QMouseEvent *event)
diff --git a/plugin/inputtypes.cpp b/plugin/inputtypes.cpp
index 344254b..a1b2f8d 100644
--- a/plugin/inputtypes.cpp
+++ b/plugin/inputtypes.cpp
@@ -1,27 +1,42 @@
#include "inputtypes.h"
+#include <QtMath>
-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);
+const CursorNavigationCommand CursorNavigationCommand::Up(-M_PI_2, 0);
+const CursorNavigationCommand CursorNavigationCommand::Down(M_PI_2, 0);
+const CursorNavigationCommand CursorNavigationCommand::Left(M_PI, 0);
+const CursorNavigationCommand CursorNavigationCommand::Right(0, 0);
CursorNavigationCommand::CursorNavigationCommand()
- :magnitude(-1), angle(-1), action(NoAction)
+ :angle(-1), angleTolerance(-1), action(NoAction)
{}
-CursorNavigationCommand::CursorNavigationCommand(float magnitude, int angle)
- :magnitude(magnitude), angle(angle), action(NoAction)
+CursorNavigationCommand::CursorNavigationCommand(qreal a, qreal tolerance)
+ :angle(CursorNavigationCommand::fitAngle(a)), angleTolerance(tolerance), action(NoAction)
{}
CursorNavigationCommand::CursorNavigationCommand(Action a)
- :magnitude(-1), angle(-1), action(a)
+ :angle(-1), angleTolerance(-1), action(a)
{}
//test if this commands angle is between given angles. clockwise from begin to end
-bool CursorNavigationCommand::angleIsBetween(int begin, int end) const
+//bool CursorNavigationCommand::angleIsBetween(qreal begin, qreal end)
+//{
+// return CursorNavigationCommand::angleIsBetween(this->angle, begin, end);
+//}
+
+bool CursorNavigationCommand::angleIsBetween(qreal angle, qreal begin, qreal end)
{
if (begin > end)
return angle >= begin || angle <= end;
else
return angle >= begin && angle <= end;
}
+
+qreal CursorNavigationCommand::fitAngle(qreal angle)
+{
+ if (angle > M_PI)
+ return -M_PI + std::fmod(angle ,M_PI);
+ else if (angle < -M_PI)
+ return M_PI + std::fmod(angle ,M_PI);
+ return angle;
+}
diff --git a/plugin/inputtypes.h b/plugin/inputtypes.h
index c6cc532..9de40a4 100644
--- a/plugin/inputtypes.h
+++ b/plugin/inputtypes.h
@@ -1,31 +1,32 @@
#ifndef INPUTTYPES_H
#define INPUTTYPES_H
+#include <QtGlobal>
+
//TODO: make these into classes w accessors
+enum Action
+{
+ NoAction,
+ Forward, //tab
+ Back, //ctrl-tab
+ Activate, //enter/click on item
+ Escape //leave scope
+};
+
//generic of way of describing the input that cursor management can handle
struct CursorNavigationCommand
{
- enum Action
- {
- NoAction,
- Forward, //tab
- Back, //ctrl-tab
- Activate, //enter/click on item
- Escape //leave scope
- };
-
CursorNavigationCommand();
- CursorNavigationCommand(float magnitude, int angle);
+ CursorNavigationCommand(qreal angle, qreal tolerance);
CursorNavigationCommand(Action a);
- //test if this commands angle is between given angles. clockwise from begin to end
- bool angleIsBetween(int begin, int end) const;
+ //bool angleIsBetween(qreal begin, qreal end);
- float magnitude; //0.0 to 1.0
- int angle; //0 to 359
+ qreal angle; //-pi to pi
+ qreal angleTolerance; //0 to pi
Action action;
static const CursorNavigationCommand Up;
@@ -33,6 +34,12 @@ struct CursorNavigationCommand
static const CursorNavigationCommand Left;
static const CursorNavigationCommand Right;
+ //test if given angle is between given sector. sector defined clockwise from begin to end
+ static bool angleIsBetween(qreal angle, qreal begin, qreal end);
+
+ //fit the given angle to be between -pi and pi
+ static qreal fitAngle(qreal angle);
+
};
/*feedback datatype returned for commands, describing command results
@@ -59,7 +66,7 @@ class CursorNavigationFeedback
{
// Q_GADGET
// Q_PROPERTY(CommandResult READ commandResult NOTIFY commandResultChanged )
-// Q_PROPERTY(CommandResult READ commandResult NOTIFY commandResultChanged )
+// Q_PROPERTY(Boundary READ boundary NOTIFY boundaryChanged )
//feedback cases;
//-cmd succesfull
diff --git a/plugin/plugin.pro b/plugin/plugin.pro
index 6f1a4c3..4d7fed9 100644
--- a/plugin/plugin.pro
+++ b/plugin/plugin.pro
@@ -15,7 +15,8 @@ SOURCES += \
inputadapter.cpp \
cursornavigationalgorithm.cpp \
spatialnavigation4dir.cpp \
- inputtypes.cpp
+ inputtypes.cpp \
+ spatialnavigation360.cpp
HEADERS += \
plugin.h \
@@ -24,7 +25,8 @@ HEADERS += \
inputadapter.h \
inputtypes.h \
cursornavigationalgorithm.h \
- spatialnavigation4dir.h
+ spatialnavigation4dir.h \
+ spatialnavigation360.h
pluginfiles.files += qmldir
diff --git a/plugin/spatialnavigation360.cpp b/plugin/spatialnavigation360.cpp
new file mode 100644
index 0000000..9898b07
--- /dev/null
+++ b/plugin/spatialnavigation360.cpp
@@ -0,0 +1,210 @@
+#include "spatialnavigation360.h"
+#include <QQuickItem>
+#include "cursornavigationattached.h"
+#include <algorithm>
+#include <QtMath>
+
+SpatialNavigation360::SpatialNavigation360()
+{
+
+}
+
+//test if point is contained in at least one of the given quadrants
+bool isPointIncluded(const std::vector<bool> &q, const QPointF &p, const QPointF &o)
+{
+ return (q[0] && (p.x()-o.x()) >= 0 && (p.y()-o.y()) >= 0) ||
+ (q[1] && (p.x()-o.x()) >= 0 && (p.y()-o.y()) < 0) ||
+ (q[2] && (p.x()-o.x()) < 0 && (p.y()-o.y()) < 0) ||
+ (q[3] && (p.x()-o.x()) < 0 && (p.y()-o.y()) >= 0);
+}
+
+//test if rect is contained in at least one of the given quadrants
+bool isRectIncluded(const std::vector<bool> &q, const QRectF &rect, const QPointF &origin)
+{
+ return !rect.contains(origin) &&
+ (isPointIncluded(q, rect.topLeft(), origin) ||
+ isPointIncluded(q, rect.bottomRight(), origin));
+}
+
+//test if angle is between start and end angles
+bool angleIsBetween(qreal angle, qreal begin, qreal end)
+{
+ if (begin > end)
+ return angle >= begin || angle <= end;
+ else
+ return angle >= begin && angle <= end;
+}
+
+//test if 2 sectors overlap
+bool sectorsOverlap(qreal begin1, qreal end1,
+ qreal begin2, qreal end2)
+{
+ return angleIsBetween(begin1, begin2, end2) ||
+ angleIsBetween(end1, begin2, end2) ||
+ angleIsBetween(begin2, begin1, end1) ||
+ angleIsBetween(end2, begin1, end1);
+}
+
+qreal pointDistance(const QPointF& p1, const QPointF& p2)
+{
+ qreal dx=p1.x()-p2.x();
+ qreal dy=p1.y()-p2.y();
+ return qSqrt(qPow(dx,2)+qPow(dy,2));
+}
+
+//minimum distance between 2 rects, calculated from their edges
+qreal rectDistance(const QRectF& rect1, const QRectF& rect2)
+{
+ bool left = rect2.bottomRight().x() < rect1.topLeft().x();
+ bool right = rect2.topLeft().x() > rect1.bottomRight().x();
+ bool bottom = rect2.topLeft().y() > rect1.bottomRight().y();
+ bool top = rect2.bottomRight().y() < rect1.topLeft().y();
+
+ if (top && left)
+ return pointDistance(rect1.topLeft(), rect2.bottomRight());
+ else if (left && bottom)
+ return pointDistance(rect1.bottomLeft(), rect2.topRight());
+ else if (bottom && right)
+ return pointDistance(rect1.bottomRight(), rect2.topLeft());
+ else if (right && top)
+ return pointDistance(rect1.topRight(), rect2.bottomRight());
+ else if (left)
+ return rect1.x() - rect2.bottomRight().x();
+ else if (right)
+ return rect2.x() - rect1.bottomRight().x();
+ else if (bottom)
+ return rect2.y() - rect1.bottomRight().y();
+ else if (top)
+ return rect1.y() - rect2.bottomRight().y();//y2 - y1b
+ else //rectangles overlap
+ return 0;
+}
+
+//get the widest arc that is less than 180 degrees, that this item covers, clockwise around the origin
+std::pair<qreal, qreal> getSector(const QRectF &rect, const QPointF &origin)
+{
+ std::pair<qreal, qreal> angles;
+ angles = std::minmax({std::atan2(rect.topLeft().y()-origin.y(), rect.topLeft().x()-origin.x()),
+ std::atan2(rect.topRight().y()-origin.y(), rect.topRight().x()-origin.x()),
+ std::atan2(rect.bottomRight().y()-origin.y(), rect.bottomRight().x()-origin.x()),
+ std::atan2(rect.bottomLeft().y()-origin.y(), rect.bottomLeft().x()-origin.x())
+ });
+
+ //if delta larger than 180, invert min and max
+ if (angles.second-angles.first > M_PI) {
+ qreal temp = angles.first;
+ angles.first = angles.second;
+ angles.second = temp;
+ }
+
+ return angles;
+}
+
+CursorNavigationAttached* SpatialNavigation360::getNextCandidate(
+ const QList<CursorNavigationAttached*> &candidates,
+ const CursorNavigationAttached *currentItem,
+ const CursorNavigationCommand& cmd)
+{
+ /* -angle should be between -180 and 180 in relation to the x axis, clockwise around the origin
+ * -depending on the input angle, find min and max x and y values which items must have
+ * -calculate angles for items points that are within the limits. from those angles, pick min and max (widest sector the item covers)
+ * -items that overlap the current item center, should be ignored
+ * -items that are within seaqrch beams angle limits, are stored along the angle ranges they have
+ * -if there are more than 1 stored items, we select the item that cuts the exact selection vector.
+ * if no item overlaps the center, pick the one closest to the current item
+ * -remember to use current item's coord system as the reference!!!
+ */
+
+ qWarning() << "##### navigation360: start, angle = " << cmd.angle;
+
+ if (candidates.isEmpty())
+ return nullptr;
+
+ if (!currentItem && candidates.size()) {
+ qDebug() << "the spatial chooser falling back to first child" << candidates.first();
+ return candidates.first();
+ }
+
+ //booleans for quadrants
+ std::vector<bool> quadrants(4);
+
+ //define selector beam sector
+// int angle1_deg, angle2_deg;
+ qreal angle1, angle2;
+
+ angle1 = CursorNavigationCommand::fitAngle(cmd.angle - cmd.angleTolerance);
+ angle2 = CursorNavigationCommand::fitAngle(cmd.angle + cmd.angleTolerance);
+
+
+// angle1_deg = (cmd.angle - cmd.angleTolerance) % 360;
+// if (angle1_deg < 0)
+// angle1_deg = 360 + angle1_deg;
+// angle2_deg = (cmd.angle + cmd.angleTolerance) % 360;
+
+// int a=angle2_deg-angle1_deg;
+// while (a > 0) {
+// quadrants[(angle1_deg/90+a/90) % 4] = true;
+// a -= 90;
+// }
+
+ quadrants[0] = sectorsOverlap(angle1, angle2, 0, M_PI_2);
+ quadrants[1] = sectorsOverlap(angle1, angle2, M_PI_2, M_PI);
+ quadrants[2] = sectorsOverlap(angle1, angle2, -M_PI, -M_PI_2);
+ quadrants[3] = sectorsOverlap(angle1, angle2, -M_PI_2, 0);
+
+ qWarning() << "navigation360: quadrants = " << quadrants;
+
+ const QRectF currentItemSceneRect = currentItem->item()->mapRectToScene(
+ QRectF( 0, 0, currentItem->item()->width(), currentItem->item()->height() ));
+
+ const QPointF origin = currentItemSceneRect.center();
+ qWarning() << "origin = " << origin;
+
+ //item that overlaps the center of the selector beam
+ CursorNavigationAttached* directHitItem = nullptr;
+ qreal directHitDistance = -1;
+ //item that overlaps selector beam does not overlap with the center
+ CursorNavigationAttached* withinToleranceItem = nullptr;
+ qreal withinToleranceDistance = -1;
+
+
+ for (auto iter: candidates)
+ {
+ const QRectF itemSceneRect = iter->item()->mapRectToScene(
+ QRectF( 0, 0, iter->item()->width(), iter->item()->height() ));
+
+ if (iter == currentItem || !iter->item()->isVisible() || !iter->item()->isEnabled())
+ continue;
+
+ //if (isRectIncluded(quadrants, itemSceneRect, origin)) {
+
+ std::pair<qreal,qreal> sector = getSector(itemSceneRect, origin);
+ qWarning() << "item " << iter->item() << " rect = " << itemSceneRect << " sector " << sector;
+
+ if (angleIsBetween(cmd.angle, sector.first, sector.second)) {
+ qWarning() << "is direct hit";
+ qreal dist = rectDistance(itemSceneRect, currentItemSceneRect);
+ if (!directHitItem || dist < directHitDistance) {
+ directHitDistance = dist;
+ directHitItem = iter;
+ }
+ } else if (!directHitItem && sectorsOverlap(angle1, angle2, sector.first, sector.second)) {
+ qWarning() << "is within tolerances";
+ qreal dist = rectDistance(itemSceneRect, currentItemSceneRect);
+ if (dist < withinToleranceDistance) {
+ withinToleranceDistance = dist;
+ withinToleranceItem = iter;
+ }
+ }
+ //}
+ }
+
+ qWarning() << "##### end, directHit = " <<
+ directHitItem << " withinTolerances = " << withinToleranceItem;
+
+ if (directHitItem)
+ return directHitItem;
+
+ return withinToleranceItem;
+
+}
diff --git a/plugin/spatialnavigation360.h b/plugin/spatialnavigation360.h
new file mode 100644
index 0000000..3b9cdb9
--- /dev/null
+++ b/plugin/spatialnavigation360.h
@@ -0,0 +1,29 @@
+#ifndef SPATIALNAVIGATION360_H
+#define SPATIALNAVIGATION360_H
+
+#include "cursornavigationalgorithm.h"
+
+/* algorithm for stepless 360 cursor navigation
+ *
+ * idea: allow user customization by providing a set of variables to use for
+ * selecting a candidate. for these, the user may set tolerances and weights.
+ * tolerance affects the first step of finding candidates. weights are
+ * used when choosing between multiple candidates, that are fit within the tolerances
+ *
+ */
+
+class SpatialNavigation360 : public CursorNavigationAlgorithm
+{
+public:
+ SpatialNavigation360();
+
+ virtual CursorNavigationAttached* getNextCandidate(
+ const QList<CursorNavigationAttached*> &candidates,
+ const CursorNavigationAttached *currentItem,
+ const CursorNavigationCommand& cmd);
+
+private:
+
+};
+
+#endif // SPATIALNAVIGATION360_H
diff --git a/plugin/spatialnavigation4dir.cpp b/plugin/spatialnavigation4dir.cpp
index a6965b0..0ccf041 100644
--- a/plugin/spatialnavigation4dir.cpp
+++ b/plugin/spatialnavigation4dir.cpp
@@ -4,6 +4,7 @@
#include <QDebug>
#include <algorithm>
#include <functional>
+#include <QtMath>
//we only compare distances to eachother so no need to calculate expensive
//square roots. centerpoint comparison is just enough for now too
@@ -53,14 +54,21 @@ CursorNavigationAttached* SpatialNavigation4Dir::getNextCandidate(
//NOTICE: overlapping candidates will be ignored for now (TODO, this needs to be changed)
- if (cmd.angleIsBetween(315, 45) || cmd.angleIsBetween(135, 225) ) {
+ qreal right_start = -M_PI_4;
+ qreal right_end = M_PI_4;
+ qreal left_start = M_PI-M_PI_4;
+ qreal left_end = -M_PI+M_PI_4;
+
+
+ if (CursorNavigationCommand::angleIsBetween(cmd.angle, right_start, right_end) ||
+ CursorNavigationCommand::angleIsBetween(cmd.angle, left_start, left_end) ) {
//if (cmd == CursorNavigationCommand::Right || cmd == CursorNavigationCommand::Left) {
isInProjection = [&currentItemSceneRect](const QRectF &itemRect) {
return !( currentItemSceneRect.y() > itemRect.y()+itemRect.height() ||
currentItemSceneRect.y()+currentItemSceneRect.height() < itemRect.y() );
};
- if (cmd.angleIsBetween(315, 45)) {
+ if (CursorNavigationCommand::angleIsBetween(cmd.angle, right_start, right_end)) {
//if (cmd == Command_Right) {
isInDirection = [&currentItemSceneRect](const QRectF &itemRect) {
return currentItemSceneRect.x()+currentItemSceneRect.width() <= itemRect.x();
@@ -71,13 +79,14 @@ CursorNavigationAttached* SpatialNavigation4Dir::getNextCandidate(
};
}
- } else if (cmd.angleIsBetween(225, 315) || cmd.angleIsBetween(45, 135)) {
+ } else if (CursorNavigationCommand::angleIsBetween(cmd.angle, left_end, right_start) ||
+ CursorNavigationCommand::angleIsBetween(cmd.angle, right_end, left_start)) {
//} else if (cmd == Command_Up || cmd == Command_Down) {
isInProjection = [&currentItemSceneRect](const QRectF &itemRect) {
return !( currentItemSceneRect.x() > itemRect.x()+itemRect.width() ||
currentItemSceneRect.x()+currentItemSceneRect.width() < itemRect.x() );
};
- if (cmd.angleIsBetween(45, 135)) {
+ if (CursorNavigationCommand::angleIsBetween(cmd.angle, right_end, left_start)) {
//if (cmd == Command_Down) {
isInDirection = [&currentItemSceneRect](const QRectF &itemRect) {
return currentItemSceneRect.y()+currentItemSceneRect.height() <= itemRect.y();