mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Fix text selection
This commit is contained in:
@@ -3,6 +3,7 @@ import UIKit
|
||||
import UIKit.UIGestureRecognizerSubclass
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
|
||||
private func findScrollView(view: UIView?) -> UIScrollView? {
|
||||
if let view = view {
|
||||
@@ -174,7 +175,11 @@ private final class TextSelectionGetureRecognizer: UIGestureRecognizer, UIGestur
|
||||
}
|
||||
|
||||
public final class TextSelectionNodeView: UIView {
|
||||
var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)?
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return self.hitTestImpl?(point, event)
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextSelectionAction {
|
||||
@@ -185,6 +190,7 @@ public enum TextSelectionAction {
|
||||
|
||||
public final class TextSelectionNode: ASDisplayNode {
|
||||
private let theme: TextSelectionTheme
|
||||
private let strings: PresentationStrings
|
||||
private let textNode: TextNode
|
||||
private let updateIsActive: (Bool) -> Void
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
@@ -199,8 +205,9 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
|
||||
public let highlightAreaNode: ASDisplayNode
|
||||
|
||||
public init(theme: TextSelectionTheme, textNode: TextNode, updateIsActive: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) {
|
||||
public init(theme: TextSelectionTheme, strings: PresentationStrings, textNode: TextNode, updateIsActive: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.textNode = textNode
|
||||
self.updateIsActive = updateIsActive
|
||||
self.present = present
|
||||
@@ -233,19 +240,14 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
(self.view as? TextSelectionNodeView)?.hitTestImpl = { [weak self] point, event in
|
||||
return self?.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
let recognizer = TextSelectionGetureRecognizer(target: nil, action: nil)
|
||||
recognizer.knobAtPoint = { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
}
|
||||
if !strongSelf.leftKnob.alpha.isZero, strongSelf.leftKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) {
|
||||
return (.left, strongSelf.leftKnob.frame.offsetBy(dx: 0.0, dy: strongSelf.leftKnob.frame.width / 2.0).center)
|
||||
}
|
||||
if !strongSelf.rightKnob.alpha.isZero, strongSelf.rightKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) {
|
||||
return (.right, strongSelf.rightKnob.frame.offsetBy(dx: 0.0, dy: -strongSelf.rightKnob.frame.width / 2.0).center)
|
||||
}
|
||||
return nil
|
||||
return self?.knobAtPoint(point)
|
||||
}
|
||||
recognizer.moveKnob = { [weak self] knob, point in
|
||||
guard let strongSelf = self, let cachedLayout = strongSelf.textNode.cachedLayout, let _ = cachedLayout.attributedString, let currentRange = strongSelf.currentRange else {
|
||||
@@ -326,6 +328,16 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
self.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
public func updateLayout() {
|
||||
if let currentRange = self.currentRange {
|
||||
let updatedMin = currentRange.0
|
||||
let updatedMax = currentRange.1
|
||||
let updatedRange = NSRange(location: min(updatedMin, updatedMax), length: max(updatedMin, updatedMax) - min(updatedMin, updatedMax))
|
||||
|
||||
self.updateSelection(range: updatedRange, animateIn: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSelection(range: NSRange?, animateIn: Bool) {
|
||||
var rects: [CGRect]?
|
||||
|
||||
@@ -390,6 +402,22 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func knobAtPoint(_ point: CGPoint) -> (Knob, CGPoint)? {
|
||||
if !self.leftKnob.alpha.isZero, self.leftKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) {
|
||||
return (.left, self.leftKnob.frame.offsetBy(dx: 0.0, dy: self.leftKnob.frame.width / 2.0).center)
|
||||
}
|
||||
if !self.rightKnob.alpha.isZero, self.rightKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) {
|
||||
return (.right, self.rightKnob.frame.offsetBy(dx: 0.0, dy: -self.rightKnob.frame.width / 2.0).center)
|
||||
}
|
||||
if !self.leftKnob.alpha.isZero, self.leftKnob.frame.insetBy(dx: -14.0, dy: -14.0).contains(point) {
|
||||
return (.left, self.leftKnob.frame.offsetBy(dx: 0.0, dy: self.leftKnob.frame.width / 2.0).center)
|
||||
}
|
||||
if !self.rightKnob.alpha.isZero, self.rightKnob.frame.insetBy(dx: -14.0, dy: -14.0).contains(point) {
|
||||
return (.right, self.rightKnob.frame.offsetBy(dx: 0.0, dy: -self.rightKnob.frame.width / 2.0).center)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func dismissSelection() {
|
||||
self.currentRange = nil
|
||||
self.updateSelection(range: nil, animateIn: false)
|
||||
@@ -409,15 +437,15 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
let text = (attributedString.string as NSString).substring(with: range)
|
||||
|
||||
var actions: [ContextMenuAction] = []
|
||||
actions.append(ContextMenuAction(content: .text(title: "Copy", accessibilityLabel: "Copy"), action: { [weak self] in
|
||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: { [weak self] in
|
||||
self?.performAction(text, .copy)
|
||||
self?.dismissSelection()
|
||||
}))
|
||||
actions.append(ContextMenuAction(content: .text(title: "Look Up", accessibilityLabel: "Look Up"), action: { [weak self] in
|
||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuLookUp, accessibilityLabel: self.strings.Conversation_ContextMenuLookUp), action: { [weak self] in
|
||||
self?.performAction(text, .lookup)
|
||||
self?.dismissSelection()
|
||||
}))
|
||||
actions.append(ContextMenuAction(content: .text(title: "Share...", accessibilityLabel: "Share"), action: { [weak self] in
|
||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
|
||||
self?.performAction(text, .share)
|
||||
self?.dismissSelection()
|
||||
}))
|
||||
@@ -428,4 +456,14 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
return (strongSelf, completeRect, rootNode, rootNode.bounds)
|
||||
}, bounce: false))
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.knobAtPoint(point) != nil {
|
||||
return self.view
|
||||
}
|
||||
if self.bounds.contains(point) {
|
||||
return self.view
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user