From f5239a4fbd3ab7234bf9f0f7d78ad5793853d13c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 26 Jun 2018 11:24:10 -0400 Subject: [PATCH] Compact layout / widow reduction. --- .../Cells/OWSMessageBubbleView.m | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 83d4a6701..2949ffc23 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -855,6 +855,38 @@ NS_ASSUME_NONNULL_BEGIN OWSMessageTextView *bodyTextView = [self configureBodyTextView]; CGSize textSize = CGSizeCeil([bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + + // "Compact" layout to reduce "widows", + // e.g. last lines with only a single word. + // + // After measuring the size of the text, we try to find smaller widths + // in which the text will fit without adding any height, by wrapping + // more text onto the last line. We use a binary search. + if (textSize.width > 0 && textSize.height > 0) { + NSUInteger upperBound = (NSUInteger)textSize.width; + NSUInteger lowerBound = 1; + // The more iterations we perform in our binary search, + // the more accurate the result, but the more expensive + // layout becomes. + const int kMaxIterations = 5; + for (int i = 0; i < kMaxIterations; i++) { + NSUInteger resizeWidth = (upperBound + lowerBound) / 2; + if (resizeWidth >= upperBound || resizeWidth <= lowerBound) { + break; + } + CGSize resizeSize = CGSizeCeil([bodyTextView sizeThatFits:CGSizeMake(resizeWidth, CGFLOAT_MAX)]); + BOOL success + = (resizeSize.width > 0 && resizeSize.width <= resizeWidth && resizeSize.height <= textSize.height); + if (success) { + // Success. + textSize = resizeSize; + upperBound = (NSUInteger)textSize.width; + } else { + // Failure. + lowerBound = resizeWidth; + } + } + } textSize.width = MIN(textSize.width, maxTextWidth); CGSize result = textSize;