1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-11-13 05:07:24 +03:00

Add NodeLayout

This commit is contained in:
Alisa Mylnikova 2018-05-07 16:14:06 +07:00
parent f035a6c8d5
commit e2c4d9650d
5 changed files with 100 additions and 80 deletions

View File

@ -7,9 +7,9 @@
class SVGCanvas: Group {
let layout: ContentLayout
let layout: NodeLayout
public init(layout: ContentLayout, contents: [Node] = []) {
public init(layout: NodeLayout, contents: [Node] = []) {
self.layout = layout
super.init(contents: contents)
}

View File

@ -69,7 +69,7 @@ open class SVGParser {
fileprivate func parse() -> Group {
let parsedXml = SWXMLHash.parse(xmlString)
var layout: ContentLayout?
var layout: NodeLayout?
for child in parsedXml.children {
if let element = child.element {
if element.name == "svg" {
@ -119,7 +119,7 @@ open class SVGParser {
}
}
fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) -> SvgContentLayout? {
fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) -> SvgNodeLayout? {
var svgDimensions: Dimensions?
if let w = getDimensionValue(element, attribute: "width"), let h = getDimensionValue(element, attribute: "height") {
svgDimensions = Dimensions(width: w, height: h)
@ -143,7 +143,7 @@ open class SVGParser {
let strings = contentModeString.components(separatedBy: CharacterSet(charactersIn: " "))
if strings.count == 1 { // none
scalingMode = parseAspectRatio(strings[0])
return SvgContentLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode)
return SvgNodeLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode)
}
guard strings.count == 2 else { fatalError("Invalid content mode") }
@ -159,7 +159,7 @@ open class SVGParser {
scalingMode = parseAspectRatio(strings[1])
}
return SvgContentLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode)
return SvgNodeLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode)
}
fileprivate func parseNode(_ node: XMLIndexer, groupStyle: [String: String] = [:]) -> Node? {

View File

@ -1,27 +1,4 @@
enum Dimension {
case percent(Double)
case pixels(Double)
init(percent: Double) {
self = .percent(percent)
}
init(pixels: Double) {
self = .pixels(pixels)
}
}
class Dimensions {
let width: Dimension
let height: Dimension
public init(width: Dimension, height: Dimension) {
self.width = width
self.height = height
}
}
public protocol ContentLayout {
static var standard: ContentLayout { get }
@ -30,24 +7,11 @@ public protocol ContentLayout {
class SvgContentLayout: ContentLayout {
public let scalingMode: AspectRatio
public let xAligningMode: Align
public let yAligningMode: Align
let scalingMode: AspectRatio
let xAligningMode: Align
let yAligningMode: Align
let svgDimensions: Dimensions?
let viewBox: Rect?
public init(scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) {
self.svgDimensions = .none
self.viewBox = .none
self.scalingMode = scalingMode ?? .meet
self.xAligningMode = xAligningMode ?? .mid
self.yAligningMode = yAligningMode ?? .mid
}
init(svgDimensions: Dimensions? = .none, viewBox: Rect? = .none, scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) {
self.svgDimensions = svgDimensions
self.viewBox = viewBox
init(scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) {
self.scalingMode = scalingMode ?? .meet
self.xAligningMode = xAligningMode ?? .mid
self.yAligningMode = yAligningMode ?? .mid

View File

@ -0,0 +1,88 @@
enum Dimension {
case percent(Double)
case pixels(Double)
init(percent: Double) {
self = .percent(percent)
}
init(pixels: Double) {
self = .pixels(pixels)
}
}
class Dimensions {
let width: Dimension
let height: Dimension
public init(width: Dimension, height: Dimension) {
self.width = width
self.height = height
}
}
public protocol NodeLayout {
func layout(in rect: Rect) -> (Transform, Locus?)
}
class SvgNodeLayout: NodeLayout {
let svgDimensions: Dimensions?
let viewBox: Rect?
let scalingMode: AspectRatio
let xAligningMode: Align
let yAligningMode: Align
init(svgDimensions: Dimensions? = .none, viewBox: Rect? = .none, scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) {
self.svgDimensions = svgDimensions
self.viewBox = viewBox
self.scalingMode = scalingMode ?? .meet
self.xAligningMode = xAligningMode ?? .mid
self.yAligningMode = yAligningMode ?? .mid
}
public func layout(in rect: Rect) -> (Transform, Locus?) {
var clip: Locus? = .none
var transform = Transform.identity
guard let dimensions = svgDimensions else {
return (transform, clip)
}
let width = dimensionToPixels(dimensions.width, framePixels: rect.w)
let height = dimensionToPixels(dimensions.height, framePixels: rect.h)
let svgSize = Size(w: width, h: height)
if let viewBox = self.viewBox {
clip = viewBox
}
let viewBox = self.viewBox ?? Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h)
if scalingMode === AspectRatio.slice {
// setup new clipping to slice extra bits
let newSize = AspectRatio.meet.fit(size: svgSize, into: viewBox)
let newX = viewBox.x + xAligningMode.align(outer: viewBox.w, inner: newSize.w)
let newY = viewBox.y + yAligningMode.align(outer: viewBox.h, inner: newSize.h)
clip = Rect(x: newX, y: newY, w: newSize.w, h: newSize.h)
}
let contentLayout = SvgContentLayout(scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode)
transform = contentLayout.layout(rect: viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h))
// move to (0, 0)
transform = transform.move(dx: -viewBox.x, dy: -viewBox.y)
return (transform, clip)
}
}
fileprivate func dimensionToPixels(_ dimension: Dimension, framePixels: Double) -> Double {
switch(dimension) {
case let .percent(percent):
return framePixels * percent / 100.0
case let .pixels(pixels):
return pixels
}
}

View File

@ -18,31 +18,8 @@ open class MacawView: MView, MGestureRecognizerDelegate {
}
didSet {
if let canvas = node as? SVGCanvas, let params = canvas.layout as? SvgContentLayout, let dimensions = params.svgDimensions {
let width = dimensionToPixels(dimensions.width, framePixels: Double(frame.width))
let height = dimensionToPixels(dimensions.height, framePixels: Double(frame.height))
let svgSize = Size(w: width, h: height)
let scalingMode = params.scalingMode
if let viewBox = params.viewBox {
canvas.clip = viewBox
}
let viewBox = params.viewBox ?? Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h)
if scalingMode === AspectRatio.slice {
// setup new clipping to slice extra bits
let newSize = AspectRatio.meet.fit(size: svgSize, into: viewBox)
let newX = viewBox.x + params.xAligningMode.align(outer: viewBox.w, inner: newSize.w)
let newY = viewBox.y + params.yAligningMode.align(outer: viewBox.h, inner: newSize.h)
node.clip = Rect(x: newX, y: newY, w: newSize.w, h: newSize.h)
}
let contentLayout = SvgContentLayout(scalingMode: scalingMode, xAligningMode: params.xAligningMode, yAligningMode: params.yAligningMode)
node.place = contentLayout.layout(rect: viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h))
// move to (0, 0)
node.place = node.place.move(dx: -viewBox.x, dy: -viewBox.y)
if let canvas = node as? SVGCanvas, let layout = canvas.layout as? SvgNodeLayout {
(canvas.place, canvas.clip) = layout.layout(in: Rect(cgRect: bounds))
}
nodesMap.add(node, view: self)
@ -200,15 +177,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
guard let ctx = context.cgContext else { return .none }
return renderer?.findNodeAt(location: location, ctx: ctx)
}
fileprivate func dimensionToPixels(_ dimension: Dimension, framePixels: Double) -> Double {
switch(dimension) {
case let .percent(percent):
return framePixels * percent / 100.0
case let .pixels(pixels):
return pixels
}
}
// MARK: - Touches