Keyboard handling in row scanner 03/161503/21
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Thu, 23 Nov 2017 17:05:11 +0000 (18:05 +0100)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 1 Dec 2017 17:09:57 +0000 (18:09 +0100)
Minor refactor for row scanner / point scanner and navigation subsystem.
Now navigable elements are put in tree, without further distinction between
rows and items. Every tree level has it's own scanning direction (horizontal
or vertical). New navigation allows easy merging different trees together,
for example embedding keyboard or running more, than one application on screen,

Change-Id: I29a30f44c4d0f3eee2aa49f05facff65787364dd

16 files changed:
src/Geometry.cpp
src/Geometry.hpp
src/NavigationInterface.cpp
src/NavigationInterface.hpp
src/PointScanner.cpp
src/PointScanner.hpp
src/RowScanner.cpp
src/RowScanner.hpp
src/ScreenScanner.hpp
src/ScreenScannerManager.cpp
src/ScreenScannerManager.hpp
src/SelectActivity.cpp
src/TextToSpeech.cpp
src/TextToSpeech.hpp
src/UIElement.cpp
src/UIElement.hpp

index 22b6744..d934cbc 100644 (file)
@@ -44,6 +44,16 @@ std::string Rectangle::toString() const
        return position.toString() + " " + size.toString();
 }
 
