Skip to content

Commit

Permalink
Column constraints (#295)
Browse files Browse the repository at this point in the history
* Added viewport based column constraints for grid

* Fixed tests
  • Loading branch information
rajdeep authored Mar 10, 2024
1 parent 876712f commit 2cccc09
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Proton/Sources/Swift/Editor/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,10 @@ open class EditorView: UIView {
set { richTextView.selectedRange = newValue }
}

public var lineFragmentPadding: CGFloat {
richTextView.textContainer.lineFragmentPadding
}

/// Typing attributes to be used. Automatically resets when the selection changes.
/// To apply an attribute in the current position such that it is applied as text is typed,
/// the attribute must be added to `typingAttributes` collection.
Expand Down
19 changes: 14 additions & 5 deletions Proton/Sources/Swift/Grid/Core/Grid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class GridRowDimension {
}

protocol GridDelegate: AnyObject {
var viewport: CGRect { get }
func grid(_ grid: Grid, shouldChangeColumnWidth proposedWidth: CGFloat, for columnIndex: Int) -> Bool
}

Expand Down Expand Up @@ -72,6 +73,10 @@ class Grid {
rowHeights.count
}

var viewport: CGRect? {
delegate?.viewport
}

init(config: GridConfiguration, cells: [GridCell], editorInitializer: GridCell.EditorInitializer? = nil) {
self.config = config
self.editorInitializer = editorInitializer
Expand Down Expand Up @@ -99,8 +104,10 @@ class Grid {
return .zero
}

let viewportWidth = viewport?.width ?? size.width

if minColumnSpan > 0 {
x = columnWidths[0..<minColumnSpan].reduce(0.0) { $0 + $1.value(basedOn: size.width)}
x = columnWidths[0..<minColumnSpan].reduce(0.0) { $0 + $1.value(basedOn: size.width, viewportWidth: viewportWidth)}
}

if minRowSpan > 0 {
Expand All @@ -109,7 +116,7 @@ class Grid {

var width: CGFloat = 0
for col in cell.columnSpan {
width += columnWidths[col].value(basedOn: size.width)
width += columnWidths[col].value(basedOn: size.width, viewportWidth: viewportWidth)
}

var height: CGFloat = 0
Expand All @@ -125,7 +132,8 @@ class Grid {
}

func sizeThatFits(size: CGSize) -> CGSize {
let width = columnWidths.reduce(0.0) { $0 + $1.value(basedOn: size.width)} + config.style.borderWidth
let viewportWidth = viewport?.width ?? size.width
let width = columnWidths.reduce(0.0) { $0 + $1.value(basedOn: size.width, viewportWidth: viewportWidth)} + config.style.borderWidth
// Account for additional height equal to borders from inset created when calculating the fames for cells
let height = currentRowHeights.reduce(0.0, +) + config.style.borderWidth
return CGSize(width: width, height: height)
Expand Down Expand Up @@ -161,10 +169,11 @@ class Grid {


func changeColumnWidth(index: Int, totalWidth: CGFloat, delta: CGFloat) {
let proposedWidth = columnWidths[index].value(basedOn: totalWidth) + delta
let viewportWidth = viewport?.width ?? totalWidth
let proposedWidth = columnWidths[index].value(basedOn: totalWidth, viewportWidth: viewportWidth) + delta
guard index < columnWidths.count,
delegate?.grid(self, shouldChangeColumnWidth: proposedWidth, for: index) ?? true else { return }
columnWidths[index].width = .fixed(columnWidths[index].value(basedOn: totalWidth) + delta)
columnWidths[index].width = .fixed(columnWidths[index].value(basedOn: totalWidth, viewportWidth: viewportWidth) + delta)
}

func changeRowHeight(index: Int, delta: CGFloat) {
Expand Down
10 changes: 7 additions & 3 deletions Proton/Sources/Swift/Grid/Core/GridConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,26 @@ class GridColumnDimension {
self.collapsedWidth = collapsedWidth
}

func value(basedOn total: CGFloat) -> CGFloat {
func value(basedOn total: CGFloat, viewportWidth: CGFloat) -> CGFloat {
guard !isCollapsed else { return collapsedWidth }
return width.value(basedOn: total)
return width.value(basedOn: total, viewportWidth: viewportWidth)
}
}

public enum GridColumnWidth {
case fixed(CGFloat)
case fractional(CGFloat)
case viewport(padding: CGFloat)

public func value(basedOn total: CGFloat) -> CGFloat {
public func value(basedOn total: CGFloat, viewportWidth: CGFloat) -> CGFloat {
switch self {
case .fixed(let value):
return value
case .fractional(let value):
return value * total
case .viewport(let padding):
let cellOverlapPixels: CGFloat = 1
return viewportWidth - (padding + cellOverlapPixels)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions Proton/Sources/Swift/Grid/View/GridContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ extension GridContentView: UIGestureRecognizerDelegate {
}

extension GridContentView: GridDelegate {
var viewport: CGRect { bounds }

func grid(_ grid: Grid, shouldChangeColumnWidth proposedWidth: CGFloat, for columnIndex: Int) -> Bool {
gridContentViewDelegate?.gridContentView(self, shouldChangeColumnWidth: proposedWidth, for: columnIndex) ?? true
}
Expand Down
3 changes: 2 additions & 1 deletion Proton/Sources/Swift/Grid/View/GridView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@ public class GridView: UIView {
private func resetShadows() {
if let frozenColumnMaxIndex {
let frozenColumnWidth = gridView.columnWidths.prefix(upTo: frozenColumnMaxIndex + 1).reduce(0) { partialResult, dimension in
partialResult + dimension.value(basedOn: frame.size.width)
let viewport = gridView.bounds
return partialResult + dimension.value(basedOn: frame.size.width, viewportWidth: viewport.width)
}
let borderOffSet = self.config.style.borderWidth
leadingShadowConstraint.constant = frozenColumnWidth + borderOffSet
Expand Down
63 changes: 63 additions & 0 deletions Proton/Tests/Grid/GridViewAttachmentSnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,69 @@ class GridViewAttachmentSnapshotTests: SnapshotTestCase {
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func testRendersGridViewAttachmentWithViewportConstraints() {
let viewController = EditorTestViewController()
let editor = viewController.editor
let config = GridConfiguration(
columnsConfiguration: [
GridColumnConfiguration(width: .viewport(padding: 0)),
],
rowsConfiguration: [
GridRowConfiguration(initialHeight: 40),
GridRowConfiguration(initialHeight: 40),
])
let attachment = GridViewAttachment(config: config)

editor.replaceCharacters(in: .zero, with: "Some text in editor")
editor.insertAttachment(in: editor.textEndRange, attachment: attachment)
editor.replaceCharacters(in: editor.textEndRange, with: "Text after grid")

XCTAssertEqual(attachment.view.containerAttachment, attachment)

viewController.render(size: CGSize(width: 400, height: 175))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
let cell = attachment.view.cellAt(rowIndex: 0, columnIndex: 0)
let lineFragmentPadding = editor.lineFragmentPadding
XCTAssertEqual(cell?.frame.width, editor.frame.width - (lineFragmentPadding * 2))

viewController.render(size: CGSize(width: 700, height: 175))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func testRendersNestedGridViewAttachmentWithViewportConstraints() {
let viewController = EditorTestViewController()
let editor = viewController.editor
let config = GridConfiguration(
columnsConfiguration: [
GridColumnConfiguration(width: .viewport(padding: 0)),
],
rowsConfiguration: [
GridRowConfiguration(initialHeight: 40),
GridRowConfiguration(initialHeight: 40),
])
let gridAttachment = GridViewAttachment(config: config)
var panel = PanelView()
panel.editor.forceApplyAttributedText = true
panel.backgroundColor = .cyan
panel.layer.borderWidth = 1.0
panel.layer.cornerRadius = 4.0
panel.layer.borderColor = UIColor.black.cgColor

let panelAttachment = Attachment(panel, size: .fullWidth)
panel.boundsObserver = panelAttachment
panel.editor.font = editor.font

panel.attributedText = NSAttributedString(string: "Text in panel\n")
panel.editor.replaceCharacters(in: panel.editor.textEndRange, with: gridAttachment.string)

editor.replaceCharacters(in: .zero, with: "Some text in editor")
editor.insertAttachment(in: editor.textEndRange, attachment: panelAttachment)
editor.replaceCharacters(in: editor.textEndRange, with: "Text after grid")

viewController.render(size: CGSize(width: 400, height: 240))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func testRendersGridViewAttachmentWithFractionalWidth() {
let viewController = EditorTestViewController()
let editor = viewController.editor
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2cccc09

Please sign in to comment.