aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntti Hölttä <AHoelttae@luxoft.com>2018-11-06 15:39:36 +0100
committerAntti Hölttä <AHoelttae@luxoft.com>2019-03-18 16:31:43 +0100
commitfbe75056a6ca179504b33c74d741c540f5b22dcd (patch)
treec535cd55d285b8e2e6b9a450a88a8e316e5ba12f
parent00fa5d9215d32124292fe3e45372acebac7b9d31 (diff)
More on scopes, works to some extent in the test program
Scope may be entered and escaped. Navigable items may have escape targets defined
-rw-r--r--DemoApplication/Page2Form.qml100
-rw-r--r--DemoApplication/Page3Form.qml89
-rw-r--r--DemoApplication/main.qml12
-rw-r--r--DemoApplication/qml.qrc1
-rw-r--r--plugin/cursornavigation.cpp92
-rw-r--r--plugin/cursornavigation.h12
-rw-r--r--plugin/cursornavigationalgorithm.cpp1
-rw-r--r--plugin/cursornavigationattached.cpp21
-rw-r--r--plugin/cursornavigationattached.h17
-rw-r--r--plugin/inputadapter.cpp4
-rw-r--r--plugin/inputtypes.h36
-rw-r--r--plugin/itemregister.cpp51
-rw-r--r--plugin/itemregister.h35
-rw-r--r--plugin/plugin.pro2
14 files changed, 315 insertions, 158 deletions
diff --git a/DemoApplication/Page2Form.qml b/DemoApplication/Page2Form.qml
index 552c3e5..891f8cc 100644
--- a/DemoApplication/Page2Form.qml
+++ b/DemoApplication/Page2Form.qml
@@ -13,52 +13,74 @@ Page {
anchors.centerIn: parent
}
- ListView {
- id: listView
+ FocusScope {
+ CursorNavigation.acceptsCursor: true
x: 385
y: 19
width: 198
height: 359
- delegate: Item {
- x: 5
- width: 80
- height: 40
-
- Row {
- id: row1
- Rectangle {
- width: 40
- height: 40
- color: colorCode
- }
+ ListView {
+ id: listView
+ anchors.fill: parent
+ spacing: 4
+ focus: true
- Text {
- text: name
- anchors.verticalCenter: parent.verticalCenter
- font.bold: true
- }
- spacing: 10
- }
- }
- model: ListModel {
- ListElement {
- name: "Grey"
- colorCode: "grey"
+ Rectangle {
+ border.width: 2
+ border.color: "red"
+ anchors.fill: parent
+ visible: listView.activeFocus
+ color: "transparent"
}
- ListElement {
- name: "Red"
- colorCode: "red"
+ highlight: Rectangle {
+ border.width: 2
+ border.color: "red"
+ color: "grey"
}
- ListElement {
- name: "Blue"
- colorCode: "blue"
+ delegate: Item {
+ x: 5
+ width: listView.width
+ height: 40
+
+ Row {
+ id: row1
+ Rectangle {
+ width: 40
+ height: 40
+ color: colorCode
+ }
+
+ Text {
+ text: name
+ anchors.verticalCenter: parent.verticalCenter
+ font.bold: true
+ }
+ spacing: 10
+ }
}
- ListElement {
- name: "Green"
- colorCode: "green"
+ model: ListModel {
+ ListElement {
+ name: "Grey"
+ colorCode: "grey"
+ }
+
+ ListElement {
+ name: "Red"
+ colorCode: "red"
+ }
+
+ ListElement {
+ name: "Blue"
+ colorCode: "blue"
+ }
+
+ ListElement {
+ name: "Green"
+ colorCode: "green"
+ }
}
}
}
@@ -83,4 +105,12 @@ Page {
y: 266
text: qsTr("Button")
}
+
+ CNButton {
+ id: button3
+ x: 400
+ y: 400
+ text: qsTr("Button")
+ }
+
}
diff --git a/DemoApplication/Page3Form.qml b/DemoApplication/Page3Form.qml
new file mode 100644
index 0000000..df06bee
--- /dev/null
+++ b/DemoApplication/Page3Form.qml
@@ -0,0 +1,89 @@
+import QtQuick 2.0
+import CursorNavigation 1.0
+
+Item {
+ FocusScope {
+ id: rootScope
+ Row {
+ spacing: 10
+
+ CNButton {
+ width: 100
+ height: 100
+ text: "alone!"
+ }
+
+ Rectangle {
+
+ width: 250
+ height: 200
+
+ Grid {
+ spacing: 5
+ columns: 2
+ rows: 2
+
+ CNButton {
+ text: "b1"
+ }
+
+ CNButton {
+ text: "b2 (default focus)"
+ focus: true
+ }
+
+ CNButton {
+ id: defaultButton
+ text: "b3 (escape target)"
+ }
+
+ CNButton {
+ text: "b4"
+ }
+
+ }
+
+ }
+
+ Rectangle {
+
+ width: 250
+ height: 200
+
+ border.width: 2
+ border.color: "gray"
+
+ FocusScope {
+ CursorNavigation.acceptsCursor: true
+ anchors.fill: parent
+ CursorNavigation.escapeTarget: defaultButton
+
+ Grid {
+ spacing: 5
+ columns: 2
+ rows: 2
+
+ CNButton {
+ text: "sb1"
+ }
+
+ CNButton {
+ text: "sb2"
+ }
+
+ CNButton {
+ text: "sb3"
+ }
+
+ CNButton {
+ text: "sb4 (default focus)"
+ focus: true
+ }
+
+ }
+ }
+
+ }
+ }
+ }
+}
diff --git a/DemoApplication/main.qml b/DemoApplication/main.qml
index 685e8dc..b408923 100644
--- a/DemoApplication/main.qml
+++ b/DemoApplication/main.qml
@@ -5,8 +5,8 @@ import CursorNavigation 1.0
ApplicationWindow {
id: window
visible: true
- width: 640
- height: 480
+ width: 800
+ height: 600
title: qsTr("Stack")
header: ToolBar {
@@ -55,6 +55,14 @@ ApplicationWindow {
drawer.close()
}
}
+ ItemDelegate {
+ text: qsTr("Page 3")
+ width: parent.width
+ onClicked: {
+ stackView.push("Page3Form.qml")
+ drawer.close()
+ }
+ }
}
}
diff --git a/DemoApplication/qml.qrc b/DemoApplication/qml.qrc
index dbc7738..058589b 100644
--- a/DemoApplication/qml.qrc
+++ b/DemoApplication/qml.qrc
@@ -6,5 +6,6 @@
<file>Page2Form.qml</file>
<file>qtquickcontrols2.conf</file>
<file>CNButton.qml</file>
+ <file>Page3Form.qml</file>
</qresource>
</RCC>
diff --git a/plugin/cursornavigation.cpp b/plugin/cursornavigation.cpp
index 7e4ba81..0011cb2 100644
--- a/plugin/cursornavigation.cpp
+++ b/plugin/cursornavigation.cpp
@@ -9,33 +9,26 @@ const char CursorNavigation::windowPropertyName[] = "cursor_navigation";
CursorNavigation::CursorNavigation(QQuickWindow *parent)
:QObject(parent)
,m_window(parent)
-,m_inputAdapter(parent, this)
+,m_inputAdapter(m_window->contentItem(), this)
,m_currentItem(nullptr)
-,m_rootItem(nullptr)
+,m_rootItem(new CursorNavigationAttached(nullptr))
{
+ m_rootItem->setParent(m_window->contentItem());
+
m_algorithms.push_back(new SpatialNavigation4Dir());
connect(m_window, &QQuickWindow::activeFocusItemChanged, this, &CursorNavigation::onActiveFocusItemChanged);
onActiveFocusItemChanged();
}
-bool CursorNavigation::inputCommand(CursorNavigationCommand cmd)
+bool CursorNavigation::inputCommand(const CursorNavigationCommand &cmd)
{
- CursorNavigationAttached *nextItem = nullptr;
-
- QList<CursorNavigationAttached*> &candidates = m_currentItem ?
- m_currentItem->m_parentNavigable->m_children :
- m_rootItem.m_children;
- for (auto alg : m_algorithms) {
- nextItem = alg->getNextCandidate(candidates, m_currentItem, cmd);
- if (nextItem) {
- setCursorOnItem(nextItem);
- break;
- }
+ if (cmd.action == CursorNavigationCommand::NoAction) {
+ return handleDirectionCommand(cmd);
+ } else {
+ return handleActionCommand(cmd);
}
-
- return true;
}
CursorNavigationAttached *CursorNavigation::qmlAttachedProperties(QObject *object)
@@ -96,7 +89,8 @@ void CursorNavigation::setCursorOnItem(CursorNavigationAttached *item)
if (item) {
item->setHasCursor(true);
m_currentItem = item;
- m_currentItem->item()->setFocus(true);
+ m_currentItem->item()->forceActiveFocus();
+ //m_currentItem->item()->setFocus(true);
qWarning() << "Set cursor to " << item->item();
} else {
qWarning() << "Set cursor to NULL";
@@ -130,8 +124,8 @@ void CursorNavigation::registerItem(CursorNavigationAttached* item)
item->m_parentNavigable=parentCNA;
parentCNA->m_children.append(item);
} else {
- m_rootItem.m_children.append(item);
- item->m_parentNavigable=&m_rootItem;
+ m_rootItem->m_children.append(item);
+ item->m_parentNavigable=m_rootItem;
}
}
@@ -145,3 +139,63 @@ void CursorNavigation::unregisterItem(CursorNavigationAttached* item)
item->m_parentNavigable->m_children.removeOne(item);
}
+bool CursorNavigation::handleDirectionCommand(const CursorNavigationCommand &cmd)
+{
+ qWarning() << "handleDirectionCommand";
+ CursorNavigationAttached *nextItem = nullptr;
+
+ QList<CursorNavigationAttached*> &candidates = m_currentItem ?
+ m_currentItem->m_parentNavigable->m_children :
+ m_rootItem->m_children;
+
+ 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 7e61ee9..12468e4 100644
--- a/plugin/cursornavigation.h
+++ b/plugin/cursornavigation.h
@@ -2,12 +2,12 @@
#define CURSORNAVIGATION_H
#include "cursornavigationattached.h"
-#include "itemregister.h"
#include "inputtypes.h"
#include "inputadapter.h"
#include <QObject>
#include <qqml.h>
+#include <QStack>
class QQuickItem;
class CursorNavigationAlgorithm;
@@ -19,7 +19,9 @@ class CursorNavigation : public QObject
public:
CursorNavigation(QQuickWindow *parent);
- bool inputCommand(CursorNavigationCommand cmd);
+ bool inputCommand(const CursorNavigationCommand &cmd);
+ void move(QVector2D moveVector);
+ void action();
static CursorNavigationAttached *qmlAttachedProperties(QObject *object);
@@ -33,6 +35,9 @@ private:
void registerItem(CursorNavigationAttached* item);
void unregisterItem(CursorNavigationAttached* item);
+ bool handleDirectionCommand(const CursorNavigationCommand &cmd);
+ bool handleActionCommand(const CursorNavigationCommand &cmd);
+
private:
static const char windowPropertyName[];
QQuickWindow *m_window;
@@ -40,7 +45,8 @@ private:
CursorNavigationAttached *m_currentItem; //item that currently has the cursor
QList<CursorNavigationAlgorithm*> m_algorithms;
//a root item that is not tied to any actual QQuickItem
- CursorNavigationAttached m_rootItem;
+ CursorNavigationAttached *m_rootItem;
+ QStack<CursorNavigationAttached*> m_scopeStack;
friend class CursorNavigationAttached;
};
diff --git a/plugin/cursornavigationalgorithm.cpp b/plugin/cursornavigationalgorithm.cpp
index 0676434..c08761f 100644
--- a/plugin/cursornavigationalgorithm.cpp
+++ b/plugin/cursornavigationalgorithm.cpp
@@ -1,5 +1,4 @@
#include "cursornavigationalgorithm.h"
-#include "itemregister.h"
CursorNavigationAlgorithm::CursorNavigationAlgorithm()
{
diff --git a/plugin/cursornavigationattached.cpp b/plugin/cursornavigationattached.cpp
index 9d4aafe..9a939ff 100644
--- a/plugin/cursornavigationattached.cpp
+++ b/plugin/cursornavigationattached.cpp
@@ -8,7 +8,8 @@ CursorNavigationAttached::CursorNavigationAttached(QQuickItem *parent)
m_cursorNavigation(nullptr),
m_parentNavigable(nullptr),
m_acceptsCursor(true),
-m_hasCursor(false)
+m_hasCursor(false),
+m_escapeTarget(nullptr)
{
if (parent)
connect(parent, &QQuickItem::windowChanged, this, &CursorNavigationAttached::onWindowChanged);
@@ -82,6 +83,20 @@ QQuickItem *CursorNavigationAttached::item() const
return static_cast<QQuickItem *>(parent());
}
+QQuickItem *CursorNavigationAttached::escapeTarget() const
+{
+ return m_escapeTarget;
+}
+
+void CursorNavigationAttached::setEscapeTarget(QQuickItem *escapeTarget)
+{
+ if (m_escapeTarget == escapeTarget)
+ return;
+
+ m_escapeTarget = escapeTarget;
+ emit escapeTargetChanged(m_escapeTarget);
+}
+
void CursorNavigationAttached::setHasCursor(bool hasCursor)
{
if (hasCursor != m_hasCursor) {
@@ -90,7 +105,7 @@ void CursorNavigationAttached::setHasCursor(bool hasCursor)
}
}
-QList<CursorNavigationAttached *> &CursorNavigationAttached::siblings()
+/*QList<CursorNavigationAttached *> &CursorNavigationAttached::siblings()
{
return m_parentNavigable->m_children;
-}
+}*/
diff --git a/plugin/cursornavigationattached.h b/plugin/cursornavigationattached.h
index 1a8314c..0f3f927 100644
--- a/plugin/cursornavigationattached.h
+++ b/plugin/cursornavigationattached.h
@@ -20,6 +20,8 @@ class CursorNavigationAttached : public QObject
Q_PROPERTY(bool childHasCursor READ hasCursor NOTIFY hasCursorChanged)
//traps cursor. a trapped cursor can not be traversed outside of the item that traps it until the escape input is given
Q_PROPERTY(bool trapsCursor READ trapsCursor WRITE setTrapsCursor NOTIFY trapsCursorChanged)
+ //item to select when
+ Q_PROPERTY(QQuickItem *escapeTarget READ escapeTarget WRITE setEscapeTarget NOTIFY escapeTargetChanged)
public:
@@ -27,26 +29,30 @@ public:
~CursorNavigationAttached();
bool acceptsCursor() const;
- void setAcceptsCursor(bool acceptsCursor);
-
bool hasCursor() const;
-
bool trapsCursor() const;
- void setTrapsCursor(bool trapsCursor);
+ QQuickItem *escapeTarget() const;
QQuickItem *item() const;
+public slots:
+ void setAcceptsCursor(bool acceptsCursor);
+ void setTrapsCursor(bool trapsCursor);
+ void setEscapeTarget(QQuickItem * escapeTarget);
+
signals:
void acceptsCursorChanged(bool acceptsCursor);
void hasCursorChanged(bool hasCursor);
void trapsCursorChanged(bool trapsCursor);
+ void escapeTargetChanged(QQuickItem * escapeTarget);
+
private slots:
void onWindowChanged(QQuickWindow *window);
private:
void setHasCursor(bool hasCursor);
- QList<CursorNavigationAttached*> &siblings();
+ //QList<CursorNavigationAttached*> &siblings();
CursorNavigation *m_cursorNavigation;
CursorNavigationAttached *m_parentNavigable;
@@ -57,6 +63,7 @@ private:
bool m_trapsCursor;
friend class CursorNavigation;
+ QQuickItem * m_escapeTarget;
};
#endif // CURSORNAVIGATIONATTACHED_H
diff --git a/plugin/inputadapter.cpp b/plugin/inputadapter.cpp
index 5814e5d..99eeb4f 100644
--- a/plugin/inputadapter.cpp
+++ b/plugin/inputadapter.cpp
@@ -58,10 +58,10 @@ bool InputAdapter::handleKeyEvent(QKeyEvent *event)
cmd.action = CursorNavigationCommand::Escape;
break;
case Qt::Key_Tab:
- cmd.action = CursorNavigationCommand::Escape;
+ cmd.action = CursorNavigationCommand::Forward;
break;
case Qt::Key_Backtab:
- cmd.action = CursorNavigationCommand::Escape;
+ cmd.action = CursorNavigationCommand::Back;
break;
default:
return false;
diff --git a/plugin/inputtypes.h b/plugin/inputtypes.h
index bf7e82e..efe7f8a 100644
--- a/plugin/inputtypes.h
+++ b/plugin/inputtypes.h
@@ -1,6 +1,9 @@
#ifndef INPUTTYPES_H
#define INPUTTYPES_H
+//TODO: make these into classes w accessors
+
+//generic of way of describing the input that cursor management can handle
struct CursorNavigationCommand
{
enum Action
@@ -30,4 +33,37 @@ struct CursorNavigationCommand
};
+/*feedback datatype returned for commands, describing command results
+ *this could be used for example to indicate that a end of a list has been reached,
+ *that might produce force feedback on certain inut devices
+ */
+enum CommandResult
+{
+ Succesful = 0,
+ AreaBoundsReached,
+ ListEndReached
+};
+
+//describing which boundary was met
+enum Boundary {
+ Undefined = 0,
+ Top = 1,
+ Bottom = 2,
+ Right = 4,
+ Left = 8
+};
+
+class CursorNavigationFeedback
+{
+// Q_GADGET
+// Q_PROPERTY(CommandResult READ commandResult NOTIFY commandResultChanged )
+// Q_PROPERTY(CommandResult READ commandResult NOTIFY commandResultChanged )
+
+ //feedback cases;
+ //-cmd succesfull
+ //-end of list reached (or just the end of whatever selection area maybe)
+ //-fell back to the previous scope
+};
+
+
#endif // INPUTTYPES_H
diff --git a/plugin/itemregister.cpp b/plugin/itemregister.cpp
deleted file mode 100644
index 24c6b07..0000000
--- a/plugin/itemregister.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*#include "itemregister.h"
-#include "cursornavigation.h"
-#include <QQuickItem>
-
-ItemRegister::ItemRegister()
-{
-
-}
-
-void ItemRegister::registerItem(QQuickItem* item)
-{
- if (!item)
- return;
-
- //find first cursor navigable parent
- QQuickItem *parentItem = item->parentItem();
- CursorNavigationAttached *parentCNA;
- while (parentItem) {
- if ((parentCNA=CursorNavigation::cursorNavigationAttachment(parentItem)))
- break;
- parentItem = parentItem->parentItem();
- }
-
- if (parentCNA)
- parentCNA->m_children.append();
-
- 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::siblingItems(QQuickItem *item) const
-{
- //find the items that are within the same scope as the argument item
- 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
deleted file mode 100644
index 9c9430a..0000000
--- a/plugin/itemregister.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*#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);
-
- //find the items that are within the same scope as the argument item
- const QList<QQuickItem*> siblingItems(QQuickItem* item) const;
-
-private Q_SLOTS:
- void onItemDestroyed(QObject *obj);
-
-private:
- //find the item's first parent that is cursor navigable
- QQuickItem *findParent(QQuickItem* item);
-
- //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.pro b/plugin/plugin.pro
index 72c6b96..6f1a4c3 100644
--- a/plugin/plugin.pro
+++ b/plugin/plugin.pro
@@ -12,7 +12,6 @@ SOURCES += \
plugin.cpp \
cursornavigation.cpp \
cursornavigationattached.cpp \
- itemregister.cpp \
inputadapter.cpp \
cursornavigationalgorithm.cpp \
spatialnavigation4dir.cpp \
@@ -22,7 +21,6 @@ HEADERS += \
plugin.h \
cursornavigation.h \
cursornavigationattached.h \
- itemregister.h \
inputadapter.h \
inputtypes.h \
cursornavigationalgorithm.h \