X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Faccessibility%2Fbridge%2Fbridge-accessible.cpp;h=e389f15b303c7b08ed5e4f6e29436376523057db;hb=refs%2Fchanges%2F62%2F265362%2F4;hp=028d0caab2418b80d930421f6d0e6f769cc984d8;hpb=e5ebc1ae58218974e96c3d73bc386a7358607206;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/accessibility/bridge/bridge-accessible.cpp b/dali/internal/accessibility/bridge/bridge-accessible.cpp index 028d0ca..e389f15 100644 --- a/dali/internal/accessibility/bridge/bridge-accessible.cpp +++ b/dali/internal/accessibility/bridge/bridge-accessible.cpp @@ -19,6 +19,7 @@ #include // EXTERNAL INCLUDES +#include #include //comment out 2 lines below to get more logs @@ -29,83 +30,82 @@ using namespace Dali::Accessibility; #define GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH 10000 -BridgeAccessible::BridgeAccessible() +namespace +{ + +bool SortVertically(Component* lhs, Component* rhs) { + auto leftRect = lhs->GetExtents(CoordinateType::WINDOW); + auto rightRect = rhs->GetExtents(CoordinateType::WINDOW); + + return leftRect.y < rightRect.y; } -void BridgeAccessible::RegisterInterfaces() +bool SortHorizontally(Component* lhs, Component* rhs) { - DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAccessible}; - AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount); - AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName); - AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription); - AddGetPropertyToInterface(desc, "Parent", &BridgeAccessible::GetParent); - AddFunctionToInterface(desc, "GetRole", &BridgeAccessible::GetRole); - AddFunctionToInterface(desc, "GetRoleName", &BridgeAccessible::GetRoleName); - AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName); - AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates); - AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes); - AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfaces); - AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex); - AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren); - AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent); - AddFunctionToInterface(desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint); - AddFunctionToInterface(desc, "GetNeighbor", &BridgeAccessible::GetNeighbor); - AddFunctionToInterface(desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo); - AddFunctionToInterface(desc, "DoGesture", &BridgeAccessible::DoGesture); - AddFunctionToInterface(desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial); - AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet); - dbusServer.addInterface("/", desc, true); + auto leftRect = lhs->GetExtents(CoordinateType::WINDOW); + auto rightRect = rhs->GetExtents(CoordinateType::WINDOW); + + return leftRect.x < rightRect.x; } -static bool AcceptObjectCheckRole(Component* obj) +std::vector> SplitLines(const std::vector& children) { - if(!obj) - return false; - switch(obj->GetRole()) - { - case Role::APPLICATION: - case Role::FILLER: - case Role::SCROLL_PANE: - case Role::SPLIT_PANE: - case Role::WINDOW: - case Role::IMAGE: - case Role::IMAGE_MAP: - case Role::LIST: - case Role::ICON: - case Role::TOOL_BAR: - case Role::REDUNDANT_OBJECT: - case Role::COLOR_CHOOSER: - case Role::TREE_TABLE: - case Role::PAGE_TAB_LIST: - case Role::PAGE_TAB: - case Role::SPIN_BUTTON: - case Role::INPUT_METHOD_WINDOW: - case Role::EMBEDDED: - case Role::INVALID: - case Role::NOTIFICATION: - case Role::DATE_EDITOR: - case Role::TABLE: + // Find first with non-zero area + auto first = std::find_if(children.begin(), children.end(), [](Component* child) -> bool { + auto extents = child->GetExtents(CoordinateType::WINDOW); + return extents.height != 0.0f && extents.width != 0.0f; + }); + + if(first == children.end()) + { + return {}; + } + + std::vector> lines(1); + Dali::Rect<> lineRect = (*first)->GetExtents(CoordinateType::WINDOW); + Dali::Rect<> rect; + + // Split into lines + for(auto it = first; it != children.end(); ++it) + { + auto child = *it; + + rect = child->GetExtents(CoordinateType::WINDOW); + if(rect.height == 0.0f || rect.width == 0.0f) { - return false; + // Zero area, ignore + continue; + } + + if(lineRect.y + (0.5 * lineRect.height) >= rect.y + (0.5 * rect.height)) + { + // Same line + lines.back().push_back(child); } - default: + else { - break; + // Start a new line + lineRect = rect; + lines.emplace_back(); + lines.back().push_back(child); } } - return true; + return lines; } static bool AcceptObjectCheckRelations(Component* obj) { - auto r = obj->GetRelationSet(); + auto relations = obj->GetRelationSet(); - for(const auto& it : r) + for(const auto& it : relations) + { if(it.relationType == RelationType::CONTROLLED_BY) + { return false; - + } + } return true; } @@ -116,48 +116,63 @@ static Component* GetScrollableParent(Accessible* obj) obj = obj->GetParent(); auto comp = dynamic_cast(obj); if(comp && comp->IsScrollable()) + { return comp; + } } return nullptr; } -static bool ObjectIsItem(Component* obj) +static bool IsObjectItem(Component* obj) { if(!obj) + { return false; + } auto role = obj->GetRole(); return role == Role::LIST_ITEM || role == Role::MENU_ITEM; } -static bool ObjectIsCollapsed(Component* obj) +static bool IsObjectCollapsed(Component* obj) { if(!obj) + { return false; + } const auto states = obj->GetStates(); return states[State::EXPANDABLE] && !states[State::EXPANDED]; } -static bool OobjectIsZeroSize(Component* obj) +static bool IsObjectZeroSize(Component* obj) { if(!obj) + { return false; - auto extents = obj->GetExtents(CoordType::WINDOW); + } + auto extents = obj->GetExtents(CoordinateType::WINDOW); return extents.height == 0 || extents.width == 0; } -static bool AcceptObject(Component* obj) +static bool IsObjectAcceptable(Component* obj) { if(!obj) + { return false; + } + const auto states = obj->GetStates(); if(!states[State::VISIBLE]) + { return false; - if(!AcceptObjectCheckRole(obj)) - return false; + } if(!AcceptObjectCheckRelations(obj)) + { return false; + } if(!states[State::HIGHLIGHTABLE]) + { return false; + } if(GetScrollableParent(obj) != nullptr) { @@ -165,12 +180,12 @@ static bool AcceptObject(Component* obj) if(parent) { - return !ObjectIsItem(obj) || !ObjectIsCollapsed(parent); + return !IsObjectItem(obj) || !IsObjectCollapsed(parent); } } else { - if(OobjectIsZeroSize(obj)) + if(IsObjectZeroSize(obj)) { return false; } @@ -182,75 +197,289 @@ static bool AcceptObject(Component* obj) return true; } -static bool AcceptObject(Accessible* obj) +static bool IsObjectAcceptable(Accessible* obj) +{ + auto component = dynamic_cast(obj); + return IsObjectAcceptable(component); +} + +static int32_t GetItemCountOfList(Accessible* obj) { - auto c = dynamic_cast(obj); - return AcceptObject(c); + int32_t itemCount = 0; + if(obj && obj->GetRole() == Role::LIST) + { + for(auto i = 0u; i < static_cast(obj->GetChildCount()); ++i) + { + auto child = obj->GetChildAtIndex(i); + if(child && child->GetRole() == Role::LIST_ITEM) + { + itemCount++; + } + } + } + return itemCount; } -static std::string objDump(Component* obj) +static int32_t GetItemCountOfFirstDescendantList(Accessible* obj) +{ + int32_t itemCount = 0; + itemCount = GetItemCountOfList(obj); + if(itemCount > 0 || !obj) + { + return itemCount; + } + + for(auto i = 0u; i < static_cast(obj->GetChildCount()); ++i) + { + auto child = obj->GetChildAtIndex(i); + itemCount = GetItemCountOfFirstDescendantList(child); + if(itemCount > 0) + { + return itemCount; + } + } + return itemCount; +} + +static std::string GetComponentInfo(Component* obj) { if(!obj) + { return "nullptr"; - std::ostringstream o; - auto e = obj->GetExtents(CoordType::SCREEN); - o << "name: " << obj->GetName() << " extent: (" << e.x << ", " - << e.y << "), [" << e.width << ", " << e.height << "]"; - return o.str(); + } + + std::ostringstream object; + auto extent = obj->GetExtents(CoordinateType::SCREEN); + object << "name: " << obj->GetName() << " extent: (" << extent.x << ", " + << extent.y << "), [" << extent.width << ", " << extent.height << "]"; + return object.str(); +} + +static std::string MakeIndent(unsigned int maxRecursionDepth) +{ + return std::string(GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH - maxRecursionDepth, ' '); +} + +static bool IsDeputy(Accessible* obj) +{ + //TODO: add deputy + return false; +} + +static Accessible* GetProxyInParent(Accessible* obj) +{ + if(!obj) + { + return nullptr; + } + + auto children = obj->GetChildren(); + for(auto& child : children) + { + if(child->IsProxy()) + { + return child; + } + } + return nullptr; +} + +static bool IsRoleAcceptableWhenNavigatingNextPrev(Accessible* obj) +{ + if(!obj) + { + return false; + } + auto role = obj->GetRole(); + return role != Role::POPUP_MENU && role != Role::DIALOG; +} + +static Accessible* FindNonDefunctChild(const std::vector& children, unsigned int currentIndex, unsigned char forward) +{ + unsigned int childrenCount = children.size(); + for(; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex) + { + Accessible* object = children[currentIndex]; + if(object && !object->GetStates()[State::DEFUNCT]) + { + return object; + } + } + return nullptr; +} + +// The auxiliary method for Depth-First Search (DFS) algorithm to find non defunct child directionally +static Accessible* FindNonDefunctChildWithDepthFirstSearch(Accessible* node, const std::vector& children, unsigned char forward) +{ + if(!node) + { + return nullptr; + } + + auto childrenCount = children.size(); + if(childrenCount > 0) + { + const bool isShowing = GetScrollableParent(node) == nullptr ? node->GetStates()[State::SHOWING] : true; + if(isShowing) + { + return FindNonDefunctChild(children, forward ? 0 : childrenCount - 1, forward); + } + } + return nullptr; +} + +static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward) +{ + if(!obj) + { + return false; + } + + auto attrs = obj->GetAttributes(); + for(auto& attr : attrs) + { + if(attr.first == "relation_chain_end") + { + if((attr.second == "prev,end" && forward == 0) || (attr.second == "next,end" && forward == 1) || attr.second == "prev,next,end") + { + return true; + } + } + } + return false; +} + +static Accessible* GetDeputyOfProxyInParent(Accessible* obj) +{ + return nullptr; +} + +static std::vector GetScrollableParents(Accessible *accessible) +{ + std::vector scrollableParents; + + while(accessible) + { + accessible = accessible->GetParent(); + auto component = dynamic_cast(accessible); + if(component && component->IsScrollable()) + { + scrollableParents.push_back(component); + } + } + return scrollableParents; +} + +static std::vector GetNonDuplicatedScrollableParents(Accessible *child, Accessible *start) +{ + auto scrollableParentsOfChild = GetScrollableParents(child); + auto scrollableParentsOfStart = GetScrollableParents(start); + + // find the first different scrollable parent by comparing from top to bottom. + // since it can not be the same after that, there is no need to compare. + while(!scrollableParentsOfChild.empty() && !scrollableParentsOfStart.empty() && scrollableParentsOfChild.back() == scrollableParentsOfStart.back()) + { + scrollableParentsOfChild.pop_back(); + scrollableParentsOfStart.pop_back(); + } + + return scrollableParentsOfChild; +} + +} // anonymous namespace + + +BridgeAccessible::BridgeAccessible() +{ } -Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType ralationType) +void BridgeAccessible::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAccessible}; + AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount); + AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName); + AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription); + AddGetPropertyToInterface(desc, "Parent", &BridgeAccessible::GetParent); + AddFunctionToInterface(desc, "GetRole", &BridgeAccessible::GetRole); + AddFunctionToInterface(desc, "GetRoleName", &BridgeAccessible::GetRoleName); + AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName); + AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates); + AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes); + AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfaces); + AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex); + AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren); + AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent); + AddFunctionToInterface(desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint); + AddFunctionToInterface(desc, "GetNeighbor", &BridgeAccessible::GetNeighbor); + AddFunctionToInterface(desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo); + AddFunctionToInterface(desc, "DoGesture", &BridgeAccessible::DoGesture); + AddFunctionToInterface(desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial); + AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet); + mDbusServer.addInterface("/", desc, true); +} + +Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType relationType) { if(!obj) + { return nullptr; + } + for(auto& relation : obj->GetRelationSet()) { - if(relation.relationType == ralationType) + if(relation.relationType == relationType) { for(auto& address : relation.targets) { auto component = dynamic_cast(Find(address)); if(component) + { return component; + } } } } return nullptr; } -static std::string makeIndent(unsigned int maxRecursionDepth) -{ - return std::string(GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH - maxRecursionDepth, ' '); -} - -Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* root, Point p, CoordType cType, unsigned int maxRecursionDepth) +Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* root, Point point, CoordinateType type, unsigned int maxRecursionDepth) { if(!root || maxRecursionDepth == 0) + { return nullptr; - auto root_component = dynamic_cast(root); - LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << makeIndent(maxRecursionDepth) << objDump(root_component); + } + + auto rootComponent = dynamic_cast(root); + LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent); - if(root_component && !root_component->Contains(p, cType)) + if(rootComponent && !rootComponent->IsAccessibleContainingPoint(point, type)) + { return nullptr; + } auto children = root->GetChildren(); for(auto childIt = children.rbegin(); childIt != children.rend(); childIt++) { //check recursively all children first - auto result = CalculateNavigableAccessibleAtPoint(*childIt, p, cType, maxRecursionDepth - 1); + auto result = CalculateNavigableAccessibleAtPoint(*childIt, point, type, maxRecursionDepth - 1); if(result) + { return result; + } } - if(root_component) + + if(rootComponent) { //Found a candidate, all its children are already checked - auto controledBy = GetObjectInRelation(root_component, RelationType::CONTROLLED_BY); + auto controledBy = GetObjectInRelation(rootComponent, RelationType::CONTROLLED_BY); if(!controledBy) - controledBy = root_component; + { + controledBy = rootComponent; + } - if(controledBy->IsProxy() || AcceptObject(controledBy)) + if(controledBy->IsProxy() || IsObjectAcceptable(controledBy)) { - LOG() << "CalculateNavigableAccessibleAtPoint: found: " << makeIndent(maxRecursionDepth) << objDump(root_component); + LOG() << "CalculateNavigableAccessibleAtPoint: found: " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent); return controledBy; } } @@ -259,69 +488,100 @@ Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* roo BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial() { - auto self = FindSelf(); - auto attributes = self->GetAttributes(); - auto name = self->GetName(); - std::string labeledByName = ""; - std::string textIfceName = ""; - auto role = static_cast(self->GetRole()); - auto states = self->GetStates(); - auto localizedName = self->GetLocalizedRoleName(); - auto childCount = static_cast(self->GetChildCount()); + auto self = FindSelf(); + auto findObjectByRelationType = [this, &self](RelationType relationType) { + auto relations = self->GetRelationSet(); + auto relation = std::find_if(relations.begin(), + relations.end(), + [relationType](const Dali::Accessibility::Relation& relation) -> bool { + return relation.relationType == relationType; + }); + return relations.end() != relation && !relation->targets.empty() ? Find(relation->targets.back()) : nullptr; + }; + + auto labellingObject = findObjectByRelationType(RelationType::LABELLED_BY); + std::string labeledByName = labellingObject ? labellingObject->GetName() : ""; + + auto describedByObject = findObjectByRelationType(RelationType::DESCRIBED_BY); double currentValue = 0.0; double minimumIncrement = 0.0; double maximumValue = 0.0; double minimumValue = 0.0; - - auto* value = dynamic_cast(self); - if(value) + auto* valueInterface = dynamic_cast(self); + if(valueInterface) { - currentValue = value->GetCurrent(); - minimumIncrement = value->GetMinimumIncrement(); - maximumValue = value->GetMaximum(); - minimumValue = value->GetMinimum(); + currentValue = valueInterface->GetCurrent(); + minimumIncrement = valueInterface->GetMinimumIncrement(); + maximumValue = valueInterface->GetMaximum(); + minimumValue = valueInterface->GetMinimum(); } - auto description = self->GetDescription(); - auto indexInParent = static_cast(self->GetIndexInParent()); - bool isSelectedInParent = false; - bool hasCheckBoxChild = false; int32_t firstSelectedChildIndex = -1; int32_t selectedChildCount = 0; + auto* selfSelectionInterface = dynamic_cast(self); + if(selfSelectionInterface) + { + selectedChildCount = selfSelectionInterface->GetSelectedChildrenCount(); + auto firstSelectedChild = selfSelectionInterface->GetSelectedChild(0); + if(firstSelectedChild) + { + firstSelectedChildIndex = firstSelectedChild->GetIndexInParent(); + } + } + auto childCount = static_cast(self->GetChildCount()); + bool hasCheckBoxChild = false; for(auto i = 0u; i < static_cast(childCount); ++i) { - auto q = self->GetChildAtIndex(i); - auto s = q->GetStates(); - if(s[State::SELECTABLE]) + auto child = self->GetChildAtIndex(i); + if(child->GetRole() == Role::CHECK_BOX) { - if(s[State::SELECTED]) - { - ++selectedChildCount; - if(firstSelectedChildIndex < 0) - firstSelectedChildIndex = static_cast(i); - } - } - if(q->GetRole() == Role::CHECK_BOX) hasCheckBoxChild = true; + break; + } } - int32_t listChildrenCount = 0; - Accessible* parent = self->GetParent(); - auto parentStateSet = parent ? parent->GetStates() : States{}; - auto parentChildCount = parent ? static_cast(parent->GetChildCount()) : 0; - auto parentRole = static_cast(parent ? parent->GetRole() : Role{}); - Accessible* describedByObject = nullptr; + auto role = static_cast(self->GetRole()); + int32_t listChildrenCount = 0; + if(role == static_cast(Role::DIALOG)) + { + listChildrenCount = GetItemCountOfFirstDescendantList(self); + } + + auto* textInterface = dynamic_cast(self); + std::string nameFromTextInterface = ""; + if(textInterface) + { + nameFromTextInterface = textInterface->GetText(0, textInterface->GetCharacterCount()); + } + + auto description = self->GetDescription(); + auto attributes = self->GetAttributes(); + auto states = self->GetStates(); + auto name = self->GetName(); + auto localizedRoleName = self->GetLocalizedRoleName(); + auto indexInParent = static_cast(self->GetIndexInParent()); + + auto parent = self->GetParent(); + auto parentRole = static_cast(parent ? parent->GetRole() : Role{}); + auto parentChildCount = parent ? static_cast(parent->GetChildCount()) : 0; + auto parentStateSet = parent ? parent->GetStates() : States{}; + bool isSelectedInParent = false; + auto* parentSelectionInterface = dynamic_cast(parent); + if(parentSelectionInterface) + { + isSelectedInParent = parentSelectionInterface->IsChildSelected(indexInParent); + } return { attributes, name, labeledByName, - textIfceName, + nameFromTextInterface, role, states, - localizedName, + localizedRoleName, childCount, currentValue, minimumIncrement, @@ -341,17 +601,23 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial() describedByObject}; } -DBus::ValueOrError BridgeAccessible::DoGesture(Dali::Accessibility::Gesture type, int32_t xBeg, int32_t yBeg, int32_t xEnd, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime) +void BridgeAccessible::SuppressScreenReader(bool suppress) { - return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, xBeg, xEnd, yBeg, yEnd, state, eventTime}); + mIsScreenReaderSuppressed = suppress; } -DBus::ValueOrError BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordType) +DBus::ValueOrError BridgeAccessible::DoGesture(Dali::Accessibility::Gesture type, int32_t startPositionX, int32_t startPositionY, int32_t endPositionX, int32_t endPositionY, Dali::Accessibility::GestureState state, uint32_t eventTime) +{ + // Please be aware of sending GestureInfo point in the different order with parameters + return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, startPositionX, endPositionX, startPositionY, endPositionY, state, eventTime}); +} + +DBus::ValueOrError BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordinateType) { Accessible* deputy = nullptr; auto accessible = FindSelf(); - auto cType = static_cast(coordType); - LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordType; + auto cType = static_cast(coordinateType); + LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordinateType; auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH); bool recurse = false; if(component) @@ -362,302 +628,309 @@ DBus::ValueOrError BridgeAccessible::GetNavig return {component, recurse, deputy}; } -static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward) +Accessible* BridgeAccessible::GetCurrentlyHighlighted() { - if(!obj) - return false; - auto attrs = obj->GetAttributes(); - for(auto& attr : attrs) + //TODO: add currently highlighted object + return nullptr; +} + +std::vector BridgeAccessible::GetValidChildren(const std::vector& children, Accessible* start) +{ + if(children.empty()) { - if(attr.first == "relation_chain_end") + return {}; + } + + std::vector vec; + + Dali::Rect<> scrollableParentExtents; + auto nonDuplicatedScrollableParents = GetNonDuplicatedScrollableParents(children.front(), start); + if (!nonDuplicatedScrollableParents.empty()) + { + scrollableParentExtents = nonDuplicatedScrollableParents.front()->GetExtents(CoordinateType::WINDOW); + } + + for(auto child : children) + { + auto* component = dynamic_cast(child); + if(component) { - if((attr.second == "prev,end" && forward == 0) || (attr.second == "next,end" && forward == 1) || attr.second == "prev,next,end") + if(nonDuplicatedScrollableParents.empty() || scrollableParentExtents.Intersects(component->GetExtents(CoordinateType::WINDOW))) { - return true; + vec.push_back(component); } } } - return false; -} -static Accessible* DeputyOfProxyInParentGet(Accessible* obj) -{ - return nullptr; + return vec; } -Accessible* BridgeAccessible::GetCurrentlyHighlighted() +void BridgeAccessible::SortChildrenFromTopLeft(std::vector& children) { - //TODO: add currently highlighted object - return nullptr; -} + if(children.empty()) + { + return; + } -std::vector BridgeAccessible::ValidChildrenGet(const std::vector& children, Accessible* start, Accessible* root) -{ - return children; -} + std::vector sortedChildren; -static bool DeputyIs(Accessible* obj) -{ - //TODO: add deputy - return false; -} + std::sort(children.begin(), children.end(), &SortVertically); -static Accessible* ProxyInParentGet(Accessible* obj) -{ - if(!obj) - return nullptr; - auto children = obj->GetChildren(); - for(auto& child : children) + for(auto& line : SplitLines(children)) { - if(child->IsProxy()) - return child; + std::sort(line.begin(), line.end(), &SortHorizontally); + sortedChildren.insert(sortedChildren.end(), line.begin(), line.end()); } - return nullptr; -} -static bool ObjectRoleIsAcceptableWhenNavigatingNextPrev(Accessible* obj) -{ - if(!obj) - return false; - auto role = obj->GetRole(); - return role != Role::POPUP_MENU && role != Role::DIALOG; + children = sortedChildren; } + template struct CycleDetection { CycleDetection(const T value) - : key(value), - currentSearchSize(1), - counter(1) + : mKey(value), + mCurrentSearchSize(1), + mCounter(1) { } - bool check(const T value) + + bool Check(const T value) { - if(key == value) + if(mKey == value) + { return true; - if(--counter == 0) + } + + if(--mCounter == 0) { - currentSearchSize <<= 1; - if(currentSearchSize == 0) + mCurrentSearchSize <<= 1; + if(mCurrentSearchSize == 0) + { return true; // UNDEFINED BEHAVIOR - counter = currentSearchSize; - key = value; + } + mCounter = mCurrentSearchSize; + mKey = value; } return false; } - T key; - unsigned int currentSearchSize; - unsigned int counter; + + T mKey; + unsigned int mCurrentSearchSize; + unsigned int mCounter; }; -static Accessible* FindNonDefunctChild(const std::vector& children, unsigned int currentIndex, unsigned char forward) +Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, unsigned char forward) { - unsigned int childrenCount = children.size(); - for(; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex) + if(!obj) { - Accessible* n = children[currentIndex]; - if(n && !n->GetStates()[State::DEFUNCT]) - return n; - } - return nullptr; -} - -static Accessible* DirectionalDepthFirstSearchTryNonDefunctChild(Accessible* node, const std::vector& children, unsigned char forward) -{ - if(!node) return nullptr; - auto childrenCount = children.size(); - if(childrenCount > 0) - { - const bool isShowing = GetScrollableParent(node) == nullptr ? node->GetStates()[State::SHOWING] : true; - if(isShowing) - { - return FindNonDefunctChild(children, forward ? 0 : childrenCount - 1, forward); - } } - return nullptr; -} -Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, Accessible* root, unsigned char forward) -{ - if(!obj) - return nullptr; auto parent = obj->GetParent(); if(!parent) + { return nullptr; + } - auto children = ValidChildrenGet(parent->GetChildren(), start, root); + auto children = GetValidChildren(parent->GetChildren(), start); + SortChildrenFromTopLeft(children); - unsigned int children_count = children.size(); - if(children_count == 0) + unsigned int childrenCount = children.size(); + if(childrenCount == 0) { return nullptr; } + unsigned int current = 0; - for(; current < children_count && children[current] != obj; ++current) - ; - if(current >= children_count) + for(; current < childrenCount && children[current] != obj; ++current) + { + } + + if(current >= childrenCount) { return nullptr; } + forward ? ++current : --current; auto ret = FindNonDefunctChild(children, current, forward); return ret; } -Accessible* BridgeAccessible::DirectionalDepthFirstSearchTryNonDefunctSibling(bool& all_children_visited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward) +Accessible* BridgeAccessible::FindNonDefunctSibling(bool& areAllChildrenVisited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward) { while(true) { - Accessible* sibling = GetNextNonDefunctSibling(node, start, root, forward); + Accessible* sibling = GetNextNonDefunctSibling(node, start, forward); if(sibling) { - node = sibling; - all_children_visited = false; + node = sibling; + areAllChildrenVisited = false; // Note that this is passed by non-const reference, so it is the caller that can determine whether this search exhausted all children. break; } // walk up... node = node->GetParent(); if(node == nullptr || node == root) + { return nullptr; + } // in backward traversing stop the walk up on parent if(!forward) + { break; + } } + return node; } -Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::GetNeighborSearchMode search_mode) +Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::NeighborSearchMode searchMode) { if(start && CheckChainEndWithAttribute(start, forward)) + { return start; + } if(root && root->GetStates()[State::DEFUNCT]) + { return NULL; + } if(start && start->GetStates()[State::DEFUNCT]) { start = NULL; forward = 1; } - if(search_mode == BridgeAccessible::GetNeighborSearchMode::recurseToOutside) + if(searchMode == BridgeAccessible::NeighborSearchMode::RECURSE_TO_OUTSIDE) { // This only works if we navigate backward, and it is not possible to // find in embedded process. In this case the deputy should be used */ - return DeputyOfProxyInParentGet(start); + return GetDeputyOfProxyInParent(start); } Accessible* node = start ? start : root; if(!node) + { return nullptr; + } // initialization of all-children-visited flag for start node - we assume // that when we begin at start node and we navigate backward, then all children // are visited, so navigation will ignore start's children and go to // previous sibling available. // Regarding condtion (start != root): - // The last object can be found only if all_children_visited is false. + // The last object can be found only if areAllChildrenVisited is false. // The start is same with root, when looking for the last object. - bool all_children_visited = (start != root) && (search_mode != BridgeAccessible::GetNeighborSearchMode::recurseFromRoot && !forward); + bool areAllChildrenVisited = (start != root) && (searchMode != BridgeAccessible::NeighborSearchMode::RECURSE_FROM_ROOT && !forward); + // true, if starting element should be ignored. this is only used in rare case of // recursive search failing to find an object. // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from // element A algorithm has to descend into BUS_B and search element B and its children. this is done // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B). // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out - // and will call us again with object A and flag search_mode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING. + // and will call us again with object A and flag searchMode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING. // this flag means, that object A was already checked previously and we should skip it and its children. - bool force_next = (search_mode == BridgeAccessible::GetNeighborSearchMode::continueAfterFailedRecursion); + bool forceNext = (searchMode == BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION); CycleDetection cycleDetection(node); while(node) { if(node->GetStates()[State::DEFUNCT]) + { return nullptr; + } // always accept proxy object from different world - if(!force_next && node->IsProxy()) + if(!forceNext && node->IsProxy()) + { return node; + } - auto children = node->GetChildren(); - children = ValidChildrenGet(children, start, root); + auto children = GetValidChildren(node->GetChildren(), start); + SortChildrenFromTopLeft(children); // do accept: // 1. not start node // 2. parent after all children in backward traversing // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element. // Objects with those roles shouldnt be reachable, when navigating next / prev. - bool all_children_visited_or_moving_forward = (children.size() == 0 || forward || all_children_visited); - if(!force_next && node != start && all_children_visited_or_moving_forward && AcceptObject(node)) + bool areAllChildrenVisitedOrMovingForward= (children.size() == 0 || forward || areAllChildrenVisited); + + if(!forceNext && node != start && areAllChildrenVisitedOrMovingForward && IsObjectAcceptable(node)) { - if(start == NULL || ObjectRoleIsAcceptableWhenNavigatingNextPrev(node)) + if(start == NULL || IsRoleAcceptableWhenNavigatingNextPrev(node)) + { return node; + } } - Accessible* next_related_in_direction = !force_next ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr; - // force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING + Accessible* nextRelatedInDirection = !forceNext ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr; + // forceNext means that the searchMode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING // in this case the node is elm_layout which is parent of proxy object. // There is an access object working for the proxy object, and the access // object could have relation information. This relation information should // be checked first before using the elm_layout as a node. - if(force_next && forward) + if(forceNext && forward) { - auto deputy = DeputyOfProxyInParentGet(node); - next_related_in_direction = - GetObjectInRelation(deputy, RelationType::FLOWS_TO); + auto deputy = GetDeputyOfProxyInParent(node); + nextRelatedInDirection = GetObjectInRelation(deputy, RelationType::FLOWS_TO); } - if(next_related_in_direction && start && start->GetStates()[State::DEFUNCT]) + if(nextRelatedInDirection && start && start->GetStates()[State::DEFUNCT]) { - next_related_in_direction = NULL; + nextRelatedInDirection = NULL; } - unsigned char want_cycle_detection = 0; - if(next_related_in_direction) + unsigned char wantCycleDetection = 0; + if(nextRelatedInDirection) { - // Check next_related_in_direction is deputy object + // Check whether nextRelatedInDirection is deputy object or not Accessible* parent; if(!forward) { // If the prev object is deputy, then go to inside of its proxy first - if(DeputyIs(next_related_in_direction)) + if(IsDeputy(nextRelatedInDirection)) { - parent = next_related_in_direction->GetParent(); - next_related_in_direction = ProxyInParentGet(parent); + parent = nextRelatedInDirection->GetParent(); + nextRelatedInDirection = GetProxyInParent(parent); } } else { // If current object is deputy, and it has relation next object, // then do not use the relation next object, and use proxy first - if(DeputyIs(node)) + if(IsDeputy(node)) { - parent = node->GetParent(); - next_related_in_direction = ProxyInParentGet(parent); + parent = node->GetParent(); + nextRelatedInDirection = GetProxyInParent(parent); } } - node = next_related_in_direction; - want_cycle_detection = 1; + node = nextRelatedInDirection; + wantCycleDetection = 1; } else { - auto child = !force_next && !all_children_visited ? DirectionalDepthFirstSearchTryNonDefunctChild(node, children, forward) : nullptr; + auto child = !forceNext && !areAllChildrenVisited ? FindNonDefunctChildWithDepthFirstSearch(node, children, forward) : nullptr; if(child) { - want_cycle_detection = 1; + wantCycleDetection = 1; } else { - if(!force_next && node == root) + if(!forceNext && node == root) + { return NULL; - all_children_visited = true; - child = DirectionalDepthFirstSearchTryNonDefunctSibling(all_children_visited, node, start, root, forward); + } + areAllChildrenVisited = true; + child = FindNonDefunctSibling(areAllChildrenVisited, node, start, root, forward); } node = child; } - force_next = 0; - if(want_cycle_detection && cycleDetection.check(node)) + + forceNext = 0; + if(wantCycleDetection && cycleDetection.Check(node)) { return NULL; } @@ -665,12 +938,12 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st return NULL; } -DBus::ValueOrError BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t search_mode) +DBus::ValueOrError BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t searchMode) { auto start = FindSelf(); rootPath = StripPrefix(rootPath); auto root = !rootPath.empty() ? Find(rootPath) : nullptr; - auto accessible = CalculateNeighbor(root, start, direction == 1, static_cast(search_mode)); + auto accessible = CalculateNeighbor(root, start, direction == 1, static_cast(searchMode)); unsigned char recurse = 0; if(accessible) { @@ -685,54 +958,74 @@ Accessible* BridgeAccessible::GetParent() // only element set as application root might return nullptr from GetParent // if you want more, then you need to change setApplicationRoot to // add/remove ApplicationRoot and make roots a vector. - auto p = FindSelf()->GetParent(); - assert(p); - return p; + auto parent = FindSelf()->GetParent(); + + return parent; } + DBus::ValueOrError> BridgeAccessible::GetChildren() { return FindSelf()->GetChildren(); } + std::string BridgeAccessible::GetDescription() { return FindSelf()->GetDescription(); } + DBus::ValueOrError BridgeAccessible::GetRole() { return static_cast(FindSelf()->GetRole()); } + DBus::ValueOrError BridgeAccessible::GetRoleName() { return FindSelf()->GetRoleName(); } + DBus::ValueOrError BridgeAccessible::GetLocalizedRoleName() { return FindSelf()->GetLocalizedRoleName(); } + DBus::ValueOrError BridgeAccessible::GetIndexInParent() { return FindSelf()->GetIndexInParent(); } + DBus::ValueOrError> BridgeAccessible::GetStates() { return FindSelf()->GetStates().GetRawData(); } + DBus::ValueOrError> BridgeAccessible::GetAttributes() { - return FindSelf()->GetAttributes(); + std::unordered_map attributes = FindSelf()->GetAttributes(); + + if(mIsScreenReaderSuppressed) + { + attributes.insert({"suppress-screen-reader", "true"}); + } + + return attributes; } + DBus::ValueOrError> BridgeAccessible::GetInterfaces() { return FindSelf()->GetInterfaces(); } + int BridgeAccessible::GetChildCount() { return FindSelf()->GetChildCount(); } + DBus::ValueOrError BridgeAccessible::GetChildAtIndex(int index) { if(index < 0) + { throw std::domain_error{"negative index (" + std::to_string(index) + ")"}; + } return FindSelf()->GetChildAtIndex(static_cast(index)); } @@ -744,6 +1037,7 @@ std::string BridgeAccessible::GetName() DBus::ValueOrError> BridgeAccessible::GetDefaultLabelInfo() { auto defaultLabel = FindSelf()->GetDefaultLabel(); + // By default, the text is taken from navigation context root's accessibility properties name and description. return {defaultLabel, static_cast(defaultLabel->GetRole()), defaultLabel->GetAttributes()}; } @@ -753,7 +1047,9 @@ DBus::ValueOrError> BridgeAccessible::Ge std::vector ret; for(auto& it : relations) + { ret.emplace_back(Relation{static_cast(it.relationType), it.targets}); + } return ret; }