diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m index 0b4c94bd2..917c9583f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN const CGFloat kOWSMessageCellCornerRadius_Large = 18; -const CGFloat kOWSMessageCellCornerRadius_Small = 2; +const CGFloat kOWSMessageCellCornerRadius_Small = 4; @interface OWSBubbleView () diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 1959609b1..36d5b851f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -276,7 +276,7 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText:displayableQuotedText conversationStyle:self.conversationStyle isOutgoing:self.isOutgoing - sharesTopBorderWithMessageBubble:!self.shouldShowSenderName]; + sharpCorners:self.sharpCornersForQuotedMessage]; quotedMessageView.delegate = self; self.quotedMessageView = quotedMessageView; @@ -558,7 +558,7 @@ NS_ASSUME_NONNULL_BEGIN return 12.f; } -- (void)configureBubbleRounding +- (UIRectCorner)sharpCorners { UIRectCorner sharpCorners = 0; @@ -570,7 +570,21 @@ NS_ASSUME_NONNULL_BEGIN sharpCorners = sharpCorners | (self.isIncoming ? UIRectCornerBottomLeft : UIRectCornerBottomRight); } - self.bubbleView.sharpCorners = sharpCorners; + return sharpCorners; +} + +- (UIRectCorner)sharpCornersForQuotedMessage +{ + if (self.viewItem.senderName) { + return UIRectCornerAllCorners; + } else { + return self.sharpCorners & UIRectCornerAllCorners; + } +} + +- (void)configureBubbleRounding +{ + self.bubbleView.sharpCorners = self.sharpCorners; } - (void)updateBubbleColor @@ -1213,7 +1227,7 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText:displayableQuotedText conversationStyle:self.conversationStyle isOutgoing:self.isOutgoing - sharesTopBorderWithMessageBubble:NO]; + sharpCorners:self.sharpCornersForQuotedMessage]; CGSize result = [quotedMessageView sizeForMaxWidth:self.conversationStyle.maxMessageWidth]; return [NSValue valueWithCGSize:CGSizeCeil(result)]; } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h index 83fb17150..6f22d4519 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText:(nullable DisplayableText *)displayableQuotedText conversationStyle:(ConversationStyle *)conversationStyle isOutgoing:(BOOL)isOutgoing - sharesTopBorderWithMessageBubble:(BOOL)sharesTopBorderWithMessageBubble; + sharpCorners:(UIRectCorner)sharpCorners; // Factory method for "message compose" views. + (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 304a26534..142656e16 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL isForPreview; @property (nonatomic, readonly) BOOL isOutgoing; -@property (nonatomic, readonly) BOOL sharesTopBorderWithMessageBubble; +@property (nonatomic, readonly) UIRectCorner sharpCorners; @property (nonatomic, readonly) UILabel *quotedAuthorLabel; @property (nonatomic, readonly) UILabel *quotedTextLabel; @@ -40,7 +40,7 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText:(nullable DisplayableText *)displayableQuotedText conversationStyle:(ConversationStyle *)conversationStyle isOutgoing:(BOOL)isOutgoing - sharesTopBorderWithMessageBubble:(BOOL)sharesTopBorderWithMessageBubble + sharpCorners:(UIRectCorner)sharpCorners { OWSAssert(quotedMessage); @@ -49,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN conversationStyle:conversationStyle isForPreview:NO isOutgoing:isOutgoing - sharesTopBorderWithMessageBubble:sharesTopBorderWithMessageBubble]; + sharpCorners:sharpCorners]; } + (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage @@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN conversationStyle:conversationStyle isForPreview:YES isOutgoing:YES - sharesTopBorderWithMessageBubble:NO]; + sharpCorners:UIRectCornerAllCorners]; [instance createContents]; return instance; } @@ -77,7 +77,7 @@ NS_ASSUME_NONNULL_BEGIN conversationStyle:(ConversationStyle *)conversationStyle isForPreview:(BOOL)isForPreview isOutgoing:(BOOL)isOutgoing - sharesTopBorderWithMessageBubble:(BOOL)sharesTopBorderWithMessageBubble + sharpCorners:(UIRectCorner)sharpCorners { self = [super init]; @@ -92,7 +92,7 @@ NS_ASSUME_NONNULL_BEGIN _isForPreview = isForPreview; _conversationStyle = conversationStyle; _isOutgoing = isOutgoing; - _sharesTopBorderWithMessageBubble = sharesTopBorderWithMessageBubble; + _sharpCorners = sharpCorners; _quotedAuthorLabel = [UILabel new]; _quotedTextLabel = [UILabel new]; @@ -151,7 +151,8 @@ NS_ASSUME_NONNULL_BEGIN self.clipsToBounds = YES; CAShapeLayer *maskLayer = [CAShapeLayer new]; - BOOL sharesTopBorderWithMessageBubble = self.sharesTopBorderWithMessageBubble; + UIRectCorner sharpCorners = self.sharpCorners; + OWSLayerView *innerBubbleView = [[OWSLayerView alloc] initWithFrame:CGRectZero layoutCallback:^(UIView *layerView) { @@ -159,26 +160,70 @@ NS_ASSUME_NONNULL_BEGIN UIBezierPath *bezierPath = [UIBezierPath new]; - CGFloat bubbleLeft = 0.f; - CGFloat bubbleRight = layerFrame.size.width; - CGFloat bubbleTop = 0.f; - CGFloat bubbleBottom = layerFrame.size.height; - CGFloat bubbleTopRounding = (sharesTopBorderWithMessageBubble ? 10.f : 4.f); - CGFloat bubbleBottomRounding = 4.f; - - [bezierPath moveToPoint:CGPointMake(bubbleLeft + bubbleTopRounding, bubbleTop)]; - [bezierPath addLineToPoint:CGPointMake(bubbleRight - bubbleTopRounding, bubbleTop)]; - [bezierPath addQuadCurveToPoint:CGPointMake(bubbleRight, bubbleTop + bubbleTopRounding) - controlPoint:CGPointMake(bubbleRight, bubbleTop)]; - [bezierPath addLineToPoint:CGPointMake(bubbleRight, bubbleBottom - bubbleBottomRounding)]; - [bezierPath addQuadCurveToPoint:CGPointMake(bubbleRight - bubbleBottomRounding, bubbleBottom) - controlPoint:CGPointMake(bubbleRight, bubbleBottom)]; - [bezierPath addLineToPoint:CGPointMake(bubbleLeft + bubbleBottomRounding, bubbleBottom)]; - [bezierPath addQuadCurveToPoint:CGPointMake(bubbleLeft, bubbleBottom - bubbleBottomRounding) - controlPoint:CGPointMake(bubbleLeft, bubbleBottom)]; - [bezierPath addLineToPoint:CGPointMake(bubbleLeft, bubbleTop + bubbleTopRounding)]; - [bezierPath addQuadCurveToPoint:CGPointMake(bubbleLeft + bubbleTopRounding, bubbleTop) - controlPoint:CGPointMake(bubbleLeft, bubbleTop)]; + const CGFloat bubbleLeft = 0.f; + const CGFloat bubbleRight = layerFrame.size.width; + const CGFloat bubbleTop = 0.f; + const CGFloat bubbleBottom = layerFrame.size.height; + + const CGFloat sharpCornerRadius = 4; + const CGFloat wideCornerRadius = 10; + + const CGFloat topLeftRounding = (sharpCorners & UIRectCornerTopLeft) ? sharpCornerRadius : wideCornerRadius; + const CGFloat topRightRounding + = (sharpCorners & UIRectCornerTopRight) ? sharpCornerRadius : wideCornerRadius; + + // bottom corners are always sharp + const CGFloat bottomRightRounding = sharpCornerRadius; + const CGFloat bottomLeftRounding = sharpCornerRadius; + + const CGFloat topAngle = 3.0f * M_PI / 2.0f; + const CGFloat rightAngle = 0.0f; + const CGFloat bottomAngle = M_PI / 2.0f; + const CGFloat leftAngle = M_PI; + + // starting just to the right of the top left corner and working clockwise + [bezierPath moveToPoint:CGPointMake(bubbleLeft + topLeftRounding, bubbleTop)]; + + // top line + [bezierPath addLineToPoint:CGPointMake(bubbleRight - topRightRounding, bubbleTop)]; + + // top right corner + [bezierPath addArcWithCenter:CGPointMake(bubbleRight - topRightRounding, bubbleTop + topRightRounding) + radius:topRightRounding + startAngle:topAngle + endAngle:rightAngle + clockwise:true]; + + // right line + [bezierPath addLineToPoint:CGPointMake(bubbleRight, bubbleBottom - bottomRightRounding)]; + + // bottom right corner + [bezierPath + addArcWithCenter:CGPointMake(bubbleRight - bottomRightRounding, bubbleBottom - bottomRightRounding) + radius:bottomRightRounding + startAngle:rightAngle + endAngle:bottomAngle + clockwise:true]; + + // bottom line + [bezierPath addLineToPoint:CGPointMake(bubbleLeft + bottomLeftRounding, bubbleBottom)]; + + // bottom left corner + [bezierPath addArcWithCenter:CGPointMake(bubbleLeft + bottomLeftRounding, bubbleBottom - bottomLeftRounding) + radius:bottomLeftRounding + startAngle:bottomAngle + endAngle:leftAngle + clockwise:true]; + + // left line + [bezierPath addLineToPoint:CGPointMake(bubbleLeft, bubbleTop + topLeftRounding)]; + + // top left corner + [bezierPath addArcWithCenter:CGPointMake(bubbleLeft + topLeftRounding, bubbleTop + topLeftRounding) + radius:topLeftRounding + startAngle:leftAngle + endAngle:topAngle + clockwise:true]; maskLayer.path = bezierPath.CGPath; }];