import Foundation import UIKit import Display public final class InstantPageTile { public let frame: CGRect public var items: [InstantPageItem] = [] init(frame: CGRect) { self.frame = frame } func draw(context: CGContext) { context.translateBy(x: -self.frame.minX, y: -self.frame.minY) for item in self.items { item.drawInTile(context: context) } context.translateBy(x: self.frame.minX, y: self.frame.minY) } } public func instantPageTilesFromLayout(_ layout: InstantPageLayout, boundingWidth: CGFloat) -> [InstantPageTile] { var tileByOrigin: [Int : InstantPageTile] = [:] let tileHeight: CGFloat = 256.0 var tileHoles: [CGRect] = [] for item in layout.items { if !item.wantsNode { let topTileIndex = max(0, Int(floor(item.frame.minY - 10.0) / tileHeight)) let bottomTileIndex = max(topTileIndex, Int(floor(item.frame.maxY + 10.0) / tileHeight)) for i in topTileIndex ... bottomTileIndex { let tile: InstantPageTile if let current = tileByOrigin[i] { tile = current } else { tile = InstantPageTile(frame: CGRect(x: 0.0, y: CGFloat(i) * tileHeight, width: boundingWidth, height: tileHeight)) tileByOrigin[i] = tile } tile.items.append(item) } } else if item.separatesTiles { tileHoles.append(item.frame) } } var finalTiles: [InstantPageTile] = [] var usedTiles = Set() for hole in tileHoles { let topTileIndex = max(0, Int(floor(hole.minY - 10.0) / tileHeight)) let bottomTileIndex = max(topTileIndex, Int(floor(hole.maxY + 10.0) / tileHeight)) for i in topTileIndex ... bottomTileIndex { if let tile = tileByOrigin[i] { if tile.frame.minY > hole.minY && tile.frame.minY < hole.maxY { let delta = hole.maxY - tile.frame.minY let updatedTile = InstantPageTile(frame: CGRect(origin: tile.frame.origin.offsetBy(dx: 0.0, dy: delta), size: CGSize(width: tile.frame.width, height: tile.frame.height - delta))) updatedTile.items.append(contentsOf: tile.items) finalTiles.append(updatedTile) usedTiles.insert(i) } else if tile.frame.maxY > hole.minY && tile.frame.minY < hole.minY { let delta = tile.frame.maxY - hole.minY let updatedTile = InstantPageTile(frame: CGRect(origin: tile.frame.origin, size: CGSize(width: tile.frame.width, height: tile.frame.height - delta))) updatedTile.items.append(contentsOf: tile.items) finalTiles.append(updatedTile) usedTiles.insert(i) } } } //let holeTile = InstantPageTile(frame: hole) //finalTiles.append(holeTile) } for (index, tile) in tileByOrigin { if !usedTiles.contains(index) { finalTiles.append(tile) } } return finalTiles.sorted(by: { lhs, rhs in return lhs.frame.minY < rhs.frame.minY }) } public func instantPageAccessibilityAreasFromLayout(_ layout: InstantPageLayout, boundingWidth: CGFloat) -> [AccessibilityAreaNode] { var result: [AccessibilityAreaNode] = [] for item in layout.items { if let item = item as? InstantPageTextItem { let itemNode = AccessibilityAreaNode() itemNode.frame = item.frame itemNode.accessibilityTraits = .staticText itemNode.accessibilityLabel = item.attributedString.string result.append(itemNode) } } return result }