mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-08 17:53:38 +00:00
Quote improvements
This commit is contained in:
parent
e48e463d51
commit
9ebff2dd6c
@ -383,7 +383,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
|
|||||||
case .spoiler:
|
case .spoiler:
|
||||||
try container.encode(8 as Int32, forKey: "t")
|
try container.encode(8 as Int32, forKey: "t")
|
||||||
case .quote:
|
case .quote:
|
||||||
try container.encode(0 as Int32, forKey: "t")
|
try container.encode(9 as Int32, forKey: "t")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -244,11 +244,6 @@
|
|||||||
[super scrollRectToVisible:rect animated:false];
|
[super scrollRectToVisible:rect animated:false];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)caretRectForPosition:(UITextPosition *)position {
|
|
||||||
CGRect rect = [super caretRectForPosition:position];
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||||
@ -507,9 +502,6 @@
|
|||||||
{
|
{
|
||||||
AS::MutexLocker l(_textKitLock);
|
AS::MutexLocker l(_textKitLock);
|
||||||
|
|
||||||
textContainerInset.top += 12.0;
|
|
||||||
textContainerInset.bottom += 12.0;
|
|
||||||
|
|
||||||
_textContainerInset = textContainerInset;
|
_textContainerInset = textContainerInset;
|
||||||
_textKitComponents.textView.textContainerInset = textContainerInset;
|
_textKitComponents.textView.textContainerInset = textContainerInset;
|
||||||
_placeholderTextKitComponents.textView.textContainerInset = textContainerInset;
|
_placeholderTextKitComponents.textView.textContainerInset = textContainerInset;
|
||||||
@ -1070,68 +1062,8 @@
|
|||||||
return [_wordKerner layoutManager:layoutManager boundingBoxForControlGlyphAtIndex:glyphIndex forTextContainer:textContainer proposedLineFragment:proposedRect glyphPosition:glyphPosition characterIndex:characterIndex];
|
return [_wordKerner layoutManager:layoutManager boundingBoxForControlGlyphAtIndex:glyphIndex forTextContainer:textContainer proposedLineFragment:proposedRect glyphPosition:glyphPosition characterIndex:characterIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager paragraphSpacingBeforeGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect {
|
|
||||||
int characterIndex = (int)[layoutManager characterIndexForGlyphAtIndex:glyphIndex];
|
|
||||||
if (characterIndex < 0 || characterIndex >= layoutManager.textStorage.length) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *attributes = [layoutManager.textStorage attributesAtIndex:characterIndex effectiveRange:nil];
|
|
||||||
NSObject *blockQuote = attributes[@"Attribute__Blockquote"];
|
|
||||||
if (blockQuote == nil) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (characterIndex != 0) {
|
|
||||||
NSDictionary *previousAttributes = [layoutManager.textStorage attributesAtIndex:characterIndex - 1 effectiveRange:nil];
|
|
||||||
NSObject *previousBlockQuote = previousAttributes[@"Attribute__Blockquote"];
|
|
||||||
if (previousBlockQuote != nil && [blockQuote isEqual:previousBlockQuote]) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 12.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager paragraphSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect {
|
|
||||||
int characterIndex = (int)[layoutManager characterIndexForGlyphAtIndex:glyphIndex];
|
|
||||||
characterIndex--;
|
|
||||||
if (characterIndex < 0) {
|
|
||||||
characterIndex = 0;
|
|
||||||
}
|
|
||||||
if (characterIndex < 0 || characterIndex >= layoutManager.textStorage.length) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *attributes = [layoutManager.textStorage attributesAtIndex:characterIndex effectiveRange:nil];
|
|
||||||
NSObject *blockQuote = attributes[@"Attribute__Blockquote"];
|
|
||||||
if (blockQuote == nil) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (characterIndex + 1 < layoutManager.textStorage.length) {
|
|
||||||
NSDictionary *nextAttributes = [layoutManager.textStorage attributesAtIndex:characterIndex + 1 effectiveRange:nil];
|
|
||||||
NSObject *nextBlockQuote = nextAttributes[@"Attribute__Blockquote"];
|
|
||||||
if (nextBlockQuote != nil && [blockQuote isEqual:nextBlockQuote]) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 12.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldSetLineFragmentRect:(inout CGRect *)lineFragmentRect lineFragmentUsedRect:(inout CGRect *)lineFragmentUsedRect baselineOffset:(inout CGFloat *)baselineOffset inTextContainer:(NSTextContainer *)textContainer forGlyphRange:(NSRange)glyphRange {
|
- (BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldSetLineFragmentRect:(inout CGRect *)lineFragmentRect lineFragmentUsedRect:(inout CGRect *)lineFragmentUsedRect baselineOffset:(inout CGFloat *)baselineOffset inTextContainer:(NSTextContainer *)textContainer forGlyphRange:(NSRange)glyphRange {
|
||||||
/*if (layoutManager.textStorage.length != 0) {
|
CGFloat fontLineHeight;
|
||||||
NSDictionary *attributes = [layoutManager.textStorage attributesAtIndex:0 effectiveRange:nil];
|
|
||||||
NSObject *blockQuote = attributes[@"Attribute__Blockquote"];
|
|
||||||
if (blockQuote != nil) {
|
|
||||||
CGRect rect = *lineFragmentRect;
|
|
||||||
rect.origin.y += 12.0;
|
|
||||||
CGRect usedRect = *lineFragmentUsedRect;
|
|
||||||
usedRect.origin.y += 12.0;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
/*CGFloat fontLineHeight;
|
|
||||||
UIFont *baseFont = _baseFont;
|
UIFont *baseFont = _baseFont;
|
||||||
if (_typingAttributes[NSFontAttributeName] != nil) {
|
if (_typingAttributes[NSFontAttributeName] != nil) {
|
||||||
baseFont = _typingAttributes[NSFontAttributeName];
|
baseFont = _typingAttributes[NSFontAttributeName];
|
||||||
@ -1154,7 +1086,7 @@
|
|||||||
|
|
||||||
*lineFragmentRect = rect;
|
*lineFragmentRect = rect;
|
||||||
*lineFragmentUsedRect = usedRect;
|
*lineFragmentUsedRect = usedRect;
|
||||||
*baselineOffset = *baselineOffset + baselineNudge;*/
|
*baselineOffset = *baselineOffset + baselineNudge;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,33 +8,89 @@ public func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, att
|
|||||||
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
||||||
var addAttribute = true
|
var addAttribute = true
|
||||||
var attributesToRemove: [NSAttributedString.Key] = []
|
var attributesToRemove: [NSAttributedString.Key] = []
|
||||||
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
||||||
for (key, _) in attributes {
|
for (key, _) in attributes {
|
||||||
if key == attribute && range == nsRange {
|
if key == attribute {
|
||||||
addAttribute = false
|
addAttribute = false
|
||||||
attributesToRemove.append(key)
|
attributesToRemove.append(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var selectionRange = state.selectionRange
|
||||||
|
|
||||||
let result = NSMutableAttributedString(attributedString: state.inputText)
|
let result = NSMutableAttributedString(attributedString: state.inputText)
|
||||||
for attribute in attributesToRemove {
|
for attribute in attributesToRemove {
|
||||||
result.removeAttribute(attribute, range: nsRange)
|
|
||||||
}
|
|
||||||
if addAttribute {
|
|
||||||
if attribute == ChatTextInputAttributes.quote {
|
if attribute == ChatTextInputAttributes.quote {
|
||||||
result.addAttribute(attribute, value: ChatTextInputTextQuoteAttribute(), range: nsRange)
|
var removeRange = nsRange
|
||||||
|
|
||||||
|
var selectionIndex = nsRange.upperBound
|
||||||
if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a {
|
if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a {
|
||||||
result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound)
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound)
|
||||||
|
selectionIndex += 1
|
||||||
|
removeRange.length += 1
|
||||||
}
|
}
|
||||||
if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a {
|
if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a {
|
||||||
result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound)
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound)
|
||||||
|
selectionIndex += 1
|
||||||
|
removeRange.location += 1
|
||||||
|
} else if nsRange.lowerBound != 0 {
|
||||||
|
removeRange.location -= 1
|
||||||
|
removeRange.length += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if removeRange.lowerBound > result.length {
|
||||||
|
removeRange = NSRange(location: result.length, length: 0)
|
||||||
|
} else if removeRange.upperBound > result.length {
|
||||||
|
removeRange = NSRange(location: removeRange.lowerBound, length: result.length - removeRange.lowerBound)
|
||||||
|
}
|
||||||
|
result.removeAttribute(attribute, range: removeRange)
|
||||||
|
|
||||||
|
if selectionRange.lowerBound > result.length {
|
||||||
|
selectionRange = result.length ..< result.length
|
||||||
|
} else if selectionRange.upperBound > result.length {
|
||||||
|
selectionRange = selectionRange.lowerBound ..< result.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent merge back
|
||||||
|
result.enumerateAttributes(in: NSRange(location: selectionIndex, length: result.length - selectionIndex), options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
||||||
|
for (key, value) in attributes {
|
||||||
|
if let _ = value as? ChatTextInputTextQuoteAttribute {
|
||||||
|
result.removeAttribute(key, range: range)
|
||||||
|
result.addAttribute(key, value: ChatTextInputTextQuoteAttribute(), range: range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectionRange = selectionIndex ..< selectionIndex
|
||||||
|
} else {
|
||||||
|
result.removeAttribute(attribute, range: nsRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if addAttribute {
|
||||||
|
if attribute == ChatTextInputAttributes.quote {
|
||||||
|
result.addAttribute(attribute, value: ChatTextInputTextQuoteAttribute(), range: nsRange)
|
||||||
|
var selectionIndex = nsRange.upperBound
|
||||||
|
if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a {
|
||||||
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound)
|
||||||
|
selectionIndex += 1
|
||||||
|
}
|
||||||
|
if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a {
|
||||||
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound)
|
||||||
|
selectionIndex += 1
|
||||||
|
}
|
||||||
|
selectionRange = selectionIndex ..< selectionIndex
|
||||||
} else {
|
} else {
|
||||||
result.addAttribute(attribute, value: true as Bool, range: nsRange)
|
result.addAttribute(attribute, value: true as Bool, range: nsRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ChatTextInputState(inputText: result, selectionRange: state.selectionRange)
|
if selectionRange.lowerBound > result.length {
|
||||||
|
selectionRange = result.length ..< result.length
|
||||||
|
} else if selectionRange.upperBound > result.length {
|
||||||
|
selectionRange = selectionRange.lowerBound ..< result.length
|
||||||
|
}
|
||||||
|
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
||||||
} else {
|
} else {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,12 +68,10 @@ public struct TextRangeRectEdge: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class TextNodeBlockQuoteData: NSObject {
|
public final class TextNodeBlockQuoteData: NSObject {
|
||||||
public let id: Int
|
|
||||||
public let title: NSAttributedString?
|
public let title: NSAttributedString?
|
||||||
public let color: UIColor
|
public let color: UIColor
|
||||||
|
|
||||||
public init(id: Int, title: NSAttributedString?, color: UIColor) {
|
public init(title: NSAttributedString?, color: UIColor) {
|
||||||
self.id = id
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.color = color
|
self.color = color
|
||||||
|
|
||||||
@ -85,9 +83,6 @@ public final class TextNodeBlockQuoteData: NSObject {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.id != other.id {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if let lhsTitle = self.title, let rhsTitle = other.title {
|
if let lhsTitle = self.title, let rhsTitle = other.title {
|
||||||
if !lhsTitle.isEqual(to: rhsTitle) {
|
if !lhsTitle.isEqual(to: rhsTitle) {
|
||||||
return false
|
return false
|
||||||
@ -1142,8 +1137,10 @@ open class TextNode: ASDisplayNode {
|
|||||||
var segmentCharacterOffset = 0
|
var segmentCharacterOffset = 0
|
||||||
while true {
|
while true {
|
||||||
var found = false
|
var found = false
|
||||||
attributedString.enumerateAttribute(NSAttributedString.Key("Attribute__Blockquote"), in: NSRange(location: segmentCharacterOffset, length: wholeStringLength - segmentCharacterOffset), using: { value, effectiveRange, _ in
|
attributedString.enumerateAttribute(NSAttributedString.Key("Attribute__Blockquote"), in: NSRange(location: segmentCharacterOffset, length: wholeStringLength - segmentCharacterOffset), using: { value, effectiveRange, stop in
|
||||||
found = true
|
found = true
|
||||||
|
stop.pointee = ObjCBool(true)
|
||||||
|
|
||||||
if segmentCharacterOffset != effectiveRange.location {
|
if segmentCharacterOffset != effectiveRange.location {
|
||||||
stringSegments.append(StringSegment(
|
stringSegments.append(StringSegment(
|
||||||
title: nil,
|
title: nil,
|
||||||
@ -1167,6 +1164,10 @@ open class TextNode: ASDisplayNode {
|
|||||||
tintColor: value.color
|
tintColor: value.color
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
segmentCharacterOffset = effectiveRange.location + effectiveRange.length
|
||||||
|
if segmentCharacterOffset < wholeStringLength && rawWholeString.character(at: segmentCharacterOffset) == 0x0a {
|
||||||
|
segmentCharacterOffset += 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
stringSegments.append(StringSegment(
|
stringSegments.append(StringSegment(
|
||||||
title: nil,
|
title: nil,
|
||||||
@ -1175,9 +1176,8 @@ open class TextNode: ASDisplayNode {
|
|||||||
isBlockQuote: false,
|
isBlockQuote: false,
|
||||||
tintColor: nil
|
tintColor: nil
|
||||||
))
|
))
|
||||||
|
segmentCharacterOffset = effectiveRange.location + effectiveRange.length
|
||||||
}
|
}
|
||||||
|
|
||||||
segmentCharacterOffset = effectiveRange.location + effectiveRange.length
|
|
||||||
})
|
})
|
||||||
if !found {
|
if !found {
|
||||||
if segmentCharacterOffset != wholeStringLength {
|
if segmentCharacterOffset != wholeStringLength {
|
||||||
@ -1992,6 +1992,9 @@ open class TextNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.setFillColor((layout.backgroundColor ?? UIColor.clear).cgColor)
|
context.setFillColor((layout.backgroundColor ?? UIColor.clear).cgColor)
|
||||||
context.fill(bounds)
|
context.fill(bounds)
|
||||||
|
|
||||||
|
context.setBlendMode(.normal)
|
||||||
|
blendMode = .normal
|
||||||
}
|
}
|
||||||
|
|
||||||
let alignment = layout.resolvedAlignment
|
let alignment = layout.resolvedAlignment
|
||||||
@ -2803,6 +2806,7 @@ open class TextView: UIView {
|
|||||||
context.setBlendMode(.copy)
|
context.setBlendMode(.copy)
|
||||||
context.setFillColor((layout.backgroundColor ?? UIColor.clear).cgColor)
|
context.setFillColor((layout.backgroundColor ?? UIColor.clear).cgColor)
|
||||||
context.fill(bounds)
|
context.fill(bounds)
|
||||||
|
context.setBlendMode(.copy)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let textShadowColor = layout.textShadowColor {
|
if let textShadowColor = layout.textShadowColor {
|
||||||
|
|||||||
@ -47,8 +47,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return [super canPerformAction:action withSender:sender];
|
return [super canPerformAction:action withSender:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -110,7 +110,7 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate {
|
|||||||
get {
|
get {
|
||||||
return self.textView.defaultTextContainerInset
|
return self.textView.defaultTextContainerInset
|
||||||
} set(value) {
|
} set(value) {
|
||||||
let targetValue = UIEdgeInsets(top: value.top, left: 0.0, bottom: value.bottom, right: 0.0)
|
let targetValue = UIEdgeInsets(top: value.top, left: value.left, bottom: value.bottom, right: value.right)
|
||||||
if self.textView.defaultTextContainerInset != value {
|
if self.textView.defaultTextContainerInset != value {
|
||||||
self.textView.defaultTextContainerInset = targetValue
|
self.textView.defaultTextContainerInset = targetValue
|
||||||
}
|
}
|
||||||
@ -183,6 +183,8 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class ChatInputTextContainer: NSTextContainer {
|
private final class ChatInputTextContainer: NSTextContainer {
|
||||||
|
var rightInset: CGFloat = 0.0
|
||||||
|
|
||||||
override var isSimpleRectangularTextContainer: Bool {
|
override var isSimpleRectangularTextContainer: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -200,6 +202,7 @@ private final class ChatInputTextContainer: NSTextContainer {
|
|||||||
|
|
||||||
result.origin.x -= 5.0
|
result.origin.x -= 5.0
|
||||||
result.size.width -= 5.0
|
result.size.width -= 5.0
|
||||||
|
result.size.width -= self.rightInset
|
||||||
|
|
||||||
if let textStorage = self.layoutManager?.textStorage {
|
if let textStorage = self.layoutManager?.textStorage {
|
||||||
let string: NSString = textStorage.string as NSString
|
let string: NSString = textStorage.string as NSString
|
||||||
@ -234,6 +237,8 @@ private final class ChatInputTextContainer: NSTextContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.size.width = max(1.0, result.size.width)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,6 +458,19 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele
|
|||||||
public func updateTextContainerInset() {
|
public func updateTextContainerInset() {
|
||||||
var result = self.defaultTextContainerInset
|
var result = self.defaultTextContainerInset
|
||||||
|
|
||||||
|
var horizontalInsetsUpdated = false
|
||||||
|
if self.customTextContainer.rightInset != result.right {
|
||||||
|
horizontalInsetsUpdated = true
|
||||||
|
self.customTextContainer.rightInset = result.right
|
||||||
|
}
|
||||||
|
if self.measurementTextContainer.rightInset != result.right {
|
||||||
|
horizontalInsetsUpdated = true
|
||||||
|
self.measurementTextContainer.rightInset = result.right
|
||||||
|
}
|
||||||
|
|
||||||
|
result.left = 0.0
|
||||||
|
result.right = 0.0
|
||||||
|
|
||||||
if self.customTextStorage.length != 0 {
|
if self.customTextStorage.length != 0 {
|
||||||
let topAttributes = self.customTextStorage.attributes(at: 0, effectiveRange: nil)
|
let topAttributes = self.customTextStorage.attributes(at: 0, effectiveRange: nil)
|
||||||
let bottomAttributes = self.customTextStorage.attributes(at: self.customTextStorage.length - 1, effectiveRange: nil)
|
let bottomAttributes = self.customTextStorage.attributes(at: self.customTextStorage.length - 1, effectiveRange: nil)
|
||||||
@ -468,6 +486,11 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele
|
|||||||
if self.textContainerInset != result {
|
if self.textContainerInset != result {
|
||||||
self.textContainerInset = result
|
self.textContainerInset = result
|
||||||
}
|
}
|
||||||
|
if horizontalInsetsUpdated {
|
||||||
|
self.customLayoutManager.invalidateLayout(forCharacterRange: NSRange(location: 0, length: self.customTextStorage.length), actualCharacterRange: nil)
|
||||||
|
self.customLayoutManager.ensureLayout(for: self.customTextContainer)
|
||||||
|
}
|
||||||
|
|
||||||
self.updateTextElements()
|
self.updateTextElements()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,6 +601,11 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele
|
|||||||
|
|
||||||
override public func caretRect(for position: UITextPosition) -> CGRect {
|
override public func caretRect(for position: UITextPosition) -> CGRect {
|
||||||
var result = super.caretRect(for: position)
|
var result = super.caretRect(for: position)
|
||||||
|
|
||||||
|
if "".isEmpty {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
guard let textStorage = self.customLayoutManager.textStorage else {
|
guard let textStorage = self.customLayoutManager.textStorage else {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3682,8 +3682,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
|
|
||||||
var actions = suggestedActions
|
var actions = suggestedActions
|
||||||
|
|
||||||
if let index = actions.firstIndex(where: { $0.description.contains("identifier = com.apple.menu.replace;") }) {
|
if #available(iOS 16.0, *) {
|
||||||
actions.remove(at: index)
|
if let index = actions.firstIndex(where: { $0.description.contains("identifier = com.apple.menu.replace;") }), let subMenu = actions[index] as? UIMenu {
|
||||||
|
var filteredChildren = subMenu.children
|
||||||
|
if let subIndex = filteredChildren.firstIndex(where: { $0.description.contains("identifier = com.apple.menu.autofill;") }) {
|
||||||
|
filteredChildren.remove(at: subIndex)
|
||||||
|
}
|
||||||
|
actions[index] = UIMenu(title: subMenu.title, subtitle: subMenu.subtitle, image: subMenu.image, identifier: subMenu.identifier, options: subMenu.options, children: filteredChildren)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if editableTextNode.attributedText == nil || editableTextNode.attributedText!.length == 0 || editableTextNode.selectedRange.length == 0 {
|
if editableTextNode.attributedText == nil || editableTextNode.attributedText!.length == 0 || editableTextNode.selectedRange.length == 0 {
|
||||||
@ -3745,11 +3751,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
] as [UIAction])
|
] as [UIAction])
|
||||||
|
|
||||||
let formatMenu = UIMenu(title: self.strings?.TextFormat_Format ?? "Format", image: nil, children: children)
|
let formatMenu = UIMenu(title: self.strings?.TextFormat_Format ?? "Format", image: nil, children: children)
|
||||||
if let index = actions.firstIndex(where: { $0.description.contains("identifier = com.apple.menu.format;") }) {
|
actions.insert(formatMenu, at: 1)
|
||||||
actions[index] = formatMenu
|
|
||||||
} else {
|
|
||||||
actions.insert(formatMenu, at: 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return UIMenu(children: actions)
|
return UIMenu(children: actions)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,6 +133,7 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
|
|||||||
var font: UIFont?
|
var font: UIFont?
|
||||||
var fontSize = fontSize
|
var fontSize = fontSize
|
||||||
if fontAttributes.contains(.blockQuote) {
|
if fontAttributes.contains(.blockQuote) {
|
||||||
|
fontAttributes.remove(.blockQuote)
|
||||||
fontSize = round(fontSize * 0.8235294117647058)
|
fontSize = round(fontSize * 0.8235294117647058)
|
||||||
}
|
}
|
||||||
if fontAttributes == [.bold, .italic, .monospace] {
|
if fontAttributes == [.bold, .italic, .monospace] {
|
||||||
@ -553,108 +554,53 @@ private func quoteRangesEqual(_ lhs: [(NSRange, ChatTextInputTextQuoteAttribute)
|
|||||||
|
|
||||||
private func refreshBlockQuotes(text: NSString, initialAttributedText: NSAttributedString, attributedText: NSMutableAttributedString, fullRange: NSRange) {
|
private func refreshBlockQuotes(text: NSString, initialAttributedText: NSAttributedString, attributedText: NSMutableAttributedString, fullRange: NSRange) {
|
||||||
var quoteRanges: [(NSRange, ChatTextInputTextQuoteAttribute)] = []
|
var quoteRanges: [(NSRange, ChatTextInputTextQuoteAttribute)] = []
|
||||||
initialAttributedText.enumerateAttribute(ChatTextInputAttributes.quote, in: fullRange, options: [], using: { value, range, _ in
|
initialAttributedText.enumerateAttributes(in: fullRange, using: { dict, range, _ in
|
||||||
if let value = value as? ChatTextInputTextQuoteAttribute {
|
if let value = dict[ChatTextInputAttributes.quote] as? ChatTextInputTextQuoteAttribute {
|
||||||
quoteRanges.append((range, value))
|
quoteRanges.append((range, value))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
quoteRanges.sort(by: { $0.0.location < $1.0.location })
|
quoteRanges.sort(by: { $0.0.location < $1.0.location })
|
||||||
let initialQuoteRanges = quoteRanges
|
let initialQuoteRanges = quoteRanges
|
||||||
|
|
||||||
for i in 0 ..< quoteRanges.count {
|
|
||||||
let range = quoteRanges[i].0
|
|
||||||
|
|
||||||
var validLower = range.lowerBound
|
|
||||||
inner1: for i in range.lowerBound ..< range.upperBound {
|
|
||||||
if let c = UnicodeScalar(text.character(at: i)) {
|
|
||||||
if textUrlCharacters.contains(c) {
|
|
||||||
validLower = i
|
|
||||||
break inner1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break inner1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var validUpper = range.upperBound
|
|
||||||
inner2: for i in (validLower ..< range.upperBound).reversed() {
|
|
||||||
if let c = UnicodeScalar(text.character(at: i)) {
|
|
||||||
if textUrlCharacters.contains(c) {
|
|
||||||
validUpper = i + 1
|
|
||||||
break inner2
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break inner2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let minLower = (i == 0) ? fullRange.lowerBound : quoteRanges[i - 1].0.upperBound
|
|
||||||
inner3: for i in (minLower ..< validLower).reversed() {
|
|
||||||
if let c = UnicodeScalar(text.character(at: i)) {
|
|
||||||
if textUrlEdgeCharacters.contains(c) {
|
|
||||||
validLower = i
|
|
||||||
} else {
|
|
||||||
break inner3
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break inner3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let maxUpper = (i == quoteRanges.count - 1) ? fullRange.upperBound : quoteRanges[i + 1].0.lowerBound
|
|
||||||
inner3: for i in validUpper ..< maxUpper {
|
|
||||||
if let c = UnicodeScalar(text.character(at: i)) {
|
|
||||||
if textUrlEdgeCharacters.contains(c) {
|
|
||||||
validUpper = i + 1
|
|
||||||
} else {
|
|
||||||
break inner3
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break inner3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quoteRanges[i] = (NSRange(location: validLower, length: validUpper - validLower), quoteRanges[i].1)
|
|
||||||
}
|
|
||||||
|
|
||||||
quoteRanges = quoteRanges.filter({ $0.0.length > 0 })
|
quoteRanges = quoteRanges.filter({ $0.0.length > 0 })
|
||||||
|
|
||||||
while quoteRanges.count > 1 {
|
for i in 0 ..< quoteRanges.count {
|
||||||
var hadReductions = false
|
var backIndex = quoteRanges[i].0.lowerBound
|
||||||
outer: for i in 0 ..< quoteRanges.count - 1 {
|
innerBack: while backIndex >= 0 {
|
||||||
if quoteRanges[i].1 === quoteRanges[i + 1].1 {
|
let character = text.character(at: backIndex)
|
||||||
var combine = true
|
if character == 0x0a {
|
||||||
inner: for j in quoteRanges[i].0.upperBound ..< quoteRanges[i + 1].0.lowerBound {
|
backIndex += 1
|
||||||
if let c = UnicodeScalar(text.character(at: j)) {
|
break innerBack
|
||||||
if textUrlCharacters.contains(c) {
|
|
||||||
} else {
|
|
||||||
combine = false
|
|
||||||
break inner
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
combine = false
|
|
||||||
break inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if combine {
|
|
||||||
hadReductions = true
|
|
||||||
quoteRanges[i] = (NSRange(location: quoteRanges[i].0.lowerBound, length: quoteRanges[i + 1].0.upperBound - quoteRanges[i].0.lowerBound), quoteRanges[i].1)
|
|
||||||
quoteRanges.remove(at: i + 1)
|
|
||||||
break outer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
backIndex -= 1
|
||||||
}
|
}
|
||||||
if !hadReductions {
|
backIndex = max(backIndex, 0)
|
||||||
break
|
|
||||||
|
if backIndex < quoteRanges[i].0.lowerBound {
|
||||||
|
quoteRanges[i].0 = NSRange(location: backIndex, length: quoteRanges[i].0.upperBound - backIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var forwardIndex = quoteRanges[i].0.upperBound
|
||||||
|
innerForward: while forwardIndex < text.length {
|
||||||
|
let character = text.character(at: forwardIndex)
|
||||||
|
if character == 0x0a {
|
||||||
|
forwardIndex -= 1
|
||||||
|
break innerForward
|
||||||
|
}
|
||||||
|
forwardIndex += 1
|
||||||
|
}
|
||||||
|
forwardIndex = min(forwardIndex, text.length - 1)
|
||||||
|
|
||||||
|
if forwardIndex > quoteRanges[i].0.upperBound - 1 {
|
||||||
|
quoteRanges[i].0 = NSRange(location: quoteRanges[i].0.lowerBound, length: forwardIndex + 1 - quoteRanges[i].0.lowerBound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if quoteRanges.count > 1 {
|
for i in (0 ..< quoteRanges.count).reversed() {
|
||||||
outer: for i in (1 ..< quoteRanges.count).reversed() {
|
inner: for mergeIndex in (i + 1 ..< quoteRanges.count).reversed() {
|
||||||
for j in 0 ..< i {
|
if quoteRanges[mergeIndex].1 === quoteRanges[i].1 || quoteRanges[mergeIndex].0.intersection(quoteRanges[i].0) != nil {
|
||||||
if quoteRanges[j].1 === quoteRanges[i].1 {
|
quoteRanges[i].0 = NSRange(location: quoteRanges[i].0.location, length: quoteRanges[mergeIndex].0.location + quoteRanges[mergeIndex].0.length - quoteRanges[i].0.location)
|
||||||
quoteRanges.remove(at: i)
|
quoteRanges.removeSubrange((i + 1) ..< (mergeIndex + 1))
|
||||||
continue outer
|
break inner
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -777,6 +723,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
|
|||||||
var font: UIFont?
|
var font: UIFont?
|
||||||
var baseFontSize = baseFontSize
|
var baseFontSize = baseFontSize
|
||||||
if fontAttributes.contains(.blockQuote) {
|
if fontAttributes.contains(.blockQuote) {
|
||||||
|
fontAttributes.remove(.blockQuote)
|
||||||
baseFontSize = round(baseFontSize * 0.8235294117647058)
|
baseFontSize = round(baseFontSize * 0.8235294117647058)
|
||||||
}
|
}
|
||||||
if fontAttributes == [.bold, .italic, .monospace] {
|
if fontAttributes == [.bold, .italic, .monospace] {
|
||||||
|
|||||||
@ -67,9 +67,6 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
|
|||||||
}
|
}
|
||||||
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
|
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
|
||||||
|
|
||||||
var nextBlockId = 0
|
|
||||||
|
|
||||||
var rangeOffset: Int = 0
|
|
||||||
for i in 0 ..< entities.count {
|
for i in 0 ..< entities.count {
|
||||||
if skipEntity {
|
if skipEntity {
|
||||||
skipEntity = false
|
skipEntity = false
|
||||||
@ -77,7 +74,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
|
|||||||
}
|
}
|
||||||
let stringLength = string.length
|
let stringLength = string.length
|
||||||
let entity = entities[i]
|
let entity = entities[i]
|
||||||
var range = NSRange(location: entity.range.lowerBound + rangeOffset, length: entity.range.upperBound - entity.range.lowerBound)
|
var range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
|
||||||
if nsString == nil {
|
if nsString == nil {
|
||||||
nsString = text as NSString
|
nsString = text as NSString
|
||||||
}
|
}
|
||||||
@ -223,38 +220,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
|
|||||||
fontAttributes[range] = .blockQuote
|
fontAttributes[range] = .blockQuote
|
||||||
}
|
}
|
||||||
|
|
||||||
let paragraphBreak = "\n"
|
string.addAttribute(NSAttributedString.Key(rawValue: "Attribute__Blockquote"), value: TextNodeBlockQuoteData(title: nil, color: baseQuoteTintColor), range: range)
|
||||||
|
|
||||||
var nsString = string.string as NSString
|
|
||||||
var stringLength = nsString.length
|
|
||||||
|
|
||||||
let paragraphRange: NSRange
|
|
||||||
if range.lowerBound == 0 {
|
|
||||||
paragraphRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
|
||||||
} else if nsString.character(at: range.lowerBound) == 0x0a {
|
|
||||||
paragraphRange = NSRange(location: range.lowerBound + 1, length: range.upperBound - range.lowerBound - 1)
|
|
||||||
} else if nsString.character(at: range.lowerBound - 1) == 0x0a {
|
|
||||||
paragraphRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
|
||||||
} else {
|
|
||||||
string.insert(NSAttributedString(string: paragraphBreak), at: range.lowerBound)
|
|
||||||
paragraphRange = NSRange(location: range.lowerBound + paragraphBreak.count, length: range.upperBound - range.lowerBound)
|
|
||||||
}
|
|
||||||
|
|
||||||
string.addAttribute(NSAttributedString.Key(rawValue: "Attribute__Blockquote"), value: TextNodeBlockQuoteData(id: nextBlockId, title: nil, color: baseQuoteTintColor), range: paragraphRange)
|
|
||||||
nextBlockId += 1
|
|
||||||
|
|
||||||
nsString = string.string as NSString
|
|
||||||
stringLength = nsString.length
|
|
||||||
|
|
||||||
if paragraphRange.upperBound < stringLength {
|
|
||||||
if nsString.character(at: paragraphRange.upperBound) == 0x0a {
|
|
||||||
string.replaceCharacters(in: NSMakeRange(paragraphRange.upperBound, 1), with: "")
|
|
||||||
rangeOffset -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rangeOffset += 0
|
|
||||||
//rangeOffset += paragraphBreak.count
|
|
||||||
case .BankCard:
|
case .BankCard:
|
||||||
string.addAttribute(NSAttributedString.Key.foregroundColor, value: linkColor, range: range)
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: linkColor, range: range)
|
||||||
if underlineLinks && underlineAllLinks {
|
if underlineLinks && underlineAllLinks {
|
||||||
@ -302,6 +268,12 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
|
|||||||
for range in ranges {
|
for range in ranges {
|
||||||
var font: UIFont?
|
var font: UIFont?
|
||||||
|
|
||||||
|
var fontAttributes = fontAttributes
|
||||||
|
var isQuote = false
|
||||||
|
if fontAttributes.contains(.blockQuote) {
|
||||||
|
isQuote = true
|
||||||
|
fontAttributes.remove(.blockQuote)
|
||||||
|
}
|
||||||
if fontAttributes == [.bold, .italic] {
|
if fontAttributes == [.bold, .italic] {
|
||||||
font = boldItalicFont
|
font = boldItalicFont
|
||||||
} else if fontAttributes == [.bold] {
|
} else if fontAttributes == [.bold] {
|
||||||
@ -314,7 +286,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
|
|||||||
font = baseFont
|
font = baseFont
|
||||||
}
|
}
|
||||||
|
|
||||||
if adjustQuoteFontSize, let fontValue = font, fontAttributes.contains(.blockQuote) {
|
if adjustQuoteFontSize, let fontValue = font, isQuote {
|
||||||
font = fontValue.withSize(round(fontValue.pointSize * 0.8235294117647058))
|
font = fontValue.withSize(round(fontValue.pointSize * 0.8235294117647058))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user