Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Sources/OpenSwiftUICore/View/Text/Text/Text+Renderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
// Status: Empty
// ID: 7F70C8A76EE0356881289646072938C0 (SwiftUICore)

#if canImport(CoreText)
import CoreText
#endif
import OpenAttributeGraphShims
public import OpenCoreGraphicsShims
import UIFoundation_Private

// TODO

Expand Down Expand Up @@ -207,6 +211,23 @@ extension Text {
/// A single line in a text layout: a collection of runs of
/// placed glyphs.
public struct Line: RandomAccessCollection, Equatable {
private enum Line {
#if canImport(CoreText)
case ctLine(CTLine, AnyTextLayoutRenderer?)
#endif
case nsLine(NSTextLineFragment)
}

private var _line: Text.Layout.Line.Line

@inline(__always)
var attributedString: NSAttributedString? {
guard case let .nsLine(nSTextLineFragment) = _line else {
return nil
}
return nSTextLineFragment.attributedString
}

/// The origin of the line.
public var origin: CGPoint

Expand Down Expand Up @@ -248,6 +269,17 @@ extension Text {
public var paragraphLayoutDirection: LayoutDirection {
_openSwiftUIUnimplementedFailure()
}

public static func == (lhs: Text.Layout.Line, rhs: Text.Layout.Line) -> Bool {
guard lhs.origin == rhs.origin else { return false }
return switch (lhs._line, rhs._line) {
#if canImport(CoreText)
case let (.ctLine(lhsLine, _), .ctLine(rhsLine, _)): lhsLine === rhsLine
#endif
case let (.nsLine(lhsLine), .nsLine(rhsLine)): lhsLine === rhsLine
default: false
}
}
}

// MARK: - Text.Layout.Run [WIP]
Expand Down Expand Up @@ -367,6 +399,8 @@ extension Text.Layout.RunSlice: Sendable {}

// TODO: AnyTextLayoutRenderer

class AnyTextLayoutRenderer {}

// MARK: - Text.LayoutKey

@available(OpenSwiftUI_v5_0, *)
Expand Down
5 changes: 5 additions & 0 deletions Sources/OpenSwiftUICore/View/Text/Text/Text+Suffix.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Status: Blocked by ResolvedStyledText
// ID: 3A0E49913D84545BECD562BC22E4DF1C (SwiftUICore)

import Foundation
import OpenAttributeGraphShims

// MARK: - Text.Suffix
Expand Down Expand Up @@ -74,6 +75,10 @@ package enum ResolvedTextSuffix: Equatable {
case truncated(Text.Layout.Line, [ShapeStyle.Pack.Style])
case alwaysVisible(Text.Layout.Line, [ShapeStyle.Pack.Style])

var accessibilityLine: NSAttributedString? {
line?.attributedString
}

package var line: Text.Layout.Line? {
switch self {
case .none: nil
Expand Down
10 changes: 10 additions & 0 deletions Sources/OpenSwiftUICore/View/Text/Text/TextNonDarwinShims.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,14 @@ extension NSMutableAttributedString {
false
}
}

package class NSTextLineFragment: NSObject {
package init(attributedString: NSAttributedString, range: NSRange) {
self.attributedString = attributedString
self.range = range
}

private(set) package var attributedString: NSAttributedString
private var range: NSRange
}
#endif
66 changes: 66 additions & 0 deletions Sources/OpenSwiftUI_SPI/Shims/UIFoundation/NSTextLineFragment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// NSTextLineFragment.h
// OpenSwiftUI_SPI

#pragma once

#import "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

// Modified based on iOS 18.5 SDK

//
// NSTextLineFragment.h
// Text Kit
//
// Copyright (c) 2017-2024, Apple Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

NS_HEADER_AUDIT_BEGIN(nullability, sendability)

#pragma mark NSTextLineFragment
// NSTextLineFragment represents a single textual layout and rendering unit inside NSTextLayoutFragment.
API_AVAILABLE(macos(12.0), ios(15.0), tvos(15.0), visionos(1.0)) API_UNAVAILABLE(watchos)
@interface NSTextLineFragment : NSObject <NSSecureCoding>
#pragma mark Initialization
- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString range:(NSRange)range NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithString:(NSString *)string attributes:(NSDictionary<NSAttributedStringKey, id> *)attributes range:(NSRange)range;

- (instancetype)init NS_UNAVAILABLE;

#pragma mark Basic properties
// The source attributed string
@property (strong, readonly) NSAttributedString *attributedString;

// The string range for the source attributed string corresponding to this line fragment
@property (readonly) NSRange characterRange;

#pragma mark Typographic bounds
// The typographic bounds specifying the dimensions of the line fragment for laying out line fragments to each other. The origin value is offset from the beginning of the line fragment group belonging to the parent layout fragment.
@property (readonly) CGRect typographicBounds;

#pragma mark Rendering
// Rendering origin for the left most glyph in the line fragment coordinate system
@property (readonly) CGPoint glyphOrigin;

// Renders the line fragment contents at the rendering origin. The origin can be specified as (CGRectGetMinX(typographicBounds), CGRectGetMinY(typographicBounds)) relative to the parent layout fragment coordinate system.
- (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context;

#pragma mark Character and point mappings
// The location of the character at the specified index. It is on the upstream edge of the glyph. It is in the coordinate system relative to the line fragment origin
- (CGPoint)locationForCharacterAtIndex:(NSInteger)index;

// The character index for point inside the line fragment coordinate system. The fraction of distance is from the upstream edge
- (NSInteger)characterIndexForPoint:(CGPoint)point;
- (CGFloat)fractionOfDistanceThroughGlyphForPoint:(CGPoint)point;

@end
NS_HEADER_AUDIT_END(nullability, sendability)

#endif
Loading