Add shared sample library and native macOS CoreText host#3
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 555911b149
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| public string Name => "FreeType"; | ||
|
|
||
| public bool IsSupported => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); |
There was a problem hiding this comment.
Gate FreeType support on native library availability
IsSupported reports true for every Linux runtime, so discovery will always prefer this backend (priority 100) over SkiaSharp. On Linux environments missing libfreetype/libfontconfig (common in slim containers), Create then fails with native-load/font-resolution errors and PretextLayout.Prepare* crashes instead of falling back to another backend. Please only advertise support after probing required native dependencies (or treat create-time failures as unsupported during discovery).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9441effc3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| [DllImport(CoreFoundationLibrary)] | ||
| private static extern IntPtr CFStringCreateWithCharacters(IntPtr allocator, string chars, nint numChars); |
There was a problem hiding this comment.
Marshal CoreFoundation strings as UTF-16
CFStringCreateWithCharacters takes a pointer to UTF-16 UniChar data, but this P/Invoke uses string with no Unicode marshalling directive, so .NET uses ANSI/UTF-8 on macOS; CreateString then passes incorrectly encoded text while still supplying a UTF-16 character count. In macOS runs this can corrupt family/text CFStrings, causing CTFontCreateWithName and text measurement to fail or return wrong widths instead of producing stable CoreText output.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6acadfae4e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| var supportedFactories = new List<IPretextTextMeasurerFactory>(); | ||
| foreach (var factoryType in candidateTypes.Values) | ||
| { | ||
| if (Activator.CreateInstance(factoryType) is not IPretextTextMeasurerFactory factory) |
There was a problem hiding this comment.
Preserve reflected backend factories under trimming
Backend discovery relies entirely on reflection (GetCustomAttributes + Activator.CreateInstance) to find and construct factories, but there are no trim annotations to root those factory types/constructors. In PublishTrimmed/AOT builds, the linker can strip factories that are only reached this way, so discovery returns no supported backend and Prepare* fails at runtime with the "No Pretext text measurer factory is configured" exception even though a backend package is referenced.
Useful? React with 👍 / 👎.
| { | ||
| prefixLength += graphemes[i].Length; | ||
| prefixWidths[i] = MeasureText(text.AsSpan(0, prefixLength)); | ||
| prefixWidths[i] = MeasureText(text.Substring(0, prefixLength)); |
There was a problem hiding this comment.
Avoid allocating substrings for every grapheme prefix
The prefix-width loop now calls text.Substring(0, prefixLength) on every grapheme step, which turns prefix measurement into O(n²) string allocations/copies for long breakable runs. This regresses prepare-time performance and GC behavior in real workloads (for example long chat/editorial paragraphs) compared to the previous span-based measurement path that did not allocate per prefix.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 73e86e1377
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| return shapedWidth; | ||
| } | ||
|
|
||
| return MeasureFallback(text); |
There was a problem hiding this comment.
Shape fallback text runs with HarfBuzz on Linux
When primary-face shaping fails, this code falls back to MeasureFallback, which measures by summing per-codepoint advances/kerning instead of shaping runs. In Linux environments where the requested family lacks some glyphs (common for emoji and many non-Latin scripts), fallback fonts are used without HarfBuzz shaping, so ligatures/joins/ZWJ sequences are mismeasured and line breaking becomes incorrect. Please shape fallback runs with HarfBuzz (per resolved face/run) rather than relying on codepoint-by-codepoint advance accumulation.
Useful? React with 👍 / 👎.
PR Summary: Shared Sample Extraction and Native macOS CoreText Host
Overview
This PR does two related things:
net10.0-macossample host that usesPretext.CoreTextwith AppKit instead of the Uno/Skia host stack.The result is that the repository now has one shared sample corpus and two host applications:
samples/PretextSamples: Uno Platform sample hostsamples/PretextSamples.MacOS: native AppKit sample host onnet10.0-macossamples/PretextSamples.Shared: shared sample data, prepared-model logic, and sample assetsWhy
Before this change, the sample logic lived inside the Uno sample project, which made it difficult to reuse the scenarios in another host without copying large amounts of code or tying the new host to Uno-specific types.
This PR addresses that by:
What Changed
1. New shared sample project
Added:
samples/PretextSamples.Shared/PretextSamples.Shared.csprojsamples/PretextSamples.Shared/GlobalUsings.cssamples/PretextSamples.Shared/Assets/shower_thoughts.jsonMoved reusable sample logic from
samples/PretextSamples/Samplesintosamples/PretextSamples.Shared/Samples:MarkdownChatData.csMarkdownChatModel.csRichNoteModel.csJustificationComparisonData.csJustificationComparisonModel.csSampleTextData.csSharedText.csAdded shared sample-specific data/catalog helpers:
SampleCatalog.csAccordionSampleData.csBubblesSampleData.csMasonrySampleData.csKey implementation points:
Pretext,Pretext.Layout, andMarkdig, which are the real dependencies of the reusable sample logic.2. Existing Uno sample host updated to consume shared sample logic
Updated:
samples/PretextSamples/PretextSamples.csprojsamples/PretextSamples/Samples/OverviewSampleView.cssamples/PretextSamples/Samples/AccordionSampleView.cssamples/PretextSamples/Samples/BubblesSampleView.cssamples/PretextSamples/Samples/MasonrySampleView.csKey changes:
PretextSamplesnow referencesPretextSamples.Shared,PretextSamples; only reusable data/model logic moved out.3. New native macOS sample host
Added full project:
samples/PretextSamples.MacOS/PretextSamples.MacOS.csprojCore host files:
AppDelegate.csMain.csViewController.csSampleShellView.csSamplePageView.csMacTheme.csGlobalUsings.csProject/packaging files:
Info.plistEntitlements.plistMain.storyboardAssets.xcassets/...The native host explicitly configures:
PretextLayout.SetTextMeasurerFactory(new CoreTextTextMeasurerFactory())This ensures the macOS sample app exercises the CoreText backend directly instead of relying on SkiaSharp fallback behavior.
4. Native AppKit sample pages
Added AppKit-native page implementations for the same sample catalog:
Pages/OverviewPageView.csPages/AccordionPageView.csPages/BubblesPageView.csPages/MasonryPageView.csPages/RichNotePageView.csPages/MarkdownChatPageView.csPages/DynamicLayoutPageView.csPages/EditorialEnginePageView.csPages/JustificationComparisonPageView.csPages/VariableAsciiPageView.csNotable implementation details:
PretextAPIs,MarkdownChatModel,Pretext.Layouthelpers and manual geometry instead of host UI measurement,5. Solution and documentation updates
Updated:
PretextSamples.slnxREADME.mdChanges include:
PretextSamples.SharedandPretextSamples.MacOSto the solution,Commit Breakdown
This work was split into three commits:
65ea557Extract shared sample models and assets84b4a70Add native macOS CoreText sample hostbbc7563Document shared and native sample hostsVerification
Verified locally with:
dotnet build samples/PretextSamples.Shared/PretextSamples.Shared.csproj dotnet build samples/PretextSamples/PretextSamples.csproj dotnet build samples/PretextSamples.MacOS/PretextSamples.MacOS.csproj -p:ValidateXcodeVersion=false dotnet build PretextSamples.slnx -p:ValidateXcodeVersion=false dotnet test tests/Pretext.Uno.Tests/Pretext.Uno.Tests.csprojTest result:
88passing tests inPretext.Uno.TestsEnvironment Note
The native macOS build required
-p:ValidateXcodeVersion=falsein this environment because:26.326.2This override was used only for local verification and was not added to the project itself.
Scope and Tradeoffs
Included
Not included
Pretextbackend contracts or native backends themselves,The macOS implementation is intentionally native-first rather than a host-agnostic UI layer trying to preserve every Uno control pattern.
Risks / Review Focus
Reviewers should pay attention to:
PretextSamples.SharedFollow-up Ideas
Potential follow-up work: