11. API 参考
公共类型命名
Sus* 是产品 API(在 using Sharq.Core 之后调用的 Runtime 与面向买家的 Editor 类型)。 Sharq* 是 .sharq 编译器与流水线(解析器、解释器、导入器)。产品接口使用 ISus*。无前缀的公共类型是封闭的祖父级集合(响应式原语、host/layer 名词,以及这些 API 的配套类型)。不要新增无前缀公共类型;新产品类型用 Sus* / ISus*,新编译器类型用 Sharq*。
SusApp — fluent bootstrap
官方文档化的应用入口点。它是 SusBootstrap 之上的一个轻量 fluent 构建器, 保证初始化顺序:panel TSS → EventSystem → 令牌级联 → OverlayHost → world panel → 字体 → custom styles → configure → mount → 主题(最后)。
public sealed class SusApp
{
public VisualElement Root { get; }
public UIDocument Document { get; }
public SusWorldSpacePanel WorldPanel { get; } // after Run/Mount; null if UseWorldSpace(false)
// Create
public static SusApp Create(UIDocument document); // ApplyDefaultTSS + root
public static SusApp Create(VisualElement root); // advanced — no TSS
// Fluent config (all return this)
public SusApp UseTheme(SusTheme theme); // default Dark; applied last
public SusApp UseTokenCascade(bool enabled = true); // L1–L5 + OverlayHost (default on)
public SusApp UseWorldSpace(bool enabled = true); // SusWorldSpacePanel + Default (default on)
public SusApp UseCustomStyles(params string[] resourcePaths); // after cascade
public SusApp UseFonts(SusFontAsset fontAsset);
public SusApp UseIcons(params ISusIconProvider[] providers);
public SusApp UseIcons(SusIconSetAsset iconSet);
public SusApp UseLogLevel(SusLogLevel level); // process gate; call before Run/Mount
public SusApp Configure(Action<VisualElement> configure); // before theme; router/manual UI
// Finalize
public VisualElement Run(); // no root component
public T Mount<T>() where T : SusComponent, new(); // Mount + Finalize
}Finalize 顺序(Run / Mount)
- 图标(
UseIcons→SusIconRegistry.RegisterProvider) - 令牌级联(
LoadTokenCascade:_palette→_font→_theme→design-tokens→_icon→ 扩展项 + OverlayHost) - World-space 面板(
EnsureWorldSpacePanel,仅当处于 Play Mode 且启用了UseWorldSpace时) - 字体(
UseFonts) - Custom styles(在 root 与 OverlayHost 上的
UseCustomStyles) - Configure 回调
- 挂载根组件(如果调用了
Mount<T>) - 主题最后应用(
SusThemeService.Instance.SetTheme(root, theme))
UseLogLevel 不属于 finalize — 它会立即设置 SusLog.Level(在 Run / Mount 之前调用是安全的)。
SusApp.Create(uiDocument)
.UseTheme(SusTheme.Dark)
.UseLogLevel(SusLogLevel.Verbose) // optional — diagnostics / audits
.UseCustomStyles("SusRuntime/demo-tokens")
.UseWorldSpace(true)
.Configure(root => BuildManualUi(root))
.Mount<HomeScreen>();
// Router apps (sus-router extension):
SusApp.Create(uiDocument)
.UseTheme(SusTheme.Dark)
.UseRouter(router, r => r.Register("/", typeof(HomeScreen)), initialPath: "/")
.Run();SusLog
Sharq.Core 中的进程级 gated logger。代理到 UnityEngine.Debug,以便 Editor Console 与游戏内 开发控制台 继续接收消息。不是 SusConsoleService 使用的环形缓冲 SusLogEntry 类型 — 该 struct 只存储被拦截的 Unity 日志,供叠加 UI 使用。
public enum SusLogLevel
{
Error = 0,
Warn = 1, // default buyer level
Info = 2,
Verbose = 3, // audits, probes, bootstrap traces
}
public static class SusLog
{
public static SusLogLevel Level { get; set; } // minimum emitted; default Warn
public static bool IsEnabled(SusLogLevel level);
public static bool IsVerbose { get; } // IsEnabled(Verbose)
public static void Error(string message);
public static void Error(string message, Object context);
public static void Warn(string message);
public static void Warn(string message, Object context);
public static void Info(string message);
public static void Verbose(string message);
public static void Diagnostic(string message); // same gate as Verbose
}默认: Warn(Error + 关键 Warn)。诊断在提升级别之前保持静默。
如何启用 Verbose
| 来源 | 效果 |
|---|---|
SusApp.UseLogLevel(SusLogLevel.Verbose) / SusLog.Level = … | 代码覆盖;在 Run / Mount 之前调用 |
Assets/sus.config.json → "logLevel": "Verbose" | 首次访问 SusLog 时读取(见 10-configuration) |
脚本定义 SUS_VERBOSE_LOGS | 初始化时把下限抬到 Verbose;配置与 UseLogLevel 不能再降低 |
优先级:define 下限 → 最后一次 UseLogLevel / SusLog.Level → 配置中的 logLevel → 默认 Warn。
对昂贵的转储先做门控:if (SusLog.IsVerbose) SusLog.Verbose(...)。
SusComponent — 基类
public abstract partial class SusComponent : VisualElement
{
// ─── Creating reactive properties ───
protected Prop<T> P<T>(T initial = default);
protected Computed<T> C<T>(Func<T> fn);
protected WatchHandle Watch<T>(Prop<T> source, Action<T, T> callback);
protected WatchHandle WatchEffect(Action fn);
// ─── Life cycle ───
protected virtual void Created(); // constructor
protected virtual void BeforeMounted(); // between Created() and Build()
protected virtual void Mounted(); // after Build() - deferred
protected virtual void Updated(); // every frame
protected virtual void BeforeUnmounted(); // BEFORE removing from the panel
protected virtual void Unmounted(); // AFTER deletion
// ─── Generation ───
protected abstract void Build(); // generated by the compiler
// ─── Provide / Inject ───
// overwrite:false (default) fires OnDuplicateProvide if the key already exists, then still writes
protected void Provide<T>(string key, T value, bool overwrite = false);
protected T Inject<T>(string key);
protected bool TryInject<T>(string key, out T value);
protected bool HasInjection(string key);
// ─── Events ───
protected void Emit<T>(string eventName, T data);
public void On(string eventName, Delegate handler);
public void Off(string eventName, Delegate handler);
// ─── Slots ───
protected void RegisterSlotContent(string name, VisualElement content,
Func<Dictionary<string, object>, VisualElement> builder); // called from the generated Build()
protected void BuildSlot(string name, Func<VisualElement, VisualElement> wrapper,
VisualElement container);
protected VisualElement GetSlotContainer(string name);
public VisualElement Slot(string name); // runtime access after Build()
}生命周期(顺序)
Constructor:
Created() → BeforeMounted() → Build() → LoadCompanionStyleSheets()
Deferred(schedule.Execute, next frame):
Mounted()
OnAttachToPanel:
ScheduleReactiveUpdates() → Updated() ~60 FPS
OnDetachFromPanel:
BeforeUnmounted() → _updateItem.Pause() → DisposeAllBindings() → Unmounted()
Updated()仅在元素挂载到面板上时运行(OnAttachToPanelHandler)。从构造函数/deferred 调用ScheduleReactiveUpdates的做法已被移除——挂载之前schedule可能不会触发。
Bind 辅助方法(响应式)
所有绑定都通过 ReactiveEffect 工作——自动订阅 Prop<T> / Computed<T>,并在任意来源变化时更新。 每个辅助方法都返回一个 WatchHandle(在 detach 时会被追踪并释放)。
// v-if: add/remove from DOM (reactive)
protected WatchHandle BindVisibility(VisualElement el, Func<bool> getter);
// v-show: toggle display (reactive)
protected WatchHandle BindShow(VisualElement el, Func<bool> getter);
// :text: bind string to Label (reactive)
protected WatchHandle BindText(Label label, Func<string> getter);
// :class: switch CSS class by condition (reactive)
protected WatchHandle BindClass(VisualElement el, string className, Func<bool> getter);
// v-for (generic, key-based diff, reactive)
protected WatchHandle BindList<T>(VisualElement container,
Func<IEnumerable<T>> source,
Func<T, int, VisualElement> itemBuilder,
Func<T, object> keySelector = null);
// v-for (generic, IEnumerable - typed item access, reactive)
protected WatchHandle BindListFor<T>(VisualElement container,
IEnumerable<T> source,
Func<T, int, VisualElement> itemBuilder,
Func<T, object> keySelector = null);v-model(双向绑定)
// Real BindModel overloads (SusComponent.Bind.cs):
BindModel(myTextField, NameProp); // TextField ↔ Prop<string>
BindModel(mySlider, VolumeProp); // Slider ↔ Prop<float>
BindModel(myToggle, MuteProp); // Toggle ↔ Prop<bool>
BindModel(myDropdown, ModeProp); // DropdownField ↔ Prop<string>组件之间的 Props
// Reactive bind on child Prop<T> (:prop="expr" in .sharq)
// case-insensitive, ReactiveEffect, auto-cleanup. Non-generic: getter returns object.
protected void BindChildProp(VisualElement child, string propName, Func<object> getter);
// Direct bind to a Prop<T> instance (old way, PascalCase-sensitive)
protected void BindProperty<T>(Prop<T> target, Func<T> getter);
// Literal prop (prop="value" in .sharq)
// case-insensitive, mutates .Value (does not replace Prop<T>)
internal static void SetChildProp(VisualElement el, string propName, object value);
// Scalar conversion: string→bool/int/float/enum/string
private static object ConvertScalar(object value, Type targetType);BindList(有状态的重新排序)
// Insert instead of Remove+Add - focus/scroll/input is not lost
// All three options (BindList<T>, BindListFor, BindListFor<T>) have been fixed.SusBootstrap
public static class SusBootstrap
{
// Mounts component T into the container.
// Loads the design-token cascade in order:
// _palette → _font → _theme → design-tokens → _icon → extras + OverlayHost
// (_global comes from SusDefault.tss / ApplyDefaultTSS — not this cascade.)
// When called for the first time, automatically creates an EventSystem (no InputModule).
public static T Mount<T>(VisualElement container) where T : SusComponent, new();
public static T Mount<T>(UIDocument uiDocument) where T : SusComponent, new();
// Cascade only (no component) — used by SusApp and manual UI.
public static void LoadTokenCascade(VisualElement container);
// Returns or creates the OverlayHost as the last child of the container.
public static OverlayHost GetOrCreateOverlay(VisualElement container);
// Finds or creates SusWorldSpacePanel + wires WorldSpaceService.Default
// (SusApp calls this automatically unless UseWorldSpace(false)).
public static SusWorldSpacePanel EnsureWorldSpacePanel(
Camera camera = null, OverlayHost overlayHost = null);
// Panel TSS (_palette + _font + _global via SusDefault.tss).
public static void ApplyDefaultTSS(UIDocument document);
}替代关系(v1 → v2)
| 旧(v1) | 新(v2) |
|---|---|
sus(UPM com.sus.sfc) | com.sharq-it.sus.core |
sharq-ui-system(SusCompiler.exe、LibSassHost) | SharqFileImporter(AssetPostprocessor) |
ElementBase —— 反射式 | SusComponent : VisualElement |
compiled ui/ —— 手动与自动混合 | generated/ —— 仅自动生成,已 .gitignored |
在生态系统中的位置
sus-core (this package)
├── sus-router — navigation (Push/Replace/Back, screens)
└── your Unity project — consumer app