From: Shinwoo Kim Date: Wed, 23 Jun 2021 11:29:30 +0000 (+0900) Subject: [ATSPI] Check higlight area using PropertyNotification X-Git-Tag: dali_2.0.36~1^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=bc2c6d28fe9649c0353e5d8b8f2245f4011f9744 [ATSPI] Check higlight area using PropertyNotification Move highlight to next or prev if highlighted object goes out of screen. This patch is using PropertyNotification only for highlighted object. Change-Id: Idaea158fa9e9a1eb569ab619210d2aa916f28f22 --- diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp index f8eec35..6d61f56 100644 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp @@ -10,6 +10,8 @@ namespace Dali { using MethodType = TestDBusWrapper::MethodType; using MessagePtr = DBusWrapper::MessagePtr; + static bool MoveOutedCalled = false; + void TestEnableSC(bool b) { static bool firstTime = true; if (b && firstTime) { @@ -71,6 +73,11 @@ namespace Dali { [wr](const MessagePtr &m) -> MessagePtr { return wr->newReplyMessage(m); }; + wr->testMethods[std::tuple{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "MoveOuted", MethodType::Method}] = + [wr](const MessagePtr &m) -> MessagePtr { + MoveOutedCalled = true; + return wr->newReplyMessage(m); + }; } auto wr = static_cast(DBusWrapper::Installed()); wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", b); @@ -267,6 +274,15 @@ namespace Dali { return std::move(std::get<0>(chs)); } + void TestResetMoveOutedCalled () + { + MoveOutedCalled = false; + } + + bool TestGetMoveOutedCalled () + { + return MoveOutedCalled; + } void printTree(const Address &root, size_t depth) { diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h index 8c3d201..4966ff2 100644 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h @@ -36,7 +36,8 @@ namespace Dali { bool TestDoAction ( const Address &adr, const std::string& name ); std::string TestGetActionKeyBinding ( const Address &adr, size_t index ); std::string TestGetActionDescription ( const Address &adr, size_t index ); - + void TestResetMoveOutedCalled (); + bool TestGetMoveOutedCalled (); } } diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp index a6b682e..6d47f06 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp @@ -1051,3 +1051,63 @@ int UtcDaliAccessibilityScrollToChildNonScrollable(void) Dali::Accessibility::TestEnableSC( false ); END_TEST; } + +int UtcDaliAccessibilityCheckHighlight(void) +{ + ToolkitTestApplication application; + Dali::Accessibility::TestEnableSC( true ); + Dali::Accessibility::TestResetMoveOutedCalled(); + + // Make precondition two children exist in parent area + PushButton parentButton = PushButton::New(); + parentButton.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + parentButton.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parentButton.SetProperty( Dali::Actor::Property::POSITION, Dali::Vector2(0.0f, 0.0f) ); + parentButton.SetProperty( Dali::Actor::Property::SIZE, Dali::Vector2(100.0f, 200.0f) ); + application.GetScene().Add( parentButton ); + + PushButton buttonA = PushButton::New(); + buttonA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + buttonA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + buttonA.SetProperty( Dali::Actor::Property::POSITION, Dali::Vector2(0.0f, 0.0f) ); + buttonA.SetProperty( Dali::Actor::Property::SIZE, Dali::Vector2(100.0f, 100.0f) ); + parentButton.Add(buttonA); + + PushButton buttonB = PushButton::New(); + buttonB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + buttonB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + buttonB.SetProperty( Dali::Actor::Property::POSITION, Dali::Vector2(0.0f, 100.0f) ); + buttonB.SetProperty( Dali::Actor::Property::SIZE, Dali::Vector2(100.0f, 100.0f) ); + parentButton.Add(buttonB); + Wait(application); + + // Move second child out of parent area + auto* accessible = dynamic_cast(Dali::Accessibility::Accessible::Get(buttonA)); + DALI_TEST_CHECK(accessible); + accessible->GrabHighlight(); + Wait(application); + + buttonA.SetProperty( Dali::Actor::Property::POSITION, Dali::Vector2(0.0f, -200.0f) ); + Wait(application); + // Need one more seding notificaiton to get correct updated position + application.SendNotification(); + DALI_TEST_EQUALS( true, Dali::Accessibility::TestGetMoveOutedCalled(), TEST_LOCATION ); + + // Reset verdict data + Dali::Accessibility::TestResetMoveOutedCalled(); + + // Move second child out of parent area + accessible = dynamic_cast(Dali::Accessibility::Accessible::Get(buttonB)); + DALI_TEST_CHECK(accessible); + accessible->GrabHighlight(); + Wait(application); + + buttonB.SetProperty( Dali::Actor::Property::POSITION, Dali::Vector2(300.0f, 100.0f) ); + Wait(application); + // Need one more seding notificaiton to get correct updated position + application.SendNotification(); + DALI_TEST_EQUALS( true, Dali::Accessibility::TestGetMoveOutedCalled(), TEST_LOCATION ); + + Dali::Accessibility::TestEnableSC( false ); + END_TEST; +} diff --git a/dali-toolkit/devel-api/controls/accessible-impl.cpp b/dali-toolkit/devel-api/controls/accessible-impl.cpp index c42cb8a..774a280 100644 --- a/dali-toolkit/devel-api/controls/accessible-impl.cpp +++ b/dali-toolkit/devel-api/controls/accessible-impl.cpp @@ -328,6 +328,22 @@ void AccessibleImpl::ScrollToSelf() } } +void AccessibleImpl::RegisterPositionPropertyNotification() +{ + auto control = Dali::Toolkit::Control::DownCast(Self()); + Internal::Control& internalControl = Toolkit::Internal::GetImplementation(control); + Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get(internalControl); + controlImpl.RegisterAccessibilityPositionPropertyNotification(); +} + +void AccessibleImpl::UnregisterPositionPropertyNotification() +{ + auto control = Dali::Toolkit::Control::DownCast(Self()); + Internal::Control& internalControl = Toolkit::Internal::GetImplementation(control); + Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get(internalControl); + controlImpl.UnregisterAccessibilityPositionPropertyNotification(); +} + bool AccessibleImpl::GrabHighlight() { Dali::Actor self = Self(); @@ -360,11 +376,15 @@ bool AccessibleImpl::GrabHighlight() SetHighlightActor(highlight); } - highlight.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - highlight.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + highlight.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); highlight.SetProperty(Actor::Property::POSITION_Z, 1.0f); highlight.SetProperty(Actor::Property::POSITION, Vector2(0.0f, 0.0f)); + // Need to set resize policy again, to update SIZE property which is set by + // AccessibleImpl_NUI. The highlight could move from AccessibleImpl_NUI to + // AccessibleImpl. In this case, highlight has incorrect size. + highlight.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); + // Remember the highlight actor, so that when the default is changed with // SetHighlightActor(), the currently displayed highlight can still be cleared. mCurrentHighlightActor = highlight; @@ -372,6 +392,7 @@ bool AccessibleImpl::GrabHighlight() self.Add(highlight); SetCurrentlyHighlightedActor(self); EmitHighlighted(true); + RegisterPositionPropertyNotification(); return true; } @@ -387,6 +408,7 @@ bool AccessibleImpl::ClearHighlight() if(GetCurrentlyHighlightedActor() == self) { + UnregisterPositionPropertyNotification(); self.Remove(mCurrentHighlightActor.GetHandle()); mCurrentHighlightActor = {}; SetCurrentlyHighlightedActor({}); @@ -495,4 +517,14 @@ Dali::Property::Index AccessibleImpl::GetDescriptionPropertyIndex() return Dali::Property::INVALID_INDEX; } +void AccessibleImpl::SetLastPosition(Vector2 position) +{ + mLastPosition = position; +} + +Vector2 AccessibleImpl::GetLastPosition() const +{ + return mLastPosition; +} + } // namespace Dali::Toolkit::DevelControl diff --git a/dali-toolkit/devel-api/controls/accessible-impl.h b/dali-toolkit/devel-api/controls/accessible-impl.h index 37e7ad3..c8c1361 100644 --- a/dali-toolkit/devel-api/controls/accessible-impl.h +++ b/dali-toolkit/devel-api/controls/accessible-impl.h @@ -47,6 +47,7 @@ struct DALI_TOOLKIT_API AccessibleImpl : public virtual Dali::Accessibility::Acc public virtual Dali::Accessibility::Action { protected: + Vector2 mLastPosition{0.0f, 0.0f}; Dali::WeakHandle mSelf; Dali::WeakHandle mCurrentHighlightActor; bool mIsModal = false; @@ -65,6 +66,16 @@ protected: void ScrollToSelf(); + /** + * @brief Register property notification to check highlighted object position + */ + void RegisterPositionPropertyNotification(); + + /** + * @brief Remove property notification added by RegisterPropertyNotification + */ + void UnregisterPositionPropertyNotification(); + public: AccessibleImpl(Dali::Actor self, Dali::Accessibility::Role role, bool modal = false); @@ -228,6 +239,18 @@ public: * @brief Returns the index of the property that represents this actor's description */ virtual Dali::Property::Index GetDescriptionPropertyIndex(); + + /** + * @brief Sets last object position + * @param[in] position Last object position + */ + void SetLastPosition(Vector2 position); + + /** + * @brief Gets last object position + * @return The Last object position + */ + Vector2 GetLastPosition() const; }; } // namespace Dali::Toolkit::DevelControl diff --git a/dali-toolkit/internal/controls/control/control-data-impl.cpp b/dali-toolkit/internal/controls/control/control-data-impl.cpp index db346bd..8fd35e0 100644 --- a/dali-toolkit/internal/controls/control/control-data-impl.cpp +++ b/dali-toolkit/internal/controls/control/control-data-impl.cpp @@ -410,6 +410,35 @@ void SetVisualsOffScene(const RegisteredVisualContainer& container, Actor parent } } +Dali::Rect<> GetShowingGeometry(Dali::Rect<> rect, Dali::Toolkit::DevelControl::AccessibleImpl* accessibleImpl) +{ + Rect<> parentRect; + Vector2 currentPosition; + auto parent = dynamic_cast(accessibleImpl->GetParent()); + + while(parent) + { + parentRect = parent->GetExtents(Dali::Accessibility::CoordinateType::WINDOW); + + currentPosition.x = rect.x; + currentPosition.y = rect.y; + + rect.x = rect.x > parentRect.x ? rect.x : parentRect.x; + rect.y = rect.y > parentRect.y ? rect.y : parentRect.y; + rect.width = currentPosition.x + rect.width < parentRect.x + parentRect.width ? currentPosition.x + rect.width - rect.x : parentRect.x + parentRect.width - rect.x; + rect.height = currentPosition.y + rect.height < parentRect.y + parentRect.height ? currentPosition.y + rect.height - rect.y : parentRect.y + parentRect.height - rect.y; + + if(rect.width < 0 || rect.height < 0) + { + return rect; + } + + parent = dynamic_cast(parent->GetParent()); + } + + return rect; +} + } // unnamed namespace // clang-format off @@ -518,6 +547,70 @@ const Control::Impl& Control::Impl::Get(const Internal::Control& internalControl return *internalControl.mImpl; } +void Control::Impl::CheckHighlightedObjectGeometry(PropertyNotification& propertyNotification) +{ + auto accessibleImpl = dynamic_cast(mAccessibilityObject.get()); + auto lastPosition = accessibleImpl->GetLastPosition(); + auto accessibleRect = accessibleImpl->GetExtents(Dali::Accessibility::CoordinateType::WINDOW); + + if(lastPosition.x == accessibleRect.x && lastPosition.y == accessibleRect.y) + { + return; + } + + auto rect = GetShowingGeometry(accessibleRect, accessibleImpl); + + // MoveOuted is sent already, no need to send it again + if(mAccessibilityMovedOutOfScreenDirection != Dali::Accessibility::MovedOutOfScreenType::NONE) + { + if(rect.width > 0 && rect.height > 0) + { + // flick next does not use MoveOuted - ScrollToSelf makes object show, so reset for sending MoveOuted next + mAccessibilityMovedOutOfScreenDirection = Dali::Accessibility::MovedOutOfScreenType::NONE; + } + return; + } + + if(rect.width < 0) + { + mAccessibilityMovedOutOfScreenDirection = (accessibleRect.x < lastPosition.x) ? Dali::Accessibility::MovedOutOfScreenType::TOP_LEFT : Dali::Accessibility::MovedOutOfScreenType::BOTTOM_RIGHT; + } + + if(rect.height < 0) + { + mAccessibilityMovedOutOfScreenDirection = (accessibleRect.y < lastPosition.y) ? Dali::Accessibility::MovedOutOfScreenType::TOP_LEFT : Dali::Accessibility::MovedOutOfScreenType::BOTTOM_RIGHT; + } + + if(mAccessibilityMovedOutOfScreenDirection != Dali::Accessibility::MovedOutOfScreenType::NONE) + { + mAccessibilityObject.get()->EmitMovedOutOfScreen(mAccessibilityMovedOutOfScreenDirection); + accessibleImpl->SetLastPosition(Vector2(0.0f, 0.0f)); + return; + } + + accessibleImpl->SetLastPosition(Vector2(accessibleRect.x, accessibleRect.y)); +} + +void Control::Impl::RegisterAccessibilityPositionPropertyNotification() +{ + if(mIsAccessibilityPositionPropertyNotificationSet) + { + return; + } + + mAccessibilityMovedOutOfScreenDirection = Dali::Accessibility::MovedOutOfScreenType::NONE; + mAccessibilityPositionNotification = mControlImpl.Self().AddPropertyNotification(Actor::Property::WORLD_POSITION, StepCondition(1.0f, 1.0f)); + mAccessibilityPositionNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED); + mAccessibilityPositionNotification.NotifySignal().Connect(this, &Control::Impl::CheckHighlightedObjectGeometry); + mIsAccessibilityPositionPropertyNotificationSet = true; +} + +void Control::Impl::UnregisterAccessibilityPositionPropertyNotification() +{ + mControlImpl.Self().RemovePropertyNotification(mAccessibilityPositionNotification); + mIsAccessibilityPositionPropertyNotificationSet = false; +} + // Gesture Detection Methods void Control::Impl::PinchDetected(Actor actor, const PinchGesture& pinch) { diff --git a/dali-toolkit/internal/controls/control/control-data-impl.h b/dali-toolkit/internal/controls/control/control-data-impl.h index e278390..4c480a0 100644 --- a/dali-toolkit/internal/controls/control/control-data-impl.h +++ b/dali-toolkit/internal/controls/control/control-data-impl.h @@ -468,6 +468,22 @@ private: */ void OnIdleCallback(); + /** + * @brief Checks highlighted object geometry if it is showing or not + * @param[in] propertyNotification PropertyNotification + */ + void CheckHighlightedObjectGeometry(Dali::PropertyNotification& propertyNotification); + + /** + * @brief Register property notification to check highlighted object position + */ + void RegisterAccessibilityPositionPropertyNotification(); + + /** + * @brief Remove property notification added by RegisterPropertyNotification + */ + void UnregisterAccessibilityPositionPropertyNotification(); + public: Control& mControlImpl; DevelControl::State mState; @@ -562,6 +578,12 @@ public: static const PropertyRegistration PROPERTY_20; static const PropertyRegistration PROPERTY_21; static const PropertyRegistration PROPERTY_22; + +private: + // Accessibility - notification for highlighted object to check if it is showing. + bool mIsAccessibilityPositionPropertyNotificationSet{false}; + Dali::PropertyNotification mAccessibilityPositionNotification; + Dali::Accessibility::MovedOutOfScreenType mAccessibilityMovedOutOfScreenDirection; }; } // namespace Internal