Skip to content

Commit

Permalink
Release 17.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
robot-divkit committed Nov 28, 2022
1 parent 2d16f3f commit 525acfa
Show file tree
Hide file tree
Showing 112 changed files with 2,541 additions and 2,017 deletions.
23 changes: 23 additions & 0 deletions Core/BaseUI/BackgroundAttribute.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2021 Yandex LLC. All rights reserved.

import CoreGraphics
import Foundation

public final class BackgroundAttribute {
public static let Key = NSAttributedString.Key("RangeBackground")

public let color: CGColor
public let range: CFRange

public init(
color: CGColor,
range: CFRange
) {
self.color = color
self.range = range
}

public func apply(to str: CFMutableAttributedString, at range: CFRange) {
CFAttributedStringSetAttribute(str, range, BackgroundAttribute.Key as CFString, self)
}
}
29 changes: 29 additions & 0 deletions Core/BaseUI/BorderAttribute.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2022 Yandex LLC. All rights reserved.

import CoreGraphics
import Foundation

public final class BorderAttribute {
public static let Key = NSAttributedString.Key("RangeBorder")

public let color: CGColor?
public let width: CGFloat?
public let cornerRadius: CGFloat?
public let range: CFRange

public init(
color: CGColor?,
width: CGFloat?,
cornerRadius: CGFloat?,
range: CFRange
) {
self.color = color
self.width = width
self.cornerRadius = cornerRadius
self.range = range
}

public func apply(to str: CFMutableAttributedString, at range: CFRange) {
CFAttributedStringSetAttribute(str, range, BorderAttribute.Key as CFString, self)
}
}
87 changes: 85 additions & 2 deletions Core/BaseUI/NSAttributedStringExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import CoreGraphics
import CoreText
import Foundation

#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif

import BaseTiny