+bool Rectangle::contains(Point pt) const
+{
+       return position.x <= pt.x && position.y <= pt.y && position.x + size.width > pt.x && position.y + size.height > pt.y;
+}
+
+Point Rectangle::getCenterPoint() const
+{
+       return { position.x + size.width / 2, position.y + size.height / 2 };
+}
+
 Rectangle Rectangle::intersect(const Rectangle first, const Rectangle second)
 {
        Rectangle rect;
index fbfcc82..b5a6836 100644 (file)
@@ -70,6 +70,8 @@ struct Rectangle {
        {
                return size.isPositive();
        }
+       bool contains(Point pt) const;
+       Point getCenterPoint() const;
 
        static Rectangle intersect(const Rectangle first, const Rectangle second);
        static Rectangle sum(const Rectangle first, const Rectangle second);
index 7277761..160955b 100644 (file)
@@ -44,6 +44,7 @@ static std::initializer_list<AtspiRole> ignoredAtspiRoles = {
        ATSPI_ROLE_SCROLL_PANE,
        ATSPI_ROLE_SPLIT_PANE,
        ATSPI_ROLE_WINDOW,
+       ATSPI_ROLE_DIALOG,
        ATSPI_ROLE_IMAGE,
        ATSPI_ROLE_LIST,
        ATSPI_ROLE_ICON,
@@ -109,60 +110,11 @@ public:
 
        ~NavigationImpl() = default;
 
-       void resetPosition() override
-       {
-               resetIndexes();
-       }
-
-       void nextRow() override
-       {
-               handleNextPrevRow(true);
-       }
-
-       void prevRow() override
-       {
-               handleNextPrevRow(false);
-       }
-
-       void nextElementInRow() override
-       {
-               handleNextPrevElementInRow(true);
-       }
-
-       void prevElementInRow() override
-       {
-               handleNextPrevElementInRow(false);
-       }
-
-       bool isSingleElementRow() override
-       {
-               auto cols = current_rows.getColumnCountInRow(current_row_index);
-               return cols && *cols == 1;
-       }
-
-       std::shared_ptr<AtspiAccessible> getCurrentElement() override
-       {
-               auto elem = current_rows.getElement(current_row_index, current_column_index);
-               if (!elem) return {};
-               return (*elem).obj;
-       }
-       void getElementAtPoint(Point pt, std::function<void(DBus::ValueOrError<std::shared_ptr<AtspiAccessible>>)> callback) override
-       {
-               if (rootVisibleObject) {
-                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                       if (!atspi)
-                               callback(DBus::Error("no atspi"));
-                       else
-                               atspi->getAtPoint(pt, Atspi::CoordType::Screen, rootVisibleObject, std::move(callback));
-               } else {
-                       callback(DBus::Error("no root object"));
-               }
-       }
-
 private:
-       std::shared_ptr<AtspiAccessible> rootObject, rootVisibleObject;
-       std::vector<std::shared_ptr<AtspiAccessible>> modalWindows;
+       std::shared_ptr<NavigationElement> contentRootElement;
        std::unique_ptr<Atspi::WatchHandler> watchHandle;
+       ecore::Timer rebuildContextTimer;
+       std::shared_ptr<AtspiAccessible> rootObject, keyboardRootObject;
 
        struct GObjectUnref {
                template <typename T> void operator()(T *t)
@@ -209,16 +161,21 @@ private:
 
        class NavigationRows
        {
-               std::vector<ObjectInfo> object_infos;
-               std::vector<RowInfo> row_infos;
+               std::vector<ObjectInfo> objectInfos;
+               std::vector<RowInfo> rowInfos;
                std::unordered_set<AtspiAccessiblePtr> knownObjects;
-               Rectangle root_pos = { { 0, 0 }, {1, 1 } };
+               Rectangle rootPos = { { 0, 0 }, {1, 1 } };
+               std::shared_ptr<AtspiAccessible> rootObject, rootVisibleObject;
+               UIElement::ApplicationCategory application = UIElement::ApplicationCategory::OTHER;
        public:
                NavigationRows() = default;
-               NavigationRows(const std::vector<AcceptedObjectInfo> &objects, Rectangle root_pos) : root_pos(root_pos)
+               NavigationRows(const std::vector<AcceptedObjectInfo> &objects, Rectangle rootPos,
+                                          std::shared_ptr<AtspiAccessible> rootObject, std::shared_ptr<AtspiAccessible> rootVisibleObject,
+                                          UIElement::ApplicationCategory application) : rootPos(rootPos), rootObject(std::move(rootObject)),
+                       rootVisibleObject(std::move(rootVisibleObject)), application(application)
                {
-                       object_infos.reserve(objects.size());
-                       row_infos.clear();
+                       objectInfos.reserve(objects.size());
+                       rowInfos.clear();
 
                        RowInfo rowInfo;
                        unsigned char new_line = 1;
@@ -227,15 +184,15 @@ private:
                        for (unsigned int i = 0; i < objects.size(); ++i) {
                                auto &a = objects[i];
                                knownObjects.insert(a.object);
-                               object_infos.push_back({});
-                               auto &objInfo = object_infos.back();
+                               objectInfos.push_back({});
+                               auto &objInfo = objectInfos.back();
                                objInfo.obj = std::move(a.object);
                                objInfo.pos = a.pos;
 
                                if (!new_line && (objInfo.pos.position.x < prev_obj_x || rowInfo.pos.position.y + rowInfo.pos.size.height <= objInfo.pos.position.y)) {
                                        // new row
                                        rowInfo.last = i - 1;
-                                       row_infos.push_back(rowInfo);
+                                       rowInfos.push_back(rowInfo);
                                        new_line = 1;
                                }
                                prev_obj_x = objInfo.pos.position.x;
@@ -249,42 +206,54 @@ private:
                        }
                        if (!new_line) {
                                rowInfo.last = (unsigned int)objects.size() - 1;
-                               row_infos.push_back(rowInfo);
+                               rowInfos.push_back(rowInfo);
                        }
                        debugDump();
                }
+               UIElement::ApplicationCategory getApplicationCategory() const
+               {
+                       return application;
+               }
+               std::shared_ptr<AtspiAccessible> getRootObject() const
+               {
+                       return rootObject;
+               }
+               std::shared_ptr<AtspiAccessible> getRootVisibleObject() const
+               {
+                       return rootVisibleObject;
+               }
                bool hasObject(const AtspiAccessiblePtr &p) const
                {
                        return knownObjects.find(p) != knownObjects.end();
                }
                unsigned int getRowCount() const
                {
-                       return (unsigned int)row_infos.size();
+                       return (unsigned int)rowInfos.size();
                }
                Optional<unsigned int> getColumnCountInRow(unsigned int row) const
                {
                        if (row == 0 || row > getRowCount()) {
                                return {};
                        }
-                       auto &ri = row_infos[row - 1];
+                       auto &ri = rowInfos[row - 1];
                        return ri.last - ri.first + 1;
                }
                Optional<ObjectInfo> getElement(unsigned int row, unsigned int col) const
                {
                        if (row == 0 || col == 0 || row > getRowCount()) return {};
-                       auto &ri = row_infos[row - 1];
+                       auto &ri = rowInfos[row - 1];
                        if (col > ri.last - ri.first + 1) return {};
-                       auto &oi = object_infos[ri.first + col - 1];
+                       auto &oi = objectInfos[ri.first + col - 1];
                        return oi;
                }
                Optional<Rectangle> getRowSize(unsigned int row) const
                {
                        if (row == 0) {
-                               DEBUG("root pos is %s", root_pos.toString().c_str());
-                               return root_pos;
+                               DEBUG("root pos is %s", rootPos.toString().c_str());
+                               return rootPos;
                        }
                        if (row <= getRowCount()) {
-                               auto &ri = row_infos[row - 1];
+                               auto &ri = rowInfos[row - 1];
                                DEBUG("row %d pos is %s", row, ri.pos.toString().c_str());
                                return ri.pos;
                        }
@@ -292,10 +261,10 @@ private:
                }
                bool findElement(unsigned int &row, unsigned int &col, const std::shared_ptr<AtspiAccessible> &obj) const
                {
-                       for (unsigned int r = 0; r < row_infos.size(); ++r) {
-                               auto &ri = row_infos[r];
+                       for (unsigned int r = 0; r < rowInfos.size(); ++r) {
+                               auto &ri = rowInfos[r];
                                for (unsigned int c = ri.first; c <= ri.last; ++c) {
-                                       auto &e = object_infos[c];
+                                       auto &e = objectInfos[c];
                                        if (e.obj == obj) {
                                                row = r + 1;
                                                col = c - ri.first + 1;
@@ -328,12 +297,12 @@ private:
                }
                void debugDump(const char *prefix = "")
                {
-                       DEBUG("%sobjects %d", prefix, (unsigned int)object_infos.size());
-                       for (unsigned int r = 0; r < row_infos.size(); ++r) {
-                               auto &ri = row_infos[r];
-                               DEBUG("%srow %3d of %3d (size %s)", prefix, r + 1, row_infos.size(), ri.pos.toString().c_str());
+                       DEBUG("%sobjects %d", prefix, (unsigned int)objectInfos.size());
+                       for (unsigned int r = 0; r < rowInfos.size(); ++r) {
+                               auto &ri = rowInfos[r];
+                               DEBUG("%srow %3d of %3d (size %s)", prefix, r + 1, rowInfos.size(), ri.pos.toString().c_str());
                                for (unsigned int c = ri.first; c <= ri.last; ++c) {
-                                       auto &e = object_infos[c];
+                                       auto &e = objectInfos[c];
                                        DEBUG("%s    element %3d %3d = %s %s", prefix, c, c - ri.first + 1, e.pos.toString().c_str(),
                                                  Atspi::getUniqueId(e.obj).c_str());
                                }
@@ -376,16 +345,14 @@ private:
                }
        }
 
-       NavigationRows reconstructNavigationRows(std::vector<AcceptedObjectInfo> objects, Rectangle root_pos)
+       NavigationRows reconstructNavigationRows(std::vector<AcceptedObjectInfo> objects, Rectangle rootPos,
+                       const AtspiAccessiblePtr &root, const AtspiAccessiblePtr &visibleRoot, UIElement::ApplicationCategory applcation)
        {
                filterRows(objects);
                sortWidgets(objects);
-               return NavigationRows { objects, root_pos };
+               return NavigationRows { objects, rootPos, root, visibleRoot, applcation };
        }
 
-       NavigationRows current_rows;
-       unsigned int current_row_index = 0, current_column_index = 0, current_rows_count = 0, current_row_column_count = 0;
-
        static bool checkIfStillRunning()
        {
                auto a = Singleton<UniversalSwitch>::instance().getAtspi();
@@ -438,7 +405,7 @@ private:
                CallbackType callback;
        };
 
-       template <typename ... ARGS> bool continueProcessingImpl(const char *filename, int fileline, const DBus::ValueOrError<ARGS...> &args)
+       template <typename ... ARGS> static bool continueProcessingImpl(const char *filename, int fileline, const DBus::ValueOrError<ARGS...> &args)
        {
                if (!args) {
                        ERROR("in %s:%d: %s", filename, fileline, args.getError().message.c_str());
@@ -453,9 +420,11 @@ private:
                const AtspiAccessiblePtr &visibleRoot,
                AtspiCollectionPtr collection,
                std::vector<AtspiAccessiblePtr> &elements,
+               UIElement::ApplicationCategory application,
                std::function<void(Optional<NavigationRows>)> callback)
        {
                DEBUG("my root is %s", Atspi::getUniqueId(root).c_str());
+               DEBUG("my visible root is %s", Atspi::getUniqueId(visibleRoot).c_str());
                DEBUG("got %d elements", elements.size());
                for (size_t i = 0; i < elements.size(); ++i)
                        DEBUG("got element %2d     %s", (unsigned int)i, Atspi::getUniqueId(elements[i]).c_str());
@@ -468,6 +437,7 @@ private:
                        std::vector<AtspiAccessiblePtr> expandableElems, expandableAndExpandedElems;
                        std::unordered_map<AtspiAccessiblePtr, size_t> ptrToIndex;
                        Optional<Rectangle> rootPos, rootPos2;
+                       UIElement::ApplicationCategory application = UIElement::ApplicationCategory::OTHER;
 
                        bool hasError = false;
                };
@@ -495,7 +465,7 @@ private:
                                }
                        }
                };
-               auto clb = [callback, updateIsCollapsed](ExecHelper & result) {
+               auto clb = [callback, updateIsCollapsed, root, visibleRoot](ExecHelper & result) {
                        auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                        if (nav) {
                                DEBUG("finalizing navigation");
@@ -512,7 +482,7 @@ private:
                                }
                                auto &r = result.all.rootPos ? result.all.rootPos : result.all.rootPos2;
                                if (!r) callback({});
-                               else callback(nav->reconstructNavigationRows(objects, *r));
+                               else callback(nav->reconstructNavigationRows(objects, *r, root, visibleRoot, result.all.application));
                        } else {
                                DEBUG("no navigation");
                                callback({});
@@ -521,6 +491,7 @@ private:
                auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
 
                auto sizes = std::make_shared<ExecHelper>(clb, elements.size());
+               sizes->all.application = application;
                auto queryChildWithGivenRoleAndChildrenCount = [ = ](const AtspiAccessiblePtr & parent, size_t index, size_t expectedCount,
                AtspiRole expectedRole, std::function<void(AtspiAccessiblePtr elem)> callback) {
                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
@@ -674,11 +645,13 @@ private:
                }
        }
 
-       void createNavigationRowsNew(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, std::function<void(Optional<NavigationRows>)> callback)
+       void createNavigationRowsNew(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot,
+                                                                UIElement::ApplicationCategory application, std::function<void(Optional<NavigationRows>)> callback)
        {
                if (!checkIfStillRunningOtherwiseCallCallback(callback)) return;
                auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               atspi->getCollectionInterface(root, [ = ](DBus::ValueOrError<AtspiCollectionPtr> col) {
+               DEBUG("visible root is %s", Atspi::getUniqueId(visibleRoot).c_str());
+               atspi->getCollectionInterface(visibleRoot, [ = ](DBus::ValueOrError<AtspiCollectionPtr> col) {
                        if (checkIfStillRunningOtherwiseCallCallback(callback, col)) {
                                auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                                atspi->getMatchedElements(std::get<0>(col), ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
@@ -689,123 +662,12 @@ private:
                                false, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
                                        if (!checkIfStillRunningOtherwiseCallCallback(callback, elems)) return;
                                        auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
-                                       nav->createNavigationRowsFromElements(root, visibleRoot, std::get<0>(col), std::get<0>(elems), callback);
+                                       nav->createNavigationRowsFromElements(root, visibleRoot, std::get<0>(col), std::get<0>(elems), application, callback);
                                });
                        }
                });
        }
 
-       void createNavigationRows(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, std::function<void(Optional<NavigationRows>)> callback)
-       {
-               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               if (!atspi) {
-                       callback({});
-                       return;
-               }
-               createNavigationRowsNew(root, visibleRoot,
-               [ = ](Optional<NavigationRows> nr) {
-                       if (!nr) {
-                               ERROR("failed");
-                       } else {
-                               callback(std::move(nr));
-                       }
-               });
-       }
-
-       void resetIndexes(void)
-       {
-               DEBUG("resetting current position");
-               current_row_index = current_column_index = 0;
-       }
-
-       void screenNavigationContextChanged(const AtspiAccessiblePtr &root, const AtspiAccessiblePtr &visibleRoot)
-       {
-               DEBUG("changing context");
-
-               current_rows_count = current_row_column_count = 0;
-               resetIndexes();
-
-               createNavigationRows(root, visibleRoot,
-               [ = ](Optional<NavigationRows> rows) {
-                       if (rows) {
-                               current_rows = std::move(*rows);
-                               current_rows_count = current_rows.getRowCount();
-                       } else {
-                               ERROR("failed to get navigation rows");
-                       }
-
-                       auto ui = std::make_shared<UIElement>(root);
-                       ASSERT(current_rows.getRowSize(0));
-                       emitCallback<NavigationCallbackType::ContextChanged>(ui, *current_rows.getRowSize(0));
-
-                       emitCallback<NavigationCallbackType::BoxMoved>(Rectangle{}, BoxPositionMode::NONE);
-                       DEBUG("context has %d rows", current_rows_count);
-               });
-       }
-
-       void handleNextPrevRow(bool nextRow)
-       {
-               unsigned int row_count = current_rows_count + 1;
-               unsigned int row = (current_row_index + (nextRow ? 1 : row_count - 1)) % row_count;
-               DEBUG("new row is %d", row);
-               current_column_index = 0;
-               if (row == 0) {
-                       emitCallback<NavigationCallbackType::DashedRow>();
-               } else {
-                       if (!nextRow && row == 1)
-                               emitCallback<NavigationCallbackType::FirstRow>();
-                       if (!nextRow && row == row_count - 1)
-                               emitCallback<NavigationCallbackType::LastRow>();
-                       if (nextRow && row == row_count - 1)
-                               emitCallback<NavigationCallbackType::LastRow>();
-                       if (nextRow && row == 1)
-                               emitCallback<NavigationCallbackType::FirstRow>();
-               }
-               current_row_index = row;
-               auto count = current_rows.getColumnCountInRow(current_row_index);
-               current_row_column_count = count ? *count : 0;
-               auto pos = current_rows.getRowSize(current_row_index);
-               if (!pos) {
-                       DEBUG("invalid row %d", current_row_index);
-               } else {
-                       DEBUG("navigation_rows_get_row_size: %s", (*pos).toString().c_str());
-                       emitCallback<NavigationCallbackType::BoxMoved>(*pos, current_row_index > 0 ? BoxPositionMode::NORMAL : BoxPositionMode::DASHED);
-               }
-       }
-
-       void handleNextPrevElementInRow(bool nextElement)
-       {
-               unsigned int col_count = current_row_column_count + 1;
-               unsigned int col = (current_column_index + (nextElement ? 1 : col_count - 1)) % col_count;
-               DEBUG("new column is %d", col);
-               if (col == 0) {
-                       emitCallback<NavigationCallbackType::DashedElementInRow>();
-               } else {
-                       if (!nextElement && col == 1)
-                               emitCallback<NavigationCallbackType::FirstElementInRow>();
-                       if (!nextElement && col == col_count - 1)
-                               emitCallback<NavigationCallbackType::LastElementInRow>();
-                       if (nextElement && col == col_count - 1)
-                               emitCallback<NavigationCallbackType::LastElementInRow>();
-                       if (nextElement && col == 1)
-                               emitCallback<NavigationCallbackType::FirstElementInRow>();
-               }
-               current_column_index = col;
-
-               Rectangle pos = {};
-               if (current_column_index) {
-                       auto e = current_rows.getElement(current_row_index, current_column_index);
-                       if (e)
-                               pos = (*e).pos;
-               } else {
-                       auto e = current_rows.getRowSize(current_row_index);
-                       if (e)
-                               pos = *e;
-               }
-
-               emitCallback<NavigationCallbackType::BoxMoved>(pos, current_column_index > 0 ? BoxPositionMode::NORMAL : BoxPositionMode::DASHED);
-       }
-
        ecore::Timer restartTimer;
 
        void rebuildContextFromZero(bool force)
@@ -846,10 +708,11 @@ private:
                                }
                        });
                };
-               auto processProcess = [ = ](AtspiAccessiblePtr app) {
+               auto buildNavigationRows = [ = ](AtspiAccessiblePtr app, UIElement::ApplicationCategory application, std::function<void(Optional<NavigationRows>)> callback) {
+                       ASSERT(app);
                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                        ASSERT(atspi);
-                       atspi->getCollectionInterface(app, [ = ](DBus::ValueOrError<AtspiCollectionPtr> col) {
+                       atspi->getCollectionInterface(app, [app, application, callback = std::move(callback)](DBus::ValueOrError<AtspiCollectionPtr> col) {
                                if (continueProcessing(col)) {
                                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                                        atspi->getMatchedElements(
@@ -857,25 +720,30 @@ private:
                                                ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
                                                Atspi::Matcher().states({ATSPI_STATE_MODAL, ATSPI_STATE_SHOWING, ATSPI_STATE_VISIBLE}, ATSPI_Collection_MATCH_ALL),
                                                false,
-                                       [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                                       [app, application, callback = std::move(callback), col = std::get<0>(col)](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
                                                if (continueProcessing(elems)) {
                                                        auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                                                        ASSERT(nav);
+                                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                       ASSERT(atspi);
                                                        if (std::get<0>(elems).empty()) {
                                                                DEBUG("no modal window found");
 
                                                                atspi->getMatchedElements(
-                                                                       std::get<0>(col),
+                                                                       col,
                                                                        ATSPI_Collection_SORT_ORDER_CANONICAL, 5,
                                                                        Atspi::Matcher().states({ATSPI_STATE_SHOWING, ATSPI_STATE_VISIBLE}, ATSPI_Collection_MATCH_ALL).
-                                                                       roles({ ATSPI_ROLE_DIALOG, ATSPI_ROLE_FRAME, ATSPI_ROLE_WINDOW, ATSPI_ROLE_PAGE_TAB, /*, ATSPI_ROLE_POPUP_MENU ? */ }, ATSPI_Collection_MATCH_ANY),
+                                                                       roles({ ATSPI_ROLE_DIALOG, ATSPI_ROLE_FRAME, ATSPI_ROLE_WINDOW, ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_INPUT_METHOD_WINDOW }, ATSPI_Collection_MATCH_ANY),
                                                                        false,
                                                                [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
                                                                        if (continueProcessing(elems)) {
                                                                                auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                                                                                ASSERT(nav);
-                                                                               ASSERT(!std::get<0>(elems).empty());
-                                                                               nav->buildContextForRoot(app, std::get<0>(elems)[0], force);
+                                                                               if (std::get<0>(elems).empty()) {
+                                                                                       callback({});
+                                                                               } else {
+                                                                                       nav->createNavigationRowsNew(app, std::get<0>(elems)[0], application, std::move(callback));
+                                                                               }
                                                                        }
                                                                });
                                                        } else {
@@ -894,6 +762,7 @@ private:
                                                                                        DEBUG("got modal window %u = %s %s", i, Atspi::getUniqueId(elems[i]).c_str(), names[i].c_str());
                                                                        }
                                                                };
+                                                               auto visibleRoot = std::get<0>(elems)[0];
                                                                auto t = std::make_shared<ElementNames>();
                                                                t->elems = std::move(std::get<0>(elems));
                                                                t->names.resize(t->elems.size());
@@ -903,8 +772,7 @@ private:
                                                                                if (name) t->names[i] = std::move(std::get<0>(name));
                                                                        });
                                                                }
-
-                                                               nav->buildContextForRoot(modal, modal, force);
+                                                               nav->createNavigationRowsNew(app, std::move(visibleRoot), application, std::move(callback));
                                                        }
                                                }
                                        });
@@ -929,18 +797,47 @@ private:
                        for (auto pid : wins.all.pids) {
                                DEBUG("got pid %d", pid);
                        }
-                       AtspiAccessiblePtr root;
+                       AtspiAccessiblePtr root, keyboardRoot;
+                       UIElement::ApplicationCategory application = UIElement::ApplicationCategory::OTHER;
+                       static const std::string keyboardRootName1 = "ise-default";
+                       // TODO: remove keyboardRootName2, once app name patch will be merged and on image
+                       static const std::string keyboardRootName2 = "";
+                       static const std::string homescreenRootName = "homescreen-efl";
                        for (auto pid : wins.all.pids) {
                                DEBUG("checking pid %d", pid);
                                auto ptr = wins.all.pidToAtspi[pid];
                                auto name = wins.all.names[ptr];
-                               if ((pid == wins.all.ourPid && wins.all.weHaveChildren) || name == "quickpanel" || name == "volume" || pid == wins.all.pids.back()) {
+                               if (pid == wins.all.ourPid && !wins.all.weHaveChildren) continue;
+                               if (name == keyboardRootName1 || name == keyboardRootName2) {
+                                       keyboardRoot = std::move(ptr);
+                               } else if (!root) {
                                        root = std::move(ptr);
-                                       break;
+                                       if (name == homescreenRootName)
+                                               application = UIElement::ApplicationCategory::HOMESCREEN;
                                }
                        }
                        if (root) {
-                               processProcess(root);
+                               struct Contextes {
+                                       NavigationImpl *self;
+                                       Optional<NavigationRows> real, keyboard;
+                                       ~Contextes()
+                                       {
+                                               ASSERT(real || keyboard);
+                                               if (!real)
+                                                       real = std::move(keyboard);
+                                               self->buildNavigationElementsTreeAndEmitContextChanged(std::move(*real), std::move(keyboard));
+                                       }
+                               };
+                               auto contextes = std::make_shared<Contextes>();
+                               contextes->self = this;
+                               buildNavigationRows(root, application, [ = ](Optional<NavigationRows> rows) {
+                                       contextes->real = std::move(rows);
+                               });
+                               if (keyboardRoot) {
+                                       buildNavigationRows(keyboardRoot, UIElement::ApplicationCategory::KEYBOARD, [ = ](Optional<NavigationRows> rows) {
+                                               contextes->keyboard = std::move(rows);
+                                       });
+                               }
                        } else {
                                auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                                if (nav) {
@@ -1016,30 +913,167 @@ private:
                });
        }
 
-       void buildContextForRoot(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, bool force)
+       static void dumpIndent(std::ostream &out, unsigned int depth)
        {
-               if (root == rootObject) {
-                       if (!force) {
-                               DEBUG("no context switch, as object %s is already the root", Atspi::getUniqueId(root).c_str());
-                               return;
+               constexpr unsigned int singleIndentDepth = 4;
+               while (depth >= singleIndentDepth) {
+                       out << "   |";
+                       depth -= singleIndentDepth;
+               }
+               while (depth > 0) {
+                       out << ' ';
+                       --depth;
+               }
+       }
+       struct NavigationElementContainer : public NavigationElement {
+               using NavigationElement::NavigationElement;
+
+               std::vector<std::shared_ptr<NavigationElement>> children;
+               Rectangle pos;
+               Direction direction = Direction::VERTICAL;
+
+               Rectangle getBounds() const override
+               {
+                       return pos;
+               }
+               Direction getScanningDirection() const override
+               {
+                       return direction;
+               }
+               const std::vector<std::shared_ptr<NavigationElement>> &getChildren() const
+               {
+                       return children;
+               }
+               std::shared_ptr<AtspiAccessible> getElement() const
+               {
+                       return nullptr;
+               }
+               std::shared_ptr<NavigationElement> getAtPoint(Point pt)
+               {
+                       if (!pos.contains(pt)) return {};
+                       for (auto &child : children) {
+                               auto z = child->getAtPoint(pt);
+                               if (z) return std::move(z);
                        }
-                       DEBUG("object %s is already the root, but context switch is forced", Atspi::getUniqueId(root).c_str());
+                       return shared_from_this();
                }
-               rootObject = root;
-               rootVisibleObject = visibleRoot;
-               screenNavigationContextChanged(root, visibleRoot);
-               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               if (atspi) {
-                       atspi->getName(root,
-                       [ = ](DBus::ValueOrError<std::string> name) {
-                               DEBUG("new CONTEXT is %s %s", Atspi::getUniqueId(root).c_str(), name ? std::get<0>(name).c_str() : "");
-                       });
-               } else {
-                       DEBUG("no atspi");
+               void dump(std::ostream &out, unsigned int depth) const override
+               {
+                       dumpIndent(out, depth);
+                       out << "*  " << pos.toString() << "\n";
+                       for (size_t i = 0; i < children.size(); ++i)
+                               children[i]->dump(out, depth + 1);
+               }
+       };
+       struct NavigationElementElement : public NavigationElement {
+               using NavigationElement::NavigationElement;
+
+               std::shared_ptr<AtspiAccessible> obj;
+               Rectangle pos;
+
+               Direction getScanningDirection() const override
+               {
+                       return Direction::VERTICAL;
+               }
+               Rectangle getBounds() const override
+               {
+                       return pos;
+               }
+               const std::vector<std::shared_ptr<NavigationElement>> &getChildren() const
+               {
+                       static std::vector<std::shared_ptr<NavigationElement>> empty;
+                       return empty;
+               }
+               std::shared_ptr<AtspiAccessible> getElement() const
+               {
+                       return obj;
+               }
+               std::shared_ptr<NavigationElement> getAtPoint(Point pt)
+               {
+                       if (pos.contains(pt)) return shared_from_this();
+                       return {};
+               }
+               void dump(std::ostream &out, unsigned int depth) const override
+               {
+                       dumpIndent(out, depth);
+                       out << "*  " << pos.toString() << "  " << Atspi::getUniqueId(obj) << " app " << (int)getApplicationCategory() << "\n";
+               }
+       };
+       void buildNavigationElementsTree(NavigationRows realContext, Optional<NavigationRows> keyboardContext)
+       {
+               using ParentType = const std::shared_ptr<NavigationElement> &;
+               std::function<std::shared_ptr<NavigationElement>(ParentType parent, size_t indexInParent, NavigationRows &ctx)> buildRows;
+               std::function<std::shared_ptr<NavigationElement>(ParentType parent, size_t indexInParent, NavigationRows &ctx, size_t)> buildSingleRow;
+               std::function<std::shared_ptr<NavigationElement>(ParentType parent, size_t indexInParent, NavigationRows &ctx, size_t, size_t)> buildSingleElement;
+
+               buildRows = [&](ParentType parent, size_t indexInParent, NavigationRows & ctx) {
+                       auto ptr = std::make_shared<NavigationElementContainer>(parent, indexInParent, ctx.getRootVisibleObject(), ctx.getApplicationCategory());
+                       ptr->direction = NavigationElement::Direction::VERTICAL;
+                       ptr->pos = *ctx.getRowSize(0);
+                       for (size_t i = 1; i <= ctx.getRowCount(); ++i) {
+                               auto p = buildSingleRow(ptr, ptr->children.size(), ctx, i);
+                               ptr->children.push_back(std::move(p));
+                       }
+                       if (!parent && keyboardContext) {
+                               auto p = buildRows(ptr, ptr->children.size(), *keyboardContext);
+                               Optional<Rectangle> r;
+                               for (auto q : p->getChildren()) {
+                                       if (!r) {
+                                               r = q->getBounds();
+                                       } else {
+                                               r = Rectangle::sum(*r, q->getBounds());
+                                       }
+                               }
+                               if (r) {
+                                       std::static_pointer_cast<NavigationElementContainer>(p)->pos = *r;
+                               }
+                               ptr->children.push_back(std::move(p));
+                       }
+                       return ptr;
+               };
+               buildSingleRow = [&](ParentType parent, size_t indexInParent, NavigationRows & ctx, size_t rowIndex) {
+                       auto ptr = std::make_shared<NavigationElementContainer>(parent, indexInParent, ctx.getRootVisibleObject(), ctx.getApplicationCategory());
+                       ptr->direction = NavigationElement::Direction::HORIZONTAL;
+                       ptr->pos = *ctx.getRowSize(rowIndex);
+                       for (size_t i = 1; i <= *ctx.getColumnCountInRow(rowIndex); ++i) {
+                               auto p = buildSingleElement(ptr, ptr->children.size(), ctx, rowIndex, i);
+                               ptr->children.push_back(std::move(p));
+                       }
+                       return ptr;
+               };
+               buildSingleElement = [&](ParentType parent, size_t indexInParent, NavigationRows & ctx, size_t rowIndex, size_t colIndex) {
+                       auto elem = ctx.getElement(rowIndex, colIndex);
+                       auto ptr = std::make_shared<NavigationElementElement>(parent, indexInParent, ctx.getRootVisibleObject(), ctx.getApplicationCategory());
+                       ptr->pos = (*elem).pos;
+                       ptr->obj = (*elem).obj;
+                       return ptr;
+               };
+               contentRootElement = buildRows({}, 0, realContext);
+               DEBUG("new context content is");
+               std::ostringstream os;
+               contentRootElement->dump(os);
+               size_t i = 0;
+               auto s = os.str();
+               while (i < s.size()) {
+                       auto j = s.find_first_of("\n", i);
+                       if (j == std::string::npos) j = s.size();
+                       DEBUG("%s", s.substr(i, j - i).c_str());
+                       i = j + 1;
                }
        }
 
-       ecore::Timer rebuildContextTimer;
+       void buildNavigationElementsTreeAndEmitContextChanged(NavigationRows realContext, Optional<NavigationRows> keyboardContext)
+       {
+               buildNavigationElementsTree(realContext, keyboardContext);
+
+               auto root = realContext.getRootVisibleObject();
+               auto pos = (*realContext.getRowSize(0)).getCenterPoint();
+               auto ui = std::make_shared<UIElement>(root, pos, realContext.getApplicationCategory());
+               rootObject = realContext.getRootObject();
+               keyboardRootObject = keyboardContext ? (*keyboardContext).getRootObject() : nullptr;
+               ASSERT(contentRootElement);
+               emitCallback<NavigationCallbackType::ContextChanged>(ui, contentRootElement);
+       }
 
        void initializeRebuildingContext(bool force, float delay = 0.2f)
        {
@@ -1047,8 +1081,9 @@ private:
                rebuildContextTimer.reset(delay,
                [ = ]() {
                        auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
-                       if (nav)
+                       if (nav) {
                                nav->rebuildContextFromZero(force);
+                       }
                        return ecore::TimerRepetitionPolicy::cancel;
                });
        }
@@ -1066,7 +1101,8 @@ private:
                case Atspi::WatchEvent::defunct:
                case Atspi::WatchEvent::hidden:
                case Atspi::WatchEvent::moved:
-                       if (current_rows.hasObject(src)) rebuild = true;
+                       // if (current_rows.hasObject(src)) // TODO: fix it
+                       rebuild = true;
                        break;
                default:
                        DEBUG("unknown event %d", (int)event);
index 0080083..499aa07 100644 (file)
 class Atspi;
 
 enum class NavigationCallbackType {
-       FirstRow, LastRow, DashedRow,
-       FirstElementInRow, LastElementInRow, DashedElementInRow,
        ContextChanged,
-       BoxMoved
 };
 
 template <NavigationCallbackType>
@@ -40,6 +37,51 @@ struct NavigationCallbackTraits {
        using CallbackFunctionType = void();
 };
 
+class NavigationElement : public std::enable_shared_from_this<NavigationElement>
+{
+public:
+       NavigationElement(std::weak_ptr<NavigationElement> parent, size_t indexInParent,
+                                         std::shared_ptr<AtspiAccessible> visibleRoot, UIElement::ApplicationCategory application)
+               : parent(std::move(parent)), indexInParent(indexInParent), visibleRoot(std::move(visibleRoot)), application(application)
+       {
+       }
+
+       enum class Direction {
+               VERTICAL, HORIZONTAL
+       };
+
+       virtual Rectangle getBounds() const = 0;
+       virtual const std::vector<std::shared_ptr<NavigationElement>> &getChildren() const = 0;
+       virtual Direction getScanningDirection() const = 0;
+       virtual std::shared_ptr<AtspiAccessible> getElement() const = 0;
+       virtual std::shared_ptr<NavigationElement> getAtPoint(Point pt) = 0;
+       virtual void dump(std::ostream &out, unsigned int depth = 0) const = 0;
+
+       std::shared_ptr<NavigationElement> getParent() const
+       {
+               return parent.lock();
+       }
+
+       size_t getIndexInParent() const
+       {
+               return indexInParent;
+       }
+
+       UIElement::ApplicationCategory getApplicationCategory() const
+       {
+               return application;
+       }
+
+       std::shared_ptr<AtspiAccessible> getVisibleRoot() const
+       {
+               return visibleRoot;
+       }
+private:
+       std::weak_ptr<NavigationElement> parent;
+       size_t indexInParent;
+       std::shared_ptr<AtspiAccessible> visibleRoot;
+       UIElement::ApplicationCategory application = UIElement::ApplicationCategory::OTHER;
+};
 
 class NavigationInterface
 {
@@ -64,15 +106,6 @@ public:
 
        virtual ~NavigationInterface();
 
-       virtual void resetPosition() = 0;
-       virtual void nextRow() = 0;
-       virtual void prevRow() = 0;
-       virtual void nextElementInRow() = 0;
-       virtual void prevElementInRow() = 0;
-       virtual bool isSingleElementRow() = 0;
-       virtual std::shared_ptr<AtspiAccessible> getCurrentElement() = 0;
-       virtual void getElementAtPoint(Point pt, std::function<void(DBus::ValueOrError<std::shared_ptr<AtspiAccessible>>)> callback) = 0;
-
        template <NavigationCallbackType type>
        CallbackHandle registerCb(std::function<typename NavigationCallbackTraits<type>::CallbackFunctionType> callback)
        {
@@ -117,8 +150,7 @@ protected:
                using CallbackFunctionType = FunctionType; \
        };
 
-SPECIALIZE_CALLBACK_TRAITS(ContextChanged, void(std::shared_ptr<UIElement>, Rectangle));
-SPECIALIZE_CALLBACK_TRAITS(BoxMoved, void(Rectangle, NavigationInterface::BoxPositionMode));
+SPECIALIZE_CALLBACK_TRAITS(ContextChanged, void(std::shared_ptr<UIElement>, std::shared_ptr<NavigationElement>));
 #undef SPECIALIZE_CALLBACK_TRAITS
 
 std::shared_ptr<NavigationInterface> createNavigationImpl();
index 7887874..f7b91d5 100644 (file)
@@ -67,15 +67,18 @@ digraph StateMachine {
 class PointScannerImpl : public ScreenScanner
 {
 public:
-       PointScannerImpl(const ScanningProperties &properties);
+       PointScannerImpl(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement);
        ~PointScannerImpl() override;
 
-       /** Forwards call to acceptEventTransition.  */
+       /**
+        * @brief Change scanner state due to user interaction.
+        */
        void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
 
        void next() override;
        void prev() override;
 
+       void clearCursor() override;
 private:
        /** Defines states for state machine in PointScannerImpl.
         *  @param START  Initial state for scanner. Set after scanner creation.
@@ -87,11 +90,6 @@ private:
         */
        enum class State {END, VERTICAL_REST, HORIZONTAL_DASHED, VERTICAL_FIRST, HORIZONTAL, START};
 
-       /** Change scanner state due to user interaction.
-        *  @see acceptAutoscanningPhase
-        */
-       void acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback);
-
        /** Callback changing scanner state after given time.
         *  @see ecore::Timer
         */
@@ -124,23 +122,32 @@ private:
        evas::Shape verticalLine;
        evas::Shape dashedLine;
 
+       std::shared_ptr<NavigationElement> rootNavigationElement;
        Point intersectionPoint;
 };
 
-std::unique_ptr<ScreenScanner> PointScanner::create(const ScanningProperties &properties)
+std::unique_ptr<ScreenScanner> PointScanner::create(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement)
 {
-       return std::unique_ptr<ScreenScanner>(new PointScannerImpl(properties));
+       return std::unique_ptr<ScreenScanner>(new PointScannerImpl(properties, std::move(rootNavigationElement)));
 }
 
-PointScannerImpl::PointScannerImpl(const ScanningProperties &properties)
-       : ScreenScanner(properties)
-{}
+PointScannerImpl::PointScannerImpl(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement)
+       : ScreenScanner(properties), rootNavigationElement(std::move(rootNavigationElement))
+{
+       startHorizontalLine();
+       state = State::HORIZONTAL;
+}
 
 PointScannerImpl::~PointScannerImpl()
 {
        disableScanner();
 }
 
+void PointScannerImpl::clearCursor()
+{
+       disableScanner();
+}
+
 void PointScannerImpl::disableScanner()
 {
        DEBUG("disabling scanner");
@@ -161,11 +168,6 @@ void PointScannerImpl::resetGraphics()
        state = State::START;
 }
 
-void PointScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
-{
-       acceptEventTransition(std::move(callback));
-}
-
 void PointScannerImpl::next()
 {
        DEBUG("'next' operation not supported in PointScanner");
@@ -175,7 +177,7 @@ void PointScannerImpl::prev()
        DEBUG("'prev' operation not supported in PointScanner");
 }
 
-void PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+void PointScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
        switch (state) {
        case State::START:
@@ -204,10 +206,21 @@ void PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::sh
                state = State::END;
 
                DEBUG("Scanning complete");
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->getElementAtPoint(intersectionPoint,
-               [ = ](DBus::ValueOrError<std::shared_ptr<AtspiAccessible>> elem) {
-                       callback(std::make_shared<UIElement>(elem ? std::move(std::get<0>(elem)) : nullptr, intersectionPoint));
-               });
+               {
+                       auto elem = rootNavigationElement->getAtPoint(intersectionPoint);
+                       if (elem) {
+                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                               auto application = elem->getApplicationCategory();
+                               atspi->getAtPoint(intersectionPoint, Atspi::CoordType::Screen, elem->getVisibleRoot(),
+                               [ = ](DBus::ValueOrError<std::shared_ptr<AtspiAccessible>> elem) {
+                                       auto uiElem = std::make_shared<UIElement>(elem ? std::move(std::get<0>(elem)) : nullptr, intersectionPoint, application);
+                                       callback(std::move(uiElem));
+                               });
+                       } else {
+                               auto uiElem = std::make_shared<UIElement>(nullptr, intersectionPoint, UIElement::ApplicationCategory::OTHER);
+                               callback(std::move(uiElem));
+                       }
+               }
                return;
 
        case State::END:
index 3627c7f..5e8c939 100644 (file)
@@ -38,9 +38,11 @@ public:
         *
         *  @param properties contains all scanning parameters
         *                    required for scanner configuration and proper scanner operating.
+        *  @param rootNavigationElement root of tree of context objects
         *  @return PointScanner instance.
         */
-       static std::unique_ptr<ScreenScanner> create(const ScanningProperties &properties);
+       static std::unique_ptr<ScreenScanner> create(const ScanningProperties &properties,
+                       std::shared_ptr<NavigationElement> rootNavigationElement);
 };
 
 #endif
index cf5baaf..19bc285 100644 (file)
@@ -25,6 +25,7 @@
 #include "UniversalSwitchLog.hpp"
 #include "Window.hpp"
 #include "utils.hpp"
+#include "Atspi.hpp"
 
 #include <Elementary.h>
 
 
 namespace
 {
-       void playFeedback()
+       void playFeedback(const AtspiAccessiblePtr &atspiElem)
        {
                auto tts = Singleton<UniversalSwitch>::instance().getTextToSpeech();
                if (tts->isEnabled()) {
-                       auto elem = Singleton<UniversalSwitch>::instance().getNavigationInterface()->getCurrentElement();
-                       auto uiElem = std::make_shared<UIElement>(std::move(elem));
-                       Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(std::move(uiElem));
+                       Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(atspiElem);
                }
                Singleton<UniversalSwitch>::instance().getSoundFeedback()->play(SoundFeedback::Sound::NAVIGATION_ITERATED);
        }
@@ -80,7 +79,7 @@ digraph StateMachine {
 class RowScannerImpl : public ScreenScanner
 {
 public:
-       RowScannerImpl(const ScanningProperties &properties);
+       RowScannerImpl(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement);
        ~RowScannerImpl() override;
        void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
        void next() override;
@@ -89,216 +88,183 @@ public:
 private:
 
        /** Defines states for state machine in RowScannerImpl.
-        *  @param START  Initial state for scanner. Set after scanner creation.
-        *  @param ROWS  Row selection stage.
-        *  @param ITEMS  Item in row selection stage.
-        *  @param END  Scanning complete. No action should be called on scanner in this state.
+        *  @param ACTIVE   Scanner is scanning items
+        *  @param END      Scanner has completed scanning and item was selected
+        *  @param INACTIVE Scanner has completed configurable amount of full cycles without selecting item and is sleeping
+        *  @param NO_ELEMENTS No items to scan
         */
-       enum class State {END, ITEMS, ROWS, START};
+       enum class State { ACTIVE, END, INACTIVE, NO_ELEMENTS};
+       struct NavigationState {
+               std::shared_ptr<NavigationElement> parent;
+               Optional<int> childIndex;
+       };
 
-       void resetTraversingUI();
-       void startTraversingUI(bool continueScanning);
-       void startScanning(bool continueScanning);
-       void stopScanning();
-       bool tryResetState();
+       void startAutoTraversingUI(bool firstTime);
+       void clearCursor() override;
+
+       void selectNewElement(NavigationState);
 
        void drawFrame(Rectangle dimensions, NavigationInterface::BoxPositionMode mode);
        void forceRender();
        void eraseFrame();
 
-       void nextCursorPosition(bool invertDirection = false);
-       void moveRow(VerticalScanningDirection direction);
-       void nextRow(bool invertDirection = false);
-       void moveElementInRow(HorizontalScanningDirection direction);
-       void nextElementInRow(bool invertDirection = false);
-
-       ecore::TimerRepetitionPolicy iterateToNextScanningElement();
-       void countInactivityAfterCompletedLoop();
-       void boxMoved(Rectangle dimensions, NavigationInterface::BoxPositionMode);
+       void moveToNextPosition(bool invertDirection = false);
+       int getFirstElementIndex(const std::shared_ptr<NavigationElement> &parent);
+       int getIncrementForCalculatingNextElement(const std::shared_ptr<NavigationElement> &parent, bool reverse);
 
-       std::vector<NavigationInterface::CallbackHandle> navigationHandles;
        ecore::Timer timer;
        evas::Shape frame;
+       State state = State::NO_ELEMENTS;
        Rectangle lastFrameDimensions;
        NavigationInterface::BoxPositionMode lastFrameMode = NavigationInterface::BoxPositionMode::NONE;
        bool skipFrameDrawing = false;
+       std::shared_ptr<NavigationElement> rootNavigationElement;
+       NavigationState currentNavState;
 
-       State state = State::START;
-       bool possibleMoveUpInHierarchy = false;
        int inactiveLoopCounter = 0;
        VerticalScanningDirection lastRowsDirection = VerticalScanningDirection::TO_BOTTOM;
 };
 
-std::unique_ptr<ScreenScanner> RowScanner::create(const ScanningProperties &properties)
+std::unique_ptr<ScreenScanner> RowScanner::create(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement)
 {
-       return std::unique_ptr<ScreenScanner>(new RowScannerImpl(properties));
+       return std::unique_ptr<ScreenScanner>(new RowScannerImpl(properties, std::move(rootNavigationElement)));
 }
 
-RowScannerImpl::RowScannerImpl(const ScanningProperties &properties)
+RowScannerImpl::RowScannerImpl(const ScanningProperties &properties, std::shared_ptr<NavigationElement> rootNavigationElement_)
        : ScreenScanner(properties)
 {
        ASSERT(properties.getAutoScanInterval() > 0, "autoScanInterval has to be bigger than 0");
-
-       auto &navigation = *Singleton<UniversalSwitch>::instance().getNavigationInterface();
-       navigation.resetPosition();
-       using CT = NavigationCallbackType;
-       navigationHandles.push_back(navigation.registerCb<CT::DashedRow>([ this ]() {
-               countInactivityAfterCompletedLoop();
-               if (this->properties.isEscapeFrameEnabled())
-                       return;
-               skipFrameDrawing = true;
-       }));
-       navigationHandles.push_back(navigation.registerCb<CT::DashedElementInRow>([ this ]() {
-               countInactivityAfterCompletedLoop();
-       }));
-       navigationHandles.push_back(navigation.registerCb<CT::BoxMoved>(
-       [ this ](Rectangle pos, NavigationInterface::BoxPositionMode mode) {
-               if (skipFrameDrawing) {
-                       skipFrameDrawing = false;
-                       if (tryResetState())
-                               return;
-                       moveRow(lastRowsDirection);
-                       return;
+       if (rootNavigationElement_ && !rootNavigationElement_->getChildren().empty()) {
+               rootNavigationElement = std::move(rootNavigationElement_);
+               if (!rootNavigationElement->getChildren().empty()) {
+                       state = State::ACTIVE;
+                       selectNewElement({ rootNavigationElement, getFirstElementIndex(rootNavigationElement) });
+                       startAutoTraversingUI(true);
                }
-               boxMoved(pos, mode);
-               playFeedback();
-       }));
+       }
 }
 
 RowScannerImpl::~RowScannerImpl()
 {
-       stopScanning();
+       eraseFrame();
 }
 
-void RowScannerImpl::boxMoved(Rectangle dimensions, NavigationInterface::BoxPositionMode mode)
+void RowScannerImpl::selectNewElement(NavigationState navState)
 {
-       DEBUG("box moved to %d %d %d %d %d", dimensions.position.x, dimensions.position.y,
-                 dimensions.size.width, dimensions.size.height, (unsigned int) mode);
+       currentNavState = std::move(navState);
+       Rectangle pos;
+       if (currentNavState.childIndex) {
+               auto c = currentNavState.parent->getChildren()[static_cast<size_t>(*currentNavState.childIndex)];
+               pos = c->getBounds();
+               playFeedback(c->getElement());
+       } else {
+               pos = currentNavState.parent->getBounds();
+               playFeedback(nullptr);
+       }
 
-       drawFrame(dimensions, mode);
+       drawFrame(pos, currentNavState.childIndex ?
+                         NavigationInterface::BoxPositionMode::NORMAL : NavigationInterface::BoxPositionMode::DASHED);
 }
 
-void RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+void RowScannerImpl::clearCursor()
 {
-       bool continueScanning = false;
-       if (possibleMoveUpInHierarchy) {
-               DEBUG("execute move up in hierarchy");
-               possibleMoveUpInHierarchy = false;
-               continueScanning = true;
-               stopScanning();
-               switch (state) {
-               case State::ITEMS:
-                       state = State::START;
-                       break;
-               case State::END:
-                       state = State::ROWS;
-                       break;
-               case State::ROWS:
-                       DEBUG("Escape UI");
-                       state = State::START;
-                       utils::EventGenerator::generateKeyPress(BACK_BUTTON_CODE);
-                       return;
-               default:
-                       ASSERT(0, "Should not be reached");
-               }
-       }
+       eraseFrame();
+       timer.reset();
+       state = State::END;
+}
 
+void RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+{
        switch (state) {
-       case State::START:
-               state = State::ROWS;
-               startScanning(continueScanning);
+       case State::NO_ELEMENTS:
                return;
-       case State::ROWS:
-               state = State::ITEMS;
-               if (!Singleton<UniversalSwitch>::instance().getNavigationInterface()->isSingleElementRow()) {
-                       startScanning(continueScanning);
-                       return;
-               }
-               DEBUG("Single element row");
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextElementInRow();
+       case State::END:
+               startAutoTraversingUI(true);
                break;
-       case State::ITEMS:
+       case State::INACTIVE:
+               inactiveLoopCounter = 0;
+               selectNewElement({ rootNavigationElement, getFirstElementIndex(rootNavigationElement) });
+               startAutoTraversingUI(true);
+               break;
+       case State::ACTIVE:
+               startAutoTraversingUI(true);
+               if (!currentNavState.childIndex) {
+                       if (currentNavState.parent == rootNavigationElement) {
+                               if (properties.isEscapeFrameEnabled()) {
+                                       utils::EventGenerator::generateKeyPress(BACK_BUTTON_CODE);
+                                       eraseFrame();
+                                       state = State::END;
+                               } else {
+                                       ASSERT(0);
+                               }
+                       } else {
+                               size_t index = currentNavState.parent->getIndexInParent();
+                               auto p = currentNavState.parent->getParent();
+                               ASSERT(p);
+                               if (index == 0) {
+                                       while (p->getChildren().size() == 1 && p->getParent())
+                                               p = p->getParent();
+                               }
+                               selectNewElement({ std::move(p), index });
+                       }
+               } else {
+                       auto c = currentNavState.parent->getChildren()[static_cast<size_t>(*currentNavState.childIndex)];
+                       ASSERT(c);
+                       while (c->getChildren().size() == 1) {
+                               c = c->getChildren()[0];
+                       }
+                       if (c->getChildren().empty()) {
+                               auto obj = c->getElement();
+                               auto application = c->getApplicationCategory();
+                               ASSERT(obj);
+                               auto pos = c->getBounds();
+                               auto mid = pos.getCenterPoint();
+                               timer.reset();
+                               state = State::END;
+                               callback(std::make_shared<UIElement>(std::move(obj), mid, application));
+                       } else {
+                               auto index = getFirstElementIndex(c);
+                               selectNewElement({ std::move(c), index });
+                       }
+               }
                break;
-       case State::END:
-               state = State::ITEMS;
-               DEBUG("Scanner will continue previous scanning");
-               startScanning(true);
-               return;
        }
-       state = State::END;
-       DEBUG("Scanning complete");
-       auto elem = Singleton<UniversalSwitch>::instance().getNavigationInterface()->getCurrentElement();
-       stopScanning();
-       callback(elem ? std::make_shared<UIElement>(std::move(elem)) : Optional<std::shared_ptr<UIElement>>());
 }
 
 void RowScannerImpl::next()
 {
-       resetTraversingUI();
-       nextCursorPosition();
+       startAutoTraversingUI(true);
+       moveToNextPosition();
 }
 
 void RowScannerImpl::prev()
 {
-       resetTraversingUI();
-       nextCursorPosition(true);
+       startAutoTraversingUI(true);
+       moveToNextPosition(true);
 }
 
-void RowScannerImpl::resetTraversingUI()
+void RowScannerImpl::startAutoTraversingUI(bool firstTime)
 {
-       inactiveLoopCounter = 0;
-       startTraversingUI(false);
-}
+       if (state == State::NO_ELEMENTS) return;
 
-void RowScannerImpl::startTraversingUI(bool continueScanning)
-{
-       if (!properties.isAutoScanEnabled())
+       state = State::ACTIVE;
+
+       if (!properties.isAutoScanEnabled()) {
+               timer.reset();
                return;
+       }
 
        auto firstElementTime = properties.getAutoScanInterval();
-       firstElementTime += continueScanning ? 0 : properties.getPauseOnFirstElementTime();
+       firstElementTime += firstTime ? properties.getPauseOnFirstElementTime() : 0;
        timer.reset({firstElementTime, properties.getAutoScanInterval()}, [this]() {
-               return iterateToNextScanningElement();
+               moveToNextPosition();
+               return timer.isSet() ? ecore::TimerRepetitionPolicy::renew : ecore::TimerRepetitionPolicy::cancel;
        });
-}
-
-void RowScannerImpl::startScanning(bool continueScanning)
-{
        inactiveLoopCounter = 0;
-       startTraversingUI(continueScanning);
-       if (continueScanning) {
-               drawFrame(lastFrameDimensions, lastFrameMode);
-               playFeedback();
-       } else {
-               nextCursorPosition();
-       }
-}
-
-void RowScannerImpl::stopScanning()
-{
-       timer.reset();
-       eraseFrame();
 }
 
-bool RowScannerImpl::tryResetState()
-{
-       possibleMoveUpInHierarchy = false;
-       if (inactiveLoopCounter >= properties.getLoopLimitToInaction()) {
-               stopScanning();
-               eraseFrame();
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->resetPosition();
-               state = State::START;
-               inactiveLoopCounter = 0;
-               DEBUG("Scanner state set to START. Timmer disabled");
-               return true;
-       }
-       return false;
-}
-
-
 void RowScannerImpl::drawFrame(Rectangle dimensions, NavigationInterface::BoxPositionMode mode)
 {
-
        if (mode == NavigationInterface::BoxPositionMode::NONE ||
                        (lastFrameDimensions != dimensions && lastFrameMode != mode)) {
                eraseFrame();
@@ -333,90 +299,70 @@ void RowScannerImpl::eraseFrame()
        elm_win_render(Singleton<UniversalSwitch>::instance().getMainWindow()->getHandler());
 }
 
-void RowScannerImpl::moveRow(VerticalScanningDirection direction)
+int RowScannerImpl::getFirstElementIndex(const std::shared_ptr<NavigationElement> &parent)
 {
-       lastRowsDirection = direction;
-       switch (direction) {
-       case VerticalScanningDirection::TO_BOTTOM:
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextRow();
-               return;
-       case VerticalScanningDirection::TO_TOP:
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->prevRow();
-               return;
-       }
-}
+       auto count = static_cast<int>(parent->getChildren().size());
+       if (count <= 1) return 0;
+       int pos = 0;
+
+       auto dir = parent->getScanningDirection();
+       if (dir == NavigationElement::Direction::VERTICAL) {
+               auto direction = properties.getDirectionVertical();
+               pos = direction == VerticalScanningDirection::TO_BOTTOM ? 0 : count - 1;
+       } else {
+               ASSERT(dir == NavigationElement::Direction::HORIZONTAL);
 
-void RowScannerImpl::nextRow(bool invertDirection)
-{
-       auto direction = properties.getDirectionVertical();
-       if (invertDirection)
-               direction = ScanningProperties::invertDirection(direction);
-       moveRow(direction);
+               auto direction = properties.getDirectionHorizontal();
+               pos = direction == HorizontalScanningDirection::TO_RIGHT ? 0 : count - 1;
+       }
+       return pos;
 }
 
-void RowScannerImpl::moveElementInRow(HorizontalScanningDirection direction)
+int RowScannerImpl::getIncrementForCalculatingNextElement(const std::shared_ptr<NavigationElement> &parent, bool invertDirection)
 {
-       switch (direction) {
-       case HorizontalScanningDirection::TO_RIGHT:
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextElementInRow();
-               return;
-       case HorizontalScanningDirection::TO_LEFT:
-               Singleton<UniversalSwitch>::instance().getNavigationInterface()->prevElementInRow();
-               return;
+       auto dir = parent->getScanningDirection();
+       if (dir == NavigationElement::Direction::VERTICAL) {
+               auto direction = properties.getDirectionVertical();
+               if (invertDirection)
+                       direction = ScanningProperties::invertDirection(direction);
+               return direction == VerticalScanningDirection::TO_BOTTOM ? 1 : -1;
        }
-}
 
-void RowScannerImpl::nextElementInRow(bool invertDirection)
-{
+       ASSERT(dir == NavigationElement::Direction::HORIZONTAL);
+
        auto direction = properties.getDirectionHorizontal();
        if (invertDirection)
                direction = ScanningProperties::invertDirection(direction);
-       moveElementInRow(direction);
-}
 
-void RowScannerImpl::nextCursorPosition(bool invertDirection)
-{
-       possibleMoveUpInHierarchy = false;
-       switch (state) {
-       case State::START:
-       case State::ROWS:
-               state = State::ROWS;
-               nextRow(invertDirection);
-               return;
-       case State::ITEMS:
-       case State::END:
-               state = State::ITEMS;
-               nextElementInRow(invertDirection);
-               return;
-       }
+       return direction == HorizontalScanningDirection::TO_RIGHT ? 1 : -1;
 }
 
-ecore::TimerRepetitionPolicy RowScannerImpl::iterateToNextScanningElement()
+void RowScannerImpl::moveToNextPosition(bool invertDirection)
 {
-       auto success = tryResetState();
-       if (success)
-               return ecore::TimerRepetitionPolicy::cancel;
-
-       switch (state) {
-       case State::START:
-               ASSERT(0, "State::START case should not be reached");
-               return ecore::TimerRepetitionPolicy::cancel;
-       case State::ROWS:
-       case State::ITEMS:
-               nextCursorPosition();
-               break;
-       case State::END:
-               ASSERT(0, "State::END case should not be reached");
-               return ecore::TimerRepetitionPolicy::cancel;
+       if (state == State::NO_ELEMENTS) return;
+
+       auto count = static_cast<int>(currentNavState.parent->getChildren().size());
+       Optional<int> newPos;
+       if (currentNavState.childIndex) {
+               auto increment = getIncrementForCalculatingNextElement(currentNavState.parent, invertDirection);
+               auto p = *currentNavState.childIndex + increment;
+               if (p >= 0 && p < count)
+                       newPos = p;
+       } else if (count > 0) {
+               newPos = getFirstElementIndex(currentNavState.parent);
        }
-
-       return timer.isSet() ? ecore::TimerRepetitionPolicy::renew : ecore::TimerRepetitionPolicy::cancel;
-}
-
-void RowScannerImpl::countInactivityAfterCompletedLoop()
-{
-       if (lastRowsDirection == properties.getDirectionVertical())
+       if (!newPos) {
                ++inactiveLoopCounter;
-       DEBUG("inactiveLoopCounter %d", inactiveLoopCounter);
-       possibleMoveUpInHierarchy = true;
+               if (inactiveLoopCounter >= properties.getLoopLimitToInaction()) {
+                       timer.reset();
+                       eraseFrame();
+                       state = State::INACTIVE;
+                       return;
+               }
+       }
+       if (!newPos && !properties.isEscapeFrameEnabled() && currentNavState.parent == rootNavigationElement && count > 0) {
+               newPos = getFirstElementIndex(currentNavState.parent);
+       }
+
+       selectNewElement({ currentNavState.parent, newPos });
 }
index 757cad7..415b6ef 100644 (file)
@@ -35,9 +35,11 @@ public:
         *  Method responsible for scanner instantiation.
         *  @param properties  contains all scanning parameters
         *         required for scanner configuration and proper scanner operating
+        *  @param rootNavigationElement root of tree of context objects
         *  @return RowScanner instance
         */
-       static std::unique_ptr<ScreenScanner> create(const ScanningProperties &properties);
+       static std::unique_ptr<ScreenScanner> create(const ScanningProperties &properties,
+                       std::shared_ptr<NavigationElement> rootNavigationElement);
 };
 
 #endif
index 49256b8..f580ddc 100644 (file)
@@ -46,6 +46,9 @@ public:
         *  @param callback functor called when scanning is complete.
         */
        virtual void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) = 0;
+
+       virtual void clearCursor() = 0;
+
        virtual void next() = 0;
        virtual void prev() = 0;
 
index 4e3f244..842110e 100644 (file)
@@ -32,11 +32,12 @@ ScreenScannerManager::ScreenScannerManager()
 
        //TODO: register for screen orientation change (probably better to do it in class Window)
        contextChangedHandle = Singleton<UniversalSwitch>::instance().getNavigationInterface()->registerCb<NavigationCallbackType::ContextChanged>(
-       [](std::shared_ptr<UIElement> obj, Rectangle rect) {
+       [](std::shared_ptr<UIElement> root, std::shared_ptr<NavigationElement> navigationContext) {
+               ASSERT(navigationContext);
                auto self = Singleton<UniversalSwitch>::instance().getScreenScannerManager();
                if (self) {
-                       self->properties.setScanningField(rect);
-                       self->onContextChanged(obj);
+                       self->properties.setScanningField(navigationContext->getBounds());
+                       self->onContextChanged(std::move(root), std::move(navigationContext));
                }
        });
        properties.setScanningField(mainWindow->getDimensions());
@@ -62,15 +63,16 @@ namespace attributes
 
        void getEscapeFrameEnabled(const std::shared_ptr<UIElement> obj, std::function<void(bool isEnabled)> callback)
        {
+               DEBUG("obj is %s", Atspi::getUniqueId(obj->getObject()).c_str());
                obj->getAttributeAsync("EscapeFrameEnabled",
                [callback](std::string value) {
-                       DEBUG("EscapeFrameEnabled: %s", value == "true" ? "true" : "false");
+                       DEBUG("EscapeFrameEnabled: %s", value.c_str());
                        callback(value == "true");
                });
        }
 }
 
-void ScreenScannerManager::onContextChanged(const std::shared_ptr<UIElement> &obj)
+void ScreenScannerManager::onContextChanged(const std::shared_ptr<UIElement> &obj, std::shared_ptr<NavigationElement> navigationContext)
 {
        DEBUG("Context changed callback invoked");
        stopAutoscanning();
@@ -78,16 +80,18 @@ void ScreenScannerManager::onContextChanged(const std::shared_ptr<UIElement> &ob
        mainWindow->bringToFront();
 
        if (!obj) {
+               rootNavigationElement = std::move(navigationContext);
                startAutoscanning();
                return;
        }
        obj->printDebug();
 
        attributes::getScanningMethod(obj,
-       [ptr = shared_from_this(), obj](Optional<ScanningMethod> method) {
+       [ptr = shared_from_this(), obj, navigationContext](Optional<ScanningMethod> method) {
                attributes::getEscapeFrameEnabled(obj,
-               [ptr, method](bool isEnabled) {
+               [ptr, method, navigationContext](bool isEnabled) {
                        ptr->properties.setEscapeFrameEnabled(isEnabled);
+                       ptr->rootNavigationElement = std::move(navigationContext);
                        ptr->startAutoscanning(method);
                });
        });
@@ -114,20 +118,27 @@ void ScreenScannerManager::startAutoscanning(Optional<ScanningMethod> method)
        });
 }
 
+void ScreenScannerManager::clearCursor()
+{
+       if (screenScanner)
+               screenScanner->clearCursor();
+}
+
 void ScreenScannerManager::delayedStartAutoScanning()
 {
        if (!scanningMethod)
                scanningMethod =  properties.getScanningMethod();
 
-       switch (*scanningMethod) {
-       case ScanningMethod::POINT:
-               screenScanner = PointScanner::create(properties);
-               break;
-       case ScanningMethod::ROW:
-               screenScanner = RowScanner::create(properties);
-               break;
+       if (rootNavigationElement) {
+               switch (*scanningMethod) {
+               case ScanningMethod::POINT:
+                       screenScanner = PointScanner::create(properties, rootNavigationElement);
+                       break;
+               case ScanningMethod::ROW:
+                       screenScanner = RowScanner::create(properties, rootNavigationElement);
+                       break;
+               }
        }
-       acceptAutoscanning();
 }
 
 void ScreenScannerManager::stopAutoscanning()
@@ -162,7 +173,7 @@ void ScreenScannerManager::acceptAutoscanning()
                if (elem) {
                        auto self = Singleton<UniversalSwitch>::instance().getScreenScannerManager();
                        if (self) {
-                               Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(*elem);
+                               Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak((*elem)->getObject());
                                self->notify(*elem);
                        }
                }
index 61c155a..6095480 100644 (file)
@@ -89,13 +89,18 @@ public:
         */
        void acceptAutoscanning();
 
+       /**
+        * @brief Removes any traces of user interface from the screen
+        */
+       void clearCursor();
+
        void next();
        void prev();
 
 private:
        void startAutoscanning(Optional<ScanningMethod> method);
 
-       void onContextChanged(const std::shared_ptr<UIElement> &obj);
+       void onContextChanged(const std::shared_ptr<UIElement> &obj, std::shared_ptr<NavigationElement> navigationContext);
 
        void delayedStartAutoScanning();
        static Eina_Bool delayedStartAutoscanningCb(void *data);
@@ -106,6 +111,7 @@ private:
        NavigationInterface::CallbackHandle contextChangedHandle;
        ScanningProperties properties;
        Optional<ScanningMethod> scanningMethod{};
+       std::shared_ptr<NavigationElement> rootNavigationElement;
 
        ecore::Timer delayedStartAutoscanningTimer;
 };
index fe63b51..cd3f329 100644 (file)
@@ -244,6 +244,8 @@ void SelectActivity::removeMenu()
        }
 
        if (popup) {
+               if (Singleton<UniversalSwitch>::instance().getScreenScannerManager())
+                       Singleton<UniversalSwitch>::instance().getScreenScannerManager()->clearCursor();
                evas_object_smart_callback_del(popup.get(), "dismissed", popupDismissedCb);
                popup.reset();
        }
index dceadc5..403551e 100644 (file)
@@ -136,7 +136,7 @@ void TextToSpeech::speak(std::string text) const
        EXIT_IF_ERROR(error);
 }
 
-void TextToSpeech::speak(const std::shared_ptr<UIElement> &element) const
+void TextToSpeech::speak(const std::shared_ptr<AtspiAccessible> &element) const
 {
        if (!enabled) {
                DEBUG("Voice feedback is disabled");
@@ -148,7 +148,7 @@ void TextToSpeech::speak(const std::shared_ptr<UIElement> &element) const
                return;
        }
 
-       Singleton<UniversalSwitch>::instance().getAtspi()->getName(element->getObject(),
+       Singleton<UniversalSwitch>::instance().getAtspi()->getName(element,
        [ptr = shared_from_this()](DBus::ValueOrError<std::string> name) {
                if (name)
                        ptr->speak(std::get<0>(name));
index 3e2bdbc..887b68a 100644 (file)
 #ifndef TEXT_TO_SPEECH_HPP
 #define TEXT_TO_SPEECH_HPP
 
-#include "UIElement.hpp"
 #include "VConf.hpp"
 
 #include <tts.h>
+#include "Atspi.hpp"
 
 #include <memory>
 #include <string>
@@ -35,7 +35,7 @@ public:
 
        bool isEnabled() const;
        void speak(std::string text) const;
-       void speak(const std::shared_ptr<UIElement> &element) const;
+       void speak(const std::shared_ptr<AtspiAccessible> &element) const;
 
 private:
        tts_h ttsHandler = nullptr;
index 0455659..6ed8491 100644 (file)
@@ -24,8 +24,9 @@
 #define NUMBER_OF_STEPS 10
 
 
-UIElement::UIElement(std::shared_ptr<AtspiAccessible> ptr, Optional<Point> scanningCoordinates)
-       : atspi(Singleton<UniversalSwitch>::instance().getAtspi()), obj(std::move(ptr)), scanningCoordinates(std::move(scanningCoordinates))
+UIElement::UIElement(std::shared_ptr<AtspiAccessible> ptr, Optional<Point> scanningCoordinates,
+                                        ApplicationCategory appCategory) : atspi(Singleton<UniversalSwitch>::instance().getAtspi()), obj(std::move(ptr)),
+       appCategory(appCategory), scanningCoordinates(std::move(scanningCoordinates))
 {
        ASSERT(atspi, "Atspi not defined");
 }
@@ -35,6 +36,11 @@ std::shared_ptr<AtspiAccessible> UIElement::getObject() const
        return obj;
 }
 
+UIElement::ApplicationCategory UIElement::getApplicationCategory() const
+{
+       return appCategory;
+}
+
 void UIElement::getAttributesAsync(std::function<void(bool)> callback)
 {
        atspi->getAttributes(obj,
index 2f22724..7cf0183 100644 (file)
 class UIElement : public std::enable_shared_from_this<UIElement>
 {
 public:
-       UIElement(std::shared_ptr<AtspiAccessible> ptr, Optional<Point> scanningCoordinates = {});
+       enum class ApplicationCategory {
+               HOMESCREEN,
+               KEYBOARD,
+               OTHER,
+       };
+
+       UIElement(std::shared_ptr<AtspiAccessible> ptr, Optional<Point> scanningCoordinates, ApplicationCategory appCategory);
 
        std::shared_ptr<AtspiAccessible> getObject() const;
 
        void getAttributeAsync(const std::string &key, std::function<void(std::string value)> callback);
        Point getScanningCoordinates() const;
+       ApplicationCategory getApplicationCategory() const;
 
        void activate();
        void increment();
@@ -46,6 +53,7 @@ private:
 
        std::shared_ptr<Atspi> atspi;
        std::shared_ptr<AtspiAccessible> obj;
+       ApplicationCategory appCategory;
 
        Optional<std::unordered_map<std::string, std::string>> attributes;
        Optional<Point> scanningCoordinates;