mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Video editor fixes
This commit is contained in:
parent
01a3152ce4
commit
3185ffb47c
@ -120,7 +120,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
private let scrollNode: ASScrollNode
|
||||
|
||||
private let textNode: ImmediateTextNode
|
||||
private var textSelectionNode: TextSelectionNode?
|
||||
private let authorNameNode: ASTextNode
|
||||
private let dateNode: ASTextNode
|
||||
private let backwardButton: HighlightableButtonNode
|
||||
@ -362,23 +361,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
}
|
||||
}
|
||||
self.statusButtonNode.addTarget(self, action: #selector(self.statusPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
let accentColor = presentationData.theme.list.itemAccentColor
|
||||
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: accentColor.withAlphaComponent(0.2), knob: accentColor), strings: presentationData.strings, textNode: self.textNode, updateIsActive: { [weak self] value in
|
||||
// self?.updateIsTextSelectionActive?(value)
|
||||
}, present: { [weak self] c, a in
|
||||
present(c, a)
|
||||
}, rootNode: self, performAction: { [weak self] text, action in
|
||||
// guard let strongSelf = self, let item = strongSelf.item else {
|
||||
// return
|
||||
// }
|
||||
// item.controllerInteraction.performTextSelectionAction(item.message.stableId, text, action)
|
||||
})
|
||||
self.textSelectionNode = textSelectionNode
|
||||
self.scrollNode.addSubnode(textSelectionNode)
|
||||
self.scrollNode.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textNode)
|
||||
textSelectionNode.frame = self.textNode.frame
|
||||
textSelectionNode.highlightAreaNode.frame = self.textNode.frame
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -608,12 +590,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + textOffset), size: textSize)
|
||||
if self.textNode.frame != textFrame {
|
||||
self.textNode.frame = textFrame
|
||||
|
||||
if let textSelectionNode = self.textSelectionNode {
|
||||
textSelectionNode.frame = textFrame
|
||||
textSelectionNode.highlightAreaNode.frame = textFrame
|
||||
textSelectionNode.updateLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private let scrollNodeHeader: ASDisplayNode
|
||||
private let scrollNodeFooter: ASDisplayNode
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
private var textSelectionNode: InstantPageTextSelectionNode?
|
||||
|
||||
private var textSelectionNode: LinkHighlightingNode?
|
||||
private var settingsNode: InstantPageSettingsNode?
|
||||
private var settingsDimNode: ASDisplayNode?
|
||||
|
||||
@ -156,31 +155,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
self?.navigationBar.setLoadProgress(value)
|
||||
}))
|
||||
|
||||
let selectionNode = InstantPageTextSelectionNode(theme: InstantPageTextSelectionTheme(selection: presentationTheme.chat.message.incoming.textSelectionColor, knob: presentationTheme.chat.message.incoming.textSelectionKnobColor), strings: strings, textItemAtLocation: { [weak self] point in
|
||||
if let strongSelf = self {
|
||||
return strongSelf.textItemAtLocation(point)
|
||||
}
|
||||
return nil
|
||||
}, updateIsActive: { active in
|
||||
|
||||
}, present: { [weak self] controller, args in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(controller, args)
|
||||
}
|
||||
}, rootNode: self, performAction: { text, action in
|
||||
// let controller = ContextMenuController(actions: [ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: {
|
||||
// UIPasteboard.general.string = text
|
||||
// }), ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
|
||||
// if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
|
||||
// strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
|
||||
// }
|
||||
// })])
|
||||
})
|
||||
// self.scrollNode.addSubnode(selectionNode)
|
||||
self.textSelectionNode = selectionNode
|
||||
|
||||
self.scrollNode.addSubnode(selectionNode.highlightAreaNode)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -484,7 +458,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.scrollNode.view.contentSize = currentLayout.contentSize
|
||||
self.scrollNodeFooter.frame = CGRect(origin: CGPoint(x: 0.0, y: currentLayout.contentSize.height), size: CGSize(width: containerLayout.size.width, height: 2000.0))
|
||||
self.textSelectionNode?.frame = CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize)
|
||||
}
|
||||
|
||||
func updateVisibleItems(visibleBounds: CGRect, animated: Bool = false) {
|
||||
@ -656,7 +629,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if effectiveContentHeight != self.scrollNode.view.contentSize.height {
|
||||
transition.animateView {
|
||||
self.scrollNode.view.contentSize = CGSize(width: currentLayout.contentSize.width, height: effectiveContentHeight)
|
||||
self.textSelectionNode?.frame = CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize)
|
||||
}
|
||||
let previousFrame = self.scrollNodeFooter.frame
|
||||
self.scrollNodeFooter.frame = CGRect(origin: CGPoint(x: 0.0, y: effectiveContentHeight), size: CGSize(width: previousFrame.width, height: 2000.0))
|
||||
@ -971,12 +943,12 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
])])
|
||||
self.present(actionSheet, nil)
|
||||
} else if let (item, parentOffset) = self.textItemAtLocation(location) {
|
||||
// let textFrame = item.frame
|
||||
// var itemRects = item.lineRects()
|
||||
// for i in 0 ..< itemRects.count {
|
||||
// itemRects[i] = itemRects[i].offsetBy(dx: parentOffset.x + textFrame.minX, dy: parentOffset.y + textFrame.minY).insetBy(dx: -2.0, dy: -2.0)
|
||||
// }
|
||||
// self.updateTextSelectionRects(itemRects, text: item.plainText())
|
||||
let textFrame = item.frame
|
||||
var itemRects = item.lineRects()
|
||||
for i in 0 ..< itemRects.count {
|
||||
itemRects[i] = itemRects[i].offsetBy(dx: parentOffset.x + textFrame.minX, dy: parentOffset.y + textFrame.minY).insetBy(dx: -2.0, dy: -2.0)
|
||||
}
|
||||
self.updateTextSelectionRects(itemRects, text: item.plainText())
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -987,6 +959,51 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateTextSelectionRects(_ rects: [CGRect], text: String?) {
|
||||
if let text = text, !rects.isEmpty {
|
||||
let textSelectionNode: LinkHighlightingNode
|
||||
if let current = self.textSelectionNode {
|
||||
textSelectionNode = current
|
||||
} else {
|
||||
textSelectionNode = LinkHighlightingNode(color: UIColor.lightGray.withAlphaComponent(0.4))
|
||||
textSelectionNode.isUserInteractionEnabled = false
|
||||
self.textSelectionNode = textSelectionNode
|
||||
self.scrollNode.addSubnode(textSelectionNode)
|
||||
}
|
||||
textSelectionNode.frame = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)
|
||||
textSelectionNode.updateRects(rects)
|
||||
|
||||
var coveringRect = rects[0]
|
||||
for i in 1 ..< rects.count {
|
||||
coveringRect = coveringRect.union(rects[i])
|
||||
}
|
||||
|
||||
let controller = ContextMenuController(actions: [ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: {
|
||||
UIPasteboard.general.string = text
|
||||
}), ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
|
||||
if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
|
||||
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
|
||||
}
|
||||
})])
|
||||
controller.dismissed = { [weak self] in
|
||||
self?.updateTextSelectionRects([], text: nil)
|
||||
}
|
||||
self.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (strongSelf.scrollNode, coveringRect.insetBy(dx: -3.0, dy: -3.0), strongSelf, strongSelf.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
textSelectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
} else if let textSelectionNode = self.textSelectionNode {
|
||||
self.textSelectionNode = nil
|
||||
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
|
||||
textSelectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> (InstantPageItem, CGFloat, Bool, [InstantPageDetailsItem])? {
|
||||
for item in items {
|
||||
if let item = item as? InstantPageAnchorItem, item.anchor == anchor {
|
||||
|
@ -3,6 +3,12 @@
|
||||
@class TGPaintSwatch;
|
||||
@class TGPhotoPaintFont;
|
||||
|
||||
typedef enum {
|
||||
TGPhotoPaintTextEntityStyleBorder,
|
||||
TGPhotoPaintTextEntityStyleClassic,
|
||||
TGPhotoPaintTextEntityStyleFrame
|
||||
} TGPhotoPaintTextEntityStyle;
|
||||
|
||||
@interface TGPhotoPaintTextEntity : TGPhotoPaintEntity
|
||||
|
||||
@property (nonatomic, strong) NSString *text;
|
||||
@ -10,10 +16,10 @@
|
||||
@property (nonatomic, strong) TGPaintSwatch *swatch;
|
||||
@property (nonatomic, assign) CGFloat baseFontSize;
|
||||
@property (nonatomic, assign) CGFloat maxWidth;
|
||||
@property (nonatomic, assign) bool stroke;
|
||||
@property (nonatomic, assign) TGPhotoPaintTextEntityStyle style;
|
||||
|
||||
@property (nonatomic, strong) UIImage *renderImage;
|
||||
|
||||
- (instancetype)initWithText:(NSString *)text font:(TGPhotoPaintFont *)font swatch:(TGPaintSwatch *)swatch baseFontSize:(CGFloat)baseFontSize maxWidth:(CGFloat)maxWidth stroke:(bool)stroke;
|
||||
- (instancetype)initWithText:(NSString *)text font:(TGPhotoPaintFont *)font swatch:(TGPaintSwatch *)swatch baseFontSize:(CGFloat)baseFontSize maxWidth:(CGFloat)maxWidth style:(TGPhotoPaintTextEntityStyle)style;
|
||||
|
||||
@end
|
||||
|
@ -40,7 +40,6 @@
|
||||
- (void)cleanup;
|
||||
|
||||
- (void)setImage:(UIImage *)image forCropRect:(CGRect)cropRect cropRotation:(CGFloat)cropRotation cropOrientation:(UIImageOrientation)cropOrientation cropMirrored:(bool)cropMirrored fullSize:(bool)fullSize;
|
||||
- (void)setVideoAsset:(AVAsset *)asset;
|
||||
- (void)setPlayerItem:(AVPlayerItem *)playerItem;
|
||||
- (void)setCIImage:(CIImage *)ciImage;
|
||||
|
||||
|
@ -160,18 +160,6 @@
|
||||
_fullSize = fullSize;
|
||||
}
|
||||
|
||||
- (void)setVideoAsset:(AVAsset *)asset {
|
||||
[_toolComposer invalidate];
|
||||
_currentProcessChain = nil;
|
||||
|
||||
[_currentInput removeAllTargets];
|
||||
PGVideoMovie *movie = [[PGVideoMovie alloc] initWithAsset:asset];
|
||||
movie.shouldRepeat = true;
|
||||
_currentInput = movie;
|
||||
|
||||
_fullSize = true;
|
||||
}
|
||||
|
||||
- (void)setPlayerItem:(AVPlayerItem *)playerItem {
|
||||
[_toolComposer invalidate];
|
||||
_currentProcessChain = nil;
|
||||
|
@ -70,6 +70,12 @@
|
||||
UIImage *_screenImage;
|
||||
UIImage *_thumbnailImage;
|
||||
|
||||
AVPlayerItem *_playerItem;
|
||||
AVPlayer *_player;
|
||||
SMetaDisposable *_playerItemDisposable;
|
||||
id _playerStartedObserver;
|
||||
id _playerReachedEndObserver;
|
||||
|
||||
id<TGMediaEditAdjustments> _initialAdjustments;
|
||||
NSString *_caption;
|
||||
|
||||
@ -395,7 +401,13 @@
|
||||
if ([next isKindOfClass:[UIImage class]]) {
|
||||
[_photoEditor setImage:(UIImage *)next forCropRect:_photoEditor.cropRect cropRotation:_photoEditor.cropRotation cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored fullSize:false];
|
||||
} else if ([next isKindOfClass:[AVAsset class]]) {
|
||||
[_photoEditor setVideoAsset:(AVAsset *)next];
|
||||
_playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next];
|
||||
_player = [AVPlayer playerWithPlayerItem:_playerItem];
|
||||
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
|
||||
_player.muted = true;
|
||||
|
||||
[_photoEditor setPlayerItem:_playerItem];
|
||||
|
||||
TGDispatchOnMainThread(^
|
||||
{
|
||||
[_previewView performTransitionInWithCompletion:^
|
||||
@ -430,6 +442,66 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_setupPlaybackStartedObserver
|
||||
{
|
||||
CMTime startTime = CMTimeMake(10, 100);
|
||||
if (_photoEditor.trimStartValue > DBL_EPSILON)
|
||||
startTime = CMTimeMakeWithSeconds(_photoEditor.trimStartValue + 0.1, NSEC_PER_SEC);
|
||||
|
||||
__weak TGPhotoEditorController *weakSelf = self;
|
||||
_playerStartedObserver = [_player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:startTime]] queue:NULL usingBlock:^
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf->_player removeTimeObserver:strongSelf->_playerStartedObserver];
|
||||
strongSelf->_playerStartedObserver = nil;
|
||||
|
||||
if (CMTimeGetSeconds(strongSelf->_player.currentItem.duration) > 0)
|
||||
[strongSelf _setupPlaybackReachedEndObserver];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_setupPlaybackReachedEndObserver
|
||||
{
|
||||
CMTime endTime = CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100));
|
||||
if (_photoEditor.trimEndValue > DBL_EPSILON && _photoEditor.trimEndValue < CMTimeGetSeconds(_player.currentItem.duration))
|
||||
endTime = CMTimeMakeWithSeconds(_photoEditor.trimEndValue - 0.1, NSEC_PER_SEC);
|
||||
|
||||
CMTime startTime = CMTimeMake(5, 100);
|
||||
if (_photoEditor.trimStartValue > DBL_EPSILON)
|
||||
startTime = CMTimeMakeWithSeconds(_photoEditor.trimStartValue + 0.05, NSEC_PER_SEC);
|
||||
|
||||
__weak TGPhotoEditorController *weakSelf = self;
|
||||
_playerReachedEndObserver = [_player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:endTime]] queue:NULL usingBlock:^
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf->_player seekToTime:startTime];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startVideoPlayback {
|
||||
NSTimeInterval startPosition = 0.0f;
|
||||
if (_photoEditor.trimStartValue > DBL_EPSILON)
|
||||
startPosition = _photoEditor.trimStartValue;
|
||||
|
||||
CMTime targetTime = CMTimeMakeWithSeconds(startPosition, NSEC_PER_SEC);
|
||||
[_player.currentItem seekToTime:targetTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
|
||||
|
||||
[self _setupPlaybackStartedObserver];
|
||||
[_player play];
|
||||
}
|
||||
|
||||
- (void)stopVideoPlayback {
|
||||
if (_playerStartedObserver != nil)
|
||||
[_player removeTimeObserver:_playerStartedObserver];
|
||||
if (_playerReachedEndObserver != nil)
|
||||
[_player removeTimeObserver:_playerReachedEndObserver];
|
||||
[_player pause];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
if (![self inFormSheet] && (self.navigationController != nil || self.dontHideStatusBar))
|
||||
@ -870,6 +942,7 @@
|
||||
strongSelf.finishedTransitionIn();
|
||||
|
||||
strongSelf->_switchingTab = false;
|
||||
[strongSelf startVideoPlayback];
|
||||
};
|
||||
|
||||
controller = paintController;
|
||||
@ -1119,6 +1192,8 @@
|
||||
strongSelf.finishedTransitionIn();
|
||||
|
||||
strongSelf->_switchingTab = false;
|
||||
|
||||
[strongSelf startVideoPlayback];
|
||||
};
|
||||
|
||||
controller = toolsController;
|
||||
|
@ -99,7 +99,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
bool _appeared;
|
||||
|
||||
TGPhotoPaintFont *_selectedTextFont;
|
||||
bool _selectedStroke;
|
||||
TGPhotoPaintTextEntityStyle _selectedTextStyle;
|
||||
|
||||
TGPhotoEntitiesContainerView *_entitiesContainerView;
|
||||
TGPhotoPaintEntityView *_currentEntityView;
|
||||
@ -156,7 +156,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
[[TGPaintNeonBrush alloc] init]
|
||||
];
|
||||
_selectedTextFont = [[TGPhotoPaintFont availableFonts] firstObject];
|
||||
_selectedStroke = true;
|
||||
_selectedTextStyle = TGPhotoPaintTextEntityStyleBorder;
|
||||
|
||||
if (_photoEditor.paintingData.undoManager != nil)
|
||||
_undoManager = [_photoEditor.paintingData.undoManager copy];
|
||||
@ -1106,10 +1106,10 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
TGPaintSwatch *currentSwatch = _portraitSettingsView.swatch;
|
||||
TGPaintSwatch *whiteSwatch = [TGPaintSwatch swatchWithColor:[UIColor whiteColor] colorLocation:1.0f brushWeight:currentSwatch.brushWeight];
|
||||
TGPaintSwatch *blackSwatch = [TGPaintSwatch swatchWithColor:[UIColor blackColor] colorLocation:0.85f brushWeight:currentSwatch.brushWeight];
|
||||
[self setCurrentSwatch:_selectedStroke ? blackSwatch : whiteSwatch sender:nil];
|
||||
[self setCurrentSwatch:_selectedTextStyle == TGPhotoPaintTextEntityStyleBorder ? blackSwatch : whiteSwatch sender:nil];
|
||||
|
||||
CGFloat maxWidth = [self fittedContentSize].width - 26.0f;
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:@"" font:_selectedTextFont swatch:_portraitSettingsView.swatch baseFontSize:[self _textBaseFontSizeForCurrentPainting] maxWidth:maxWidth stroke:_selectedStroke];
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:@"" font:_selectedTextFont swatch:_portraitSettingsView.swatch baseFontSize:[self _textBaseFontSizeForCurrentPainting] maxWidth:maxWidth style:_selectedTextStyle];
|
||||
entity.position = [self startPositionRelativeToEntity:nil];
|
||||
entity.angle = [self startRotation];
|
||||
|
||||
@ -1413,7 +1413,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
|
||||
- (void)presentTextSettingsView
|
||||
{
|
||||
TGPhotoTextSettingsView *view = [[TGPhotoTextSettingsView alloc] initWithFonts:[TGPhotoPaintFont availableFonts] selectedFont:_selectedTextFont selectedStroke:_selectedStroke];
|
||||
TGPhotoTextSettingsView *view = [[TGPhotoTextSettingsView alloc] initWithFonts:[TGPhotoPaintFont availableFonts] selectedFont:_selectedTextFont selectedStyle:_selectedTextStyle];
|
||||
|
||||
__weak TGPhotoPaintController *weakSelf = self;
|
||||
view.fontChanged = ^(TGPhotoPaintFont *font)
|
||||
@ -1429,21 +1429,21 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
|
||||
[strongSelf settingsWrapperPressed];
|
||||
};
|
||||
view.strokeChanged = ^(bool stroke)
|
||||
view.styleChanged = ^(TGPhotoPaintTextEntityStyle style)
|
||||
{
|
||||
__strong TGPhotoPaintController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_selectedStroke = stroke;
|
||||
strongSelf->_selectedTextStyle = style;
|
||||
|
||||
if (stroke && [strongSelf->_portraitSettingsView.swatch.color isEqual:[UIColor whiteColor]])
|
||||
if (style == TGPhotoPaintTextEntityStyleBorder && [strongSelf->_portraitSettingsView.swatch.color isEqual:[UIColor whiteColor]])
|
||||
{
|
||||
TGPaintSwatch *currentSwatch = strongSelf->_portraitSettingsView.swatch;
|
||||
TGPaintSwatch *blackSwatch = [TGPaintSwatch swatchWithColor:[UIColor blackColor] colorLocation:0.85f brushWeight:currentSwatch.brushWeight];
|
||||
[strongSelf setCurrentSwatch:blackSwatch sender:nil];
|
||||
}
|
||||
else if (!stroke && [strongSelf->_portraitSettingsView.swatch.color isEqual:UIColorRGB(0x000000)])
|
||||
else if (style != TGPhotoPaintTextEntityStyleBorder && [strongSelf->_portraitSettingsView.swatch.color isEqual:UIColorRGB(0x000000)])
|
||||
{
|
||||
TGPaintSwatch *currentSwatch = strongSelf->_portraitSettingsView.swatch;
|
||||
TGPaintSwatch *whiteSwatch = [TGPaintSwatch swatchWithColor:[UIColor whiteColor] colorLocation:1.0f brushWeight:currentSwatch.brushWeight];
|
||||
@ -1451,7 +1451,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
}
|
||||
|
||||
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)strongSelf->_currentEntityView;
|
||||
[textView setStroke:stroke];
|
||||
[textView setStyle:style];
|
||||
|
||||
[strongSelf settingsWrapperPressed];
|
||||
};
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
@implementation TGPhotoPaintTextEntity
|
||||
|
||||
- (instancetype)initWithText:(NSString *)text font:(TGPhotoPaintFont *)font swatch:(TGPaintSwatch *)swatch baseFontSize:(CGFloat)baseFontSize maxWidth:(CGFloat)maxWidth stroke:(bool)stroke
|
||||
- (instancetype)initWithText:(NSString *)text font:(TGPhotoPaintFont *)font swatch:(TGPaintSwatch *)swatch baseFontSize:(CGFloat)baseFontSize maxWidth:(CGFloat)maxWidth style:(TGPhotoPaintTextEntityStyle)style
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
@ -15,7 +15,7 @@
|
||||
_swatch = swatch;
|
||||
_baseFontSize = baseFontSize;
|
||||
_maxWidth = maxWidth;
|
||||
_stroke = stroke;
|
||||
_style = style;
|
||||
self.scale = 1.0f;
|
||||
}
|
||||
return self;
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)__unused zone
|
||||
{
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:self.text font:self.font swatch:self.swatch baseFontSize:self.baseFontSize maxWidth:self.maxWidth stroke:self.stroke];
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:self.text font:self.font swatch:self.swatch baseFontSize:self.baseFontSize maxWidth:self.maxWidth style:self.style];
|
||||
|
||||
entity->_uuid = self.uuid;
|
||||
entity.position = self.position;
|
||||
@ -46,7 +46,7 @@
|
||||
return false;
|
||||
|
||||
TGPhotoPaintTextEntity *entity = (TGPhotoPaintTextEntity *)object;
|
||||
return entity.uuid == self.uuid && [entity.text isEqualToString:self.text] && [entity.font isEqual:self.font] && [entity.swatch isEqual:self.swatch] && fabs(entity.baseFontSize - self.baseFontSize) < FLT_EPSILON && fabs(entity.maxWidth - self.maxWidth) < FLT_EPSILON && entity.stroke == self.stroke && CGPointEqualToPoint(entity.position, self.position) && fabs(entity.scale - self.scale) < FLT_EPSILON && fabs(entity.angle - self.angle) < FLT_EPSILON && entity.mirrored == self.mirrored;
|
||||
return entity.uuid == self.uuid && [entity.text isEqualToString:self.text] && [entity.font isEqual:self.font] && [entity.swatch isEqual:self.swatch] && fabs(entity.baseFontSize - self.baseFontSize) < FLT_EPSILON && fabs(entity.maxWidth - self.maxWidth) < FLT_EPSILON && entity.style == self.style && CGPointEqualToPoint(entity.position, self.position) && fabs(entity.scale - self.scale) < FLT_EPSILON && fabs(entity.angle - self.angle) < FLT_EPSILON && entity.mirrored == self.mirrored;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -21,7 +21,7 @@
|
||||
- (instancetype)initWithEntity:(TGPhotoPaintTextEntity *)entity;
|
||||
- (void)setFont:(TGPhotoPaintFont *)font;
|
||||
- (void)setSwatch:(TGPaintSwatch *)swatch;
|
||||
- (void)setStroke:(bool)stroke;
|
||||
- (void)setStyle:(TGPhotoPaintTextEntityStyle)style;
|
||||
|
||||
@property (nonatomic, readonly) bool isEditing;
|
||||
- (void)beginEditing;
|
||||
@ -35,5 +35,7 @@
|
||||
@property (nonatomic, strong) UIColor *strokeColor;
|
||||
@property (nonatomic, assign) CGFloat strokeWidth;
|
||||
@property (nonatomic, assign) CGPoint strokeOffset;
|
||||
@property (nonatomic, strong) UIColor *frameColor;
|
||||
@property (nonatomic, assign) CGFloat frameWidthInset;
|
||||
|
||||
@end
|
||||
|
@ -30,6 +30,14 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
@property (nonatomic, strong) UIColor *strokeColor;
|
||||
@property (nonatomic, assign) CGFloat strokeWidth;
|
||||
@property (nonatomic, assign) CGPoint strokeOffset;
|
||||
@property (nonatomic, assign) UIColor *frameColor;
|
||||
@property (nonatomic, assign) CGFloat frameWidthInset;
|
||||
@property (nonatomic, assign) CGFloat frameCornerRadius;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGPhotoTextStorage : NSTextStorage
|
||||
|
||||
@end
|
||||
|
||||
@ -40,7 +48,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
TGPhotoPaintFont *_font;
|
||||
CGFloat _baseFontSize;
|
||||
CGFloat _maxWidth;
|
||||
bool _stroke;
|
||||
TGPhotoPaintTextEntityStyle _style;
|
||||
|
||||
TGPhotoTextView *_textView;
|
||||
}
|
||||
@ -68,8 +76,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
_textView.selectable = false;
|
||||
_textView.contentInset = UIEdgeInsetsZero;
|
||||
_textView.showsHorizontalScrollIndicator = false;
|
||||
_textView.showsVerticalScrollIndicator = false;
|
||||
_textView.textContainerInset = UIEdgeInsetsZero;
|
||||
_textView.showsVerticalScrollIndicator = false;;
|
||||
_textView.scrollsToTop = false;
|
||||
_textView.scrollEnabled = false;
|
||||
_textView.textContainerInset = UIEdgeInsetsZero;
|
||||
@ -80,9 +87,10 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
_textView.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
_textView.spellCheckingType = UITextSpellCheckingTypeNo;
|
||||
_textView.font = [UIFont boldSystemFontOfSize:_baseFontSize];
|
||||
_textView.frameWidthInset = floor(_baseFontSize * 0.03);
|
||||
|
||||
[self setSwatch:entity.swatch];
|
||||
[self setStroke:entity.stroke];
|
||||
[self setStyle:entity.style];
|
||||
|
||||
[self addSubview:_textView];
|
||||
}
|
||||
@ -96,7 +104,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
|
||||
- (TGPhotoPaintTextEntity *)entity
|
||||
{
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:_textView.text font:_font swatch:_swatch baseFontSize:_baseFontSize maxWidth:_maxWidth stroke:_stroke];
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:_textView.text font:_font swatch:_swatch baseFontSize:_baseFontSize maxWidth:_maxWidth style:_style];
|
||||
entity.uuid = _entityUUID;
|
||||
entity.angle = self.angle;
|
||||
entity.scale = self.scale;
|
||||
@ -109,7 +117,6 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
CGRect rect = self.bounds;
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(rect.size.width, rect.size.height), false, 1.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
[self drawViewHierarchyInRect:rect afterScreenUpdates:false];
|
||||
|
||||
@ -142,6 +149,8 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
_textView.editable = false;
|
||||
_textView.selectable = false;
|
||||
|
||||
_textView.text = [_textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
if (self.finishedEditing != nil)
|
||||
self.finishedEditing(self);
|
||||
}
|
||||
@ -166,25 +175,28 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
{
|
||||
_font = font;
|
||||
_textView.font = [UIFont boldSystemFontOfSize:_baseFontSize];
|
||||
_textView.frameWidthInset = floor(_baseFontSize * 0.03);
|
||||
|
||||
[self sizeToFit];
|
||||
}
|
||||
|
||||
- (void)setStroke:(bool)stroke
|
||||
- (void)setStyle:(TGPhotoPaintTextEntityStyle)style
|
||||
{
|
||||
_stroke = stroke;
|
||||
if (stroke)
|
||||
{
|
||||
_textView.layer.shadowRadius = 0.0f;
|
||||
_textView.layer.shadowOpacity = 0.0f;
|
||||
_textView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
|
||||
_textView.layer.shadowColor = [[UIColor clearColor] CGColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
_textView.layer.shadowColor = [[UIColor blackColor] CGColor];
|
||||
_textView.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
|
||||
_textView.layer.shadowOpacity = 0.4f;
|
||||
_textView.layer.shadowRadius = 4.0f;
|
||||
_style = style;
|
||||
switch (_style) {
|
||||
case TGPhotoPaintTextEntityStyleClassic:
|
||||
_textView.layer.shadowColor = [[UIColor blackColor] CGColor];
|
||||
_textView.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
|
||||
_textView.layer.shadowOpacity = 0.4f;
|
||||
_textView.layer.shadowRadius = 4.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
_textView.layer.shadowRadius = 0.0f;
|
||||
_textView.layer.shadowOpacity = 0.0f;
|
||||
_textView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
|
||||
_textView.layer.shadowColor = [[UIColor clearColor] CGColor];
|
||||
break;
|
||||
}
|
||||
|
||||
[self updateColor];
|
||||
@ -193,15 +205,45 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
|
||||
- (void)updateColor
|
||||
{
|
||||
if (_stroke)
|
||||
{
|
||||
_textView.textColor = [UIColor whiteColor];
|
||||
_textView.strokeColor = _swatch.color;
|
||||
}
|
||||
else
|
||||
{
|
||||
_textView.textColor = _swatch.color;
|
||||
_textView.strokeColor = nil;
|
||||
switch (_style) {
|
||||
case TGPhotoPaintTextEntityStyleClassic:
|
||||
{
|
||||
_textView.textColor = _swatch.color;
|
||||
_textView.strokeColor = nil;
|
||||
_textView.frameColor = nil;
|
||||
}
|
||||
break;
|
||||
|
||||
case TGPhotoPaintTextEntityStyleBorder:
|
||||
{
|
||||
_textView.textColor = [UIColor whiteColor];
|
||||
_textView.strokeColor = _swatch.color;
|
||||
_textView.frameColor = nil;
|
||||
}
|
||||
break;
|
||||
|
||||
case TGPhotoPaintTextEntityStyleFrame:
|
||||
{
|
||||
CGFloat lightness = 0.0f;
|
||||
CGFloat r = 0.0f;
|
||||
CGFloat g = 0.0f;
|
||||
CGFloat b = 0.0f;
|
||||
|
||||
if ([_swatch.color getRed:&r green:&g blue:&b alpha:NULL]) {
|
||||
lightness = 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
} else if ([_swatch.color getWhite:&r alpha:NULL]) {
|
||||
lightness = r;
|
||||
}
|
||||
|
||||
if (lightness > 0.87) {
|
||||
_textView.textColor = [UIColor blackColor];
|
||||
} else {
|
||||
_textView.textColor = [UIColor whiteColor];
|
||||
}
|
||||
_textView.strokeColor = nil;
|
||||
_textView.frameColor = _swatch.color;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,7 +483,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] init];
|
||||
TGPhotoTextStorage *textStorage = [[TGPhotoTextStorage alloc] init];
|
||||
TGPhotoTextLayoutManager *layoutManager = [[TGPhotoTextLayoutManager alloc] init];
|
||||
|
||||
NSTextContainer *container = [[NSTextContainer alloc] initWithSize:CGSizeMake(0.0f, CGFLOAT_MAX)];
|
||||
@ -449,7 +491,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
[layoutManager addTextContainer:container];
|
||||
[textStorage addLayoutManager:layoutManager];
|
||||
|
||||
return [self initWithFrame:frame textContainer:container];;
|
||||
return [self initWithFrame:frame textContainer:container];
|
||||
}
|
||||
|
||||
- (CGRect)caretRectForPosition:(UITextPosition *)position
|
||||
@ -492,10 +534,49 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (UIColor *)frameColor {
|
||||
return ((TGPhotoTextLayoutManager *)self.layoutManager).frameColor;
|
||||
}
|
||||
|
||||
- (void)setFrameColor:(UIColor *)frameColor {
|
||||
[(TGPhotoTextLayoutManager *)self.layoutManager setFrameColor:frameColor];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (CGFloat)frameWidthInset {
|
||||
return ((TGPhotoTextLayoutManager *)self.layoutManager).frameWidthInset;
|
||||
}
|
||||
|
||||
- (void)setFrameWidthInset:(CGFloat)frameWidthInset {
|
||||
[(TGPhotoTextLayoutManager *)self.layoutManager setFrameWidthInset:frameWidthInset];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font {
|
||||
[super setFont:font];
|
||||
|
||||
self.layoutManager.textContainers.firstObject.lineFragmentPadding = floor(font.pointSize * 0.3);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TGPhotoTextLayoutManager
|
||||
{
|
||||
CGFloat _radius;
|
||||
NSInteger _maxIndex;
|
||||
NSArray *_pointArray;
|
||||
UIBezierPath *_path;
|
||||
NSMutableArray *_rectArray;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_radius = 8.0f;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showCGGlyphs:(const CGGlyph *)glyphs positions:(const CGPoint *)positions count:(NSUInteger)glyphCount font:(UIFont *)font matrix:(CGAffineTransform)textMatrix attributes:(NSDictionary<NSString *,id> *)attributes inContext:(CGContextRef)context
|
||||
{
|
||||
@ -520,4 +601,189 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
[super showCGGlyphs:glyphs positions:positions count:glyphCount font:font matrix:textMatrix attributes:attributes inContext:context];
|
||||
}
|
||||
|
||||
- (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {
|
||||
[super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin];
|
||||
|
||||
if (self.frameColor != nil) {
|
||||
NSRange range = [self characterRangeForGlyphRange:glyphsToShow actualGlyphRange:NULL];
|
||||
NSRange glyphRange = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL];
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSaveGState(context);
|
||||
CGContextTranslateCTM(context, origin.x, origin.y);
|
||||
|
||||
CGContextSetBlendMode(context, kCGBlendModeNormal);
|
||||
CGContextSetFillColorWithColor(context, self.frameColor.CGColor);
|
||||
CGContextSetStrokeColorWithColor(context, self.frameColor.CGColor);
|
||||
|
||||
_path = nil;
|
||||
[self.rectArray removeAllObjects];
|
||||
|
||||
[self enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {
|
||||
|
||||
CGRect newRect = CGRectMake(usedRect.origin.x - self.frameWidthInset, usedRect.origin.y, usedRect.size.width + self.frameWidthInset * 2, usedRect.size.height);
|
||||
NSValue *value = [NSValue valueWithCGRect:newRect];
|
||||
[self.rectArray addObject:value];
|
||||
}];
|
||||
|
||||
[self preProccess];
|
||||
|
||||
for (int i = 0; i < self.rectArray.count; i ++) {
|
||||
NSValue *curValue = [self.rectArray objectAtIndex:i];
|
||||
CGRect cur = curValue.CGRectValue;
|
||||
_radius = cur.size.height * 0.18;
|
||||
[self.path appendPath:[UIBezierPath bezierPathWithRoundedRect:cur cornerRadius:_radius]];
|
||||
CGRect last = CGRectNull;
|
||||
if (i > 0) {
|
||||
NSValue *lastValue = [self.rectArray objectAtIndex:i-1];
|
||||
last = lastValue.CGRectValue;
|
||||
CGPoint a = cur.origin;
|
||||
CGPoint b = CGPointMake(CGRectGetMaxX(cur), cur.origin.y);
|
||||
CGPoint c = CGPointMake(last.origin.x, CGRectGetMaxY(last));
|
||||
CGPoint d = CGPointMake(CGRectGetMaxX(last), CGRectGetMaxY(last));
|
||||
|
||||
if (a.x - c.x >= 2 * _radius) {
|
||||
UIBezierPath * addPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(a.x - _radius, a.y + _radius) radius:_radius startAngle:M_PI_2 * 3 endAngle:0 clockwise:YES];
|
||||
|
||||
[addPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(a.x + _radius, a.y + _radius) radius:_radius startAngle:M_PI endAngle:3 * M_PI_2 clockwise:YES]];
|
||||
[addPath addLineToPoint:CGPointMake(a.x - _radius, a.y)];
|
||||
[self.path appendPath:addPath];
|
||||
}
|
||||
if (a.x == c.x) {
|
||||
[self.path moveToPoint:CGPointMake(a.x, a.y - _radius)];
|
||||
[self.path addLineToPoint:CGPointMake(a.x, a.y + _radius)];
|
||||
[self.path addArcWithCenter:CGPointMake(a.x + _radius, a.y + _radius) radius:_radius startAngle:M_PI endAngle:M_PI_2 * 3 clockwise:YES];
|
||||
[self.path addArcWithCenter:CGPointMake(a.x + _radius, a.y - _radius) radius:_radius startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
|
||||
}
|
||||
if (d.x - b.x >= 2 * _radius) {
|
||||
UIBezierPath * addPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(b.x + _radius, b.y + _radius) radius:_radius startAngle:M_PI_2 * 3 endAngle:M_PI clockwise:NO];
|
||||
[addPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(b.x - _radius, b.y + _radius) radius:_radius startAngle:0 endAngle:3 * M_PI_2 clockwise:NO]];
|
||||
[addPath addLineToPoint:CGPointMake(b.x + _radius, b.y)];
|
||||
[self.path appendPath:addPath];
|
||||
}
|
||||
if (d.x == b.x) {
|
||||
[self.path moveToPoint:CGPointMake(b.x, b.y - _radius)];
|
||||
[self.path addLineToPoint:CGPointMake(b.x, b.y + _radius)];
|
||||
[self.path addArcWithCenter:CGPointMake(b.x - _radius, b.y + _radius) radius:_radius startAngle:0 endAngle:M_PI_2 * 3 clockwise:NO];
|
||||
[self.path addArcWithCenter:CGPointMake(b.x - _radius, b.y - _radius) radius:_radius startAngle:M_PI_2 endAngle:0 clockwise:NO];
|
||||
}
|
||||
if (c.x - a.x >= 2 * _radius) {
|
||||
UIBezierPath * addPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(c.x - _radius, c.y - _radius) radius:_radius startAngle:M_PI_2 endAngle:0 clockwise:NO];
|
||||
[addPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(c.x + _radius, c.y - _radius) radius:_radius startAngle:M_PI endAngle:M_PI_2 clockwise:NO]];
|
||||
[addPath addLineToPoint:CGPointMake(c.x - _radius, c.y)];
|
||||
[self.path appendPath:addPath];
|
||||
}
|
||||
if (b.x - d.x >= 2 * _radius) {
|
||||
UIBezierPath * addPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(d.x + _radius, d.y - _radius) radius:_radius startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
|
||||
[addPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(d.x - _radius, d.y - _radius) radius:_radius startAngle:0 endAngle:M_PI_2 clockwise:YES]];
|
||||
[addPath addLineToPoint:CGPointMake(d.x + _radius, d.y)];
|
||||
[self.path appendPath:addPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
[self.path fill];
|
||||
[self.path stroke];
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIBezierPath *)path {
|
||||
if (!_path) {
|
||||
_path = [UIBezierPath bezierPath];
|
||||
}
|
||||
return _path;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)rectArray {
|
||||
if (!_rectArray) {
|
||||
_rectArray = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _rectArray;
|
||||
}
|
||||
|
||||
- (void)preProccess {
|
||||
_maxIndex = 0;
|
||||
if (self.rectArray.count < 2) {
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < self.rectArray.count; i++) {
|
||||
_maxIndex = i;
|
||||
[self processRectIndex:i];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processRectIndex:(int) index {
|
||||
if (self.rectArray.count < 2 || index < 1 || index > _maxIndex) {
|
||||
return;
|
||||
}
|
||||
NSValue *value1 = [self.rectArray objectAtIndex:index - 1];
|
||||
NSValue *value2 = [self.rectArray objectAtIndex:index];
|
||||
CGRect last = value1.CGRectValue;
|
||||
CGRect cur = value2.CGRectValue;
|
||||
_radius = cur.size.height * 0.18;
|
||||
|
||||
BOOL t1 = ((cur.origin.x - last.origin.x < 2 * _radius) && (cur.origin.x > last.origin.x)) || ((CGRectGetMaxX(cur) - CGRectGetMaxX(last) > -2 * _radius) && (CGRectGetMaxX(cur) < CGRectGetMaxX(last)));
|
||||
BOOL t2 = ((last.origin.x - cur.origin.x < 2 * _radius) && (last.origin.x > cur.origin.x)) || ((CGRectGetMaxX(last) - CGRectGetMaxX(cur) > -2 * _radius) && (CGRectGetMaxX(last) < CGRectGetMaxX(cur)));
|
||||
|
||||
if (t2) {
|
||||
CGRect newRect = CGRectMake(cur.origin.x, last.origin.y, cur.size.width, last.size.height);
|
||||
NSValue *newValue = [NSValue valueWithCGRect:newRect];
|
||||
[self.rectArray replaceObjectAtIndex:index - 1 withObject:newValue];
|
||||
[self processRectIndex:index - 1];
|
||||
}
|
||||
if (t1) {
|
||||
CGRect newRect = CGRectMake(last.origin.x, cur.origin.y, last.size.width, cur.size.height);
|
||||
NSValue *newValue = [NSValue valueWithCGRect:newRect];
|
||||
[self.rectArray replaceObjectAtIndex:index withObject:newValue];
|
||||
[self processRectIndex:index + 1];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TGPhotoTextStorage
|
||||
{
|
||||
NSTextStorage *_impl;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_impl = [NSTextStorage new];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)string
|
||||
{
|
||||
return _impl.string;
|
||||
}
|
||||
|
||||
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
|
||||
{
|
||||
return [_impl attributesAtIndex:location effectiveRange:range];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str {
|
||||
[self beginEditing];
|
||||
[_impl replaceCharactersInRange:range withString:str];
|
||||
[self edited:NSTextStorageEditedCharacters range:range changeInLength:(NSInteger)str.length - (NSInteger)range.length];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)setAttributes:(NSDictionary<NSAttributedStringKey,id> *)attrs range:(NSRange)range {
|
||||
[self beginEditing];
|
||||
[_impl setAttributes:attrs range:range];
|
||||
[self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1,15 +1,16 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TGPhotoPaintSettingsView.h"
|
||||
#import "TGPhotoPaintFont.h"
|
||||
#import "TGPhotoPaintTextEntity.h"
|
||||
|
||||
@interface TGPhotoTextSettingsView : UIView <TGPhotoPaintPanelView>
|
||||
|
||||
@property (nonatomic, copy) void (^fontChanged)(TGPhotoPaintFont *font);
|
||||
@property (nonatomic, copy) void (^strokeChanged)(bool stroke);
|
||||
@property (nonatomic, copy) void (^styleChanged)(TGPhotoPaintTextEntityStyle style);
|
||||
|
||||
@property (nonatomic, strong) TGPhotoPaintFont *font;
|
||||
@property (nonatomic, assign) bool stroke;
|
||||
@property (nonatomic, assign) TGPhotoPaintTextEntityStyle style;
|
||||
|
||||
- (instancetype)initWithFonts:(NSArray *)fonts selectedFont:(TGPhotoPaintFont *)font selectedStroke:(bool)selectedStroke;
|
||||
- (instancetype)initWithFonts:(NSArray *)fonts selectedFont:(TGPhotoPaintFont *)font selectedStyle:(TGPhotoPaintTextEntityStyle)selectedStyle;
|
||||
|
||||
@end
|
||||
|
@ -22,8 +22,6 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
NSArray *_fontViews;
|
||||
NSArray *_fontSeparatorViews;
|
||||
UIImageView *_selectedCheckView;
|
||||
|
||||
UIView *_separatorView;
|
||||
}
|
||||
@end
|
||||
|
||||
@ -31,7 +29,7 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
|
||||
@synthesize interfaceOrientation = _interfaceOrientation;
|
||||
|
||||
- (instancetype)initWithFonts:(NSArray *)fonts selectedFont:(TGPhotoPaintFont *)__unused selectedFont selectedStroke:(bool)selectedStroke
|
||||
- (instancetype)initWithFonts:(NSArray *)fonts selectedFont:(TGPhotoPaintFont *)__unused selectedFont selectedStyle:(TGPhotoPaintTextEntityStyle)selectedStyle
|
||||
{
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self)
|
||||
@ -53,10 +51,10 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
outlineButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
outlineButton.titleLabel.font = font;
|
||||
outlineButton.contentEdgeInsets = UIEdgeInsetsMake(0.0f, 44.0f, 0.0f, 0.0f);
|
||||
outlineButton.tag = 0;
|
||||
outlineButton.tag = TGPhotoPaintTextEntityStyleBorder;
|
||||
[outlineButton setTitle:@"" forState:UIControlStateNormal];
|
||||
[outlineButton setTitleColor:[UIColor clearColor]];
|
||||
[outlineButton addTarget:self action:@selector(strokeValueChanged:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[outlineButton addTarget:self action:@selector(styleValueChanged:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:outlineButton];
|
||||
[fontViews addObject:outlineButton];
|
||||
|
||||
@ -75,27 +73,52 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
UIView *separatorView = [[UIView alloc] init];
|
||||
separatorView.backgroundColor = UIColorRGB(0xd6d6da);
|
||||
[self addSubview:separatorView];
|
||||
|
||||
[separatorViews addObject:separatorView];
|
||||
|
||||
TGModernButton *regularButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, TGPhotoTextSettingsViewMargin + TGPhotoTextSettingsItemHeight, 0, 0)];
|
||||
regularButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
regularButton.titleLabel.font = font;
|
||||
regularButton.contentEdgeInsets = UIEdgeInsetsMake(0.0f, 44.0f, 0.0f, 0.0f);
|
||||
regularButton.tag = 1;
|
||||
regularButton.tag = TGPhotoPaintTextEntityStyleClassic;
|
||||
[regularButton setTitle:TGLocalized(@"Paint.Regular") forState:UIControlStateNormal];
|
||||
[regularButton setTitleColor:[UIColor blackColor]];
|
||||
[regularButton addTarget:self action:@selector(strokeValueChanged:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[regularButton addTarget:self action:@selector(styleValueChanged:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:regularButton];
|
||||
[fontViews addObject:regularButton];
|
||||
|
||||
separatorView = [[UIView alloc] init];
|
||||
separatorView.backgroundColor = UIColorRGB(0xd6d6da);
|
||||
[self addSubview:separatorView];
|
||||
[separatorViews addObject:separatorView];
|
||||
|
||||
TGModernButton *frameButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, TGPhotoTextSettingsViewMargin + TGPhotoTextSettingsItemHeight + TGPhotoTextSettingsItemHeight, 0, 0)];
|
||||
frameButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
frameButton.titleLabel.font = font;
|
||||
frameButton.contentEdgeInsets = UIEdgeInsetsMake(0.0f, 44.0f, 0.0f, 0.0f);
|
||||
frameButton.tag = TGPhotoPaintTextEntityStyleFrame;
|
||||
[frameButton setTitle:@"" forState:UIControlStateNormal];
|
||||
[frameButton setTitleColor:[UIColor blackColor]];
|
||||
[frameButton addTarget:self action:@selector(styleValueChanged:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:frameButton];
|
||||
[fontViews addObject:frameButton];
|
||||
|
||||
textView = [[TGPhotoTextView alloc] init];
|
||||
textView.backgroundColor = [UIColor clearColor];
|
||||
textView.textColor = [UIColor whiteColor];
|
||||
textView.frameColor = [UIColor blackColor];
|
||||
textView.font = font;
|
||||
textView.text = @"Framed";
|
||||
[textView sizeToFit];
|
||||
textView.frame = CGRectMake(39.0f, ceil((TGPhotoTextSettingsItemHeight - textView.frame.size.height) / 2.0f) - 1.0f, ceil(textView.frame.size.width), ceil(textView.frame.size.height + 0.5f));
|
||||
[frameButton addSubview:textView];
|
||||
|
||||
_fontViews = fontViews;
|
||||
_fontSeparatorViews = separatorViews;
|
||||
|
||||
_selectedCheckView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PaintCheck")];
|
||||
_selectedCheckView.frame = CGRectMake(15.0f, 16.0f, _selectedCheckView.frame.size.width, _selectedCheckView.frame.size.height);
|
||||
|
||||
[self setStroke:selectedStroke];
|
||||
[self setStyle:selectedStyle];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -108,10 +131,10 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
self.fontChanged(_fonts[sender.tag]);
|
||||
}
|
||||
|
||||
- (void)strokeValueChanged:(TGModernButton *)sender
|
||||
- (void)styleValueChanged:(TGModernButton *)sender
|
||||
{
|
||||
if (self.strokeChanged != nil)
|
||||
self.strokeChanged(1 - sender.tag);
|
||||
if (self.styleChanged != nil)
|
||||
self.styleChanged((TGPhotoPaintTextEntityStyle)sender.tag);
|
||||
}
|
||||
|
||||
- (void)present
|
||||
@ -145,14 +168,14 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
}];
|
||||
}
|
||||
|
||||
- (bool)stroke
|
||||
- (TGPhotoPaintTextEntityStyle)style
|
||||
{
|
||||
return 1 - _selectedCheckView.superview.tag;
|
||||
return (TGPhotoPaintTextEntityStyle)_selectedCheckView.superview.tag;
|
||||
}
|
||||
|
||||
- (void)setStroke:(bool)stroke
|
||||
- (void)setStyle:(TGPhotoPaintTextEntityStyle)style
|
||||
{
|
||||
[_fontViews[1 - stroke] addSubview:_selectedCheckView];
|
||||
[_fontViews[style] addSubview:_selectedCheckView];
|
||||
}
|
||||
|
||||
- (NSString *)font
|
||||
@ -236,8 +259,6 @@ const CGFloat TGPhotoTextSettingsItemHeight = 44.0f;
|
||||
{
|
||||
view.frame = CGRectMake(TGPhotoTextSettingsViewMargin + 44.0f, TGPhotoTextSettingsViewMargin + TGPhotoTextSettingsItemHeight * (index + 1), self.frame.size.width - TGPhotoTextSettingsViewMargin * 2 - 44.0f, thickness);
|
||||
}];
|
||||
|
||||
_separatorView.frame = CGRectMake(TGPhotoTextSettingsViewMargin, TGPhotoTextSettingsViewMargin + TGPhotoTextSettingsItemHeight * _fontViews.count, self.frame.size.width - TGPhotoTextSettingsViewMargin * 2, thickness);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -88,7 +88,7 @@ const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5;
|
||||
} else if ([dict[@"type"] isEqualToString:@"text"]) {
|
||||
UIImage *renderImage = [[UIImage alloc] initWithData:dict[@"data"]];
|
||||
if (renderImage != nil) {
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:nil font:nil swatch:nil baseFontSize:0.0 maxWidth:0.0 stroke:false];
|
||||
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:nil font:nil swatch:nil baseFontSize:0.0 maxWidth:0.0 style:TGPhotoPaintTextEntityStyleClassic];
|
||||
entity.uuid = [dict[@"uuid"] integerValue];
|
||||
entity.position = [dict[@"position"] CGPointValue];
|
||||
entity.scale = [dict[@"scale"] floatValue];
|
||||
|
@ -15,6 +15,7 @@ protocol LegacyPaintEntity {
|
||||
var scale: CGFloat { get }
|
||||
var angle: CGFloat { get }
|
||||
var baseSize: CGSize? { get }
|
||||
var mirrored: Bool { get }
|
||||
|
||||
func image(for time: CMTime, completion: @escaping (CIImage?) -> Void)
|
||||
}
|
||||
@ -36,6 +37,10 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity {
|
||||
return self.entity.baseSize
|
||||
}
|
||||
|
||||
var mirrored: Bool {
|
||||
return self.entity.mirrored
|
||||
}
|
||||
|
||||
let account: Account
|
||||
let file: TelegramMediaFile
|
||||
let entity: TGPhotoPaintStickerEntity
|
||||
@ -183,6 +188,10 @@ private class LegacyPaintTextEntity: LegacyPaintEntity {
|
||||
var baseSize: CGSize? {
|
||||
return nil
|
||||
}
|
||||
|
||||
var mirrored: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
let entity: TGPhotoPaintTextEntity
|
||||
|
||||
@ -239,8 +248,8 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender
|
||||
|
||||
public func entities(for time: CMTime, size: CGSize, completion: (([CIImage]?) -> Void)!) {
|
||||
let entities = self.entities
|
||||
// let originalSize = self.originalSize
|
||||
// let cropRect = self.cropRect
|
||||
let maxSide = max(size.width, size.height)
|
||||
let paintingScale = maxSide / 1920.0
|
||||
|
||||
self.queue.async {
|
||||
if entities.isEmpty {
|
||||
@ -262,9 +271,6 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender
|
||||
}
|
||||
entity.image(for: time, completion: { image in
|
||||
if var image = image {
|
||||
let maxSide = max(size.width, size.height)
|
||||
let paintingScale = maxSide / 1920.0
|
||||
|
||||
var transform = CGAffineTransform(translationX: -image.extent.midX, y: -image.extent.midY)
|
||||
image = image.transformed(by: transform)
|
||||
|
||||
@ -276,6 +282,9 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender
|
||||
transform = CGAffineTransform(translationX: entity.position.x * paintingScale, y: size.height - entity.position.y * paintingScale)
|
||||
transform = transform.rotated(by: CGFloat.pi * 2 - entity.angle)
|
||||
transform = transform.scaledBy(x: scale, y: scale)
|
||||
if entity.mirrored {
|
||||
transform = transform.scaledBy(x: -1.0, y: 1.0)
|
||||
}
|
||||
|
||||
image = image.transformed(by: transform)
|
||||
let _ = images.modify { current in
|
||||
|
Loading…
x
Reference in New Issue
Block a user