extension NSAttributedString {
Expand Down Expand Up @@ -314,6 +320,8 @@ extension NSAttributedString {
verticalPosition: verticalPosition,
rect: rect,
actionKey: nil,
backgroundKey: nil,
borderKey: nil,
selectedRange: nil
) as AttributedStringLayout<Void>
}
Expand All @@ -330,6 +338,8 @@ extension NSAttributedString {
rect: rect,
truncationToken: truncationToken,
actionKey: nil,
backgroundKey: nil,
borderKey: nil,
selectedRange: nil
)
}
Expand All @@ -340,6 +350,8 @@ extension NSAttributedString {
rect: CGRect,
truncationToken: NSAttributedString? = nil,
actionKey: NSAttributedString.Key?,
backgroundKey: NSAttributedString.Key?,
borderKey: NSAttributedString.Key?,
selectedRange: Range<Int>?
) -> AttributedStringLayout<ActionType> {
context?.saveGState()
Expand Down Expand Up @@ -368,6 +380,8 @@ extension NSAttributedString {
rect: transformedRect,
truncationToken: truncationToken,
actionKey: actionKey,
backgroundKey: backgroundKey,
borderKey: borderKey,
selectedRange: selectedRange
)
}
Expand All @@ -378,6 +392,8 @@ extension NSAttributedString {
rect: transformedRect,
truncationToken: truncationToken,
actionKey: actionKey,
backgroundKey: backgroundKey,
borderKey: borderKey,
selectedRange: selectedRange
)
}
Expand All @@ -388,6 +404,8 @@ extension NSAttributedString {
rect: CGRect,
truncationToken: NSAttributedString?,
actionKey: NSAttributedString.Key?,
backgroundKey: NSAttributedString.Key?,
borderKey: NSAttributedString.Key?,
selectedRange _: Range<Int>?
) -> AttributedStringLayout<ActionType> {
let layout = makeTextLayout(
Expand Down Expand Up @@ -432,7 +450,9 @@ extension NSAttributedString {
at: textPosition,
in: context,
layoutY: rect.maxY - lineOriginY,
actionKey: actionKey
actionKey: actionKey,
backgroundKey: backgroundKey,
borderKey: borderKey
) as [AttributedStringLayout<ActionType>.Run]
runsLayout += lineRunsLayout
} else {
Expand Down Expand Up @@ -944,7 +964,9 @@ extension CTLine {
at position: CGPoint,
in context: CGContext,
layoutY: CGFloat,
actionKey: NSAttributedString.Key?
actionKey: NSAttributedString.Key?,
backgroundKey: NSAttributedString.Key?,
borderKey: NSAttributedString.Key?
) -> [AttributedStringLayout<ActionType>.Run] {
var runsWithActions = [AttributedStringLayout<ActionType>.Run]()

Expand All @@ -964,6 +986,59 @@ extension CTLine {
)
)
}
#if os(iOS)
let border = borderKey.flatMap(run.border)
let background = backgroundKey.flatMap(run.background)

if background != nil || border != nil {
var corners: UIRectCorner = []

if let border {
let leftIndex = CTLineGetStringIndexForPosition(self, runPosition - position.x)
let rightIndex = CTLineGetStringIndexForPosition(
self,
runPosition.movingX(by: run.typographicBounds.width - position.x)
)
if (leftIndex...rightIndex).contains(border.range.location) {
corners = [.topLeft, .bottomLeft]
}
if (leftIndex...rightIndex).contains(border.range.location + border.range.length - 1) {
corners.update(with: [.topRight, .bottomRight])
}
}

let borderWidth = border?.width ?? 0

let scaleX = (run.typographicBounds.width - borderWidth) / run.typographicBounds.width
let scaleY = (run.typographicBounds.height - borderWidth) / run.typographicBounds.height

let path = UIBezierPath(
roundedRect: CGRect(
origin: .zero,
size: CGSize(
width: run.typographicBounds.width,
height: run.typographicBounds.height
)
),
byRoundingCorners: corners,
cornerRadii: CGSize(squareDimension: border?.cornerRadius ?? 0)
)
path.apply(CGAffineTransform(scaleX: scaleX, y: scaleY))
path.apply(CGAffineTransform(
translationX: runPosition.x + borderWidth / 2,
y: runPosition.y + borderWidth / 2 - run.typographicBounds.descent
))

context.saveGState()
context.setFillColor(background?.color ?? Color.clear.cgColor)
context.setStrokeColor(border?.color ?? Color.clear.cgColor)
context.setLineWidth(borderWidth)
context.addPath(path.cgPath)
context.closePath()
context.drawPath(using: .fillStroke)
context.restoreGState()
}
#endif

context.textPosition = position
context.inSeparateGState {
Expand Down Expand Up @@ -1095,6 +1170,14 @@ extension CTRun {
fileprivate func action<ActionType>(for key: NSAttributedString.Key) -> ActionType? {
attribute(withName: key) as ActionType?
}

fileprivate func background(for key: NSAttributedString.Key) -> BackgroundAttribute? {
attribute(withName: key) as BackgroundAttribute?
}

fileprivate func border(for key: NSAttributedString.Key) -> BorderAttribute? {
attribute(withName: key) as BorderAttribute?
}
}

extension NSMutableAttributedString {
Expand Down
3 changes: 2 additions & 1 deletion Core/CommonCore/ImageRedrawingStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import BaseUI
public struct ImageRedrawingStyle: Equatable {
public static func ==(lhs: ImageRedrawingStyle, rhs: ImageRedrawingStyle) -> Bool {
lhs.tintColor == rhs.tintColor &&
lhs.tintMode == rhs.tintMode
lhs.tintMode == rhs.tintMode &&
lhs.effects == rhs.effects
}

let tintColor: Color?
Expand Down
2 changes: 1 addition & 1 deletion Core/CommonCore/MetalImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fileprivate func redrawingImage(
if let tintColor = imageRedrawingStyle?.tintColor,
let coloredImage = ImageGeneratorType.constantColor(color: tintColor.ciColor)
.imageGenerator() {
let mode = imageRedrawingStyle?.tintMode ?? .sourceAtop
let mode = imageRedrawingStyle?.tintMode ?? .sourceIn
tintModeFilter = { mode.composerType.imageComposer($0)(coloredImage) }
} else {
tintModeFilter = identityFilter
Expand Down
2 changes: 1 addition & 1 deletion DivKit/DivKitInfo.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
public enum DivKitInfo {
public static let version = "16.0.0"
public static let version = "17.0.0"
}
4 changes: 2 additions & 2 deletions DivKit/Expressions/Serialization/FieldExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ extension Field {
)
if case let .failure(errors) = result,
errors.count == 1,
case .left(.noData) = errors.first {
case .noData = errors.first {
return .noValue
}
return result
Expand All @@ -148,7 +148,7 @@ extension Field {
)
if case let .failure(errors) = result,
errors.count == 1,
case .left(.noData) = errors.first {
case .noData = errors.first {
return .noValue
}
return result
Expand Down
39 changes: 38 additions & 1 deletion DivKit/Extensions/DivTextExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,20 @@ extension DivText: DivBlockModeling {
let typos = [fontTypo, colorTypo, heightTypo, spacingTypo, strikethroughTypo, underlineTypo]
.compactMap { $0 }
let actions = range.makeActions(context: context.actionContext)
if typos.isEmpty, actions == nil {
if typos.isEmpty, actions == nil, range.background == nil, range.border == nil {
return
}

let actualEnd = min(end, CFAttributedStringGetLength(string))
let cfRange = CFRange(location: start, length: actualEnd - start)
typos.forEach { $0.apply(to: string, at: cfRange) }
string.apply(actions, at: cfRange)

range.makeBackground(range: cfRange, resolver: context.expressionResolver)?
.apply(to: string, at: cfRange)

range.makeBorder(range: cfRange, resolver: context.expressionResolver)?
.apply(to: string, at: cfRange)
}

private func makeGradient(_ expressionResolver: ExpressionResolver) -> Gradient? {
Expand Down Expand Up @@ -265,6 +271,37 @@ extension DivText.Image {
}
}

extension DivText.Range {
fileprivate func makeBorder(
range: CFRange,
resolver: ExpressionResolver
) -> BorderAttribute? {
guard let border else { return nil }
let color = border.stroke?.resolveColor(resolver)
let width = border.stroke?.resolveWidth(resolver)
let cornerRadius = border.resolveCornerRadius(resolver)
return BorderAttribute(
color: color?.cgColor,
width: width.flatMap(CGFloat.init),
cornerRadius:
cornerRadius.flatMap(CGFloat.init),
range: range
)
}

fileprivate func makeBackground(
range: CFRange,
resolver: ExpressionResolver
) -> BackgroundAttribute? {
guard let background else { return nil }
switch background {
case let .divSolidBackground(solid):
let color = solid.resolveColor(resolver) ?? .clear
return BackgroundAttribute(color: color.cgColor, range: range)
}
}
}

extension DivLineStyle {
fileprivate var underlineStyle: UnderlineStyle {
switch self {
Expand Down
16 changes: 8 additions & 8 deletions DivKit/generated_sources/BooleanVariableTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public final class BooleanVariableTemplate: TemplateValue, TemplateDeserializabl
let nameValue = parent?.name?.resolveValue(context: context, validator: ResolvedValue.nameValidator) ?? .noValue
let valueValue = parent?.value?.resolveValue(context: context) ?? .noValue
var errors = mergeErrors(
nameValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "name", level: .error)) },
valueValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "value", level: .error)) }
nameValue.errorsOrWarnings?.map { .nestedObjectError(field: "name", error: $0) },
valueValue.errorsOrWarnings?.map { .nestedObjectError(field: "value", error: $0) }
)
if case .noValue = nameValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "name")))
errors.append(.requiredFieldIsMissing(field: "name"))
}
if case .noValue = valueValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "value")))
errors.append(.requiredFieldIsMissing(field: "value"))
}
guard
let nameNonNil = nameValue.value,
Expand Down Expand Up @@ -82,14 +82,14 @@ public final class BooleanVariableTemplate: TemplateValue, TemplateDeserializabl
}
}
var errors = mergeErrors(
nameValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "name", level: .error)) },
valueValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "value", level: .error)) }
nameValue.errorsOrWarnings?.map { .nestedObjectError(field: "name", error: $0) },
valueValue.errorsOrWarnings?.map { .nestedObjectError(field: "value", error: $0) }
)
if case .noValue = nameValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "name")))
errors.append(.requiredFieldIsMissing(field: "name"))
}
if case .noValue = valueValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "value")))
errors.append(.requiredFieldIsMissing(field: "value"))
}
guard
let nameNonNil = nameValue.value,
Expand Down
16 changes: 8 additions & 8 deletions DivKit/generated_sources/ColorVariableTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public final class ColorVariableTemplate: TemplateValue, TemplateDeserializable
let nameValue = parent?.name?.resolveValue(context: context, validator: ResolvedValue.nameValidator) ?? .noValue
let valueValue = parent?.value?.resolveValue(context: context, transform: Color.color(withHexString:)) ?? .noValue
var errors = mergeErrors(
nameValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "name", level: .error)) },
valueValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "value", level: .error)) }
nameValue.errorsOrWarnings?.map { .nestedObjectError(field: "name", error: $0) },
valueValue.errorsOrWarnings?.map { .nestedObjectError(field: "value", error: $0) }
)
if case .noValue = nameValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "name")))
errors.append(.requiredFieldIsMissing(field: "name"))
}
if case .noValue = valueValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "value")))
errors.append(.requiredFieldIsMissing(field: "value"))
}
guard
let nameNonNil = nameValue.value,
Expand Down Expand Up @@ -82,14 +82,14 @@ public final class ColorVariableTemplate: TemplateValue, TemplateDeserializable
}
}
var errors = mergeErrors(
nameValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "name", level: .error)) },
valueValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "value", level: .error)) }
nameValue.errorsOrWarnings?.map { .nestedObjectError(field: "name", error: $0) },
valueValue.errorsOrWarnings?.map { .nestedObjectError(field: "value", error: $0) }
)
if case .noValue = nameValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "name")))
errors.append(.requiredFieldIsMissing(field: "name"))
}
if case .noValue = valueValue {
errors.append(.left(DeserializationError.requiredFieldIsMissing(fieldName: "value")))
errors.append(.requiredFieldIsMissing(field: "value"))
}
guard
let nameNonNil = nameValue.value,
Expand Down
Loading

0 comments on commit 525acfa

Please sign in to comment.