engine: pin retained glyph text contract

This commit is contained in:
gamer147
2026-07-30 18:05:43 -04:00
parent 74d6509e3d
commit 41001f6f77
9 changed files with 227 additions and 35 deletions

View File

@@ -0,0 +1,56 @@
namespace Age.Engine.Model;
[Flags]
public enum AdvTextOverflowFlags
{
None = 0,
Horizontal = 1,
Vertical = 2,
}
/// <summary>
/// Native AGE's 20-byte live ADV glyph record. Rectangle values are edges, matching the
/// retained-object bind ABI; they are not x/y/width/height values.
/// </summary>
public readonly record struct AdvRetainedGlyphRecord(
int PublicationChainFlag,
int Left,
int Top,
int Right,
int Bottom)
{
public int Width => Right - Left;
public int Height => Bottom - Top;
public bool PublishesNextInSameTick => PublicationChainFlag == 1;
}
/// <summary>The script-owned retained-object binding associated with one live ADV layout.</summary>
public readonly record struct AdvTextLayoutPresentationBinding(
int LayoutSlot,
int SourceSurfaceSlot,
long FirstObjectHandle,
long ObjectCapacity,
long WaitIndicatorObjectHandle);
/// <summary>Platform-neutral rules established from AGE's native retained-glyph workers.</summary>
public static class AdvRetainedTextContract
{
public static AdvTextOverflowFlags CheckOverflow(
int rightBound, int bottomBound, int glyphRight, int glyphBottom)
{
var result = AdvTextOverflowFlags.None;
if (rightBound < glyphRight) result |= AdvTextOverflowFlags.Horizontal;
if (bottomBound < glyphBottom) result |= AdvTextOverflowFlags.Vertical;
return result;
}
/// <summary>
/// CP932 closing punctuation which AGE keeps on the preceding horizontal line even when the
/// current glyph crosses the right bound.
/// </summary>
public static bool PreventsHorizontalWrapBefore(ushort cp932)
=> cp932 is 0x8141 or 0x8142 or 0x8176; // 、 。 」
public static bool TimedPublicationHandleExists(int revealIndex, long objectCapacity)
=> revealIndex >= 0 && revealIndex < objectCapacity;
}

View File

@@ -195,6 +195,15 @@ public sealed class AdvTextHistory
layout.TextObjectRangeCount = count;
}
public AdvTextLayoutPresentationBinding GetPresentationBinding(int requestedSlot)
{
int slot = ResolveLayout(requestedSlot);
var layout = GetOrCreateLayout(slot);
return new AdvTextLayoutPresentationBinding(
slot, checked(slot + 0x14), layout.TextObjectRangeFirst,
layout.TextObjectRangeCount, layout.WaitIndicatorObjectHandle);
}
/// <summary>
/// Resolve layouts whose complete native retained-glyph handle interval is covered by one op-0x1f7
/// erase. The Label backend collapses those glyph objects into live text runs, so full native range
@@ -275,20 +284,9 @@ public sealed class AdvTextHistory
_entries.Clear();
_entries.AddRange(entries);
_pendingGroupStarts.Clear();
_layouts.Clear();
foreach (AdvTextHistoryRecord record in records)
{
var layout = GetOrCreateLayout(record.Layout.Slot);
layout.Width = record.Layout.Width;
layout.Height = record.Layout.Height;
layout.OriginX = record.Layout.OriginX;
layout.OriginY = record.Layout.OriginY;
layout.CursorX = record.Layout.CursorX;
layout.CursorY = record.Layout.CursorY;
layout.Right = record.Layout.Right;
layout.Bottom = record.Layout.Bottom;
}
CurrentLayoutSlot = entries.Count > 0 ? entries[^1].LayoutSlot : 0;
// Native text_history_deserialize clears and reconstructs only the history entry/record vectors.
// Live layout definitions, selected layout, surfaces, and retained-object bindings remain owned by
// the initialized text manager and are rebuilt by the script restore path as needed.
_navigationAnchorIndex = entries.Count - 1;
}