mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
supported hover and press actions
commit_hash:dfd0403da40ba5f5d7de502611b918ad8d37d98e
This commit is contained in:
@@ -63,4 +63,8 @@ extension [DivAction] {
|
||||
public func uiActions(context: DivBlockModelingContext) -> [UserInterfaceAction] {
|
||||
compactMap { $0.uiAction(context: context) }
|
||||
}
|
||||
|
||||
func nonEmptyUIActions(context: DivBlockModelingContext) -> NonEmptyArray<UserInterfaceAction>? {
|
||||
NonEmptyArray(uiActions(context: context))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ protocol DivActionsHolder {
|
||||
var actionAnimation: DivAnimation { get }
|
||||
var doubletapActions: [DivAction]? { get }
|
||||
var longtapActions: [DivAction]? { get }
|
||||
var pressStartActions: [DivAction]? { get }
|
||||
var pressEndActions: [DivAction]? { get }
|
||||
var hoverStartActions: [DivAction]? { get }
|
||||
var hoverEndActions: [DivAction]? { get }
|
||||
|
||||
func resolveCaptureFocusOnAction(_ resolver: ExpressionResolver) -> Bool
|
||||
}
|
||||
@@ -31,24 +35,6 @@ extension DivActionsHolder {
|
||||
|
||||
return NonEmptyArray(allActions)
|
||||
}
|
||||
|
||||
fileprivate func makeDoubleTapActions(
|
||||
context: DivBlockModelingContext
|
||||
) -> NonEmptyArray<UserInterfaceAction>? {
|
||||
if let actions = doubletapActions?.uiActions(context: context) {
|
||||
return NonEmptyArray(actions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fileprivate func makeLongTapActions(
|
||||
context: DivBlockModelingContext
|
||||
) -> LongTapActions? {
|
||||
if let actions = longtapActions?.uiActions(context: context) {
|
||||
return NonEmptyArray(actions).map(LongTapActions.actions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Block {
|
||||
@@ -62,9 +48,17 @@ extension Block {
|
||||
}
|
||||
|
||||
let actions = actionsHolder.makeActions(context: context)
|
||||
let doubletapActions = actionsHolder.makeDoubleTapActions(context: context)
|
||||
let longtapActions = actionsHolder.makeLongTapActions(context: context)
|
||||
if actions == nil, doubletapActions == nil, longtapActions == nil {
|
||||
let doubletapActions = actionsHolder.doubletapActions?.nonEmptyUIActions(context: context)
|
||||
let longtapActions = actionsHolder.longtapActions?.nonEmptyUIActions(context: context)
|
||||
.map(LongTapActions.actions)
|
||||
let pressStartActions = actionsHolder.pressStartActions?.nonEmptyUIActions(context: context)
|
||||
let pressEndActions = actionsHolder.pressEndActions?.nonEmptyUIActions(context: context)
|
||||
let hoverStartActions = actionsHolder.hoverStartActions?.nonEmptyUIActions(context: context)
|
||||
let hoverEndActions = actionsHolder.hoverEndActions?.nonEmptyUIActions(context: context)
|
||||
|
||||
if actions == nil, doubletapActions == nil, longtapActions == nil,
|
||||
pressStartActions == nil, pressEndActions == nil,
|
||||
hoverStartActions == nil, hoverEndActions == nil {
|
||||
return self
|
||||
}
|
||||
|
||||
@@ -75,6 +69,10 @@ extension Block {
|
||||
.resolveActionAnimation(context.expressionResolver),
|
||||
doubleTapActions: doubletapActions,
|
||||
longTapActions: longtapActions,
|
||||
pressStartActions: pressStartActions,
|
||||
pressEndActions: pressEndActions,
|
||||
hoverStartActions: hoverStartActions,
|
||||
hoverEndActions: hoverEndActions,
|
||||
path: context.path,
|
||||
captureFocusOnAction: actionsHolder.resolveCaptureFocusOnAction(context.expressionResolver)
|
||||
)
|
||||
|
||||
@@ -22,6 +22,10 @@ extension Block {
|
||||
actionAnimation: ActionAnimation? = nil,
|
||||
doubleTapActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
longTapActions: LongTapActions? = nil,
|
||||
pressStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
pressEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
analyticsURL: URL? = nil,
|
||||
visibilityParams: VisibilityParams? = nil,
|
||||
tooltips: [BlockTooltip]? = nil,
|
||||
@@ -70,6 +74,10 @@ extension Block {
|
||||
actionAnimation: actionAnimation ?? block.actionAnimation,
|
||||
doubleTapActions: doubleTapActions ?? block.doubleTapActions,
|
||||
longTapActions: longTapActions ?? block.longTapActions,
|
||||
pressStartActions: pressStartActions ?? block.pressStartActions,
|
||||
pressEndActions: pressEndActions ?? block.pressEndActions,
|
||||
hoverStartActions: hoverStartActions ?? block.hoverStartActions,
|
||||
hoverEndActions: hoverEndActions ?? block.hoverEndActions,
|
||||
analyticsURL: (analyticsURL ?? block.analyticsURL) as URL?,
|
||||
boundary: boundary ?? block.boundary,
|
||||
border: (border ?? block.border) as BlockBorder?,
|
||||
@@ -99,6 +107,10 @@ extension Block {
|
||||
actionAnimation: actionAnimation,
|
||||
doubleTapActions: doubleTapActions,
|
||||
longTapActions: longTapActions,
|
||||
pressStartActions: pressStartActions,
|
||||
pressEndActions: pressEndActions,
|
||||
hoverStartActions: hoverStartActions,
|
||||
hoverEndActions: hoverEndActions,
|
||||
analyticsURL: analyticsURL,
|
||||
boundary: boundary ?? DecoratingBlock.defaultBoundary,
|
||||
border: border,
|
||||
@@ -134,6 +146,10 @@ extension Block {
|
||||
actionAnimation: ActionAnimation? = nil,
|
||||
doubleTapActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
longTapActions: LongTapActions? = nil,
|
||||
pressStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
pressEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
analyticsURL: URL? = nil,
|
||||
shadow: BlockShadow? = nil,
|
||||
visibilityParams: VisibilityParams? = nil,
|
||||
@@ -157,6 +173,10 @@ extension Block {
|
||||
actionAnimation: actionAnimation,
|
||||
doubleTapActions: doubleTapActions,
|
||||
longTapActions: longTapActions,
|
||||
pressStartActions: pressStartActions,
|
||||
pressEndActions: pressEndActions,
|
||||
hoverStartActions: hoverStartActions,
|
||||
hoverEndActions: hoverEndActions,
|
||||
analyticsURL: analyticsURL,
|
||||
visibilityParams: visibilityParams,
|
||||
tooltips: tooltips,
|
||||
|
||||
@@ -16,6 +16,10 @@ final class DecoratingBlock: WrapperBlock {
|
||||
let actionAnimation: ActionAnimation?
|
||||
let doubleTapActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let longTapActions: LongTapActions?
|
||||
let pressStartActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let pressEndActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let hoverStartActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let hoverEndActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let analyticsURL: URL?
|
||||
let boundary: BoundaryTrait
|
||||
let border: BlockBorder?
|
||||
@@ -39,6 +43,10 @@ final class DecoratingBlock: WrapperBlock {
|
||||
actionAnimation: ActionAnimation? = nil,
|
||||
doubleTapActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
longTapActions: LongTapActions? = nil,
|
||||
pressStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
pressEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
analyticsURL: URL? = nil,
|
||||
boundary: BoundaryTrait = DecoratingBlock.defaultBoundary,
|
||||
border: BlockBorder? = nil,
|
||||
@@ -61,6 +69,10 @@ final class DecoratingBlock: WrapperBlock {
|
||||
self.actionAnimation = actionAnimation
|
||||
self.doubleTapActions = doubleTapActions
|
||||
self.longTapActions = longTapActions
|
||||
self.pressStartActions = pressStartActions
|
||||
self.pressEndActions = pressEndActions
|
||||
self.hoverStartActions = hoverStartActions
|
||||
self.hoverEndActions = hoverEndActions
|
||||
self.analyticsURL = analyticsURL
|
||||
self.boundary = boundary
|
||||
self.border = border
|
||||
@@ -118,6 +130,10 @@ final class DecoratingBlock: WrapperBlock {
|
||||
&& actions == other.actions
|
||||
&& longTapActions == other.longTapActions
|
||||
&& doubleTapActions == other.doubleTapActions
|
||||
&& pressStartActions == other.pressStartActions
|
||||
&& pressEndActions == other.pressEndActions
|
||||
&& hoverStartActions == other.hoverStartActions
|
||||
&& hoverEndActions == other.hoverEndActions
|
||||
&& actionAnimation == other.actionAnimation
|
||||
&& analyticsURL == other.analyticsURL
|
||||
&& boundary == other.boundary
|
||||
@@ -154,6 +170,10 @@ extension DecoratingBlock {
|
||||
actionAnimation: ActionAnimation? = nil,
|
||||
doubleTapActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
longTapActions: LongTapActions? = nil,
|
||||
pressStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
pressEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverStartActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
hoverEndActions: NonEmptyArray<UserInterfaceAction>? = nil,
|
||||
analyticsURL: URL?? = nil,
|
||||
boundary: BoundaryTrait? = nil,
|
||||
border: BlockBorder?? = nil,
|
||||
@@ -177,6 +197,10 @@ extension DecoratingBlock {
|
||||
actionAnimation: actionAnimation ?? self.actionAnimation,
|
||||
doubleTapActions: doubleTapActions ?? self.doubleTapActions,
|
||||
longTapActions: longTapActions ?? self.longTapActions,
|
||||
pressStartActions: pressStartActions ?? self.pressStartActions,
|
||||
pressEndActions: pressEndActions ?? self.pressEndActions,
|
||||
hoverStartActions: hoverStartActions ?? self.hoverStartActions,
|
||||
hoverEndActions: hoverEndActions ?? self.hoverEndActions,
|
||||
analyticsURL: analyticsURL ?? self.analyticsURL,
|
||||
boundary: boundary ?? self.boundary,
|
||||
border: border ?? self.border,
|
||||
|
||||
+63
-2
@@ -37,6 +37,10 @@ extension DecoratingBlock {
|
||||
actionAnimation: actionAnimation,
|
||||
doubleTapActions: doubleTapActions,
|
||||
longTapActions: longTapActions,
|
||||
pressStartActions: pressStartActions,
|
||||
pressEndActions: pressEndActions,
|
||||
hoverStartActions: hoverStartActions,
|
||||
hoverEndActions: hoverEndActions,
|
||||
analyticsURL: analyticsURL,
|
||||
boundary: boundary,
|
||||
border: border,
|
||||
@@ -113,6 +117,10 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
let actionAnimation: ActionAnimation?
|
||||
let doubleTapActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let longTapActions: LongTapActions?
|
||||
let pressStartActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let pressEndActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let hoverStartActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let hoverEndActions: NonEmptyArray<UserInterfaceAction>?
|
||||
let analyticsURL: URL?
|
||||
let boundary: BoundaryTrait
|
||||
let border: BlockBorder?
|
||||
@@ -143,6 +151,22 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
var shouldHandleDoubleTap: Bool {
|
||||
doubleTapActions != nil
|
||||
}
|
||||
|
||||
var shouldHandlePress: Bool {
|
||||
pressStartActions != nil || pressEndActions != nil
|
||||
}
|
||||
|
||||
var shouldHandleHover: Bool {
|
||||
hoverStartActions != nil || hoverEndActions != nil
|
||||
}
|
||||
|
||||
var shouldHandleAnyAction: Bool {
|
||||
shouldHandleTap ||
|
||||
shouldHandleLongTap ||
|
||||
shouldHandleDoubleTap ||
|
||||
shouldHandlePress ||
|
||||
shouldHandleHover
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var model: Model!
|
||||
@@ -183,6 +207,13 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
}
|
||||
}
|
||||
|
||||
private var hoverRecognizer: UIHoverGestureRecognizer? {
|
||||
didSet {
|
||||
oldValue.flatMap(removeGestureRecognizer(_:))
|
||||
hoverRecognizer.flatMap(addGestureRecognizer(_:))
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
guard oldValue != isHighlighted else {
|
||||
@@ -239,10 +270,11 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
}
|
||||
|
||||
private func configureRecognizers() {
|
||||
guard model.shouldHandleTap else {
|
||||
guard model.shouldHandleAnyAction else {
|
||||
tapRecognizer?.isEnabled = false
|
||||
doubleTapRecognizer?.isEnabled = false
|
||||
longPressRecognizer?.isEnabled = false
|
||||
hoverRecognizer?.isEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -265,9 +297,17 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
)
|
||||
}
|
||||
|
||||
if model.shouldHandleHover, hoverRecognizer == nil {
|
||||
hoverRecognizer = UIHoverGestureRecognizer(
|
||||
target: self,
|
||||
action: #selector(handleHover)
|
||||
)
|
||||
}
|
||||
|
||||
tapRecognizer?.isEnabled = model.shouldHandleTap
|
||||
doubleTapRecognizer?.isEnabled = model.shouldHandleDoubleTap
|
||||
longPressRecognizer?.isEnabled = model.shouldHandleLongTap
|
||||
hoverRecognizer?.isEnabled = model.shouldHandleHover
|
||||
}
|
||||
|
||||
private func checkTouchableArea() {
|
||||
@@ -315,7 +355,7 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result === self {
|
||||
return model.shouldHandleTap ? self : nil
|
||||
return model.shouldHandleAnyAction ? self : nil
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
@@ -365,6 +405,16 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
updateVoiceOverFocus()
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
model.pressStartActions?.asArray().perform(sendingFrom: self)
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
model.pressEndActions?.asArray().perform(sendingFrom: self)
|
||||
}
|
||||
|
||||
func configure(
|
||||
model: Model,
|
||||
observer: ElementStateObserver?,
|
||||
@@ -549,6 +599,17 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func handleHover(recognizer: UIHoverGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
model.hoverStartActions?.asArray().perform(sendingFrom: self)
|
||||
case .ended, .cancelled, .failed:
|
||||
model.hoverEndActions?.asArray().perform(sendingFrom: self)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
if model?.tooltips.isEmpty == false {
|
||||
renderingDelegate?.tooltipAnchorViewRemoved(anchorView: self)
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"$description": "translations.json#/div_actionable_press_start_actions",
|
||||
"platforms": [
|
||||
"web",
|
||||
"android"
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"press_end_actions": {
|
||||
@@ -54,7 +55,8 @@
|
||||
"$description": "translations.json#/div_actionable_press_end_actions",
|
||||
"platforms": [
|
||||
"web",
|
||||
"android"
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"hover_start_actions": {
|
||||
@@ -65,7 +67,8 @@
|
||||
"$description": "translations.json#/div_actionable_hover_start_actions",
|
||||
"platforms": [
|
||||
"web",
|
||||
"android"
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"hover_end_actions": {
|
||||
@@ -76,7 +79,8 @@
|
||||
"$description": "translations.json#/div_actionable_hover_end_actions",
|
||||
"platforms": [
|
||||
"web",
|
||||
"android"
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"action_animation": {
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
],
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios",
|
||||
"web"
|
||||
],
|
||||
"file": "actions/hover-and-press.json"
|
||||
|
||||
Reference in New Issue
Block a user