Fix handling of max content widths in the message cell layout.
This commit is contained in:
parent
b64503cce2
commit
17befbeeee
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -515,6 +515,12 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if let contentWidth = cellMeasurement.value(key: contentWidthKey) {
|
||||
componentView.layoutConstraints.append(outerContentView.autoSetDimension(.width, toSize: contentWidth))
|
||||
} else {
|
||||
owsFailDebug("Missing contentWidth.")
|
||||
}
|
||||
|
||||
if let subview = outerAvatarView {
|
||||
subview.setContentHuggingHigh()
|
||||
subview.setCompressionResistanceHigh()
|
||||
@ -694,21 +700,41 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
return CVStackView.measure(config: config, subviewSizes: subviewSizes)
|
||||
}
|
||||
|
||||
private let contentWidthKey = "contentWidthKey"
|
||||
|
||||
public func measure(maxWidth maxWidthChatHistory: CGFloat, measurementBuilder: CVCellMeasurement.Builder) -> CGSize {
|
||||
owsAssertDebug(maxWidthChatHistory > 0)
|
||||
|
||||
let maxWidth = min(maxWidthChatHistory, conversationStyle.maxMessageWidth)
|
||||
let outerStackViewMaxWidth = max(0, maxWidth - cellLayoutMargins.totalWidth)
|
||||
let outerStackViewMaxWidth = max(0, maxWidthChatHistory - cellLayoutMargins.totalWidth)
|
||||
var cellHStackSize = CGSize.zero
|
||||
var outerStackViewSize: CGSize = .zero
|
||||
|
||||
if hasSenderAvatarLayout {
|
||||
// Sender avatar in groups.
|
||||
outerStackViewSize.width += ConversationStyle.groupMessageAvatarDiameter + ConversationStyle.messageStackSpacing
|
||||
outerStackViewSize.height = max(outerStackViewSize.height, ConversationStyle.groupMessageAvatarDiameter)
|
||||
}
|
||||
if isShowingSelectionUI {
|
||||
outerStackViewSize.width += selectionViewWidth + selectionViewSpacing
|
||||
}
|
||||
if !isIncoming, hasSendFailureBadge {
|
||||
outerStackViewSize.width += sendFailureBadgeSize + sendFailureBadgeSpacing
|
||||
}
|
||||
// The message cell's "outer" stack can contain many views:
|
||||
// sender avatar, selection UI, send failure badge.
|
||||
// The message cell's "content" stack must fit within the
|
||||
// remaining space in the "outer" stack.
|
||||
let contentMaxWidth = max(0,
|
||||
min(conversationStyle.maxMessageWidth,
|
||||
outerStackViewMaxWidth - outerStackViewSize.width))
|
||||
|
||||
func measureStackView(config: CVStackViewConfig,
|
||||
componentKeys keys: [CVComponentKey]) -> CGSize {
|
||||
let contentMaxWidth = max(0, outerStackViewMaxWidth - outerStackViewSize.width)
|
||||
let maxStackWidth = max(0, contentMaxWidth - config.layoutMargins.totalWidth)
|
||||
return self.measureStackView(config: config,
|
||||
measurementBuilder: measurementBuilder,
|
||||
componentKeys: keys,
|
||||
maxWidth: contentMaxWidth)
|
||||
maxWidth: maxStackWidth)
|
||||
}
|
||||
|
||||
let topFullWidthSubcomponents = subcomponents(forKeys: topFullWidthCVComponentKeys)
|
||||
@ -720,6 +746,8 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
func applyContentMeasurement(_ size: CGSize) {
|
||||
outerStackViewSize.width += size.width
|
||||
outerStackViewSize.height = max(outerStackViewSize.height, size.height)
|
||||
|
||||
measurementBuilder.setValue(key: contentWidthKey, value: size.width)
|
||||
}
|
||||
|
||||
if nil != stickerOverlaySubcomponent {
|
||||
@ -772,7 +800,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
componentKeys: bottomNestedCVComponentKeys))
|
||||
}
|
||||
if let bottomButtons = bottomButtons {
|
||||
let subviewSize = bottomButtons.measure(maxWidth: maxWidth,
|
||||
let subviewSize = bottomButtons.measure(maxWidth: contentMaxWidth,
|
||||
measurementBuilder: measurementBuilder)
|
||||
|
||||
// We store the measured size so that we can pin the
|
||||
@ -787,12 +815,6 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
subviewSizes: subviewSizes))
|
||||
}
|
||||
|
||||
if hasSenderAvatarLayout {
|
||||
// Sender avatar in groups.
|
||||
outerStackViewSize.width += ConversationStyle.groupMessageAvatarDiameter + ConversationStyle.messageStackSpacing
|
||||
outerStackViewSize.height = max(outerStackViewSize.height, ConversationStyle.groupMessageAvatarDiameter)
|
||||
}
|
||||
|
||||
cellHStackSize.width += outerStackViewSize.width
|
||||
let minBubbleWidth = kOWSMessageCellCornerRadius_Large * 2
|
||||
cellHStackSize.width = max(cellHStackSize.width, minBubbleWidth)
|
||||
@ -1140,7 +1162,6 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
// MARK: -
|
||||
|
||||
override required init() {
|
||||
|
||||
avatarView.autoSetDimensions(to: CGSize(square: ConversationStyle.groupMessageAvatarDiameter))
|
||||
|
||||
bubbleView.layoutMargins = .zero
|
||||
|
||||
@ -71,6 +71,7 @@ public class CVMediaAlbumView: UIStackView {
|
||||
maxMessageWidth: CGFloat) {
|
||||
|
||||
if let measuredSize = cellMeasurement.size(key: Self.measurementKey) {
|
||||
Logger.verbose("---- measuredSize.1: \(measuredSize), aspect: \(measuredSize.aspectRatio)")
|
||||
layoutConstraints.append(self.autoSetDimension(.height, toSize: measuredSize.height))
|
||||
} else {
|
||||
owsFailDebug("Missing measuredSize.")
|
||||
@ -78,12 +79,25 @@ public class CVMediaAlbumView: UIStackView {
|
||||
|
||||
for (index, itemView) in itemViews.enumerated() {
|
||||
if let measuredSize = cellMeasurement.size(key: Self.measurementKey(imageIndex: index)) {
|
||||
layoutConstraints.append(contentsOf: itemView.autoSetDimensions(to: measuredSize))
|
||||
Logger.verbose("---- measuredSize.2: \(measuredSize), aspect: \(measuredSize.aspectRatio)")
|
||||
itemView.logFrameLater(withLabel: "---- itemView")
|
||||
// The item heights should always exactly match the layout.
|
||||
layoutConstraints.append(itemView.autoSetDimension(.height, toSize: measuredSize.height))
|
||||
// The media album view's width might be larger than
|
||||
// expected due to other components in the message.
|
||||
//
|
||||
// Therefore item widths might need to adjust and
|
||||
// should not be required.
|
||||
NSLayoutConstraint.autoSetPriority(UILayoutPriority.defaultHigh) {
|
||||
layoutConstraints.append(itemView.autoSetDimension(.width, toSize: measuredSize.width))
|
||||
}
|
||||
} else {
|
||||
owsFailDebug("Missing measuredSize for image.")
|
||||
}
|
||||
}
|
||||
|
||||
logFrameLater(withLabel: "---- media album view")
|
||||
|
||||
switch itemViews.count {
|
||||
case 0:
|
||||
owsFailDebug("No item views.")
|
||||
@ -279,9 +293,12 @@ public class CVMediaAlbumView: UIStackView {
|
||||
|
||||
let mediaSize = mediaAlbumItem.mediaSize
|
||||
guard mediaSize.width > 0 && mediaSize.height > 0 else {
|
||||
owsFailDebug("Invalid mediaSize.")
|
||||
// owsFailDebug("Invalid mediaSize.")
|
||||
return nil
|
||||
}
|
||||
Logger.verbose("---- maxWidth: \(maxWidth)")
|
||||
Logger.verbose("---- mediaSize: \(mediaSize), aspect: \(mediaSize.width / mediaSize.height)")
|
||||
|
||||
// Honor the content aspect ratio for single media.
|
||||
var contentAspectRatio = mediaSize.width / mediaSize.height
|
||||
// Clamp the aspect ratio so that very thin/wide content is presented
|
||||
@ -318,6 +335,9 @@ public class CVMediaAlbumView: UIStackView {
|
||||
}
|
||||
|
||||
let size = buildSingleMediaSize() ?? CGSize(square: maxWidth)
|
||||
|
||||
Logger.verbose("---- size: \(size), aspect: \(size.aspectRatio)")
|
||||
|
||||
measurementBuilder.setSize(key: measurementKey(imageIndex: 0),
|
||||
size: size)
|
||||
measurementBuilder.setSize(key: measurementKey, size: size)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user